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