common.c revision 106137
140939Sdes/*-
240939Sdes * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
340939Sdes * All rights reserved.
440939Sdes *
540939Sdes * Redistribution and use in source and binary forms, with or without
640939Sdes * modification, are permitted provided that the following conditions
740939Sdes * are met:
840939Sdes * 1. Redistributions of source code must retain the above copyright
940939Sdes *    notice, this list of conditions and the following disclaimer
1040939Sdes *    in this position and unchanged.
1140939Sdes * 2. Redistributions in binary form must reproduce the above copyright
1240939Sdes *    notice, this list of conditions and the following disclaimer in the
1340939Sdes *    documentation and/or other materials provided with the distribution.
1440939Sdes * 3. The name of the author may not be used to endorse or promote products
1540939Sdes *    derived from this software without specific prior written permission
1640939Sdes *
1740939Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1840939Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1940939Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2040939Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2140939Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2240939Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2340939Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2440939Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2540939Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2640939Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2740939Sdes */
2840939Sdes
2984203Sdillon#include <sys/cdefs.h>
3084203Sdillon__FBSDID("$FreeBSD: head/lib/libfetch/common.c 106137 2002-10-29 12:17:43Z obrien $");
3184203Sdillon
3241862Sdes#include <sys/param.h>
3340939Sdes#include <sys/socket.h>
3455557Sdes#include <sys/time.h>
3562981Sdes#include <sys/uio.h>
3640939Sdes#include <netinet/in.h>
3740939Sdes
3840939Sdes#include <errno.h>
3940939Sdes#include <netdb.h>
4060924Sdes#include <stdarg.h>
4141862Sdes#include <stdlib.h>
4241862Sdes#include <stdio.h>
4340939Sdes#include <string.h>
4440939Sdes#include <unistd.h>
4540939Sdes
4640939Sdes#include "fetch.h"
4740939Sdes#include "common.h"
4840939Sdes
4940975Sdes
5040939Sdes/*** Local data **************************************************************/
5140939Sdes
5240939Sdes/*
5340939Sdes * Error messages for resolver errors
5440939Sdes */
5540939Sdesstatic struct fetcherr _netdb_errlist[] = {
5690267Sdes	{ EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
5790267Sdes	{ EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
5890267Sdes	{ EAI_FAIL,	FETCH_RESOLV,	"Non-recoverable resolver failure" },
5990267Sdes	{ EAI_NONAME,	FETCH_RESOLV,	"No address record" },
6090267Sdes	{ -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
6140939Sdes};
6240939Sdes
6362981Sdes/* End-of-Line */
6475891Sarchiestatic const char ENDL[2] = "\r\n";
6540939Sdes
6662981Sdes
6740939Sdes/*** Error-reporting functions ***********************************************/
6840939Sdes
6940939Sdes/*
7040939Sdes * Map error code to string
7140939Sdes */
7260924Sdesstatic struct fetcherr *
7340975Sdes_fetch_finderr(struct fetcherr *p, int e)
7440939Sdes{
7590267Sdes	while (p->num != -1 && p->num != e)
7690267Sdes		p++;
7790267Sdes	return (p);
7840939Sdes}
7940939Sdes
8040939Sdes/*
8140939Sdes * Set error code
8240939Sdes */
8340939Sdesvoid
8440939Sdes_fetch_seterr(struct fetcherr *p, int e)
8540939Sdes{
8690267Sdes	p = _fetch_finderr(p, e);
8790267Sdes	fetchLastErrCode = p->cat;
8890267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
8940939Sdes}
9040939Sdes
9140939Sdes/*
9240939Sdes * Set error code according to errno
9340939Sdes */
9440939Sdesvoid
9540939Sdes_fetch_syserr(void)
9640939Sdes{
9790267Sdes	switch (errno) {
9890267Sdes	case 0:
9990267Sdes		fetchLastErrCode = FETCH_OK;
10090267Sdes		break;
10190267Sdes	case EPERM:
10290267Sdes	case EACCES:
10390267Sdes	case EROFS:
10490267Sdes	case EAUTH:
10590267Sdes	case ENEEDAUTH:
10690267Sdes		fetchLastErrCode = FETCH_AUTH;
10790267Sdes		break;
10890267Sdes	case ENOENT:
10990267Sdes	case EISDIR: /* XXX */
11090267Sdes		fetchLastErrCode = FETCH_UNAVAIL;
11190267Sdes		break;
11290267Sdes	case ENOMEM:
11390267Sdes		fetchLastErrCode = FETCH_MEMORY;
11490267Sdes		break;
11590267Sdes	case EBUSY:
11690267Sdes	case EAGAIN:
11790267Sdes		fetchLastErrCode = FETCH_TEMP;
11890267Sdes		break;
11990267Sdes	case EEXIST:
12090267Sdes		fetchLastErrCode = FETCH_EXISTS;
12190267Sdes		break;
12290267Sdes	case ENOSPC:
12390267Sdes		fetchLastErrCode = FETCH_FULL;
12490267Sdes		break;
12590267Sdes	case EADDRINUSE:
12690267Sdes	case EADDRNOTAVAIL:
12790267Sdes	case ENETDOWN:
12890267Sdes	case ENETUNREACH:
12990267Sdes	case ENETRESET:
13090267Sdes	case EHOSTUNREACH:
13190267Sdes		fetchLastErrCode = FETCH_NETWORK;
13290267Sdes		break;
13390267Sdes	case ECONNABORTED:
13490267Sdes	case ECONNRESET:
13590267Sdes		fetchLastErrCode = FETCH_ABORT;
13690267Sdes		break;
13790267Sdes	case ETIMEDOUT:
13890267Sdes		fetchLastErrCode = FETCH_TIMEOUT;
13990267Sdes		break;
14090267Sdes	case ECONNREFUSED:
14190267Sdes	case EHOSTDOWN:
14290267Sdes		fetchLastErrCode = FETCH_DOWN;
14390267Sdes		break;
14490267Sdesdefault:
14590267Sdes		fetchLastErrCode = FETCH_UNKNOWN;
14690267Sdes	}
14790267Sdes	snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
14840939Sdes}
14940939Sdes
15040939Sdes
15141862Sdes/*
15241862Sdes * Emit status message
15341862Sdes */
15460924Sdesvoid
15575891Sarchie_fetch_info(const char *fmt, ...)
15641862Sdes{
15790267Sdes	va_list ap;
15890267Sdes
15990267Sdes	va_start(ap, fmt);
16090267Sdes	vfprintf(stderr, fmt, ap);
16190267Sdes	va_end(ap);
16290267Sdes	fputc('\n', stderr);
16341862Sdes}
16441862Sdes
16541862Sdes
16640939Sdes/*** Network-related utility functions ***************************************/
16740939Sdes
16840939Sdes/*
16968551Sdes * Return the default port for a scheme
17068551Sdes */
17168551Sdesint
17275891Sarchie_fetch_default_port(const char *scheme)
17368551Sdes{
17490267Sdes	struct servent *se;
17568551Sdes
17690267Sdes	if ((se = getservbyname(scheme, "tcp")) != NULL)
17790267Sdes		return (ntohs(se->s_port));
17890267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
17990267Sdes		return (FTP_DEFAULT_PORT);
18090267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
18190267Sdes		return (HTTP_DEFAULT_PORT);
18290267Sdes	return (0);
18368551Sdes}
18468551Sdes
18568551Sdes/*
18668551Sdes * Return the default proxy port for a scheme
18768551Sdes */
18868551Sdesint
18975891Sarchie_fetch_default_proxy_port(const char *scheme)
19068551Sdes{
19190267Sdes	if (strcasecmp(scheme, SCHEME_FTP) == 0)
19290267Sdes		return (FTP_DEFAULT_PROXY_PORT);
19390267Sdes	if (strcasecmp(scheme, SCHEME_HTTP) == 0)
19490267Sdes		return (HTTP_DEFAULT_PROXY_PORT);
19590267Sdes	return (0);
19668551Sdes}
19768551Sdes
19898117Sdes
19968551Sdes/*
20097866Sdes * Create a connection for an existing descriptor.
20197866Sdes */
20297866Sdesconn_t *
20397866Sdes_fetch_reopen(int sd)
20497866Sdes{
20597866Sdes	conn_t *conn;
20697866Sdes
20797866Sdes	/* allocate and fill connection structure */
20897866Sdes	if ((conn = calloc(1, sizeof *conn)) == NULL)
20997866Sdes		return (NULL);
21097866Sdes	conn->sd = sd;
21198117Sdes	++conn->ref;
21297866Sdes	return (conn);
21397866Sdes}
21497866Sdes
21597866Sdes
21697866Sdes/*
21798117Sdes * Bump a connection's reference count.
21898117Sdes */
21998117Sdesconn_t *
22098117Sdes_fetch_ref(conn_t *conn)
22198117Sdes{
22298117Sdes
22398117Sdes	++conn->ref;
22498117Sdes	return (conn);
22598117Sdes}
22698117Sdes
22798117Sdes
22898117Sdes/*
22940939Sdes * Establish a TCP connection to the specified port on the specified host.
23040939Sdes */
23197856Sdesconn_t *
23275891Sarchie_fetch_connect(const char *host, int port, int af, int verbose)
23340939Sdes{
23497856Sdes	conn_t *conn;
23590267Sdes	char pbuf[10];
23690267Sdes	struct addrinfo hints, *res, *res0;
23790267Sdes	int sd, err;
23840939Sdes
23990267Sdes	DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
24041862Sdes
24190267Sdes	if (verbose)
24290267Sdes		_fetch_info("looking up %s", host);
24340939Sdes
24490267Sdes	/* look up host name and set up socket address structure */
24590267Sdes	snprintf(pbuf, sizeof(pbuf), "%d", port);
24690267Sdes	memset(&hints, 0, sizeof(hints));
24790267Sdes	hints.ai_family = af;
24890267Sdes	hints.ai_socktype = SOCK_STREAM;
24990267Sdes	hints.ai_protocol = 0;
25090267Sdes	if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
25190267Sdes		_netdb_seterr(err);
25297856Sdes		return (NULL);
25390267Sdes	}
25490267Sdes
25590267Sdes	if (verbose)
25690267Sdes		_fetch_info("connecting to %s:%d", host, port);
25790267Sdes
25890267Sdes	/* try to connect */
25990267Sdes	for (sd = -1, res = res0; res; res = res->ai_next) {
26090267Sdes		if ((sd = socket(res->ai_family, res->ai_socktype,
26162981Sdes			 res->ai_protocol)) == -1)
26290267Sdes			continue;
26390267Sdes		if (connect(sd, res->ai_addr, res->ai_addrlen) != -1)
26490267Sdes			break;
26590267Sdes		close(sd);
26690267Sdes		sd = -1;
26790267Sdes	}
26890267Sdes	freeaddrinfo(res0);
26990267Sdes	if (sd == -1) {
27090267Sdes		_fetch_syserr();
27197856Sdes		return (NULL);
27290267Sdes	}
27340939Sdes
274103459Sfenner	if ((conn = _fetch_reopen(sd)) == NULL) {
275103459Sfenner		_fetch_syserr();
27697856Sdes		close(sd);
277103459Sfenner	}
27897856Sdes	return (conn);
27940939Sdes}
28041989Sdes
28141989Sdes
28255557Sdes/*
28397868Sdes * Enable SSL on a connection.
28497868Sdes */
28597868Sdesint
28697868Sdes_fetch_ssl(conn_t *conn, int verbose)
28797868Sdes{
28897868Sdes
28997891Sdes#ifdef WITH_SSL
29097868Sdes	/* Init the SSL library and context */
29197868Sdes	if (!SSL_library_init()){
29297868Sdes		fprintf(stderr, "SSL library init failed\n");
29397868Sdes		return (-1);
29497868Sdes	}
29597868Sdes
29697868Sdes	SSL_load_error_strings();
29797868Sdes
29897868Sdes	conn->ssl_meth = SSLv23_client_method();
29997868Sdes	conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
30097868Sdes
30197868Sdes	conn->ssl = SSL_new(conn->ssl_ctx);
30297868Sdes	if (conn->ssl == NULL){
30397868Sdes		fprintf(stderr, "SSL context creation failed\n");
30497868Sdes		return (-1);
30597868Sdes	}
30697868Sdes	SSL_set_fd(conn->ssl, conn->sd);
30797868Sdes	if (SSL_connect(conn->ssl) == -1){
30897868Sdes		ERR_print_errors_fp(stderr);
30997868Sdes		return (-1);
31097868Sdes	}
31197868Sdes
31297868Sdes	if (verbose) {
31397868Sdes		X509_NAME *name;
31497868Sdes		char *str;
31597868Sdes
31697868Sdes		fprintf(stderr, "SSL connection established using %s\n",
31797868Sdes		    SSL_get_cipher(conn->ssl));
31897868Sdes		conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
31997868Sdes		name = X509_get_subject_name(conn->ssl_cert);
32097868Sdes		str = X509_NAME_oneline(name, 0, 0);
32197868Sdes		printf("Certificate subject: %s\n", str);
32297868Sdes		free(str);
32397868Sdes		name = X509_get_issuer_name(conn->ssl_cert);
32497868Sdes		str = X509_NAME_oneline(name, 0, 0);
32597868Sdes		printf("Certificate issuer: %s\n", str);
32697868Sdes		free(str);
32797868Sdes	}
32897868Sdes
32997868Sdes	return (0);
33097891Sdes#else
33197891Sdes	(void)conn;
33297891Sdes	(void)verbose;
33397891Sdes	fprintf(stderr, "SSL support disabled\n");
33497891Sdes	return (-1);
33597891Sdes#endif
33697868Sdes}
33797868Sdes
33898117Sdes
33997868Sdes/*
34097866Sdes * Read a character from a connection w/ timeout
34155557Sdes */
34297866Sdesssize_t
34397866Sdes_fetch_read(conn_t *conn, char *buf, size_t len)
34455557Sdes{
34590267Sdes	struct timeval now, timeout, wait;
34690267Sdes	fd_set readfds;
34797866Sdes	ssize_t rlen, total;
34890267Sdes	int r;
34990267Sdes
350106137Sobrien	rlen = 0;
351106137Sobrien
35255557Sdes	if (fetchTimeout) {
35397866Sdes		FD_ZERO(&readfds);
35490267Sdes		gettimeofday(&timeout, NULL);
35590267Sdes		timeout.tv_sec += fetchTimeout;
35655557Sdes	}
35790267Sdes
35897866Sdes	total = 0;
35997866Sdes	while (len > 0) {
36097866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
36197856Sdes			FD_SET(conn->sd, &readfds);
36290267Sdes			gettimeofday(&now, NULL);
36390267Sdes			wait.tv_sec = timeout.tv_sec - now.tv_sec;
36490267Sdes			wait.tv_usec = timeout.tv_usec - now.tv_usec;
36590267Sdes			if (wait.tv_usec < 0) {
36690267Sdes				wait.tv_usec += 1000000;
36790267Sdes				wait.tv_sec--;
36890267Sdes			}
369106137Sobrien			if (wait.tv_sec < 0)
370106137Sobrien				return (rlen);
37197866Sdes			errno = 0;
37297856Sdes			r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
37390267Sdes			if (r == -1) {
37490267Sdes				if (errno == EINTR && fetchRestartCalls)
37590267Sdes					continue;
37690267Sdes				return (-1);
37790267Sdes			}
37890267Sdes		}
37997891Sdes#ifdef WITH_SSL
38097866Sdes		if (conn->ssl != NULL)
38197866Sdes			rlen = SSL_read(conn->ssl, buf, len);
38297866Sdes		else
38397891Sdes#endif
38497866Sdes			rlen = read(conn->sd, buf, len);
385106049Sdes		if (rlen == 0)
386106049Sdes			break;
38797866Sdes		if (rlen < 0) {
38890267Sdes			if (errno == EINTR && fetchRestartCalls)
38990267Sdes				continue;
39090267Sdes			return (-1);
39190267Sdes		}
39297866Sdes		len -= rlen;
39397866Sdes		buf += rlen;
39497866Sdes		total += rlen;
39597866Sdes	}
39697866Sdes	return (total);
39797866Sdes}
39897866Sdes
39998117Sdes
40097866Sdes/*
40197866Sdes * Read a line of text from a connection w/ timeout
40297866Sdes */
40397866Sdes#define MIN_BUF_SIZE 1024
40497866Sdes
40597866Sdesint
40697866Sdes_fetch_getln(conn_t *conn)
40797866Sdes{
40897866Sdes	char *tmp;
40997866Sdes	size_t tmpsize;
41097866Sdes	char c;
411106137Sobrien	int error;
41297866Sdes
41397866Sdes	if (conn->buf == NULL) {
41497866Sdes		if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
41597866Sdes			errno = ENOMEM;
41697866Sdes			return (-1);
41797866Sdes		}
41897866Sdes		conn->bufsize = MIN_BUF_SIZE;
41997866Sdes	}
42097866Sdes
42197866Sdes	conn->buf[0] = '\0';
42297866Sdes	conn->buflen = 0;
42397866Sdes
42497866Sdes	do {
425106137Sobrien		error = _fetch_read(conn, &c, 1);
426106137Sobrien		if (error == -1)
42797866Sdes			return (-1);
428106137Sobrien		else if (error == 0)
429106137Sobrien			break;
43097856Sdes		conn->buf[conn->buflen++] = c;
43197856Sdes		if (conn->buflen == conn->bufsize) {
43297856Sdes			tmp = conn->buf;
43397856Sdes			tmpsize = conn->bufsize * 2 + 1;
43497856Sdes			if ((tmp = realloc(tmp, tmpsize)) == NULL) {
43590267Sdes				errno = ENOMEM;
43690267Sdes				return (-1);
43790267Sdes			}
43897856Sdes			conn->buf = tmp;
43997856Sdes			conn->bufsize = tmpsize;
44090267Sdes		}
44190267Sdes	} while (c != '\n');
44290267Sdes
44397856Sdes	conn->buf[conn->buflen] = '\0';
44497856Sdes	DEBUG(fprintf(stderr, "<<< %s", conn->buf));
44590267Sdes	return (0);
44655557Sdes}
44755557Sdes
44855557Sdes
44962981Sdes/*
45097866Sdes * Write to a connection w/ timeout
45162981Sdes */
45297866Sdesssize_t
45397866Sdes_fetch_write(conn_t *conn, const char *buf, size_t len)
45497866Sdes{
45597866Sdes	struct timeval now, timeout, wait;
45697866Sdes	fd_set writefds;
45797866Sdes	ssize_t wlen, total;
45897866Sdes	int r;
45997866Sdes
460106137Sobrien	total = 0;
461106137Sobrien
46297866Sdes	if (fetchTimeout) {
46397866Sdes		FD_ZERO(&writefds);
46497866Sdes		gettimeofday(&timeout, NULL);
46597866Sdes		timeout.tv_sec += fetchTimeout;
46697866Sdes	}
46797866Sdes
468106137Sobrien	while (len > 0) {
46997866Sdes		while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
47097866Sdes			FD_SET(conn->sd, &writefds);
47197866Sdes			gettimeofday(&now, NULL);
47297866Sdes			wait.tv_sec = timeout.tv_sec - now.tv_sec;
47397866Sdes			wait.tv_usec = timeout.tv_usec - now.tv_usec;
47497866Sdes			if (wait.tv_usec < 0) {
47597866Sdes				wait.tv_usec += 1000000;
47697866Sdes				wait.tv_sec--;
47797866Sdes			}
47897866Sdes			if (wait.tv_sec < 0) {
47997866Sdes				errno = ETIMEDOUT;
48097866Sdes				return (-1);
48197866Sdes			}
48297866Sdes			errno = 0;
48397866Sdes			r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
48497866Sdes			if (r == -1) {
48597866Sdes				if (errno == EINTR && fetchRestartCalls)
48697866Sdes					continue;
48797866Sdes				return (-1);
48897866Sdes			}
48997866Sdes		}
49097866Sdes		errno = 0;
49197891Sdes#ifdef WITH_SSL
49297866Sdes		if (conn->ssl != NULL)
493106137Sobrien			wlen = SSL_write(conn->ssl, buf, len);
49497866Sdes		else
49597891Sdes#endif
496106137Sobrien			wlen = write(conn->sd, buf, len);
497106137Sobrien		if (wlen == 0)
49897866Sdes			/* we consider a short write a failure */
49997866Sdes			return (-1);
50097866Sdes		if (wlen < 0) {
50197866Sdes			if (errno == EINTR && fetchRestartCalls)
50297866Sdes				continue;
50397866Sdes			return (-1);
50497866Sdes		}
505106137Sobrien		len -= wlen;
506106137Sobrien		buf += wlen;
50797866Sdes		total += wlen;
50897866Sdes	}
50997866Sdes	return (total);
51097866Sdes}
51197866Sdes
51298117Sdes
51397866Sdes/*
51497866Sdes * Write a line of text to a connection w/ timeout
51597866Sdes */
51662981Sdesint
51797856Sdes_fetch_putln(conn_t *conn, const char *str, size_t len)
51862981Sdes{
51998748Sdes
52098748Sdes	DEBUG(fprintf(stderr, ">>> %s\n", str));
521106137Sobrien	if (_fetch_write(conn, str, len) == -1 ||
522106137Sobrien	    _fetch_write(conn, ENDL, sizeof ENDL) == -1)
52390267Sdes		return (-1);
52490267Sdes	return (0);
52562981Sdes}
52662981Sdes
52762981Sdes
52897856Sdes/*
52997856Sdes * Close connection
53097856Sdes */
53197856Sdesint
53297856Sdes_fetch_close(conn_t *conn)
53397856Sdes{
53497856Sdes	int ret;
53597856Sdes
53698117Sdes	if (--conn->ref > 0)
53798117Sdes		return (0);
53897856Sdes	ret = close(conn->sd);
53997856Sdes	free(conn);
54097856Sdes	return (ret);
54197856Sdes}
54297856Sdes
54397856Sdes
54441989Sdes/*** Directory-related utility functions *************************************/
54541989Sdes
54641989Sdesint
54741989Sdes_fetch_add_entry(struct url_ent **p, int *size, int *len,
54890267Sdes    const char *name, struct url_stat *us)
54941989Sdes{
55090267Sdes	struct url_ent *tmp;
55141989Sdes
55290267Sdes	if (*p == NULL) {
55390268Sdes		*size = 0;
55490267Sdes		*len = 0;
55541989Sdes	}
55641989Sdes
55790267Sdes	if (*len >= *size - 1) {
55890268Sdes		tmp = realloc(*p, (*size * 2 + 1) * sizeof **p);
55990267Sdes		if (tmp == NULL) {
56090267Sdes			errno = ENOMEM;
56190267Sdes			_fetch_syserr();
56290267Sdes			return (-1);
56390267Sdes		}
56490268Sdes		*size = (*size * 2 + 1);
56590267Sdes		*p = tmp;
56690267Sdes	}
56741989Sdes
56890267Sdes	tmp = *p + *len;
56990267Sdes	snprintf(tmp->name, PATH_MAX, "%s", name);
57090267Sdes	bcopy(us, &tmp->stat, sizeof *us);
57141989Sdes
57290267Sdes	(*len)++;
57390267Sdes	(++tmp)->name[0] = 0;
57490267Sdes
57590267Sdes	return (0);
57641989Sdes}
577