xref: /onnv-gate/usr/src/cmd/backup/restore/utilities.c (revision 1053:667012633a0d)
10Sstevel@tonic-gate /*
2*1053Smaheshvs  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
70Sstevel@tonic-gate /*	  All Rights Reserved	*/
80Sstevel@tonic-gate 
90Sstevel@tonic-gate /*
100Sstevel@tonic-gate  * Copyright (c) 1983 Regents of the University of California.
110Sstevel@tonic-gate  * All rights reserved.  The Berkeley software License Agreement
120Sstevel@tonic-gate  * specifies the terms and conditions for redistribution.
130Sstevel@tonic-gate  */
140Sstevel@tonic-gate 
150Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
160Sstevel@tonic-gate 
170Sstevel@tonic-gate #include "restore.h"
180Sstevel@tonic-gate #include <ctype.h>
190Sstevel@tonic-gate #include <errno.h>
200Sstevel@tonic-gate #include <syslog.h>
210Sstevel@tonic-gate #include <limits.h>
220Sstevel@tonic-gate /* LINTED: this file really is necessary */
230Sstevel@tonic-gate #include <euc.h>
240Sstevel@tonic-gate #include <widec.h>
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * Insure that all the components of a pathname exist. Note that
280Sstevel@tonic-gate  * lookupname() and addentry() both expect complex names as
290Sstevel@tonic-gate  * input arguments, so a double NULL needs to be added to each name.
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate void
pathcheck(char * name)32*1053Smaheshvs pathcheck(char *name)
330Sstevel@tonic-gate {
340Sstevel@tonic-gate 	char *cp, save;
350Sstevel@tonic-gate 	struct entry *ep;
360Sstevel@tonic-gate 	char *start;
370Sstevel@tonic-gate 
380Sstevel@tonic-gate 	start = strchr(name, '/');
390Sstevel@tonic-gate 	if (start == 0)
400Sstevel@tonic-gate 		return;
410Sstevel@tonic-gate 	for (cp = start; *cp != '\0'; cp++) {
420Sstevel@tonic-gate 		if (*cp != '/')
430Sstevel@tonic-gate 			continue;
440Sstevel@tonic-gate 		*cp = '\0';
450Sstevel@tonic-gate 		save = *(cp+1);
460Sstevel@tonic-gate 		*(cp+1) = '\0';
470Sstevel@tonic-gate 		ep = lookupname(name);
480Sstevel@tonic-gate 		if (ep == NIL) {
490Sstevel@tonic-gate 			ep = addentry(name, psearch(name), NODE);
500Sstevel@tonic-gate 			newnode(ep);
510Sstevel@tonic-gate 		}
520Sstevel@tonic-gate 		/* LINTED: result fits in a short */
530Sstevel@tonic-gate 		ep->e_flags |= NEW|KEEP;
540Sstevel@tonic-gate 		*cp = '/';
550Sstevel@tonic-gate 		*(cp+1) = save;
560Sstevel@tonic-gate 	}
570Sstevel@tonic-gate }
580Sstevel@tonic-gate 
590Sstevel@tonic-gate /*
600Sstevel@tonic-gate  * Change a name to a unique temporary name.
610Sstevel@tonic-gate  */
620Sstevel@tonic-gate void
mktempname(struct entry * ep)63*1053Smaheshvs mktempname(struct entry *ep)
640Sstevel@tonic-gate {
650Sstevel@tonic-gate 	char *newname;
660Sstevel@tonic-gate 
670Sstevel@tonic-gate 	if (ep->e_flags & TMPNAME)
680Sstevel@tonic-gate 		badentry(ep, gettext("mktempname: called with TMPNAME"));
690Sstevel@tonic-gate 	/* LINTED: result fits in a short */
700Sstevel@tonic-gate 	ep->e_flags |= TMPNAME;
710Sstevel@tonic-gate 	newname = savename(gentempname(ep));
720Sstevel@tonic-gate 	renameit(myname(ep), newname);
730Sstevel@tonic-gate 	freename(ep->e_name);
740Sstevel@tonic-gate 	ep->e_name = newname;
750Sstevel@tonic-gate 	/* LINTED: savename guarantees strlen will fit */
760Sstevel@tonic-gate 	ep->e_namlen = strlen(ep->e_name);
770Sstevel@tonic-gate }
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /*
800Sstevel@tonic-gate  * Generate a temporary name for an entry.
810Sstevel@tonic-gate  */
820Sstevel@tonic-gate char *
gentempname(struct entry * ep)83*1053Smaheshvs gentempname(struct entry *ep)
840Sstevel@tonic-gate {
850Sstevel@tonic-gate 	static char name[MAXPATHLEN];
860Sstevel@tonic-gate 	struct entry *np;
870Sstevel@tonic-gate 	long i = 0;
880Sstevel@tonic-gate 
890Sstevel@tonic-gate 	for (np = lookupino(ep->e_ino); np != NIL && np != ep; np = np->e_links)
900Sstevel@tonic-gate 		i++;
910Sstevel@tonic-gate 	if (np == NIL)
920Sstevel@tonic-gate 		badentry(ep, gettext("not on ino list"));
930Sstevel@tonic-gate 	(void) snprintf(name, sizeof (name), "%s%ld%lu", TMPHDR, i, ep->e_ino);
940Sstevel@tonic-gate 	return (name);
950Sstevel@tonic-gate }
960Sstevel@tonic-gate 
970Sstevel@tonic-gate /*
980Sstevel@tonic-gate  * Rename a file or directory.
990Sstevel@tonic-gate  */
1000Sstevel@tonic-gate void
renameit(char * fp,char * tp)101*1053Smaheshvs renameit(char *fp, char *tp)
1020Sstevel@tonic-gate {
1030Sstevel@tonic-gate 	int fromfd, tofd;
1040Sstevel@tonic-gate 	char *from, *to;
1050Sstevel@tonic-gate 	char tobuf[MAXPATHLEN];
1060Sstevel@tonic-gate 	char *pathend;
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate 	resolve(fp, &fromfd, &from);
1090Sstevel@tonic-gate 	/*
1100Sstevel@tonic-gate 	 * The to pointer argument is assumed to be either a fully
1110Sstevel@tonic-gate 	 * specified path (starting with "./") or a simple temporary
1120Sstevel@tonic-gate 	 * file name (starting with TMPHDR).  If passed a simple temp
1130Sstevel@tonic-gate 	 * file name, we need to set up the descriptors explicitly.
1140Sstevel@tonic-gate 	 */
1150Sstevel@tonic-gate 	if (strncmp(tp, TMPHDR, sizeof (TMPHDR) - 1) == 0) {
1160Sstevel@tonic-gate 		tofd = fromfd;
1170Sstevel@tonic-gate 		if ((pathend = strrchr(from, '/')) != NULL) {
1180Sstevel@tonic-gate 			strncpy(tobuf, from, pathend - from + 1);
1190Sstevel@tonic-gate 			tobuf[pathend - from + 1] = NULL;
1200Sstevel@tonic-gate 			strlcat(tobuf, tp, sizeof (tobuf));
1210Sstevel@tonic-gate 			to = tobuf;
1220Sstevel@tonic-gate 		} else {
1230Sstevel@tonic-gate 			to = tp;
1240Sstevel@tonic-gate 		}
1250Sstevel@tonic-gate 	} else
1260Sstevel@tonic-gate 		resolve(tp, &tofd, &to);
1270Sstevel@tonic-gate 	if (renameat(fromfd, from, tofd, to) < 0) {
1280Sstevel@tonic-gate 		int saverr = errno;
1290Sstevel@tonic-gate 		(void) fprintf(stderr,
1300Sstevel@tonic-gate 		    gettext("Warning: cannot rename %s to %s: %s\n"),
1310Sstevel@tonic-gate 		    from, to, strerror(saverr));
1320Sstevel@tonic-gate 		(void) fflush(stderr);
1330Sstevel@tonic-gate 	} else {
1340Sstevel@tonic-gate 		vprintf(stdout, gettext("rename %s to %s\n"), from, to);
1350Sstevel@tonic-gate 	}
1360Sstevel@tonic-gate 	if (fromfd != AT_FDCWD) (void) close(fromfd);
1370Sstevel@tonic-gate 	if (tofd != AT_FDCWD) (void) close(tofd);
1380Sstevel@tonic-gate }
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate /*
1410Sstevel@tonic-gate  * Create a new node (directory). Note that, because we have no
1420Sstevel@tonic-gate  * mkdirat() function, fchdir() must be used set up the appropriate
1430Sstevel@tonic-gate  * name space context prior to the call to mkdir() if we are
1440Sstevel@tonic-gate  * operating in attribute space.
1450Sstevel@tonic-gate  */
1460Sstevel@tonic-gate void
newnode(struct entry * np)147*1053Smaheshvs newnode(struct entry *np)
1480Sstevel@tonic-gate {
1490Sstevel@tonic-gate 	char *cp;
1500Sstevel@tonic-gate 	int dfd;
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 	if (np->e_type != NODE)
1530Sstevel@tonic-gate 		badentry(np, gettext("newnode: not a node"));
1540Sstevel@tonic-gate 	resolve(myname(np), &dfd, &cp);
1550Sstevel@tonic-gate 	if (dfd != AT_FDCWD) {
1560Sstevel@tonic-gate 		if (fchdir(dfd) < 0) {
1570Sstevel@tonic-gate 			int saverr = errno;
1580Sstevel@tonic-gate 			(void) fprintf(stderr,
1590Sstevel@tonic-gate 				gettext("Warning: cannot create %s: %s"),
1600Sstevel@tonic-gate 				cp, strerror(saverr));
1610Sstevel@tonic-gate 			(void) fflush(stderr);
1620Sstevel@tonic-gate 			(void) close(dfd);
1630Sstevel@tonic-gate 			return;
1640Sstevel@tonic-gate 		}
1650Sstevel@tonic-gate 	}
1660Sstevel@tonic-gate 	if (mkdir(cp, 0777) < 0) {
1670Sstevel@tonic-gate 		int saverr = errno;
1680Sstevel@tonic-gate 		/* LINTED: result fits in a short */
1690Sstevel@tonic-gate 		np->e_flags |= EXISTED;
1700Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("Warning: "));
1710Sstevel@tonic-gate 		(void) fflush(stderr);
1720Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: %s\n", cp, strerror(saverr));
1730Sstevel@tonic-gate 	} else {
1740Sstevel@tonic-gate 		vprintf(stdout, gettext("Make node %s\n"), cp);
1750Sstevel@tonic-gate 	}
1760Sstevel@tonic-gate 	if (dfd != AT_FDCWD) {
1770Sstevel@tonic-gate 		fchdir(savepwd);
1780Sstevel@tonic-gate 		(void) close(dfd);
1790Sstevel@tonic-gate 	}
1800Sstevel@tonic-gate }
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate /*
1830Sstevel@tonic-gate  * Remove an old node (directory). See comment above on newnode()
1840Sstevel@tonic-gate  * for explanation of fchdir() use below.
1850Sstevel@tonic-gate  */
1860Sstevel@tonic-gate void
removenode(struct entry * ep)187*1053Smaheshvs removenode(struct entry *ep)
1880Sstevel@tonic-gate {
1890Sstevel@tonic-gate 	char *cp;
1900Sstevel@tonic-gate 	int dfd;
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	if (ep->e_type != NODE)
1930Sstevel@tonic-gate 		badentry(ep, gettext("removenode: not a node"));
1940Sstevel@tonic-gate 	if (ep->e_entries != NIL)
1950Sstevel@tonic-gate 		badentry(ep, gettext("removenode: non-empty directory"));
1960Sstevel@tonic-gate 	/* LINTED: result fits in a short */
1970Sstevel@tonic-gate 	ep->e_flags |= REMOVED;
1980Sstevel@tonic-gate 	/* LINTED: result fits in a short */
1990Sstevel@tonic-gate 	ep->e_flags &= ~TMPNAME;
2000Sstevel@tonic-gate 	resolve(myname(ep), &dfd, &cp);
2010Sstevel@tonic-gate 	if (dfd != AT_FDCWD) {
2020Sstevel@tonic-gate 		if (fchdir(dfd) < 0) {
2030Sstevel@tonic-gate 			int saverr = errno;
2040Sstevel@tonic-gate 			(void) fprintf(stderr,
2050Sstevel@tonic-gate 				gettext("Warning: cannot remove %s: %s"),
2060Sstevel@tonic-gate 				cp, strerror(saverr));
2070Sstevel@tonic-gate 			(void) fflush(stderr);
2080Sstevel@tonic-gate 			(void) close(dfd);
2090Sstevel@tonic-gate 			return;
2100Sstevel@tonic-gate 		}
2110Sstevel@tonic-gate 	}
2120Sstevel@tonic-gate 	if (rmdir(cp) < 0) {	/* NOTE: could use unlinkat (..,REMOVEDIR) */
2130Sstevel@tonic-gate 		int saverr = errno;
2140Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("Warning: %s: %s\n"),
2150Sstevel@tonic-gate 			cp, strerror(saverr));
2160Sstevel@tonic-gate 		(void) fflush(stderr);
2170Sstevel@tonic-gate 	} else {
2180Sstevel@tonic-gate 		vprintf(stdout, gettext("Remove node %s\n"), cp);
2190Sstevel@tonic-gate 	}
2200Sstevel@tonic-gate 	if (dfd != AT_FDCWD) {
2210Sstevel@tonic-gate 		(void) fchdir(savepwd);
2220Sstevel@tonic-gate 		(void) close(dfd);
2230Sstevel@tonic-gate 	}
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate /*
2270Sstevel@tonic-gate  * Remove a leaf.
2280Sstevel@tonic-gate  */
2290Sstevel@tonic-gate void
removeleaf(struct entry * ep)230*1053Smaheshvs removeleaf(struct entry *ep)
2310Sstevel@tonic-gate {
2320Sstevel@tonic-gate 	char *cp;
2330Sstevel@tonic-gate 	int dfd;
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	if (ep->e_type != LEAF)
2360Sstevel@tonic-gate 		badentry(ep, gettext("removeleaf: not a leaf"));
2370Sstevel@tonic-gate 	/* LINTED: result fits in a short */
2380Sstevel@tonic-gate 	ep->e_flags |= REMOVED;
2390Sstevel@tonic-gate 	/* LINTED: result fits in a short */
2400Sstevel@tonic-gate 	ep->e_flags &= ~TMPNAME;
2410Sstevel@tonic-gate 	resolve(myname(ep), &dfd, &cp);
2420Sstevel@tonic-gate 	if (unlinkat(dfd, cp, 0) < 0) {
2430Sstevel@tonic-gate 		int saverr = errno;
2440Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("Warning: %s: %s\n"),
2450Sstevel@tonic-gate 			cp, strerror(saverr));
2460Sstevel@tonic-gate 		(void) fflush(stderr);
2470Sstevel@tonic-gate 	} else {
2480Sstevel@tonic-gate 		vprintf(stdout, gettext("Remove leaf %s\n"), cp);
2490Sstevel@tonic-gate 	}
2500Sstevel@tonic-gate 	if (dfd != AT_FDCWD)
2510Sstevel@tonic-gate 		(void) close(dfd);
2520Sstevel@tonic-gate }
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate /*
2550Sstevel@tonic-gate  * Create a link.
2560Sstevel@tonic-gate  *	This function assumes that the context has already been set
2570Sstevel@tonic-gate  *	for the link file to be created (i.e., we have "fchdir-ed"
2580Sstevel@tonic-gate  *	into attribute space already if this is an attribute link).
2590Sstevel@tonic-gate  */
260*1053Smaheshvs int
lf_linkit(char * existing,char * new,int type)261*1053Smaheshvs lf_linkit(char *existing, char *new, int type)
2620Sstevel@tonic-gate {
2630Sstevel@tonic-gate 	char linkbuf[MAXPATHLEN];
2640Sstevel@tonic-gate 	struct stat64 s1[1], s2[1];
2650Sstevel@tonic-gate 	char *name;
2660Sstevel@tonic-gate 	int dfd, l, result;
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 	resolve(existing, &dfd, &name);
2690Sstevel@tonic-gate 	if (dfd == -1) {
2700Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
2710Sstevel@tonic-gate 			"Warning: cannot restore %s link %s->%s\n"),
2720Sstevel@tonic-gate 			(type == SYMLINK ? "symbolic" : "hard"), new, existing);
2730Sstevel@tonic-gate 		result = FAIL;
2740Sstevel@tonic-gate 		goto out;
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 	if (type == SYMLINK) {
2770Sstevel@tonic-gate 		if (symlink(name, new) < 0) {
2780Sstevel@tonic-gate 			/* No trailing \0 from readlink(2) */
2790Sstevel@tonic-gate 			if (((l = readlink(new, linkbuf, sizeof (linkbuf)))
2800Sstevel@tonic-gate 			    > 0) &&
2810Sstevel@tonic-gate 			    (l == strlen(name)) &&
2820Sstevel@tonic-gate 			    (strncmp(linkbuf, name, l) == 0)) {
2830Sstevel@tonic-gate 				vprintf(stdout,
2840Sstevel@tonic-gate 				    gettext("Symbolic link %s->%s ok\n"),
2850Sstevel@tonic-gate 				    new, name);
2860Sstevel@tonic-gate 				result = GOOD;
2870Sstevel@tonic-gate 				goto out;
2880Sstevel@tonic-gate 			} else {
2890Sstevel@tonic-gate 				int saverr = errno;
2900Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
2910Sstevel@tonic-gate 			    "Warning: cannot create symbolic link %s->%s: %s"),
2920Sstevel@tonic-gate 				    new, name, strerror(saverr));
2930Sstevel@tonic-gate 				(void) fflush(stderr);
2940Sstevel@tonic-gate 				result = FAIL;
2950Sstevel@tonic-gate 				goto out;
2960Sstevel@tonic-gate 			}
2970Sstevel@tonic-gate 		}
2980Sstevel@tonic-gate 	} else if (type == HARDLINK) {
2990Sstevel@tonic-gate 		if (link(name, new) < 0) {
3000Sstevel@tonic-gate 			int saverr = errno;
3010Sstevel@tonic-gate 			if ((stat64(name, s1) == 0) &&
3020Sstevel@tonic-gate 			    (stat64(new, s2) == 0) &&
3030Sstevel@tonic-gate 			    (s1->st_dev == s2->st_dev) &&
3040Sstevel@tonic-gate 			    (s1->st_ino == s2->st_ino)) {
3050Sstevel@tonic-gate 				vprintf(stdout,
3060Sstevel@tonic-gate 				    gettext("Hard link %s->%s ok\n"),
3070Sstevel@tonic-gate 				    new, name);
3080Sstevel@tonic-gate 				result = GOOD;
3090Sstevel@tonic-gate 				goto out;
3100Sstevel@tonic-gate 			} else {
3110Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
3120Sstevel@tonic-gate 			    "Warning: cannot create hard link %s->%s: %s\n"),
3130Sstevel@tonic-gate 				    new, name, strerror(saverr));
3140Sstevel@tonic-gate 				(void) fflush(stderr);
3150Sstevel@tonic-gate 				result = FAIL;
3160Sstevel@tonic-gate 				goto out;
3170Sstevel@tonic-gate 			}
3180Sstevel@tonic-gate 		}
3190Sstevel@tonic-gate 	} else {
3200Sstevel@tonic-gate 		panic(gettext("%s: unknown type %d\n"), "linkit", type);
3210Sstevel@tonic-gate 		result = FAIL;
3220Sstevel@tonic-gate 		goto out;
3230Sstevel@tonic-gate 	}
3240Sstevel@tonic-gate 	result = GOOD;
3250Sstevel@tonic-gate 	if (type == SYMLINK)
3260Sstevel@tonic-gate 		vprintf(stdout, gettext("Create symbolic link %s->%s\n"),
3270Sstevel@tonic-gate 		    new, name);
3280Sstevel@tonic-gate 	else
3290Sstevel@tonic-gate 		vprintf(stdout, gettext("Create hard link %s->%s\n"),
3300Sstevel@tonic-gate 		    new, name);
3310Sstevel@tonic-gate out:
3320Sstevel@tonic-gate 	if (dfd != AT_FDCWD) {
3330Sstevel@tonic-gate 		(void) close(dfd);
3340Sstevel@tonic-gate 	}
3350Sstevel@tonic-gate 	return (result);
3360Sstevel@tonic-gate }
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate /*
3390Sstevel@tonic-gate  * Find lowest-numbered inode (above "start") that needs to be extracted.
3400Sstevel@tonic-gate  * Caller knows that a return value of maxino means there's nothing left.
3410Sstevel@tonic-gate  */
3420Sstevel@tonic-gate ino_t
lowerbnd(ino_t start)343*1053Smaheshvs lowerbnd(ino_t start)
3440Sstevel@tonic-gate {
3450Sstevel@tonic-gate 	struct entry *ep;
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	for (; start < maxino; start++) {
3480Sstevel@tonic-gate 		ep = lookupino(start);
3490Sstevel@tonic-gate 		if (ep == NIL || ep->e_type == NODE)
3500Sstevel@tonic-gate 			continue;
3510Sstevel@tonic-gate 		if (ep->e_flags & (NEW|EXTRACT))
3520Sstevel@tonic-gate 			return (start);
3530Sstevel@tonic-gate 	}
3540Sstevel@tonic-gate 	return (start);
3550Sstevel@tonic-gate }
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate /*
3580Sstevel@tonic-gate  * Find highest-numbered inode (below "start") that needs to be extracted.
3590Sstevel@tonic-gate  */
3600Sstevel@tonic-gate ino_t
upperbnd(ino_t start)361*1053Smaheshvs upperbnd(ino_t start)
3620Sstevel@tonic-gate {
3630Sstevel@tonic-gate 	struct entry *ep;
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	for (; start > ROOTINO; start--) {
3660Sstevel@tonic-gate 		ep = lookupino(start);
3670Sstevel@tonic-gate 		if (ep == NIL || ep->e_type == NODE)
3680Sstevel@tonic-gate 			continue;
3690Sstevel@tonic-gate 		if (ep->e_flags & (NEW|EXTRACT))
3700Sstevel@tonic-gate 			return (start);
3710Sstevel@tonic-gate 	}
3720Sstevel@tonic-gate 	return (start);
3730Sstevel@tonic-gate }
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate /*
3760Sstevel@tonic-gate  * report on a badly formed entry
3770Sstevel@tonic-gate  */
3780Sstevel@tonic-gate void
badentry(struct entry * ep,char * msg)379*1053Smaheshvs badentry(struct entry *ep, char *msg)
3800Sstevel@tonic-gate {
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("bad entry: %s\n"), msg);
3830Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("name: %s\n"), myname(ep));
3840Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("parent name %s\n"),
3850Sstevel@tonic-gate 		myname(ep->e_parent));
3860Sstevel@tonic-gate 	if (ep->e_sibling != NIL)
3870Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("sibling name: %s\n"),
3880Sstevel@tonic-gate 		    myname(ep->e_sibling));
3890Sstevel@tonic-gate 	if (ep->e_entries != NIL)
3900Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("next entry name: %s\n"),
3910Sstevel@tonic-gate 		    myname(ep->e_entries));
3920Sstevel@tonic-gate 	if (ep->e_links != NIL)
3930Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("next link name: %s\n"),
3940Sstevel@tonic-gate 		    myname(ep->e_links));
3950Sstevel@tonic-gate 	if (ep->e_xattrs != NIL)
3960Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("attribute root name: %s\n"),
3970Sstevel@tonic-gate 		    myname(ep->e_xattrs));
3980Sstevel@tonic-gate 	if (ep->e_next != NIL)
3990Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("next hashchain name: %s\n"),
4000Sstevel@tonic-gate 		    myname(ep->e_next));
4010Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("entry type: %s\n"),
4020Sstevel@tonic-gate 	    ep->e_type == NODE ? gettext("NODE") : gettext("LEAF"));
4030Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("inode number: %lu\n"), ep->e_ino);
4040Sstevel@tonic-gate 	panic(gettext("flags: %s\n"), flagvalues(ep));
4050Sstevel@tonic-gate 	/* Our callers are expected to handle our returning. */
4060Sstevel@tonic-gate }
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate /*
4090Sstevel@tonic-gate  * Construct a string indicating the active flag bits of an entry.
4100Sstevel@tonic-gate  */
4110Sstevel@tonic-gate char *
flagvalues(struct entry * ep)412*1053Smaheshvs flagvalues(struct entry *ep)
4130Sstevel@tonic-gate {
4140Sstevel@tonic-gate 	static char flagbuf[BUFSIZ];
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	(void) strlcpy(flagbuf, gettext("|NIL"), sizeof (flagbuf));
4170Sstevel@tonic-gate 	flagbuf[0] = '\0';
4180Sstevel@tonic-gate 	if (ep->e_flags & REMOVED)
4190Sstevel@tonic-gate 		(void) strlcat(flagbuf, gettext("|REMOVED"), sizeof (flagbuf));
4200Sstevel@tonic-gate 	if (ep->e_flags & TMPNAME)
4210Sstevel@tonic-gate 		(void) strlcat(flagbuf, gettext("|TMPNAME"), sizeof (flagbuf));
4220Sstevel@tonic-gate 	if (ep->e_flags & EXTRACT)
4230Sstevel@tonic-gate 		(void) strlcat(flagbuf, gettext("|EXTRACT"), sizeof (flagbuf));
4240Sstevel@tonic-gate 	if (ep->e_flags & NEW)
4250Sstevel@tonic-gate 		(void) strlcat(flagbuf, gettext("|NEW"), sizeof (flagbuf));
4260Sstevel@tonic-gate 	if (ep->e_flags & KEEP)
4270Sstevel@tonic-gate 		(void) strlcat(flagbuf, gettext("|KEEP"), sizeof (flagbuf));
4280Sstevel@tonic-gate 	if (ep->e_flags & EXISTED)
4290Sstevel@tonic-gate 		(void) strlcat(flagbuf, gettext("|EXISTED"), sizeof (flagbuf));
4300Sstevel@tonic-gate 	if (ep->e_flags & XATTR)
4310Sstevel@tonic-gate 		(void) strlcat(flagbuf, gettext("|XATTR"), sizeof (flagbuf));
4320Sstevel@tonic-gate 	if (ep->e_flags & XATTRROOT)
4330Sstevel@tonic-gate 		(void) strlcat(flagbuf, gettext("|XATTRROOT"),
4340Sstevel@tonic-gate 						sizeof (flagbuf));
4350Sstevel@tonic-gate 	return (&flagbuf[1]);
4360Sstevel@tonic-gate }
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate /*
4390Sstevel@tonic-gate  * Check to see if a name is on a dump tape.
4400Sstevel@tonic-gate  */
4410Sstevel@tonic-gate ino_t
dirlookup(char * name)442*1053Smaheshvs dirlookup(char *name)
4430Sstevel@tonic-gate {
4440Sstevel@tonic-gate 	ino_t ino;
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 	ino = psearch(name);
4470Sstevel@tonic-gate 	if (ino == 0 || BIT(ino, dumpmap) == 0)
4480Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s is not on volume\n"), name);
4490Sstevel@tonic-gate 	return (ino);
4500Sstevel@tonic-gate }
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate /*
4530Sstevel@tonic-gate  * Elicit a reply.
4540Sstevel@tonic-gate  */
455*1053Smaheshvs int
reply(char * question)456*1053Smaheshvs reply(char *question)
4570Sstevel@tonic-gate {
4580Sstevel@tonic-gate 	char *yesorno = gettext("yn"); /* must be two characters, "yes" first */
4590Sstevel@tonic-gate 	int c;
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	do	{
4620Sstevel@tonic-gate 		(void) fprintf(stderr, "%s? [%s] ", question, yesorno);
4630Sstevel@tonic-gate 		(void) fflush(stderr);
4640Sstevel@tonic-gate 		c = getc(terminal);
4650Sstevel@tonic-gate 		while (c != '\n' && getc(terminal) != '\n') {
4660Sstevel@tonic-gate 			if (ferror(terminal)) {
4670Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
4680Sstevel@tonic-gate 					"Error reading response\n"));
4690Sstevel@tonic-gate 				(void) fflush(stderr);
4700Sstevel@tonic-gate 				return (FAIL);
4710Sstevel@tonic-gate 			}
4720Sstevel@tonic-gate 			if (feof(terminal))
4730Sstevel@tonic-gate 				return (FAIL);
4740Sstevel@tonic-gate 		}
4750Sstevel@tonic-gate 		if (isupper(c))
4760Sstevel@tonic-gate 			c = tolower(c);
4770Sstevel@tonic-gate 	} while (c != yesorno[0] && c != yesorno[1]);
4780Sstevel@tonic-gate 	if (c == yesorno[0])
4790Sstevel@tonic-gate 		return (GOOD);
4800Sstevel@tonic-gate 	return (FAIL);
4810Sstevel@tonic-gate }
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate /*
4840Sstevel@tonic-gate  * handle unexpected inconsistencies
4850Sstevel@tonic-gate  */
4860Sstevel@tonic-gate /*
4870Sstevel@tonic-gate  * Note that a panic w/ EOF on the tty means all panics will return...
4880Sstevel@tonic-gate  */
4890Sstevel@tonic-gate #ifdef __STDC__
4900Sstevel@tonic-gate #include <stdarg.h>
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate /* VARARGS1 */
4930Sstevel@tonic-gate void
panic(const char * msg,...)4940Sstevel@tonic-gate panic(const char *msg, ...)
4950Sstevel@tonic-gate {
4960Sstevel@tonic-gate 	va_list	args;
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 	va_start(args, msg);
4990Sstevel@tonic-gate 	(void) vfprintf(stderr, msg, args);
5000Sstevel@tonic-gate 	va_end(args);
5010Sstevel@tonic-gate 	if (reply(gettext("abort")) == GOOD) {
5020Sstevel@tonic-gate 		if (reply(gettext("dump core")) == GOOD)
5030Sstevel@tonic-gate 			abort();
5040Sstevel@tonic-gate 		done(1);
5050Sstevel@tonic-gate 	}
5060Sstevel@tonic-gate }
5070Sstevel@tonic-gate #else
5080Sstevel@tonic-gate #include <varargs.h>
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate /* VARARGS1 */
5110Sstevel@tonic-gate void
panic(va_dcl)512*1053Smaheshvs panic(va_dcl)
5130Sstevel@tonic-gate {
5140Sstevel@tonic-gate 	va_list	args;
5150Sstevel@tonic-gate 	char	*msg;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 	va_start(args);
5180Sstevel@tonic-gate 	msg = va_arg(args, char *);
5190Sstevel@tonic-gate 	(void) vfprintf(stderr, msg, args);
5200Sstevel@tonic-gate 	va_end(args);
5210Sstevel@tonic-gate 	if (reply(gettext("abort")) == GOOD) {
5220Sstevel@tonic-gate 		if (reply(gettext("dump core")) == GOOD)
5230Sstevel@tonic-gate 			abort();
5240Sstevel@tonic-gate 		done(1);
5250Sstevel@tonic-gate 	}
5260Sstevel@tonic-gate #endif
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate /*
5290Sstevel@tonic-gate  * Locale-specific version of ctime
5300Sstevel@tonic-gate  */
5310Sstevel@tonic-gate char *
532*1053Smaheshvs lctime(time_t *tp)
5330Sstevel@tonic-gate {
5340Sstevel@tonic-gate 	static char buf[256];
5350Sstevel@tonic-gate 	struct tm *tm;
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 	tm = localtime(tp);
5380Sstevel@tonic-gate 	(void) strftime(buf, sizeof (buf), "%c\n", tm);
5390Sstevel@tonic-gate 	return (buf);
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate static int
5430Sstevel@tonic-gate statcmp(const struct stat *left, const struct stat *right)
5440Sstevel@tonic-gate {
5450Sstevel@tonic-gate 	int result = 1;
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	if ((left->st_dev == right->st_dev) &&
5480Sstevel@tonic-gate 	    (left->st_ino == right->st_ino) &&
5490Sstevel@tonic-gate 	    (left->st_mode == right->st_mode) &&
5500Sstevel@tonic-gate 	    (left->st_nlink == right->st_nlink) &&
5510Sstevel@tonic-gate 	    (left->st_uid == right->st_uid) &&
5520Sstevel@tonic-gate 	    (left->st_gid == right->st_gid) &&
5530Sstevel@tonic-gate 	    (left->st_rdev == right->st_rdev) &&
5540Sstevel@tonic-gate 	    (left->st_ctim.tv_sec == right->st_ctim.tv_sec) &&
5550Sstevel@tonic-gate 	    (left->st_ctim.tv_nsec == right->st_ctim.tv_nsec) &&
5560Sstevel@tonic-gate 	    (left->st_mtim.tv_sec == right->st_mtim.tv_sec) &&
5570Sstevel@tonic-gate 	    (left->st_mtim.tv_nsec == right->st_mtim.tv_nsec) &&
5580Sstevel@tonic-gate 	    (left->st_blksize == right->st_blksize) &&
5590Sstevel@tonic-gate 	    (left->st_blocks == right->st_blocks)) {
5600Sstevel@tonic-gate 		result = 0;
5610Sstevel@tonic-gate 	}
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	return (result);
5640Sstevel@tonic-gate }
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate /*
5670Sstevel@tonic-gate  * Safely open a file.
5680Sstevel@tonic-gate  */
5690Sstevel@tonic-gate int
5700Sstevel@tonic-gate safe_open(int dfd, const char *filename, int mode, int perms)
5710Sstevel@tonic-gate {
5720Sstevel@tonic-gate 	static int init_syslog = 1;
5730Sstevel@tonic-gate 	int fd;
5740Sstevel@tonic-gate 	int working_mode;
5750Sstevel@tonic-gate 	int saverr;
5760Sstevel@tonic-gate 	char *errtext;
5770Sstevel@tonic-gate 	struct stat pre_stat, pre_lstat;
5780Sstevel@tonic-gate 	struct stat post_stat, post_lstat;
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	if (init_syslog) {
5810Sstevel@tonic-gate 		openlog(progname, LOG_CONS, LOG_DAEMON);
5820Sstevel@tonic-gate 		init_syslog = 0;
5830Sstevel@tonic-gate 	}
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 	/*
5860Sstevel@tonic-gate 	 * Don't want to be spoofed into trashing something we
5870Sstevel@tonic-gate 	 * shouldn't, thus the following rigamarole.  If it doesn't
5880Sstevel@tonic-gate 	 * exist, we create it and proceed.  Otherwise, require that
5890Sstevel@tonic-gate 	 * what's there be a real file with no extraneous links and
5900Sstevel@tonic-gate 	 * owned by whoever ran us.
5910Sstevel@tonic-gate 	 *
5920Sstevel@tonic-gate 	 * The silliness with using both lstat() and fstat() is to avoid
5930Sstevel@tonic-gate 	 * race-condition games with someone replacing the file with a
5940Sstevel@tonic-gate 	 * symlink after we've opened it.  If there was an flstat(),
5950Sstevel@tonic-gate 	 * we wouldn't need the fstat().
5960Sstevel@tonic-gate 	 *
5970Sstevel@tonic-gate 	 * The initial open with the hard-coded flags is ok even if we
5980Sstevel@tonic-gate 	 * are intending to open only for reading.  If it succeeds,
5990Sstevel@tonic-gate 	 * then the file did not exist, and we'll synthesize an appropriate
6000Sstevel@tonic-gate 	 * complaint below.  Otherwise, it does exist, so we won't be
6010Sstevel@tonic-gate 	 * truncating it with the open.
6020Sstevel@tonic-gate 	 */
6030Sstevel@tonic-gate 	if ((fd = openat(dfd, filename,
6040Sstevel@tonic-gate 	    O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_LARGEFILE, perms)) < 0) {
6050Sstevel@tonic-gate 		if (errno == EEXIST) {
6060Sstevel@tonic-gate 			if (fstatat(dfd, filename, &pre_lstat,
6070Sstevel@tonic-gate 						AT_SYMLINK_NOFOLLOW) < 0) {
6080Sstevel@tonic-gate 				saverr = errno;
6090Sstevel@tonic-gate 				(void) close(fd);
6100Sstevel@tonic-gate 				errno = saverr;
6110Sstevel@tonic-gate 				return (-1);
6120Sstevel@tonic-gate 			}
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 			if (fstatat(dfd, filename, &pre_stat, 0) < 0) {
6150Sstevel@tonic-gate 				saverr = errno;
6160Sstevel@tonic-gate 				(void) close(fd);
6170Sstevel@tonic-gate 				errno = saverr;
6180Sstevel@tonic-gate 				return (-1);
6190Sstevel@tonic-gate 			}
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 			working_mode = mode & (O_WRONLY|O_RDWR|O_RDONLY);
6220Sstevel@tonic-gate 			working_mode |= O_LARGEFILE;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 			if ((fd = openat(dfd, filename, working_mode)) < 0) {
6250Sstevel@tonic-gate 				if (errno == ENOENT) {
6260Sstevel@tonic-gate 					errtext = gettext(
6270Sstevel@tonic-gate "Unexpected condition detected: %s used to exist, but doesn't any longer\n");
6280Sstevel@tonic-gate 					(void) fprintf(stderr, errtext,
6290Sstevel@tonic-gate 					    filename);
6300Sstevel@tonic-gate 					syslog(LOG_WARNING, errtext, filename);
6310Sstevel@tonic-gate 					errno = ENOENT;
6320Sstevel@tonic-gate 				}
6330Sstevel@tonic-gate 				return (-1);
6340Sstevel@tonic-gate 			}
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 			if (fstatat(fd, NULL, &post_lstat,
6370Sstevel@tonic-gate 						AT_SYMLINK_NOFOLLOW) < 0) {
6380Sstevel@tonic-gate 				saverr = errno;
6390Sstevel@tonic-gate 				(void) close(fd);
6400Sstevel@tonic-gate 				errno = saverr;
6410Sstevel@tonic-gate 				return (-1);
6420Sstevel@tonic-gate 			}
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 			if (fstatat(fd, NULL, &post_stat, 0) < 0) {
6450Sstevel@tonic-gate 				saverr = errno;
6460Sstevel@tonic-gate 				(void) close(fd);
6470Sstevel@tonic-gate 				errno = saverr;
6480Sstevel@tonic-gate 				return (-1);
6490Sstevel@tonic-gate 			}
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 			if (statcmp(&pre_lstat, &post_lstat) != 0) {
6520Sstevel@tonic-gate 				errtext = gettext(
6530Sstevel@tonic-gate "Unexpected condition detected: %s's lstat(2) information changed\n");
6540Sstevel@tonic-gate 				(void) fprintf(stderr, errtext, filename);
6550Sstevel@tonic-gate 				syslog(LOG_WARNING, errtext, filename);
6560Sstevel@tonic-gate 				errno = EPERM;
6570Sstevel@tonic-gate 				return (-1);
6580Sstevel@tonic-gate 			}
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 			if (statcmp(&pre_stat, &post_stat) != 0) {
6610Sstevel@tonic-gate 				errtext = gettext(
6620Sstevel@tonic-gate "Unexpected condition detected: %s's stat(2) information changed\n");
6630Sstevel@tonic-gate 				(void) fprintf(stderr, errtext, filename);
6640Sstevel@tonic-gate 				syslog(LOG_WARNING, errtext, filename);
6650Sstevel@tonic-gate 				errno = EPERM;
6660Sstevel@tonic-gate 				return (-1);
6670Sstevel@tonic-gate 			}
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate 			/*
6700Sstevel@tonic-gate 			 * If inode, device, or type are wrong, bail out.
6710Sstevel@tonic-gate 			 */
6720Sstevel@tonic-gate 			if ((!S_ISREG(post_lstat.st_mode) ||
6730Sstevel@tonic-gate 			    (post_stat.st_ino != post_lstat.st_ino) ||
6740Sstevel@tonic-gate 			    (post_stat.st_dev != post_lstat.st_dev))) {
6750Sstevel@tonic-gate 				errtext = gettext(
6760Sstevel@tonic-gate 	    "Unexpected condition detected: %s is not a regular file\n");
6770Sstevel@tonic-gate 				(void) fprintf(stderr, errtext, filename);
6780Sstevel@tonic-gate 				syslog(LOG_WARNING, errtext, filename);
6790Sstevel@tonic-gate 				(void) close(fd);
6800Sstevel@tonic-gate 				errno = EPERM;
6810Sstevel@tonic-gate 				return (-1);
6820Sstevel@tonic-gate 			}
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate 			/*
6850Sstevel@tonic-gate 			 * Bad link count implies someone's linked our
6860Sstevel@tonic-gate 			 * target to something else, which we probably
6870Sstevel@tonic-gate 			 * shouldn't step on.
6880Sstevel@tonic-gate 			 */
6890Sstevel@tonic-gate 			if (post_lstat.st_nlink != 1) {
6900Sstevel@tonic-gate 				errtext = gettext(
6910Sstevel@tonic-gate 	    "Unexpected condition detected: %s must have exactly one link\n");
6920Sstevel@tonic-gate 				(void) fprintf(stderr, errtext, filename);
6930Sstevel@tonic-gate 				syslog(LOG_WARNING, errtext, filename);
6940Sstevel@tonic-gate 				(void) close(fd);
6950Sstevel@tonic-gate 				errno = EPERM;
6960Sstevel@tonic-gate 				return (-1);
6970Sstevel@tonic-gate 			}
6980Sstevel@tonic-gate 			/*
6990Sstevel@tonic-gate 			 * Root might make a file, but non-root might
7000Sstevel@tonic-gate 			 * need to open it.  If the permissions let us
7010Sstevel@tonic-gate 			 * get this far, then let it through.
7020Sstevel@tonic-gate 			 */
7030Sstevel@tonic-gate 			if (post_lstat.st_uid != getuid() &&
7040Sstevel@tonic-gate 			    post_lstat.st_uid != 0) {
7050Sstevel@tonic-gate 				errtext = gettext(
7060Sstevel@tonic-gate "Unsupported condition detected: %s must be owned by uid %ld or 0\n");
7070Sstevel@tonic-gate 				(void) fprintf(stderr, errtext, filename,
7080Sstevel@tonic-gate 				    (long)getuid());
7090Sstevel@tonic-gate 				syslog(LOG_WARNING, errtext, filename,
7100Sstevel@tonic-gate 				    (long)getuid());
7110Sstevel@tonic-gate 				(void) close(fd);
7120Sstevel@tonic-gate 				errno = EPERM;
7130Sstevel@tonic-gate 				return (-1);
7140Sstevel@tonic-gate 			}
7150Sstevel@tonic-gate 			if (mode & (O_WRONLY|O_TRUNC)) {
7160Sstevel@tonic-gate 				if (ftruncate(fd, (off_t)0) < 0) {
7170Sstevel@tonic-gate 					(void) fprintf(stderr,
7180Sstevel@tonic-gate 					    "ftruncate(%s): %s\n",
7190Sstevel@tonic-gate 					    filename, strerror(errno));
7200Sstevel@tonic-gate 					(void) close(fd);
7210Sstevel@tonic-gate 					return (-1);
7220Sstevel@tonic-gate 				}
7230Sstevel@tonic-gate 			}
7240Sstevel@tonic-gate 		} else {
7250Sstevel@tonic-gate 			/*
7260Sstevel@tonic-gate 			 * Didn't exist, but couldn't open it.
7270Sstevel@tonic-gate 			 */
7280Sstevel@tonic-gate 			return (-1);
7290Sstevel@tonic-gate 		}
7300Sstevel@tonic-gate 	} else {
7310Sstevel@tonic-gate 		/*
7320Sstevel@tonic-gate 		 * If truncating open succeeded for a read-only open,
7330Sstevel@tonic-gate 		 * bail out, as we really shouldn't have succeeded.
7340Sstevel@tonic-gate 		 */
7350Sstevel@tonic-gate 		if (mode & O_RDONLY) {
7360Sstevel@tonic-gate 			/* Undo the O_CREAT */
7370Sstevel@tonic-gate 			(void) unlinkat(dfd, filename, 0);
7380Sstevel@tonic-gate 			(void) fprintf(stderr, "open(%s): %s\n",
7390Sstevel@tonic-gate 			    filename, strerror(ENOENT));
7400Sstevel@tonic-gate 			(void) close(fd);
7410Sstevel@tonic-gate 			errno = ENOENT;
7420Sstevel@tonic-gate 			return (-1);
7430Sstevel@tonic-gate 		}
7440Sstevel@tonic-gate 	}
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	return (fd);
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate /*
7500Sstevel@tonic-gate  * STDIO version of safe_open.  Equivalent to fopen64(...).
7510Sstevel@tonic-gate  */
7520Sstevel@tonic-gate FILE *
7530Sstevel@tonic-gate safe_fopen(const char *filename, const char *smode, int perms)
7540Sstevel@tonic-gate {
7550Sstevel@tonic-gate 	int fd;
7560Sstevel@tonic-gate 	int bmode;
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate 	/*
7590Sstevel@tonic-gate 	 * accepts only modes  "r", "r+", and "w"
7600Sstevel@tonic-gate 	 */
7610Sstevel@tonic-gate 	if (smode[0] == 'r') {
7620Sstevel@tonic-gate 		if (smode[1] == '\0') {
7630Sstevel@tonic-gate 			bmode = O_RDONLY;
7640Sstevel@tonic-gate 		} else if ((smode[1] == '+') && (smode[2] == '\0')) {
7650Sstevel@tonic-gate 			bmode = O_RDWR;
7660Sstevel@tonic-gate 		}
7670Sstevel@tonic-gate 	} else if ((smode[0] == 'w') && (smode[1] == '\0')) {
7680Sstevel@tonic-gate 		bmode = O_WRONLY;
7690Sstevel@tonic-gate 	} else {
7700Sstevel@tonic-gate 		(void) fprintf(stderr,
7710Sstevel@tonic-gate 		    gettext("internal error: safe_fopen: invalid mode `%s'\n"),
7720Sstevel@tonic-gate 		    smode);
7730Sstevel@tonic-gate 		return (NULL);
7740Sstevel@tonic-gate 	}
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	fd = safe_open(AT_FDCWD, filename, bmode, perms);
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate 	/*
7790Sstevel@tonic-gate 	 * caller is expected to report error.
7800Sstevel@tonic-gate 	 */
7810Sstevel@tonic-gate 	if (fd >= 0)
7820Sstevel@tonic-gate 		return (fdopen(fd, smode));
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate 	return ((FILE *)NULL);
7850Sstevel@tonic-gate }
7860Sstevel@tonic-gate 
7870Sstevel@tonic-gate /*
7880Sstevel@tonic-gate  * Read the contents of a directory.
7890Sstevel@tonic-gate  */
7900Sstevel@tonic-gate int
791*1053Smaheshvs mkentry(char *name, ino_t ino, struct arglist *ap)
7920Sstevel@tonic-gate {
7930Sstevel@tonic-gate 	struct afile *fp;
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	if (ap->base == NULL) {
7960Sstevel@tonic-gate 		ap->nent = 20;
7970Sstevel@tonic-gate 		ap->base = (struct afile *)calloc((unsigned)ap->nent,
7980Sstevel@tonic-gate 			sizeof (*(ap->base)));
7990Sstevel@tonic-gate 		if (ap->base == NULL) {
8000Sstevel@tonic-gate 			(void) fprintf(stderr,
8010Sstevel@tonic-gate 				gettext("%s: out of memory\n"), ap->cmd);
8020Sstevel@tonic-gate 			return (FAIL);
8030Sstevel@tonic-gate 		}
8040Sstevel@tonic-gate 	}
8050Sstevel@tonic-gate 	if (ap->head == NULL)
8060Sstevel@tonic-gate 		ap->head = ap->last = ap->base;
8070Sstevel@tonic-gate 	fp = ap->last;
8080Sstevel@tonic-gate 	fp->fnum = ino;
8090Sstevel@tonic-gate 	fp->fname = savename(name);
8100Sstevel@tonic-gate 	fp++;
8110Sstevel@tonic-gate 	if (fp == ap->head + ap->nent) {
8120Sstevel@tonic-gate 		ap->base = (struct afile *)realloc((char *)ap->base,
8130Sstevel@tonic-gate 		    (size_t)(2 * ap->nent * (size_t)sizeof (*(ap->base))));
8140Sstevel@tonic-gate 		if (ap->base == NULL) {
8150Sstevel@tonic-gate 			(void) fprintf(stderr,
8160Sstevel@tonic-gate 				gettext("%s: out of memory\n"), ap->cmd);
8170Sstevel@tonic-gate 			return (FAIL);
8180Sstevel@tonic-gate 		}
8190Sstevel@tonic-gate 		ap->head = ap->base;
8200Sstevel@tonic-gate 		fp = ap->head + ap->nent;
8210Sstevel@tonic-gate 		ap->nent *= 2;
8220Sstevel@tonic-gate 	}
8230Sstevel@tonic-gate 	ap->last = fp;
8240Sstevel@tonic-gate 	return (GOOD);
8250Sstevel@tonic-gate }
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate #ifdef __STDC__
8280Sstevel@tonic-gate static int gmatch(wchar_t *, wchar_t *);
8290Sstevel@tonic-gate static int addg(struct direct *, char *, char *, struct arglist *);
8300Sstevel@tonic-gate #else
8310Sstevel@tonic-gate static int gmatch();
8320Sstevel@tonic-gate static int addg();
8330Sstevel@tonic-gate #endif
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate /*
8360Sstevel@tonic-gate  * XXX  This value is ASCII (but not language) dependent.  In
8370Sstevel@tonic-gate  * ASCII, it is the DEL character (unlikely to appear in paths).
8380Sstevel@tonic-gate  * If you are compiling on an EBCDIC-based machine, re-define
8390Sstevel@tonic-gate  * this (0x7f is '"') to be something like 0x7 (DEL).  It's
8400Sstevel@tonic-gate  * either this hack or re-write the expand() algorithm...
8410Sstevel@tonic-gate  */
8420Sstevel@tonic-gate #define	DELIMCHAR	((char)0x7f)
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate /*
8450Sstevel@tonic-gate  * Expand a file name.
8460Sstevel@tonic-gate  * "as" is the pattern to expand.
8470Sstevel@tonic-gate  * "rflg" non-zero indicates that we're recursing.
8480Sstevel@tonic-gate  * "ap" is where to put the results of the expansion.
8490Sstevel@tonic-gate  *
8500Sstevel@tonic-gate  * Our caller guarantees that "as" is at least the string ".".
8510Sstevel@tonic-gate  */
8520Sstevel@tonic-gate int
853*1053Smaheshvs expand(char *as, int rflg, struct arglist *ap)
8540Sstevel@tonic-gate {
8550Sstevel@tonic-gate 	int		count, size;
8560Sstevel@tonic-gate 	char		dir = 0;
8570Sstevel@tonic-gate 	char		*rescan = 0;
8580Sstevel@tonic-gate 	RST_DIR		*dirp;
8590Sstevel@tonic-gate 	char		*s, *cs;
8600Sstevel@tonic-gate 	int		sindex, rindexa, lindex;
8610Sstevel@tonic-gate 	struct direct	*dp;
8620Sstevel@tonic-gate 	char		slash;
8630Sstevel@tonic-gate 	char		*rs;
8640Sstevel@tonic-gate 	char		c;
8650Sstevel@tonic-gate 	wchar_t 	w_fname[PATH_MAX+1];
8660Sstevel@tonic-gate 	wchar_t		w_pname[PATH_MAX+1];
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 	/*
8690Sstevel@tonic-gate 	 * check for meta chars
8700Sstevel@tonic-gate 	 */
8710Sstevel@tonic-gate 	s = cs = as;
8720Sstevel@tonic-gate 	slash = 0;
8730Sstevel@tonic-gate 	while (*cs != '*' && *cs != '?' && *cs != '[') {
8740Sstevel@tonic-gate 		if (*cs++ == 0) {
8750Sstevel@tonic-gate 			if (rflg && slash)
8760Sstevel@tonic-gate 				break;
8770Sstevel@tonic-gate 			else
8780Sstevel@tonic-gate 				return (0);
8790Sstevel@tonic-gate 		} else if (*cs == '/') {
8800Sstevel@tonic-gate 			slash++;
8810Sstevel@tonic-gate 		}
8820Sstevel@tonic-gate 	}
8830Sstevel@tonic-gate 	for (;;) {
8840Sstevel@tonic-gate 		if (cs == s) {
8850Sstevel@tonic-gate 			s = "";
8860Sstevel@tonic-gate 			break;
8870Sstevel@tonic-gate 		} else if (*--cs == '/') {
8880Sstevel@tonic-gate 			*cs = 0;
8890Sstevel@tonic-gate 			if (s == cs)
8900Sstevel@tonic-gate 				s = "/";
8910Sstevel@tonic-gate 			break;
8920Sstevel@tonic-gate 		}
8930Sstevel@tonic-gate 	}
8940Sstevel@tonic-gate 	if ((dirp = rst_opendir(s)) != NULL)
8950Sstevel@tonic-gate 		dir++;
8960Sstevel@tonic-gate 	count = 0;
8970Sstevel@tonic-gate 	if (*cs == 0)
8980Sstevel@tonic-gate 		*cs++ = DELIMCHAR;
8990Sstevel@tonic-gate 	if (dir) {
9000Sstevel@tonic-gate 		/*
9010Sstevel@tonic-gate 		 * check for rescan
9020Sstevel@tonic-gate 		 */
9030Sstevel@tonic-gate 		rs = cs;
9040Sstevel@tonic-gate 		do {
9050Sstevel@tonic-gate 			if (*rs == '/') {
9060Sstevel@tonic-gate 				rescan = rs;
9070Sstevel@tonic-gate 				*rs = 0;
9080Sstevel@tonic-gate 			}
9090Sstevel@tonic-gate 		} while (*rs++);
9100Sstevel@tonic-gate 		/* LINTED: result fits into an int */
9110Sstevel@tonic-gate 		sindex = (int)(ap->last - ap->head);
9120Sstevel@tonic-gate 		(void) mbstowcs(w_pname, cs, PATH_MAX);
9130Sstevel@tonic-gate 		w_pname[PATH_MAX - 1] = 0;
9140Sstevel@tonic-gate 		while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
9150Sstevel@tonic-gate 			if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
9160Sstevel@tonic-gate 				continue;
9170Sstevel@tonic-gate 			if ((*dp->d_name == '.' && *cs != '.'))
9180Sstevel@tonic-gate 				continue;
9190Sstevel@tonic-gate 			(void) mbstowcs(w_fname, dp->d_name, PATH_MAX);
9200Sstevel@tonic-gate 			w_fname[PATH_MAX - 1] = 0;
9210Sstevel@tonic-gate 			if (gmatch(w_fname, w_pname)) {
9220Sstevel@tonic-gate 				if (addg(dp, s, rescan, ap) < 0) {
9230Sstevel@tonic-gate 					rst_closedir(dirp);
9240Sstevel@tonic-gate 					return (-1);
9250Sstevel@tonic-gate 				}
9260Sstevel@tonic-gate 				count++;
9270Sstevel@tonic-gate 			}
9280Sstevel@tonic-gate 		}
9290Sstevel@tonic-gate 		if (rescan) {
9300Sstevel@tonic-gate 			rindexa = sindex;
9310Sstevel@tonic-gate 			/* LINTED: result fits into an int */
9320Sstevel@tonic-gate 			lindex = (int)(ap->last - ap->head);
9330Sstevel@tonic-gate 			if (count) {
9340Sstevel@tonic-gate 				count = 0;
9350Sstevel@tonic-gate 				while (rindexa < lindex) {
9360Sstevel@tonic-gate 					size = expand(ap->head[rindexa].fname,
9370Sstevel@tonic-gate 					    1, ap);
9380Sstevel@tonic-gate 					if (size < 0) {
9390Sstevel@tonic-gate 						rst_closedir(dirp);
9400Sstevel@tonic-gate 						return (size);
9410Sstevel@tonic-gate 					}
9420Sstevel@tonic-gate 					count += size;
9430Sstevel@tonic-gate 					rindexa++;
9440Sstevel@tonic-gate 				}
9450Sstevel@tonic-gate 			}
9460Sstevel@tonic-gate 			/* LINTED: lint is confused about pointer size/type */
9470Sstevel@tonic-gate 			bcopy((void *)(&ap->head[lindex]),
9480Sstevel@tonic-gate 			    (void *)(&ap->head[sindex]),
9490Sstevel@tonic-gate 			    (size_t)((ap->last - &ap->head[rindexa])) *
9500Sstevel@tonic-gate 			    sizeof (*ap->head));
9510Sstevel@tonic-gate 			ap->last -= lindex - sindex;
9520Sstevel@tonic-gate 			*rescan = '/';
9530Sstevel@tonic-gate 		}
9540Sstevel@tonic-gate 		rst_closedir(dirp);
9550Sstevel@tonic-gate 	}
9560Sstevel@tonic-gate 	s = as;
9570Sstevel@tonic-gate 	while ((c = *s) != '\0')
9580Sstevel@tonic-gate 		*s++ = (c != DELIMCHAR ? c : '/');
9590Sstevel@tonic-gate 
9600Sstevel@tonic-gate 	return (count);
9610Sstevel@tonic-gate }
9620Sstevel@tonic-gate 
9630Sstevel@tonic-gate /*
9640Sstevel@tonic-gate  * Check for a name match
9650Sstevel@tonic-gate  */
9660Sstevel@tonic-gate static int
967*1053Smaheshvs gmatch(wchar_t *s, wchar_t *p)
9680Sstevel@tonic-gate {
9690Sstevel@tonic-gate 	long	scc;	/* source character to text */
9700Sstevel@tonic-gate 	wchar_t	c;	/* pattern character to match */
9710Sstevel@tonic-gate 	char	ok;	/* [x-y] range match status */
9720Sstevel@tonic-gate 	long	lc;	/* left character of [x-y] range */
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 	scc = *s++;
9750Sstevel@tonic-gate 	switch (c = *p++) {
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 	case '[':
9780Sstevel@tonic-gate 		ok = 0;
9790Sstevel@tonic-gate 		lc = -1;
9800Sstevel@tonic-gate 		while (c = *p++) {
9810Sstevel@tonic-gate 			if (c == ']') {
9820Sstevel@tonic-gate 				return (ok ? gmatch(s, p) : 0);
9830Sstevel@tonic-gate 			} else if (c == '-') {
9840Sstevel@tonic-gate 				wchar_t rc = *p++;
9850Sstevel@tonic-gate 				/*
9860Sstevel@tonic-gate 				 * Check both ends must belong to
9870Sstevel@tonic-gate 				 * the same codeset.
9880Sstevel@tonic-gate 				 */
9890Sstevel@tonic-gate 				if (wcsetno(lc) != wcsetno(rc)) {
9900Sstevel@tonic-gate 					/*
9910Sstevel@tonic-gate 					 * If not, ignore the '-'
9920Sstevel@tonic-gate 					 * operator and [x-y] is
9930Sstevel@tonic-gate 					 * treated as if it were
9940Sstevel@tonic-gate 					 * [xy].
9950Sstevel@tonic-gate 					 */
9960Sstevel@tonic-gate 					if (scc == lc)
9970Sstevel@tonic-gate 						ok++;
9980Sstevel@tonic-gate 					if (scc == (lc = rc))
9990Sstevel@tonic-gate 						ok++;
10000Sstevel@tonic-gate 				} else if (lc <= scc && scc <= rc)
10010Sstevel@tonic-gate 					ok++;
10020Sstevel@tonic-gate 			} else {
10030Sstevel@tonic-gate 				lc = c;
10040Sstevel@tonic-gate 				if (scc == lc)
10050Sstevel@tonic-gate 					ok++;
10060Sstevel@tonic-gate 			}
10070Sstevel@tonic-gate 		}
10080Sstevel@tonic-gate 		/* No closing bracket => failure */
10090Sstevel@tonic-gate 		return (0);
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 	default:
10120Sstevel@tonic-gate 		if (c != scc)
10130Sstevel@tonic-gate 			return (0);
10140Sstevel@tonic-gate 		/*FALLTHROUGH*/
10150Sstevel@tonic-gate 
10160Sstevel@tonic-gate 	case '?':
10170Sstevel@tonic-gate 		return (scc ? gmatch(s, p) : 0);
10180Sstevel@tonic-gate 
10190Sstevel@tonic-gate 	case '*':
10200Sstevel@tonic-gate 		if (*p == 0)
10210Sstevel@tonic-gate 			return (1);
10220Sstevel@tonic-gate 		s--;
10230Sstevel@tonic-gate 		while (*s) {
10240Sstevel@tonic-gate 			if (gmatch(s++, p))
10250Sstevel@tonic-gate 				return (1);
10260Sstevel@tonic-gate 		}
10270Sstevel@tonic-gate 		return (0);
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 	case 0:
10300Sstevel@tonic-gate 		return (scc == 0);
10310Sstevel@tonic-gate 	}
10320Sstevel@tonic-gate }
10330Sstevel@tonic-gate 
10340Sstevel@tonic-gate /*
10350Sstevel@tonic-gate  * Construct a matched name.
10360Sstevel@tonic-gate  */
10370Sstevel@tonic-gate static int
1038*1053Smaheshvs addg(struct direct *dp, char *as1, char *as3, struct arglist *ap)
10390Sstevel@tonic-gate {
10400Sstevel@tonic-gate 	char	*s1, *s2, *limit;
10410Sstevel@tonic-gate 	int	c;
10420Sstevel@tonic-gate 	char	buf[MAXPATHLEN + 1];
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate 	s2 = buf;
10450Sstevel@tonic-gate 	limit = buf + sizeof (buf) - 1;
10460Sstevel@tonic-gate 	s1 = as1;
10470Sstevel@tonic-gate 	while ((c = *s1++) != '\0' && s2 < limit) {
10480Sstevel@tonic-gate 		if (c == DELIMCHAR) {
10490Sstevel@tonic-gate 			*s2++ = '/';
10500Sstevel@tonic-gate 			break;
10510Sstevel@tonic-gate 		}
10520Sstevel@tonic-gate 		/* LINTED narrowing cast */
10530Sstevel@tonic-gate 		*s2++ = (char)c;
10540Sstevel@tonic-gate 	}
10550Sstevel@tonic-gate 	s1 = dp->d_name;
10560Sstevel@tonic-gate 	while ((*s2 = *s1++) != '\0' && s2 < limit)
10570Sstevel@tonic-gate 		s2++;
10580Sstevel@tonic-gate 	s1 = as3;
10590Sstevel@tonic-gate 	if (s1 != NULL && s2 < limit) {
10600Sstevel@tonic-gate 		*s2++ = '/';
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 		while ((*s2++ = *++s1) != '\0' && s2 < limit) {
10630Sstevel@tonic-gate 			continue;
10640Sstevel@tonic-gate 			/*LINTED [empty loop body]*/
10650Sstevel@tonic-gate 		}
10660Sstevel@tonic-gate 	}
10670Sstevel@tonic-gate 	*s2 = '\0';
10680Sstevel@tonic-gate 	if (mkentry(buf, dp->d_ino, ap) == FAIL)
10690Sstevel@tonic-gate 		return (-1);
10700Sstevel@tonic-gate 	return (0);
10710Sstevel@tonic-gate }
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate /*
10750Sstevel@tonic-gate  * Resolve a "complex" pathname (as generated by myname()) into
10760Sstevel@tonic-gate  * a file descriptor and a relative path.  The file descriptor
10770Sstevel@tonic-gate  * will reference the hidden directory containing the attribute
10780Sstevel@tonic-gate  * named by the relative path.  If the provided path is not
10790Sstevel@tonic-gate  * complex, the returned file descriptor will be AT_FDCWD and rpath
10800Sstevel@tonic-gate  * will equal path.
10810Sstevel@tonic-gate  *
10820Sstevel@tonic-gate  * This function is intended to be used to transform a complex
10830Sstevel@tonic-gate  * pathname into a pair of handles that can be used to actually
10840Sstevel@tonic-gate  * manipulate the named file.  Since extended attributes have
10850Sstevel@tonic-gate  * an independant name space, a file descriptor for a directory
10860Sstevel@tonic-gate  * in the attribute name space is necessary to actually manipulate
10870Sstevel@tonic-gate  * the attribute file (via the path-relative xxxat() system calls
10880Sstevel@tonic-gate  * or a call to fchdir()).
10890Sstevel@tonic-gate  *
10900Sstevel@tonic-gate  * In the event of an error, the returned file descriptor will be
10910Sstevel@tonic-gate  * -1.  It is expected that callers will either check for this
10920Sstevel@tonic-gate  * condition directly, or attempt to use the descriptor, fail, and
10930Sstevel@tonic-gate  * generate an appropriate context-specific error message.
10940Sstevel@tonic-gate  *
10950Sstevel@tonic-gate  * This function is pretty much a no-op for "simple" (non-attribute)
10960Sstevel@tonic-gate  * paths.
10970Sstevel@tonic-gate  */
10980Sstevel@tonic-gate void
1099*1053Smaheshvs resolve(char *path, int *fd, char **rpath)
11000Sstevel@tonic-gate {
11010Sstevel@tonic-gate 	int	tfd;
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 	*fd = tfd = AT_FDCWD;
11040Sstevel@tonic-gate 	*rpath = path;
11050Sstevel@tonic-gate 	path = *rpath + strlen(*rpath) +1;
11060Sstevel@tonic-gate 	while (*path != '\0' &&
11070Sstevel@tonic-gate 		(*fd = openat64(tfd, *rpath, O_RDONLY)) > 0) {
11080Sstevel@tonic-gate 		if (tfd != AT_FDCWD) (void) close(tfd);
11090Sstevel@tonic-gate 		tfd = *fd;
11100Sstevel@tonic-gate 		*rpath = path;
11110Sstevel@tonic-gate 		path = *rpath + strlen(*rpath) +1;
11120Sstevel@tonic-gate 	}
11130Sstevel@tonic-gate 	if (*fd == AT_FDCWD)
11140Sstevel@tonic-gate 		return;
11150Sstevel@tonic-gate 	if (*fd < 0 || (*fd = openat64(tfd, ".", O_RDONLY|O_XATTR)) < 0) {
11160Sstevel@tonic-gate 		int saverr = errno;
11170Sstevel@tonic-gate 		(void) fprintf(stderr,
11180Sstevel@tonic-gate 			gettext("Warning: cannot fully resolve %s: %s"),
11190Sstevel@tonic-gate 			path, strerror(saverr));
11200Sstevel@tonic-gate 		(void) fflush(stderr);
11210Sstevel@tonic-gate 	}
11220Sstevel@tonic-gate 	if (tfd != AT_FDCWD) (void) close(tfd);
11230Sstevel@tonic-gate }
11240Sstevel@tonic-gate 
11250Sstevel@tonic-gate /*
11260Sstevel@tonic-gate  * Copy a complex pathname to another string.  Note that the
11270Sstevel@tonic-gate  * length returned by this function is the number of characters
11280Sstevel@tonic-gate  * up to (but not including) the final NULL.
11290Sstevel@tonic-gate  */
11300Sstevel@tonic-gate int
1131*1053Smaheshvs complexcpy(char *s1, char *s2, int max)
11320Sstevel@tonic-gate {
11330Sstevel@tonic-gate 	int	nullseen = 0;
11340Sstevel@tonic-gate 	int	len = 0;
11350Sstevel@tonic-gate 
11360Sstevel@tonic-gate 	while (len++ < max) {
11370Sstevel@tonic-gate 		*s1++ = *s2;
11380Sstevel@tonic-gate 		if (*s2++ == '\0') {
11390Sstevel@tonic-gate 			if (nullseen)
11400Sstevel@tonic-gate 				return (len-1);
11410Sstevel@tonic-gate 			else
11420Sstevel@tonic-gate 				nullseen = 1;
11430Sstevel@tonic-gate 		} else {
11440Sstevel@tonic-gate 			nullseen = 0;
11450Sstevel@tonic-gate 		}
11460Sstevel@tonic-gate 	}
11470Sstevel@tonic-gate 	*s1 = '\0';
11480Sstevel@tonic-gate 	if (nullseen == 0)
11490Sstevel@tonic-gate 		*--s1 = '\0';
11500Sstevel@tonic-gate 	fprintf(stderr,
11510Sstevel@tonic-gate 		gettext("Warning: unterminated source string in complexcpy\n"));
11520Sstevel@tonic-gate 	return (max-1);
11530Sstevel@tonic-gate }
1154