Fix NULL pointer constant issues in cmd
[unleashed.git] / usr / src / cmd / backup / restore / utilities.c
blob302e39d7e30ebc1649849f8a941aee14970c6e39
1 /*
2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
9 /*
10 * Copyright (c) 1983 Regents of the University of California.
11 * All rights reserved. The Berkeley software License Agreement
12 * specifies the terms and conditions for redistribution.
15 #include "restore.h"
16 #include <ctype.h>
17 #include <errno.h>
18 #include <syslog.h>
19 #include <limits.h>
20 /* LINTED: this file really is necessary */
21 #include <euc.h>
22 #include <widec.h>
25 * Insure that all the components of a pathname exist. Note that
26 * lookupname() and addentry() both expect complex names as
27 * input arguments, so a double NULL needs to be added to each name.
29 void
30 pathcheck(char *name)
32 char *cp, save;
33 struct entry *ep;
34 char *start;
36 start = strchr(name, '/');
37 if (start == 0)
38 return;
39 for (cp = start; *cp != '\0'; cp++) {
40 if (*cp != '/')
41 continue;
42 *cp = '\0';
43 save = *(cp+1);
44 *(cp+1) = '\0';
45 ep = lookupname(name);
46 if (ep == NIL) {
47 ep = addentry(name, psearch(name), NODE);
48 newnode(ep);
50 /* LINTED: result fits in a short */
51 ep->e_flags |= NEW|KEEP;
52 *cp = '/';
53 *(cp+1) = save;
58 * Change a name to a unique temporary name.
60 void
61 mktempname(struct entry *ep)
63 char *newname;
65 if (ep->e_flags & TMPNAME)
66 badentry(ep, gettext("mktempname: called with TMPNAME"));
67 /* LINTED: result fits in a short */
68 ep->e_flags |= TMPNAME;
69 newname = savename(gentempname(ep));
70 renameit(myname(ep), newname);
71 freename(ep->e_name);
72 ep->e_name = newname;
73 /* LINTED: savename guarantees strlen will fit */
74 ep->e_namlen = strlen(ep->e_name);
78 * Generate a temporary name for an entry.
80 char *
81 gentempname(struct entry *ep)
83 static char name[MAXPATHLEN];
84 struct entry *np;
85 long i = 0;
87 for (np = lookupino(ep->e_ino); np != NIL && np != ep; np = np->e_links)
88 i++;
89 if (np == NIL)
90 badentry(ep, gettext("not on ino list"));
91 (void) snprintf(name, sizeof (name), "%s%ld%lu", TMPHDR, i, ep->e_ino);
92 return (name);
96 * Rename a file or directory.
98 void
99 renameit(char *fp, char *tp)
101 int fromfd, tofd;
102 char *from, *to;
103 char tobuf[MAXPATHLEN];
104 char *pathend;
106 resolve(fp, &fromfd, &from);
108 * The to pointer argument is assumed to be either a fully
109 * specified path (starting with "./") or a simple temporary
110 * file name (starting with TMPHDR). If passed a simple temp
111 * file name, we need to set up the descriptors explicitly.
113 if (strncmp(tp, TMPHDR, sizeof (TMPHDR) - 1) == 0) {
114 tofd = fromfd;
115 if ((pathend = strrchr(from, '/')) != NULL) {
116 strncpy(tobuf, from, pathend - from + 1);
117 tobuf[pathend - from + 1] = '\0';
118 strlcat(tobuf, tp, sizeof (tobuf));
119 to = tobuf;
120 } else {
121 to = tp;
123 } else
124 resolve(tp, &tofd, &to);
125 if (renameat(fromfd, from, tofd, to) < 0) {
126 int saverr = errno;
127 (void) fprintf(stderr,
128 gettext("Warning: cannot rename %s to %s: %s\n"),
129 from, to, strerror(saverr));
130 (void) fflush(stderr);
131 } else {
132 vprintf(stdout, gettext("rename %s to %s\n"), from, to);
134 if (fromfd != AT_FDCWD) (void) close(fromfd);
135 if (tofd != AT_FDCWD) (void) close(tofd);
139 * Create a new node (directory). Note that, because we have no
140 * mkdirat() function, fchdir() must be used set up the appropriate
141 * name space context prior to the call to mkdir() if we are
142 * operating in attribute space.
144 void
145 newnode(struct entry *np)
147 char *cp;
148 int dfd;
150 if (np->e_type != NODE)
151 badentry(np, gettext("newnode: not a node"));
152 resolve(myname(np), &dfd, &cp);
153 if (dfd != AT_FDCWD) {
154 if (fchdir(dfd) < 0) {
155 int saverr = errno;
156 (void) fprintf(stderr,
157 gettext("Warning: cannot create %s: %s"),
158 cp, strerror(saverr));
159 (void) fflush(stderr);
160 (void) close(dfd);
161 return;
164 if (mkdir(cp, 0777) < 0) {
165 int saverr = errno;
166 /* LINTED: result fits in a short */
167 np->e_flags |= EXISTED;
168 (void) fprintf(stderr, gettext("Warning: "));
169 (void) fflush(stderr);
170 (void) fprintf(stderr, "%s: %s\n", cp, strerror(saverr));
171 } else {
172 vprintf(stdout, gettext("Make node %s\n"), cp);
174 if (dfd != AT_FDCWD) {
175 fchdir(savepwd);
176 (void) close(dfd);
181 * Remove an old node (directory). See comment above on newnode()
182 * for explanation of fchdir() use below.
184 void
185 removenode(struct entry *ep)
187 char *cp;
188 int dfd;
190 if (ep->e_type != NODE)
191 badentry(ep, gettext("removenode: not a node"));
192 if (ep->e_entries != NIL)
193 badentry(ep, gettext("removenode: non-empty directory"));
194 /* LINTED: result fits in a short */
195 ep->e_flags |= REMOVED;
196 /* LINTED: result fits in a short */
197 ep->e_flags &= ~TMPNAME;
198 resolve(myname(ep), &dfd, &cp);
199 if (dfd != AT_FDCWD) {
200 if (fchdir(dfd) < 0) {
201 int saverr = errno;
202 (void) fprintf(stderr,
203 gettext("Warning: cannot remove %s: %s"),
204 cp, strerror(saverr));
205 (void) fflush(stderr);
206 (void) close(dfd);
207 return;
210 if (rmdir(cp) < 0) { /* NOTE: could use unlinkat (..,REMOVEDIR) */
211 int saverr = errno;
212 (void) fprintf(stderr, gettext("Warning: %s: %s\n"),
213 cp, strerror(saverr));
214 (void) fflush(stderr);
215 } else {
216 vprintf(stdout, gettext("Remove node %s\n"), cp);
218 if (dfd != AT_FDCWD) {
219 (void) fchdir(savepwd);
220 (void) close(dfd);
225 * Remove a leaf.
227 void
228 removeleaf(struct entry *ep)
230 char *cp;
231 int dfd;
233 if (ep->e_type != LEAF)
234 badentry(ep, gettext("removeleaf: not a leaf"));
235 /* LINTED: result fits in a short */
236 ep->e_flags |= REMOVED;
237 /* LINTED: result fits in a short */
238 ep->e_flags &= ~TMPNAME;
239 resolve(myname(ep), &dfd, &cp);
240 if (unlinkat(dfd, cp, 0) < 0) {
241 int saverr = errno;
242 (void) fprintf(stderr, gettext("Warning: %s: %s\n"),
243 cp, strerror(saverr));
244 (void) fflush(stderr);
245 } else {
246 vprintf(stdout, gettext("Remove leaf %s\n"), cp);
248 if (dfd != AT_FDCWD)
249 (void) close(dfd);
253 * Create a link.
254 * This function assumes that the context has already been set
255 * for the link file to be created (i.e., we have "fchdir-ed"
256 * into attribute space already if this is an attribute link).
259 lf_linkit(char *existing, char *new, int type)
261 char linkbuf[MAXPATHLEN];
262 struct stat64 s1[1], s2[1];
263 char *name;
264 int dfd, l, result;
266 resolve(existing, &dfd, &name);
267 if (dfd == -1) {
268 (void) fprintf(stderr, gettext(
269 "Warning: cannot restore %s link %s->%s\n"),
270 (type == SYMLINK ? "symbolic" : "hard"), new, existing);
271 result = FAIL;
272 goto out;
274 if (type == SYMLINK) {
275 if (symlink(name, new) < 0) {
276 /* No trailing \0 from readlink(2) */
277 if (((l = readlink(new, linkbuf, sizeof (linkbuf)))
278 > 0) &&
279 (l == strlen(name)) &&
280 (strncmp(linkbuf, name, l) == 0)) {
281 vprintf(stdout,
282 gettext("Symbolic link %s->%s ok\n"),
283 new, name);
284 result = GOOD;
285 goto out;
286 } else {
287 int saverr = errno;
288 (void) fprintf(stderr, gettext(
289 "Warning: cannot create symbolic link %s->%s: %s"),
290 new, name, strerror(saverr));
291 (void) fflush(stderr);
292 result = FAIL;
293 goto out;
296 } else if (type == HARDLINK) {
297 if (link(name, new) < 0) {
298 int saverr = errno;
299 if ((stat64(name, s1) == 0) &&
300 (stat64(new, s2) == 0) &&
301 (s1->st_dev == s2->st_dev) &&
302 (s1->st_ino == s2->st_ino)) {
303 vprintf(stdout,
304 gettext("Hard link %s->%s ok\n"),
305 new, name);
306 result = GOOD;
307 goto out;
308 } else {
309 (void) fprintf(stderr, gettext(
310 "Warning: cannot create hard link %s->%s: %s\n"),
311 new, name, strerror(saverr));
312 (void) fflush(stderr);
313 result = FAIL;
314 goto out;
317 } else {
318 panic(gettext("%s: unknown type %d\n"), "linkit", type);
319 result = FAIL;
320 goto out;
322 result = GOOD;
323 if (type == SYMLINK)
324 vprintf(stdout, gettext("Create symbolic link %s->%s\n"),
325 new, name);
326 else
327 vprintf(stdout, gettext("Create hard link %s->%s\n"),
328 new, name);
329 out:
330 if (dfd != AT_FDCWD) {
331 (void) close(dfd);
333 return (result);
337 * Find lowest-numbered inode (above "start") that needs to be extracted.
338 * Caller knows that a return value of maxino means there's nothing left.
340 ino_t
341 lowerbnd(ino_t start)
343 struct entry *ep;
345 for (; start < maxino; start++) {
346 ep = lookupino(start);
347 if (ep == NIL || ep->e_type == NODE)
348 continue;
349 if (ep->e_flags & (NEW|EXTRACT))
350 return (start);
352 return (start);
356 * Find highest-numbered inode (below "start") that needs to be extracted.
358 ino_t
359 upperbnd(ino_t start)
361 struct entry *ep;
363 for (; start > ROOTINO; start--) {
364 ep = lookupino(start);
365 if (ep == NIL || ep->e_type == NODE)
366 continue;
367 if (ep->e_flags & (NEW|EXTRACT))
368 return (start);
370 return (start);
374 * report on a badly formed entry
376 void
377 badentry(struct entry *ep, char *msg)
380 (void) fprintf(stderr, gettext("bad entry: %s\n"), msg);
381 (void) fprintf(stderr, gettext("name: %s\n"), myname(ep));
382 (void) fprintf(stderr, gettext("parent name %s\n"),
383 myname(ep->e_parent));
384 if (ep->e_sibling != NIL)
385 (void) fprintf(stderr, gettext("sibling name: %s\n"),
386 myname(ep->e_sibling));
387 if (ep->e_entries != NIL)
388 (void) fprintf(stderr, gettext("next entry name: %s\n"),
389 myname(ep->e_entries));
390 if (ep->e_links != NIL)
391 (void) fprintf(stderr, gettext("next link name: %s\n"),
392 myname(ep->e_links));
393 if (ep->e_xattrs != NIL)
394 (void) fprintf(stderr, gettext("attribute root name: %s\n"),
395 myname(ep->e_xattrs));
396 if (ep->e_next != NIL)
397 (void) fprintf(stderr, gettext("next hashchain name: %s\n"),
398 myname(ep->e_next));
399 (void) fprintf(stderr, gettext("entry type: %s\n"),
400 ep->e_type == NODE ? gettext("NODE") : gettext("LEAF"));
401 (void) fprintf(stderr, gettext("inode number: %lu\n"), ep->e_ino);
402 panic(gettext("flags: %s\n"), flagvalues(ep));
403 /* Our callers are expected to handle our returning. */
407 * Construct a string indicating the active flag bits of an entry.
409 char *
410 flagvalues(struct entry *ep)
412 static char flagbuf[BUFSIZ];
414 (void) strlcpy(flagbuf, gettext("|NIL"), sizeof (flagbuf));
415 flagbuf[0] = '\0';
416 if (ep->e_flags & REMOVED)
417 (void) strlcat(flagbuf, gettext("|REMOVED"), sizeof (flagbuf));
418 if (ep->e_flags & TMPNAME)
419 (void) strlcat(flagbuf, gettext("|TMPNAME"), sizeof (flagbuf));
420 if (ep->e_flags & EXTRACT)
421 (void) strlcat(flagbuf, gettext("|EXTRACT"), sizeof (flagbuf));
422 if (ep->e_flags & NEW)
423 (void) strlcat(flagbuf, gettext("|NEW"), sizeof (flagbuf));
424 if (ep->e_flags & KEEP)
425 (void) strlcat(flagbuf, gettext("|KEEP"), sizeof (flagbuf));
426 if (ep->e_flags & EXISTED)
427 (void) strlcat(flagbuf, gettext("|EXISTED"), sizeof (flagbuf));
428 if (ep->e_flags & XATTR)
429 (void) strlcat(flagbuf, gettext("|XATTR"), sizeof (flagbuf));
430 if (ep->e_flags & XATTRROOT)
431 (void) strlcat(flagbuf, gettext("|XATTRROOT"),
432 sizeof (flagbuf));
433 return (&flagbuf[1]);
437 * Check to see if a name is on a dump tape.
439 ino_t
440 dirlookup(char *name)
442 ino_t ino;
444 ino = psearch(name);
445 if (ino == 0 || BIT(ino, dumpmap) == 0)
446 (void) fprintf(stderr, gettext("%s is not on volume\n"), name);
447 return (ino);
451 * Elicit a reply.
454 reply(char *question)
456 char *yesorno = gettext("yn"); /* must be two characters, "yes" first */
457 int c;
459 do {
460 (void) fprintf(stderr, "%s? [%s] ", question, yesorno);
461 (void) fflush(stderr);
462 c = getc(terminal);
463 while (c != '\n' && getc(terminal) != '\n') {
464 if (ferror(terminal)) {
465 (void) fprintf(stderr, gettext(
466 "Error reading response\n"));
467 (void) fflush(stderr);
468 return (FAIL);
470 if (feof(terminal))
471 return (FAIL);
473 if (isupper(c))
474 c = tolower(c);
475 } while (c != yesorno[0] && c != yesorno[1]);
476 if (c == yesorno[0])
477 return (GOOD);
478 return (FAIL);
482 * handle unexpected inconsistencies
485 * Note that a panic w/ EOF on the tty means all panics will return...
487 #ifdef __STDC__
488 #include <stdarg.h>
490 /* VARARGS1 */
491 void
492 panic(const char *msg, ...)
494 va_list args;
496 va_start(args, msg);
497 (void) vfprintf(stderr, msg, args);
498 va_end(args);
499 if (reply(gettext("abort")) == GOOD) {
500 if (reply(gettext("dump core")) == GOOD)
501 abort();
502 done(1);
505 #else
506 #include <varargs.h>
508 /* VARARGS1 */
509 void
510 panic(va_dcl)
512 va_list args;
513 char *msg;
515 va_start(args);
516 msg = va_arg(args, char *);
517 (void) vfprintf(stderr, msg, args);
518 va_end(args);
519 if (reply(gettext("abort")) == GOOD) {
520 if (reply(gettext("dump core")) == GOOD)
521 abort();
522 done(1);
524 #endif
527 * Locale-specific version of ctime
529 char *
530 lctime(time_t *tp)
532 static char buf[256];
533 struct tm *tm;
535 tm = localtime(tp);
536 (void) strftime(buf, sizeof (buf), "%c\n", tm);
537 return (buf);
540 static int
541 statcmp(const struct stat *left, const struct stat *right)
543 int result = 1;
545 if ((left->st_dev == right->st_dev) &&
546 (left->st_ino == right->st_ino) &&
547 (left->st_mode == right->st_mode) &&
548 (left->st_nlink == right->st_nlink) &&
549 (left->st_uid == right->st_uid) &&
550 (left->st_gid == right->st_gid) &&
551 (left->st_rdev == right->st_rdev) &&
552 (left->st_ctim.tv_sec == right->st_ctim.tv_sec) &&
553 (left->st_ctim.tv_nsec == right->st_ctim.tv_nsec) &&
554 (left->st_mtim.tv_sec == right->st_mtim.tv_sec) &&
555 (left->st_mtim.tv_nsec == right->st_mtim.tv_nsec) &&
556 (left->st_blksize == right->st_blksize) &&
557 (left->st_blocks == right->st_blocks)) {
558 result = 0;
561 return (result);
565 * Safely open a file.
568 safe_open(int dfd, const char *filename, int mode, int perms)
570 static int init_syslog = 1;
571 int fd;
572 int working_mode;
573 int saverr;
574 char *errtext;
575 struct stat pre_stat, pre_lstat;
576 struct stat post_stat, post_lstat;
578 if (init_syslog) {
579 openlog(progname, LOG_CONS, LOG_DAEMON);
580 init_syslog = 0;
584 * Don't want to be spoofed into trashing something we
585 * shouldn't, thus the following rigamarole. If it doesn't
586 * exist, we create it and proceed. Otherwise, require that
587 * what's there be a real file with no extraneous links and
588 * owned by whoever ran us.
590 * The silliness with using both lstat() and fstat() is to avoid
591 * race-condition games with someone replacing the file with a
592 * symlink after we've opened it. If there was an flstat(),
593 * we wouldn't need the fstat().
595 * The initial open with the hard-coded flags is ok even if we
596 * are intending to open only for reading. If it succeeds,
597 * then the file did not exist, and we'll synthesize an appropriate
598 * complaint below. Otherwise, it does exist, so we won't be
599 * truncating it with the open.
601 if ((fd = openat(dfd, filename,
602 O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_LARGEFILE, perms)) < 0) {
603 if (errno == EEXIST) {
604 if (fstatat(dfd, filename, &pre_lstat,
605 AT_SYMLINK_NOFOLLOW) < 0) {
606 saverr = errno;
607 (void) close(fd);
608 errno = saverr;
609 return (-1);
612 if (fstatat(dfd, filename, &pre_stat, 0) < 0) {
613 saverr = errno;
614 (void) close(fd);
615 errno = saverr;
616 return (-1);
619 working_mode = mode & (O_WRONLY|O_RDWR|O_RDONLY);
620 working_mode |= O_LARGEFILE;
622 if ((fd = openat(dfd, filename, working_mode)) < 0) {
623 if (errno == ENOENT) {
624 errtext = gettext(
625 "Unexpected condition detected: %s used to exist, but doesn't any longer\n");
626 (void) fprintf(stderr, errtext,
627 filename);
628 syslog(LOG_WARNING, errtext, filename);
629 errno = ENOENT;
631 return (-1);
634 if (fstatat(fd, NULL, &post_lstat,
635 AT_SYMLINK_NOFOLLOW) < 0) {
636 saverr = errno;
637 (void) close(fd);
638 errno = saverr;
639 return (-1);
642 if (fstatat(fd, NULL, &post_stat, 0) < 0) {
643 saverr = errno;
644 (void) close(fd);
645 errno = saverr;
646 return (-1);
649 if (statcmp(&pre_lstat, &post_lstat) != 0) {
650 errtext = gettext(
651 "Unexpected condition detected: %s's lstat(2) information changed\n");
652 (void) fprintf(stderr, errtext, filename);
653 syslog(LOG_WARNING, errtext, filename);
654 errno = EPERM;
655 return (-1);
658 if (statcmp(&pre_stat, &post_stat) != 0) {
659 errtext = gettext(
660 "Unexpected condition detected: %s's stat(2) information changed\n");
661 (void) fprintf(stderr, errtext, filename);
662 syslog(LOG_WARNING, errtext, filename);
663 errno = EPERM;
664 return (-1);
668 * If inode, device, or type are wrong, bail out.
670 if ((!S_ISREG(post_lstat.st_mode) ||
671 (post_stat.st_ino != post_lstat.st_ino) ||
672 (post_stat.st_dev != post_lstat.st_dev))) {
673 errtext = gettext(
674 "Unexpected condition detected: %s is not a regular file\n");
675 (void) fprintf(stderr, errtext, filename);
676 syslog(LOG_WARNING, errtext, filename);
677 (void) close(fd);
678 errno = EPERM;
679 return (-1);
683 * Bad link count implies someone's linked our
684 * target to something else, which we probably
685 * shouldn't step on.
687 if (post_lstat.st_nlink != 1) {
688 errtext = gettext(
689 "Unexpected condition detected: %s must have exactly one link\n");
690 (void) fprintf(stderr, errtext, filename);
691 syslog(LOG_WARNING, errtext, filename);
692 (void) close(fd);
693 errno = EPERM;
694 return (-1);
697 * Root might make a file, but non-root might
698 * need to open it. If the permissions let us
699 * get this far, then let it through.
701 if (post_lstat.st_uid != getuid() &&
702 post_lstat.st_uid != 0) {
703 errtext = gettext(
704 "Unsupported condition detected: %s must be owned by uid %ld or 0\n");
705 (void) fprintf(stderr, errtext, filename,
706 (long)getuid());
707 syslog(LOG_WARNING, errtext, filename,
708 (long)getuid());
709 (void) close(fd);
710 errno = EPERM;
711 return (-1);
713 if (mode & (O_WRONLY|O_TRUNC)) {
714 if (ftruncate(fd, (off_t)0) < 0) {
715 (void) fprintf(stderr,
716 "ftruncate(%s): %s\n",
717 filename, strerror(errno));
718 (void) close(fd);
719 return (-1);
722 } else {
724 * Didn't exist, but couldn't open it.
726 return (-1);
728 } else {
730 * If truncating open succeeded for a read-only open,
731 * bail out, as we really shouldn't have succeeded.
733 if (mode & O_RDONLY) {
734 /* Undo the O_CREAT */
735 (void) unlinkat(dfd, filename, 0);
736 (void) fprintf(stderr, "open(%s): %s\n",
737 filename, strerror(ENOENT));
738 (void) close(fd);
739 errno = ENOENT;
740 return (-1);
744 return (fd);
748 * STDIO version of safe_open. Equivalent to fopen64(...).
750 FILE *
751 safe_fopen(const char *filename, const char *smode, int perms)
753 int fd;
754 int bmode;
757 * accepts only modes "r", "r+", and "w"
759 if (smode[0] == 'r') {
760 if (smode[1] == '\0') {
761 bmode = O_RDONLY;
762 } else if ((smode[1] == '+') && (smode[2] == '\0')) {
763 bmode = O_RDWR;
765 } else if ((smode[0] == 'w') && (smode[1] == '\0')) {
766 bmode = O_WRONLY;
767 } else {
768 (void) fprintf(stderr,
769 gettext("internal error: safe_fopen: invalid mode `%s'\n"),
770 smode);
771 return (NULL);
774 fd = safe_open(AT_FDCWD, filename, bmode, perms);
777 * caller is expected to report error.
779 if (fd >= 0)
780 return (fdopen(fd, smode));
782 return ((FILE *)NULL);
786 * Read the contents of a directory.
789 mkentry(char *name, ino_t ino, struct arglist *ap)
791 struct afile *fp;
793 if (ap->base == NULL) {
794 ap->nent = 20;
795 ap->base = (struct afile *)calloc((unsigned)ap->nent,
796 sizeof (*(ap->base)));
797 if (ap->base == NULL) {
798 (void) fprintf(stderr,
799 gettext("%s: out of memory\n"), ap->cmd);
800 return (FAIL);
803 if (ap->head == NULL)
804 ap->head = ap->last = ap->base;
805 fp = ap->last;
806 fp->fnum = ino;
807 fp->fname = savename(name);
808 fp++;
809 if (fp == ap->head + ap->nent) {
810 ap->base = (struct afile *)realloc((char *)ap->base,
811 (size_t)(2 * ap->nent * (size_t)sizeof (*(ap->base))));
812 if (ap->base == NULL) {
813 (void) fprintf(stderr,
814 gettext("%s: out of memory\n"), ap->cmd);
815 return (FAIL);
817 ap->head = ap->base;
818 fp = ap->head + ap->nent;
819 ap->nent *= 2;
821 ap->last = fp;
822 return (GOOD);
825 #ifdef __STDC__
826 static int gmatch(wchar_t *, wchar_t *);
827 static int addg(struct direct *, char *, char *, struct arglist *);
828 #else
829 static int gmatch();
830 static int addg();
831 #endif
834 * XXX This value is ASCII (but not language) dependent. In
835 * ASCII, it is the DEL character (unlikely to appear in paths).
836 * If you are compiling on an EBCDIC-based machine, re-define
837 * this (0x7f is '"') to be something like 0x7 (DEL). It's
838 * either this hack or re-write the expand() algorithm...
840 #define DELIMCHAR ((char)0x7f)
843 * Expand a file name.
844 * "as" is the pattern to expand.
845 * "rflg" non-zero indicates that we're recursing.
846 * "ap" is where to put the results of the expansion.
848 * Our caller guarantees that "as" is at least the string ".".
851 expand(char *as, int rflg, struct arglist *ap)
853 int count, size;
854 char dir = 0;
855 char *rescan = 0;
856 RST_DIR *dirp;
857 char *s, *cs;
858 int sindex, rindexa, lindex;
859 struct direct *dp;
860 char slash;
861 char *rs;
862 char c;
863 wchar_t w_fname[PATH_MAX+1];
864 wchar_t w_pname[PATH_MAX+1];
867 * check for meta chars
869 s = cs = as;
870 slash = 0;
871 while (*cs != '*' && *cs != '?' && *cs != '[') {
872 if (*cs++ == 0) {
873 if (rflg && slash)
874 break;
875 else
876 return (0);
877 } else if (*cs == '/') {
878 slash++;
881 for (;;) {
882 if (cs == s) {
883 s = "";
884 break;
885 } else if (*--cs == '/') {
886 *cs = 0;
887 if (s == cs)
888 s = "/";
889 break;
892 if ((dirp = rst_opendir(s)) != NULL)
893 dir++;
894 count = 0;
895 if (*cs == 0)
896 *cs++ = DELIMCHAR;
897 if (dir) {
899 * check for rescan
901 rs = cs;
902 do {
903 if (*rs == '/') {
904 rescan = rs;
905 *rs = 0;
907 } while (*rs++);
908 /* LINTED: result fits into an int */
909 sindex = (int)(ap->last - ap->head);
910 (void) mbstowcs(w_pname, cs, PATH_MAX);
911 w_pname[PATH_MAX - 1] = 0;
912 while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
913 if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
914 continue;
915 if ((*dp->d_name == '.' && *cs != '.'))
916 continue;
917 (void) mbstowcs(w_fname, dp->d_name, PATH_MAX);
918 w_fname[PATH_MAX - 1] = 0;
919 if (gmatch(w_fname, w_pname)) {
920 if (addg(dp, s, rescan, ap) < 0) {
921 rst_closedir(dirp);
922 return (-1);
924 count++;
927 if (rescan) {
928 rindexa = sindex;
929 /* LINTED: result fits into an int */
930 lindex = (int)(ap->last - ap->head);
931 if (count) {
932 count = 0;
933 while (rindexa < lindex) {
934 size = expand(ap->head[rindexa].fname,
935 1, ap);
936 if (size < 0) {
937 rst_closedir(dirp);
938 return (size);
940 count += size;
941 rindexa++;
944 /* LINTED: lint is confused about pointer size/type */
945 bcopy((void *)(&ap->head[lindex]),
946 (void *)(&ap->head[sindex]),
947 (size_t)((ap->last - &ap->head[rindexa])) *
948 sizeof (*ap->head));
949 ap->last -= lindex - sindex;
950 *rescan = '/';
952 rst_closedir(dirp);
954 s = as;
955 while ((c = *s) != '\0')
956 *s++ = (c != DELIMCHAR ? c : '/');
958 return (count);
962 * Check for a name match
964 static int
965 gmatch(wchar_t *s, wchar_t *p)
967 long scc; /* source character to text */
968 wchar_t c; /* pattern character to match */
969 char ok; /* [x-y] range match status */
970 long lc; /* left character of [x-y] range */
972 scc = *s++;
973 switch (c = *p++) {
975 case '[':
976 ok = 0;
977 lc = -1;
978 while (c = *p++) {
979 if (c == ']') {
980 return (ok ? gmatch(s, p) : 0);
981 } else if (c == '-') {
982 wchar_t rc = *p++;
984 * Check both ends must belong to
985 * the same codeset.
987 if (wcsetno(lc) != wcsetno(rc)) {
989 * If not, ignore the '-'
990 * operator and [x-y] is
991 * treated as if it were
992 * [xy].
994 if (scc == lc)
995 ok++;
996 if (scc == (lc = rc))
997 ok++;
998 } else if (lc <= scc && scc <= rc)
999 ok++;
1000 } else {
1001 lc = c;
1002 if (scc == lc)
1003 ok++;
1006 /* No closing bracket => failure */
1007 return (0);
1009 default:
1010 if (c != scc)
1011 return (0);
1012 /*FALLTHROUGH*/
1014 case '?':
1015 return (scc ? gmatch(s, p) : 0);
1017 case '*':
1018 if (*p == 0)
1019 return (1);
1020 s--;
1021 while (*s) {
1022 if (gmatch(s++, p))
1023 return (1);
1025 return (0);
1027 case 0:
1028 return (scc == 0);
1033 * Construct a matched name.
1035 static int
1036 addg(struct direct *dp, char *as1, char *as3, struct arglist *ap)
1038 char *s1, *s2, *limit;
1039 int c;
1040 char buf[MAXPATHLEN + 1];
1042 s2 = buf;
1043 limit = buf + sizeof (buf) - 1;
1044 s1 = as1;
1045 while ((c = *s1++) != '\0' && s2 < limit) {
1046 if (c == DELIMCHAR) {
1047 *s2++ = '/';
1048 break;
1050 /* LINTED narrowing cast */
1051 *s2++ = (char)c;
1053 s1 = dp->d_name;
1054 while ((*s2 = *s1++) != '\0' && s2 < limit)
1055 s2++;
1056 s1 = as3;
1057 if (s1 != NULL && s2 < limit) {
1058 *s2++ = '/';
1060 while ((*s2++ = *++s1) != '\0' && s2 < limit) {
1061 continue;
1062 /*LINTED [empty loop body]*/
1065 *s2 = '\0';
1066 if (mkentry(buf, dp->d_ino, ap) == FAIL)
1067 return (-1);
1068 return (0);
1073 * Resolve a "complex" pathname (as generated by myname()) into
1074 * a file descriptor and a relative path. The file descriptor
1075 * will reference the hidden directory containing the attribute
1076 * named by the relative path. If the provided path is not
1077 * complex, the returned file descriptor will be AT_FDCWD and rpath
1078 * will equal path.
1080 * This function is intended to be used to transform a complex
1081 * pathname into a pair of handles that can be used to actually
1082 * manipulate the named file. Since extended attributes have
1083 * an independant name space, a file descriptor for a directory
1084 * in the attribute name space is necessary to actually manipulate
1085 * the attribute file (via the path-relative xxxat() system calls
1086 * or a call to fchdir()).
1088 * In the event of an error, the returned file descriptor will be
1089 * -1. It is expected that callers will either check for this
1090 * condition directly, or attempt to use the descriptor, fail, and
1091 * generate an appropriate context-specific error message.
1093 * This function is pretty much a no-op for "simple" (non-attribute)
1094 * paths.
1096 void
1097 resolve(char *path, int *fd, char **rpath)
1099 int tfd;
1101 *fd = tfd = AT_FDCWD;
1102 *rpath = path;
1103 path = *rpath + strlen(*rpath) +1;
1104 while (*path != '\0' &&
1105 (*fd = openat64(tfd, *rpath, O_RDONLY)) > 0) {
1106 if (tfd != AT_FDCWD) (void) close(tfd);
1107 tfd = *fd;
1108 *rpath = path;
1109 path = *rpath + strlen(*rpath) +1;
1111 if (*fd == AT_FDCWD)
1112 return;
1113 if (*fd < 0 || (*fd = openat64(tfd, ".", O_RDONLY|O_XATTR)) < 0) {
1114 int saverr = errno;
1115 (void) fprintf(stderr,
1116 gettext("Warning: cannot fully resolve %s: %s"),
1117 path, strerror(saverr));
1118 (void) fflush(stderr);
1120 if (tfd != AT_FDCWD) (void) close(tfd);
1124 * Copy a complex pathname to another string. Note that the
1125 * length returned by this function is the number of characters
1126 * up to (but not including) the final NULL.
1129 complexcpy(char *s1, char *s2, int max)
1131 int nullseen = 0;
1132 int len = 0;
1134 while (len++ < max) {
1135 *s1++ = *s2;
1136 if (*s2++ == '\0') {
1137 if (nullseen)
1138 return (len-1);
1139 else
1140 nullseen = 1;
1141 } else {
1142 nullseen = 0;
1145 *s1 = '\0';
1146 if (nullseen == 0)
1147 *--s1 = '\0';
1148 fprintf(stderr,
1149 gettext("Warning: unterminated source string in complexcpy\n"));
1150 return (max-1);