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