1 /*
2 * Copyright 2006 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 #include "restore.h"
16 /* undef MAXNAMLEN to prevent compiler warnings about redef in dirent.h */
17 #undef MAXNAMLEN
18 #include <dirent.h>
19
20 static char *keyval(int);
21 static void removexattrs(struct entry *);
22 static void movexattrs(char *, char *);
23
24 /*
25 * This implements the 't' option.
26 * List entries on the tape.
27 */
28 long
listfile(char * name,ino_t ino,int type)29 listfile(char *name, ino_t ino, int type)
30 {
31 long descend = hflag ? GOOD : FAIL;
32
33 if (BIT(ino, dumpmap) == 0) {
34 return (descend);
35 }
36 vprintf(stdout, "%s", type == LEAF ? gettext("leaf") : gettext("dir "));
37 (void) fprintf(stdout, "%10lu\t%s\n", ino, name);
38 return (descend);
39 }
40
41 /*
42 * This implements the 'x' option.
43 * Request that new entries be extracted.
44 */
45 long
addfile(char * name,ino_t ino,int type)46 addfile(char *name, ino_t ino, int type)
47 {
48 struct entry *ep;
49 long descend = hflag ? GOOD : FAIL;
50 char buf[100];
51
52 /* Don't know if ino_t is long or long long, so be safe w/ *printf() */
53
54 if (BIT(ino, dumpmap) == 0) {
55 if (mflag) {
56 dprintf(stdout, gettext(
57 "%s: not on the volume\n"), name);
58 } else {
59 dprintf(stdout, gettext(
60 "inode %llu: not on the volume\n"),
61 (u_longlong_t)ino);
62 }
63 return (descend);
64 }
65 if (!mflag) {
66 (void) snprintf(buf, sizeof (buf), "./%llu", (u_longlong_t)ino);
67 buf[sizeof (buf) - 1] = '\0';
68 name = buf;
69 if (type == NODE) {
70 (void) genliteraldir(name, ino);
71 return (descend);
72 }
73 }
74 ep = lookupino(ino);
75 if (ep != NIL) {
76 if (strcmp(name, myname(ep)) == 0) {
77 /* LINTED: result fits into a short */
78 ep->e_flags |= NEW;
79 return (descend);
80 }
81 type |= LINK;
82 }
83 ep = addentry(name, ino, type);
84 if (type == NODE)
85 newnode(ep);
86 /* LINTED: result fits into a short */
87 ep->e_flags |= NEW;
88 return (descend);
89 }
90
91 /*
92 * This is used by the 'i' option to undo previous requests made by addfile.
93 * Delete entries from the request queue.
94 */
95 /* ARGSUSED */
96 long
deletefile(char * name,ino_t ino,int type)97 deletefile(char *name, ino_t ino, int type)
98 {
99 long descend = hflag ? GOOD : FAIL;
100 struct entry *ep;
101
102 if (BIT(ino, dumpmap) == 0) {
103 return (descend);
104 }
105 ep = lookupino(ino);
106 if (ep != NIL) {
107 /* LINTED: result fits into a short */
108 ep->e_flags &= ~NEW;
109 }
110 return (descend);
111 }
112
113 /*
114 * The following four routines implement the incremental
115 * restore algorithm. The first removes old entries, the second
116 * does renames and calculates the extraction list, the third
117 * cleans up link names missed by the first two, and the final
118 * one deletes old directories.
119 *
120 * Directories cannot be immediately deleted, as they may have
121 * other files in them which need to be moved out first. As
122 * directories to be deleted are found, they are put on the
123 * following deletion list. After all deletions and renames
124 * are done, this list is actually deleted.
125 */
126 static struct entry *removelist;
127
128 /*
129 * Remove unneeded leaves from the old tree.
130 * Remove directories from the lookup chains.
131 */
132 void
removeoldleaves(void)133 removeoldleaves(void)
134 {
135 struct entry *ep;
136 ino_t i;
137
138 vprintf(stdout, gettext("Mark entries to be removed.\n"));
139 for (i = ROOTINO + 1; i < maxino; i++) {
140 if (BIT(i, clrimap))
141 continue;
142 ep = lookupino(i);
143 if (ep == NIL)
144 continue;
145 while (ep != NIL) {
146 dprintf(stdout, gettext("%s: REMOVE\n"), myname(ep));
147 removexattrs(ep->e_xattrs);
148 if (ep->e_type == LEAF) {
149 removeleaf(ep);
150 freeentry(ep);
151 } else {
152 mktempname(ep);
153 deleteino(ep->e_ino);
154 /*
155 * once the inode is deleted from the symbol
156 * table, the e_next field is reusable
157 */
158 ep->e_next = removelist;
159 removelist = ep;
160 }
161 ep = ep->e_links;
162 }
163 }
164 }
165
166 /*
167 * For each directory entry on the incremental tape, determine which
168 * category it falls into as follows:
169 * KEEP - entries that are to be left alone.
170 * NEW - new entries to be added.
171 * EXTRACT - files that must be updated with new contents.
172 * LINK - new links to be added.
173 * Renames are done at the same time.
174 */
175 long
nodeupdates(char * name,ino_t ino,int type)176 nodeupdates(char *name, ino_t ino, int type)
177 {
178 struct entry *ep, *np, *ip;
179 long descend = GOOD;
180 int lookuptype = 0;
181 int key = 0;
182 /* key values */
183 #define ONTAPE 0x1 /* inode is on the tape */
184 #define INOFND 0x2 /* inode already exists */
185 #define NAMEFND 0x4 /* name already exists */
186 #define MODECHG 0x8 /* mode of inode changed */
187
188 /*
189 * This routine is called once for each element in the
190 * directory hierarchy, with a full path name.
191 * The "type" value is incorrectly specified as LEAF for
192 * directories that are not on the dump tape.
193 *
194 * Check to see if the file is on the tape.
195 */
196 if (BIT(ino, dumpmap))
197 key |= ONTAPE;
198 /*
199 * Check to see if the name exists, and if the name is a link.
200 */
201 np = lookupname(name);
202 if (np != NIL) {
203 key |= NAMEFND;
204 ip = lookupino(np->e_ino);
205 if (ip == NULL) {
206 (void) fprintf(stderr,
207 gettext("corrupted symbol table\n"));
208 done(1);
209 }
210 if (ip != np)
211 lookuptype = LINK;
212 }
213 /*
214 * Check to see if the inode exists, and if one of its links
215 * corresponds to the name (if one was found).
216 */
217 ip = lookupino(ino);
218 if (ip != NIL) {
219 key |= INOFND;
220 for (ep = ip->e_links; ep != NIL; ep = ep->e_links) {
221 if (ep == np) {
222 /*
223 * Need to set the NEW flag on the hard link
224 * so it gets created because we extract the
225 * "parent". If the NAMEFND key is set, remove
226 * the leaf.
227 */
228 if (ip->e_flags & EXTRACT) {
229 if (key & NAMEFND) {
230 removeleaf(np);
231 freeentry(np);
232 np = NIL;
233 key &= ~NAMEFND;
234 }
235 ep->e_flags |= NEW;
236 } else {
237 ip = ep;
238 }
239 break;
240 }
241 }
242 }
243 /*
244 * If both a name and an inode are found, but they do not
245 * correspond to the same file, then both the inode that has
246 * been found and the inode corresponding to the name that
247 * has been found need to be renamed. The current pathname
248 * is the new name for the inode that has been found. Since
249 * all files to be deleted have already been removed, the
250 * named file is either a now-unneeded link, or it must live
251 * under a new name in this dump level. If it is a link, it
252 * can be removed. If it is not a link, it is given a
253 * temporary name in anticipation that it will be renamed
254 * when it is later found by inode number.
255 */
256 if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) {
257 if (lookuptype == LINK) {
258 removeleaf(np);
259 freeentry(np);
260 } else {
261 dprintf(stdout,
262 gettext("name/inode conflict, mktempname %s\n"),
263 myname(np));
264 mktempname(np);
265 }
266 np = NIL;
267 key &= ~NAMEFND;
268 }
269 if ((key & ONTAPE) &&
270 (((key & INOFND) && ip->e_type != type) ||
271 ((key & NAMEFND) && np->e_type != type)))
272 key |= MODECHG;
273
274 /*
275 * Decide on the disposition of the file based on its flags.
276 * Note that we have already handled the case in which
277 * a name and inode are found that correspond to different files.
278 * Thus if both NAMEFND and INOFND are set then ip == np.
279 */
280 switch (key) {
281
282 /*
283 * A previously existing file has been found.
284 * Mark it as KEEP so that other links to the inode can be
285 * detected, and so that it will not be reclaimed by the search
286 * for unreferenced names.
287 */
288 case INOFND|NAMEFND:
289 /* LINTED: result fits into a short */
290 ip->e_flags |= KEEP;
291 dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
292 flagvalues(ip));
293 break;
294
295 /*
296 * A file on the tape has a name which is the same as a name
297 * corresponding to a different file in the previous dump.
298 * Since all files to be deleted have already been removed,
299 * this file is either a now-unneeded link, or it must live
300 * under a new name in this dump level. If it is a link, it
301 * can simply be removed. If it is not a link, it is given a
302 * temporary name in anticipation that it will be renamed
303 * when it is later found by inode number (see INOFND case
304 * below). The entry is then treated as a new file.
305 */
306 case ONTAPE|NAMEFND:
307 case ONTAPE|NAMEFND|MODECHG:
308 if (lookuptype == LINK || key == (ONTAPE|NAMEFND)) {
309 removeleaf(np);
310 freeentry(np);
311 } else {
312 /*
313 * Create a temporary node only if MODECHG.
314 */
315 mktempname(np);
316 }
317 /*FALLTHROUGH*/
318
319 /*
320 * A previously non-existent file.
321 * Add it to the file system, and request its extraction.
322 * If it is a directory, create it immediately.
323 * (Since the name is unused there can be no conflict)
324 */
325 case ONTAPE:
326 ep = addentry(name, ino, type);
327 if (type == NODE)
328 newnode(ep);
329 /* LINTED: result fits into a short */
330 ep->e_flags |= NEW|KEEP;
331 dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
332 flagvalues(ep));
333 break;
334
335 /*
336 * A file with the same inode number, but a different
337 * name has been found. If the other name has not already
338 * been found (indicated by the KEEP flag, see above) then
339 * this must be a new name for the file, and it is renamed.
340 * If the other name has been found then this must be a
341 * link to the file. Hard links to directories are not
342 * permitted, and are either deleted or converted to
343 * symbolic links. Finally, if the file is on the tape,
344 * a request is made to extract it.
345 */
346 case ONTAPE|INOFND:
347 if (type == LEAF && (ip->e_flags & KEEP) == 0) {
348 /* LINTED: result fits into a short */
349 ip->e_flags |= EXTRACT;
350 }
351 /*FALLTHROUGH*/
352 case INOFND:
353 if ((ip->e_flags & KEEP) == 0) {
354 renameit(myname(ip), name);
355 moveentry(ip, name);
356 /* LINTED: result fits into a short */
357 ip->e_flags |= KEEP;
358 dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
359 flagvalues(ip));
360 break;
361 }
362 if (ip->e_type == NODE) {
363 descend = FAIL;
364 (void) fprintf(stderr, gettext(
365 "deleted hard link %s to directory %s\n"),
366 name, myname(ip));
367 break;
368 }
369 ep = addentry(name, ino, type|LINK);
370 /* LINTED: result fits into a short */
371 ep->e_flags |= NEW;
372 dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name,
373 flagvalues(ep));
374 break;
375
376 /*
377 * A previously known file which is to be updated.
378 */
379 case ONTAPE|INOFND|NAMEFND:
380 /*
381 * Extract leaf nodes.
382 */
383 if (type == LEAF) {
384 /* LINTED: result fits into a short */
385 np->e_flags |= EXTRACT;
386 }
387 /* LINTED: result fits into a short */
388 np->e_flags |= KEEP;
389 dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
390 flagvalues(np));
391 break;
392
393 /*
394 * An inode is being reused in a completely different way.
395 * Normally an extract can simply do an "unlink" followed
396 * by a "creat". Here we must do effectively the same
397 * thing. The complications arise because we cannot really
398 * delete a directory since it may still contain files
399 * that we need to rename, so we delete it from the symbol
400 * table, and put it on the list to be deleted eventually.
401 * Conversely if a directory is to be created, it must be
402 * done immediately, rather than waiting until the
403 * extraction phase.
404 */
405 case ONTAPE|INOFND|MODECHG:
406 case ONTAPE|INOFND|NAMEFND|MODECHG:
407 if (ip->e_flags & KEEP) {
408 badentry(ip, gettext("cannot KEEP and change modes"));
409 break;
410 }
411 if (ip->e_type == LEAF) {
412 /* changing from leaf to node */
413 removeleaf(ip);
414 freeentry(ip);
415 ip = addentry(name, ino, type);
416 newnode(ip);
417 } else {
418 /* changing from node to leaf */
419 if ((ip->e_flags & TMPNAME) == 0)
420 mktempname(ip);
421 deleteino(ip->e_ino);
422 ip->e_next = removelist;
423 removelist = ip;
424 ip = addentry(name, ino, type);
425 }
426 /* LINTED: result fits into a short */
427 ip->e_flags |= NEW|KEEP;
428 dprintf(stdout, "[%s] %s: %s\n", keyval(key), name,
429 flagvalues(ip));
430 break;
431
432 /*
433 * A hard link to a directory that has been removed.
434 * Ignore it.
435 */
436 case NAMEFND:
437 dprintf(stdout, gettext("[%s] %s: Extraneous name\n"),
438 keyval(key), name);
439 descend = FAIL;
440 break;
441
442 /*
443 * If we find a directory entry for a file that is not on
444 * the tape, then we must have found a file that was created
445 * while the dump was in progress. Since we have no contents
446 * for it, we discard the name knowing that it will be on the
447 * next incremental tape.
448 */
449 case 0:
450 (void) fprintf(stderr,
451 gettext("%s: (inode %lu) not found on volume\n"),
452 name, ino);
453 break;
454
455 /*
456 * If any of these arise, something is grievously wrong with
457 * the current state of the symbol table.
458 */
459 case INOFND|NAMEFND|MODECHG:
460 case NAMEFND|MODECHG:
461 case INOFND|MODECHG:
462 (void) fprintf(stderr, "[%s] %s: %s\n",
463 keyval(key), name, gettext("inconsistent state"));
464 done(1);
465 /* NOTREACHED */
466
467 /*
468 * These states "cannot" arise for any state of the symbol table.
469 */
470 case ONTAPE|MODECHG:
471 case MODECHG:
472 default:
473 (void) fprintf(stderr, "[%s] %s: %s\n",
474 keyval(key), name, gettext("impossible state"));
475 done(1);
476 /* NOTREACHED */
477 }
478 return (descend);
479 }
480
481 /*
482 * Calculate the active flags in a key.
483 */
484 static char *
keyval(int key)485 keyval(int key)
486 {
487 static char keybuf[32];
488
489 /* Note longest case is everything except |NIL */
490
491 (void) strcpy(keybuf, "|NIL");
492 keybuf[0] = '\0';
493 if (key & ONTAPE)
494 (void) strcat(keybuf, "|ONTAPE");
495 if (key & INOFND)
496 (void) strcat(keybuf, "|INOFND");
497 if (key & NAMEFND)
498 (void) strcat(keybuf, "|NAMEFND");
499 if (key & MODECHG)
500 (void) strcat(keybuf, "|MODECHG");
501 return (&keybuf[1]);
502 }
503
504 /*
505 * Find unreferenced link names.
506 */
507 void
findunreflinks(void)508 findunreflinks(void)
509 {
510 struct entry *ep, *np;
511 ino_t i;
512
513 vprintf(stdout, gettext("Find unreferenced names.\n"));
514 for (i = ROOTINO; i < maxino; i++) {
515 ep = lookupino(i);
516 if (ep == NIL || ep->e_type == LEAF || BIT(i, dumpmap) == 0)
517 continue;
518 for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
519 if (np->e_flags == 0) {
520 dprintf(stdout, gettext(
521 "%s: remove unreferenced name\n"),
522 myname(np));
523 removeleaf(np);
524 freeentry(np);
525 }
526 }
527 }
528 /*
529 * Any leaves remaining in removed directories are unreferenced.
530 */
531 for (ep = removelist; ep != NIL; ep = ep->e_next) {
532 for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
533 if (np->e_type == LEAF) {
534 if (np->e_flags != 0)
535 badentry(np, gettext(
536 "unreferenced with flags"));
537 dprintf(stdout, gettext(
538 "%s: remove unreferenced name\n"),
539 myname(np));
540 removeleaf(np);
541 freeentry(np);
542 }
543 }
544 }
545 }
546
547 /*
548 * Remove old nodes (directories).
549 * Note that this routine runs in O(N*D) where:
550 * N is the number of directory entries to be removed.
551 * D is the maximum depth of the tree.
552 * If N == D this can be quite slow. If the list were
553 * topologically sorted, the deletion could be done in
554 * time O(N).
555 */
556 void
removeoldnodes(void)557 removeoldnodes(void)
558 {
559 struct entry *ep, **prev;
560 long change;
561
562 vprintf(stdout, gettext("Remove old nodes (directories).\n"));
563 do {
564 change = 0;
565 prev = &removelist;
566 for (ep = removelist; ep != NIL; ep = *prev) {
567 if (ep->e_entries != NIL) {
568 prev = &ep->e_next;
569 continue;
570 }
571 *prev = ep->e_next;
572 removenode(ep);
573 freeentry(ep);
574 change++;
575 }
576 } while (change);
577 for (ep = removelist; ep != NIL; ep = ep->e_next)
578 badentry(ep, gettext("cannot remove, non-empty"));
579 }
580
581 /*
582 * This is the routine used to extract files for the 'r' command.
583 * Extract new leaves.
584 */
585 void
createleaves(char * symtabfile)586 createleaves(char *symtabfile)
587 {
588 struct entry *ep;
589 char name[MAXCOMPLEXLEN];
590 ino_t first;
591 int curvol;
592
593 if (command == 'R') {
594 vprintf(stdout, gettext("Continue extraction of new leaves\n"));
595 } else {
596 vprintf(stdout, gettext("Extract new leaves.\n"));
597 dumpsymtable(symtabfile, volno);
598 }
599 first = lowerbnd(ROOTINO);
600 curvol = volno;
601 while (curfile.ino < maxino) {
602 first = lowerbnd(first);
603 /*
604 * If the next available file is not the one which we
605 * expect then we have missed one or more files. Since
606 * we do not request files that were not on the tape,
607 * the lost files must have been due to a tape read error,
608 * or a file that was removed while the dump was in progress.
609 *
610 * The loop will terminate with first == maxino, if not
611 * sooner. Due to the e_flags manipulation, lowerbnd()
612 * will never return its argument.
613 */
614 while (first < curfile.ino) {
615 ep = lookupino(first);
616 if (ep == NIL) {
617 (void) fprintf(stderr,
618 gettext("%d: bad first\n"), first);
619 done(1);
620 }
621 (void) fprintf(stderr,
622 gettext("%s: not found on volume\n"),
623 myname(ep));
624 /* LINTED: result fits into a short */
625 ep->e_flags &= ~(NEW|EXTRACT);
626 first = lowerbnd(first);
627 }
628 /*
629 * If we find files on the tape that have no corresponding
630 * directory entries, then we must have found a file that
631 * was created while the dump was in progress. Since we have
632 * no name for it, we discard it knowing that it will be
633 * on the next incremental tape.
634 */
635 if (first != curfile.ino) {
636 (void) fprintf(stderr,
637 gettext("expected next file %d, got %d\n"),
638 first, curfile.ino);
639 skipfile();
640 goto next;
641 }
642 ep = lookupino(curfile.ino);
643 if (ep == NIL) {
644 (void) fprintf(stderr,
645 gettext("unknown file on volume\n"));
646 done(1);
647 }
648 if ((ep->e_flags & (NEW|EXTRACT)) == 0)
649 badentry(ep, gettext("unexpected file on volume"));
650 /*
651 * If the file is to be extracted, then the old file must
652 * be removed since its type may change from one leaf type
653 * to another (eg "file" to "character special"). But we
654 * also need to preserve any existing extended attributes;
655 * so first rename the file, then move its attributes, then
656 * remove it.
657 */
658 if ((ep->e_flags & EXTRACT) != 0) {
659 char *sname = savename(ep->e_name);
660 complexcpy(name, myname(ep), MAXCOMPLEXLEN);
661 mktempname(ep);
662 (void) extractfile(name);
663 movexattrs(myname(ep), name);
664 removeleaf(ep);
665 freename(ep->e_name);
666 ep->e_name = sname;
667 ep->e_namlen = strlen(ep->e_name);
668 /* LINTED: result fits into a short */
669 ep->e_flags &= ~REMOVED;
670 } else {
671 (void) extractfile(myname(ep));
672 }
673 /* LINTED: result fits into a short */
674 ep->e_flags &= ~(NEW|EXTRACT);
675 /*
676 * We checkpoint the restore after every tape reel, so
677 * as to simplify the amount of work required by the
678 * 'R' command.
679 */
680 next:
681 if (curvol != volno) {
682 dumpsymtable(symtabfile, volno);
683 skipmaps();
684 curvol = volno;
685 }
686 }
687 }
688
689 /*
690 * This is the routine used to extract files for the 'x' and 'i' commands.
691 * Efficiently extract a subset of the files on a tape.
692 */
693 void
createfiles(void)694 createfiles(void)
695 {
696 ino_t first, next, last;
697 struct entry *ep;
698 int curvol, nextvol;
699
700 vprintf(stdout, gettext("Extract requested files\n"));
701 first = lowerbnd(ROOTINO);
702 last = upperbnd(maxino - 1);
703 nextvol = volnumber(first);
704 if (nextvol == 0) {
705 curfile.action = SKIP;
706 getvol(1);
707 skipmaps();
708 skipdirs();
709 }
710 for (;;) {
711 first = lowerbnd(first);
712 last = upperbnd(last);
713 /*
714 * Check to see if any files remain to be extracted
715 */
716 if (first > last)
717 return;
718 /*
719 * If a map of inode numbers to tape volumes is
720 * available, then select the next volume to be read.
721 */
722 if (nextvol > 0) {
723 nextvol = volnumber(first);
724 if (nextvol != volno) {
725 curfile.action = UNKNOWN;
726 getvol(nextvol);
727 skipmaps();
728 }
729 }
730 /*
731 * Reject any volumes with inodes greater than
732 * the last one needed. This will only be true
733 * if the above code has not selected a volume.
734 */
735 while (curfile.ino > last) {
736 curfile.action = SKIP;
737 getvol(0);
738 skipmaps();
739 skipdirs();
740 }
741 /*
742 * Decide on the next inode needed.
743 * Skip across the inodes until it is found
744 * or an out of order volume change is encountered
745 */
746 next = lowerbnd(curfile.ino);
747 do {
748 curvol = volno;
749 while (next > curfile.ino && volno == curvol)
750 skipfile();
751 skipmaps();
752 skipdirs();
753 } while (volno == curvol + 1);
754 /*
755 * If volume change out of order occurred the
756 * current state must be recalculated
757 */
758 if (volno != curvol)
759 continue;
760 /*
761 * If the current inode is greater than the one we were
762 * looking for then we missed the one we were looking for.
763 * Since we only attempt to extract files listed in the
764 * dump map, the lost files must have been due to a tape
765 * read error, or a file that was removed while the dump
766 * was in progress. Thus we report all requested files
767 * between the one we were looking for, and the one we
768 * found as missing, and delete their request flags.
769 */
770 while (next < curfile.ino) {
771 ep = lookupino(next);
772 if (ep == NIL) {
773 (void) fprintf(stderr,
774 gettext("corrupted symbol table\n"));
775 done(1);
776 }
777 (void) fprintf(stderr,
778 gettext("%s: not found on volume\n"),
779 myname(ep));
780 /* LINTED: result fits into a short */
781 ep->e_flags &= ~NEW;
782 next = lowerbnd(next);
783 }
784 /*
785 * The current inode is the one that we are looking for,
786 * so extract it per its requested name.
787 */
788 if (next == curfile.ino && next <= last) {
789 ep = lookupino(next);
790 if (ep == NIL) {
791 (void) fprintf(stderr,
792 gettext("corrupted symbol table\n"));
793 done(1);
794 }
795 (void) extractfile(myname(ep));
796 /* LINTED: result fits into a short */
797 ep->e_flags &= ~NEW;
798 if (volno != curvol)
799 skipmaps();
800 }
801 }
802 }
803
804 /*
805 * Add links.
806 */
807 void
createlinks(void)808 createlinks(void)
809 {
810 struct entry *np, *ep;
811 ino_t i;
812 int dfd;
813 char *to, *from;
814 int saverr;
815
816 vprintf(stdout, gettext("Add links\n"));
817 for (i = ROOTINO; i < maxino; i++) {
818 ep = lookupino(i);
819 if (ep == NIL)
820 continue;
821 to = savename(myname(ep));
822 for (np = ep->e_links; np != NIL; np = np->e_links) {
823 if ((np->e_flags & NEW) == 0)
824 continue;
825 resolve(myname(np), &dfd, &from);
826 if (dfd != AT_FDCWD) {
827 if (fchdir(dfd) < 0) {
828 saverr = errno;
829 (void) fprintf(stderr,
830 gettext(
831 "%s->%s: link failed: %s\n"),
832 from, to, strerror(saverr));
833 (void) close(dfd);
834 continue;
835 }
836 }
837 if (ep->e_type == NODE) {
838 (void) lf_linkit(to, from, SYMLINK);
839 } else {
840 (void) lf_linkit(to, from, HARDLINK);
841 }
842 /* LINTED: result fits into a short */
843 np->e_flags &= ~NEW;
844 if (dfd != AT_FDCWD) {
845 fchdir(savepwd);
846 (void) close(dfd);
847 }
848 }
849 freename(to);
850 }
851 }
852
853 /*
854 * Check the symbol table.
855 * We do this to insure that all the requested work was done, and
856 * that no temporary names remain.
857 */
858 void
checkrestore(void)859 checkrestore(void)
860 {
861 struct entry *ep;
862 ino_t i;
863
864 vprintf(stdout, gettext("Check the symbol table.\n"));
865 for (i = ROOTINO; i < maxino; i++) {
866 for (ep = lookupino(i); ep != NIL; ep = ep->e_links) {
867 /* LINTED: result fits into a short */
868 ep->e_flags &= ~KEEP;
869 if (ep->e_type == NODE) {
870 /* LINTED: result fits into a short */
871 ep->e_flags &= ~(NEW|EXISTED);
872 }
873 if ((ep->e_flags & ~(XATTR|XATTRROOT)) != 0)
874 badentry(ep, gettext("incomplete operations"));
875 }
876 }
877 }
878
879 /*
880 * Compare with the directory structure on the tape
881 * A paranoid check that things are as they should be.
882 */
883 long
verifyfile(char * name,ino_t ino,int type)884 verifyfile(char *name, ino_t ino, int type)
885 {
886 struct entry *np, *ep;
887 long descend = GOOD;
888
889 ep = lookupname(name);
890 if (ep == NIL) {
891 (void) fprintf(stderr,
892 gettext("Warning: missing name %s\n"), name);
893 return (FAIL);
894 }
895 np = lookupino(ino);
896 if (np != ep)
897 descend = FAIL;
898 for (; np != NIL; np = np->e_links)
899 if (np == ep)
900 break;
901 if (np == NIL) {
902 (void) fprintf(stderr, gettext("missing inumber %d\n"), ino);
903 done(1);
904 }
905 if (ep->e_type == LEAF && type != LEAF)
906 badentry(ep, gettext("type should be LEAF"));
907 return (descend);
908 }
909
910 /*
911 * This routine does not actually remove any attribute files, it
912 * just removes entries from the symbol table. The attribute files
913 * themselves are assumed to be removed automatically when the
914 * parent file is removed.
915 */
916 static void
removexattrs(struct entry * ep)917 removexattrs(struct entry *ep)
918 {
919 struct entry *np = ep;
920
921 if (ep == NIL)
922 return;
923 for (np = ep->e_entries; np != NIL; np = np->e_sibling) {
924 if (np->e_type == NODE) {
925 removexattrs(np);
926 } else {
927 np->e_flags |= REMOVED;
928 freeentry(np);
929 }
930 }
931 ep->e_flags |= REMOVED;
932 freeentry(ep);
933 }
934
935 /*
936 * Move all the extended attributes associated with orig to
937 * the file named by the second argument (targ).
938 */
939 static void
movexattrs(char * orig,char * targ)940 movexattrs(char *orig, char *targ)
941 {
942 char *to, *from;
943 int fromfd, fromdir, tofd, todir, tfd;
944 DIR *dirp = NULL;
945 struct dirent *dp = NULL;
946
947 fromfd = tofd = fromdir = todir = tfd = -1;
948
949 resolve(orig, &tfd, &from);
950 if (tfd == AT_FDCWD && pathconf(orig, _PC_XATTR_EXISTS) != 1) {
951 /* no attributes to move */
952 return;
953 }
954 if ((fromfd = openat64(tfd, from, O_RDONLY|O_NONBLOCK)) == -1) {
955 fprintf(stderr, gettext("%s: cannot move attributes: "), from);
956 perror("");
957 if (tfd != AT_FDCWD) (void) close(tfd);
958 goto out;
959 }
960
961 if (fpathconf(fromfd, _PC_XATTR_EXISTS) != 1) {
962 /* no attributes to move */
963 if (tfd != AT_FDCWD) (void) close(tfd);
964 goto out;
965 }
966 if ((fromdir = openat64(fromfd, ".",
967 O_RDONLY|O_NONBLOCK|O_XATTR)) == -1) {
968 fprintf(stderr, gettext("%s: cannot access attributes: "),
969 from);
970 perror("");
971 if (tfd != AT_FDCWD)
972 (void) close(tfd);
973 goto out;
974 }
975 if (tfd != AT_FDCWD) (void) close(tfd);
976
977 resolve(targ, &tfd, &to);
978 if ((tofd = openat64(tfd, to, O_RDONLY|O_NONBLOCK)) == -1 ||
979 (todir = openat64(tofd, ".", O_RDONLY|O_NONBLOCK|O_XATTR)) == -1) {
980 fprintf(stderr, gettext("%s: cannot create attributes: "), to);
981 perror("");
982 goto out;
983 }
984 if (tfd != AT_FDCWD) (void) close(tfd);
985 (void) close(tofd);
986
987 if ((tfd = dup(fromdir)) == -1 ||
988 (dirp = fdopendir(tfd)) == NULL) {
989 fprintf(stderr,
990 gettext("%s: cannot allocate DIR structure to attribute "
991 "directory: "), from);
992 perror("");
993 if (tfd != -1) (void) close(tfd);
994 goto out;
995 }
996
997 while ((dp = readdir(dirp)) != NULL) {
998 if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
999 (dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
1000 dp->d_name[2] == '\0'))
1001 continue;
1002 if ((renameat(fromdir, dp->d_name, todir, dp->d_name)) == -1) {
1003 fprintf(stderr,
1004 gettext("%s: cannot move attribute %s: "),
1005 from, dp->d_name);
1006 goto out;
1007 }
1008 }
1009 out:
1010 if (fromfd != -1)
1011 (void) close(fromfd);
1012 if (tofd != -1)
1013 (void) close(tofd);
1014 if (dirp != NULL)
1015 (void) closedir(dirp);
1016 if (fromdir != -1)
1017 (void) close(fromdir);
1018 if (todir != -1)
1019 (void) close(todir);
1020 }
1021