9402 restore: this statement may fall through
[unleashed.git] / usr / src / cmd / backup / restore / symtab.c
blob5bffc3d5c44abdf5faa5c3816b4e9e7d7977ec3e
1 /*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
7 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
8 /* All Rights Reserved */
11 * Copyright (c) 1996,1998,2001 by Sun Microsystems, Inc.
12 * All rights reserved.
16 * These routines maintain the symbol table which tracks the state
17 * of the file system being restored. They provide lookup by either
18 * name or inode number. They also provide for creation, deletion,
19 * and renaming of entries. Because of the dynamic nature of pathnames,
20 * names should not be saved, but always constructed just before they
21 * are needed, by calling "myname".
24 #include "restore.h"
25 #include <limits.h>
28 * The following variables define the inode symbol table.
29 * The primary hash table is dynamically allocated based on
30 * the number of inodes in the file system (maxino), scaled by
31 * HASHFACTOR. The variable "entry" points to the hash table;
32 * the variable "entrytblsize" indicates its size (in entries).
34 #define HASHFACTOR 5
35 static struct entry **entry;
36 static uint_t entrytblsize;
38 #ifdef __STDC__
39 static void addino(ino_t, struct entry *);
40 static struct entry *lookupparent(char *);
41 static void removeentry(struct entry *);
42 #else
43 static void addino();
44 static struct entry *lookupparent();
45 static void removeentry();
46 #endif
49 * Look up an entry by inode number
51 struct entry *
52 lookupino(inum)
53 ino_t inum;
55 struct entry *ep;
57 if (inum < ROOTINO || inum >= maxino)
58 return (NIL);
59 for (ep = entry[inum % entrytblsize]; ep != NIL; ep = ep->e_next)
60 if (ep->e_ino == inum)
61 return (ep);
62 return (NIL);
66 * We now ignore inodes that are out of range. This
67 * allows us to attempt to proceed in the face of
68 * a corrupted archive, albeit with future complaints
69 * about failed inode lookups. We only complain once
70 * about range problems, to avoid irritating the user
71 * without providing any useful information. Failed
72 * lookups have the bogus name, which is useful, so
73 * they always happen.
75 static int complained_about_range = 0;
78 * Add an entry into the entry table
80 static void
81 addino(inum, np)
82 ino_t inum;
83 struct entry *np;
85 struct entry **epp;
87 if (inum < ROOTINO || inum >= maxino) {
88 if (!complained_about_range) {
89 panic(gettext("%s: out of range %d\n"),
90 "addino", inum);
91 complained_about_range = 1;
93 return;
95 epp = &entry[inum % entrytblsize];
96 np->e_ino = inum;
97 np->e_next = *epp;
98 *epp = np;
99 if (dflag)
100 for (np = np->e_next; np != NIL; np = np->e_next)
101 if (np->e_ino == inum)
102 badentry(np, gettext("duplicate inum"));
106 * Delete an entry from the entry table. We assume our caller
107 * arranges for the necessary memory reclamation, if needed.
109 void
110 deleteino(inum)
111 ino_t inum;
113 struct entry *next;
114 struct entry **prev;
116 if (inum < ROOTINO || inum >= maxino) {
117 if (!complained_about_range) {
118 panic(gettext("%s: out of range %d\n"),
119 "deleteino", inum);
120 complained_about_range = 1;
122 return;
125 prev = &entry[inum % entrytblsize];
126 for (next = *prev; next != NIL; next = next->e_next) {
127 if (next->e_ino == inum) {
128 next->e_ino = 0;
129 *prev = next->e_next;
130 return;
132 prev = &next->e_next;
137 * Look up an entry by name.
138 * NOTE: this function handles "complex" pathnames (as returned
139 * by myname()) for extended file attributes. The name string
140 * provided to this function should be terminated with *two*
141 * NULL characters.
143 struct entry *
144 lookupname(name)
145 char *name;
147 struct entry *ep;
148 char *np, *cp;
149 char buf[MAXPATHLEN];
151 if (strlen(name) > (sizeof (buf) - 1)) {
152 (void) fprintf(stderr, gettext("%s: ignoring too-long name\n"),
153 "lookupname");
154 return (NIL);
157 cp = name;
158 for (ep = lookupino(ROOTINO); ep != NIL; ep = ep->e_entries) {
159 np = buf;
160 while (*cp != '/' && *cp != '\0')
161 *np++ = *cp++;
162 *np = '\0';
163 for (; ep != NIL; ep = ep->e_sibling)
164 if (strcmp(ep->e_name, buf) == 0)
165 break;
166 if (*cp++ == '\0') {
167 if (*cp != '\0') {
168 ep = ep->e_xattrs;
170 * skip over the "./" prefix on all
171 * extended attribute paths
173 cp += 2;
175 if (*cp == '\0')
176 return (ep);
178 if (ep == NIL)
179 break;
181 return (NIL);
185 * Look up the parent of a pathname. This routine accepts complex
186 * names so the provided name argument must terminate with two NULLs.
188 static struct entry *
189 lookupparent(name)
190 char *name;
192 struct entry *ep;
193 char *tailindex, savechar, *lastpart;
194 int xattrparent = 0;
196 /* find the last component of the complex name */
197 lastpart = name;
198 LASTPART(lastpart);
199 tailindex = strrchr(lastpart, '/');
200 if (tailindex == 0) {
201 if (lastpart == name)
202 return (NIL);
204 * tailindex normaly points to the '/' character
205 * dividing the path, but in the case of an extended
206 * attribute transition it will point to the NULL
207 * separator in front of the attribute path.
209 tailindex = lastpart - 1;
210 xattrparent = 1;
211 } else {
212 *tailindex = '\0';
214 savechar = *(tailindex+1);
215 *(tailindex+1) = '\0';
216 ep = lookupname(name);
217 if (ep != NIL && !xattrparent && ep->e_type != NODE)
218 panic(gettext("%s is not a directory\n"), name);
219 if (!xattrparent) *tailindex = '/';
220 *(tailindex+1) = savechar;
221 return (ep);
225 * Determine the current pathname of a node or leaf.
226 * The returned pathname will be multiple strings with NULL separators:
228 * ./<path>/entry\0<path>/attrentry\0<path>/...\0\0
229 * ^ ^ ^ ^
230 * return pntr entry attr recursive attr terminator
232 * Guaranteed to return a name that fits within MAXCOMPLEXLEN and is
233 * terminated with two NULLs.
235 char *
236 myname(ep)
237 struct entry *ep;
239 char *cp;
240 struct entry *root = lookupino(ROOTINO);
241 static char namebuf[MAXCOMPLEXLEN];
243 cp = &namebuf[MAXCOMPLEXLEN - 3];
244 *(cp + 1) = '\0';
245 *(cp + 2) = '\0';
246 while (cp > &namebuf[ep->e_namlen]) {
247 cp -= ep->e_namlen;
248 bcopy(ep->e_name, cp, (size_t)ep->e_namlen);
249 if (ep == root)
250 return (cp);
251 if (ep->e_flags & XATTRROOT)
252 *(--cp) = '\0';
253 else
254 *(--cp) = '/';
255 ep = ep->e_parent;
257 panic(gettext("%s%s: pathname too long\n"), "...", cp);
258 return (cp);
262 * Unused symbol table entries are linked together on a freelist
263 * headed by the following pointer.
265 static struct entry *freelist = NIL;
268 * add an entry to the symbol table
270 struct entry *
271 addentry(name, inum, type)
272 char *name;
273 ino_t inum;
274 int type;
276 struct entry *np, *ep;
277 char *cp;
279 if (freelist != NIL) {
280 np = freelist;
281 freelist = np->e_next;
282 (void) bzero((char *)np, (size_t)sizeof (*np));
283 } else {
284 np = (struct entry *)calloc(1, sizeof (*np));
285 if (np == NIL) {
286 (void) fprintf(stderr,
287 gettext("no memory to extend symbol table\n"));
288 done(1);
291 np->e_type = type & ~(LINK|ROOT);
292 if (inattrspace)
293 np->e_flags |= XATTR;
294 ep = lookupparent(name);
295 if (ep == NIL) {
296 if (inum != ROOTINO || lookupino(ROOTINO) != NIL) {
297 (void) fprintf(stderr, gettext(
298 "%s: bad name %s\n"), "addentry", name);
299 assert(0);
300 done(1);
302 np->e_name = savename(name);
303 /* LINTED: savename guarantees that strlen fits in e_namlen */
304 np->e_namlen = strlen(name);
305 np->e_parent = np;
306 addino(ROOTINO, np);
307 return (np);
310 if (np->e_flags & XATTR) {
312 * skip to the last part of the complex string: it
313 * containes the extended attribute file name.
315 LASTPART(name);
317 cp = strrchr(name, '/');
318 if (cp == NULL)
319 cp = name;
320 else
321 cp++;
323 np->e_name = savename(cp);
324 /* LINTED: savename guarantees that strlen will fit */
325 np->e_namlen = strlen(np->e_name);
326 np->e_parent = ep;
328 * Extended attribute root directories must be linked to their
329 * "parents" via the e_xattrs field. Other entries are simply
330 * added to their parent directories e_entries list.
332 if ((type & ROOT) && (np->e_flags & XATTR)) {
333 /* link this extended attribute root dir to its "parent" */
334 ep->e_xattrs = np;
335 } else {
336 /* add this entry to the entry list of the parent dir */
337 np->e_sibling = ep->e_entries;
338 ep->e_entries = np;
340 if (type & LINK) {
341 ep = lookupino(inum);
342 if (ep == NIL) {
343 /* XXX just bail on this one and continue? */
344 (void) fprintf(stderr,
345 gettext("link to non-existent name\n"));
346 done(1);
348 np->e_ino = inum;
349 np->e_links = ep->e_links;
350 ep->e_links = np;
351 } else if (inum != 0) {
352 ep = lookupino(inum);
353 if (ep != NIL)
354 panic(gettext("duplicate entry\n"));
355 else
356 addino(inum, np);
358 return (np);
362 * delete an entry from the symbol table
364 void
365 freeentry(ep)
366 struct entry *ep;
368 struct entry *np;
369 ino_t inum;
371 if ((ep->e_flags & REMOVED) == 0)
372 badentry(ep, gettext("not marked REMOVED"));
373 if (ep->e_type == NODE) {
374 if (ep->e_links != NIL)
375 badentry(ep, gettext("freeing referenced directory"));
376 if (ep->e_entries != NIL)
377 badentry(ep, gettext("freeing non-empty directory"));
379 if (ep->e_ino != 0) {
380 np = lookupino(ep->e_ino);
381 if (np == NIL)
382 badentry(ep, gettext("lookupino failed"));
383 if (np == ep) {
384 inum = ep->e_ino;
385 deleteino(inum);
386 if (ep->e_links != NIL)
387 addino(inum, ep->e_links);
388 } else {
389 for (; np != NIL; np = np->e_links) {
390 if (np->e_links == ep) {
391 np->e_links = ep->e_links;
392 break;
395 if (np == NIL)
396 badentry(ep, gettext("link not found"));
399 removeentry(ep);
400 freename(ep->e_name);
401 ep->e_next = freelist;
402 freelist = ep;
406 * Relocate an entry in the tree structure
408 void
409 moveentry(ep, newname)
410 struct entry *ep;
411 char *newname;
413 struct entry *np;
414 char *cp;
416 np = lookupparent(newname);
417 if (np == NIL)
418 badentry(ep, gettext("cannot move ROOT"));
419 if (np != ep->e_parent) {
420 removeentry(ep);
421 ep->e_parent = np;
422 ep->e_sibling = np->e_entries;
423 np->e_entries = ep;
425 /* find the last component of the complex name */
426 LASTPART(newname);
427 cp = strrchr(newname, '/') + 1;
428 if (cp == (char *)1)
429 cp = newname;
430 freename(ep->e_name);
431 ep->e_name = savename(cp);
432 /* LINTED: savename guarantees that strlen will fit */
433 ep->e_namlen = strlen(cp);
434 if (strcmp(gentempname(ep), ep->e_name) == 0) {
435 /* LINTED: result fits in a short */
436 ep->e_flags |= TMPNAME;
437 } else {
438 /* LINTED: result fits in a short */
439 ep->e_flags &= ~TMPNAME;
444 * Remove an entry in the tree structure
446 static void
447 removeentry(ep)
448 struct entry *ep;
450 struct entry *np;
452 np = ep->e_parent;
453 if (ep->e_flags & XATTRROOT) {
454 if (np->e_xattrs == ep)
455 np->e_xattrs = NIL;
456 else
457 badentry(ep, gettext(
458 "parent does not reference this xattr tree"));
459 } else if (np->e_entries == ep) {
460 np->e_entries = ep->e_sibling;
461 } else {
462 for (np = np->e_entries; np != NIL; np = np->e_sibling) {
463 if (np->e_sibling == ep) {
464 np->e_sibling = ep->e_sibling;
465 break;
468 if (np == NIL)
469 badentry(ep, gettext(
470 "cannot find entry in parent list"));
475 * Table of unused string entries, sorted by length.
477 * Entries are allocated in STRTBLINCR sized pieces so that names
478 * of similar lengths can use the same entry. The value of STRTBLINCR
479 * is chosen so that every entry has at least enough space to hold
480 * a "struct strtbl" header. Thus every entry can be linked onto an
481 * apprpriate free list.
483 * NB. The macro "allocsize" below assumes that "struct strhdr"
484 * has a size that is a power of two. Also, an extra byte is
485 * allocated for the string to provide space for the two NULL
486 * string terminator required for extended attribute paths.
488 struct strhdr {
489 struct strhdr *next;
492 #define STRTBLINCR ((size_t)sizeof (struct strhdr))
493 #define allocsize(size) (((size) + 2 + STRTBLINCR - 1) & ~(STRTBLINCR - 1))
495 static struct strhdr strtblhdr[allocsize(MAXCOMPLEXLEN) / STRTBLINCR];
498 * Allocate space for a name. It first looks to see if it already
499 * has an appropriate sized entry, and if not allocates a new one.
501 char *
502 savename(name)
503 char *name;
505 struct strhdr *np;
506 size_t len, as;
507 char *cp;
509 if (name == NULL) {
510 (void) fprintf(stderr, gettext("bad name\n"));
511 done(1);
513 len = strlen(name);
514 if (len > MAXPATHLEN) {
515 (void) fprintf(stderr, gettext("name too long\n"));
516 done(1);
518 as = allocsize(len);
519 np = strtblhdr[as / STRTBLINCR].next;
520 if (np != NULL) {
521 strtblhdr[as / STRTBLINCR].next = np->next;
522 cp = (char *)np;
523 } else {
524 /* Note that allocsize() adds 2 for the trailing \0s */
525 cp = malloc(as);
526 if (cp == NULL) {
527 (void) fprintf(stderr,
528 gettext("no space for string table\n"));
529 done(1);
532 (void) strcpy(cp, name);
533 /* add an extra null for complex (attribute) name support */
534 cp[len+1] = '\0';
535 return (cp);
539 * Free space for a name. The resulting entry is linked onto the
540 * appropriate free list.
542 void
543 freename(name)
544 char *name;
546 struct strhdr *tp, *np;
548 /* NULL case should never happen, but might as well be careful */
549 if (name != NULL) {
550 tp = &strtblhdr[allocsize(strlen(name)) / STRTBLINCR];
551 /*LINTED [name points to at least sizeof (struct strhdr)]*/
552 np = (struct strhdr *)name;
553 np->next = tp->next;
554 tp->next = np;
559 * Useful quantities placed at the end of a dumped symbol table.
561 struct symtableheader {
562 int volno;
563 uint_t stringsize;
564 uint_t entrytblsize;
565 time_t dumptime;
566 time_t dumpdate;
567 ino_t maxino;
568 uint_t ntrec;
572 * dump a snapshot of the symbol table
574 void
575 dumpsymtable(filename, checkpt)
576 char *filename;
577 int checkpt;
579 struct entry *ep, *tep;
580 ino_t i;
581 struct entry temp, *tentry;
582 int mynum = 1;
583 uint_t stroff;
584 FILE *fp;
585 struct symtableheader hdr;
587 vprintf(stdout, gettext("Check pointing the restore\n"));
588 if ((fp = safe_fopen(filename, "w", 0600)) == (FILE *)NULL) {
589 perror("fopen");
590 (void) fprintf(stderr,
591 gettext("cannot create save file %s for symbol table\n"),
592 filename);
593 done(1);
595 clearerr(fp);
597 * Assign an index to each entry
598 * Write out the string entries
600 for (i = ROOTINO; i < maxino; i++) {
601 for (ep = lookupino(i); ep != NIL; ep = ep->e_links) {
602 ep->e_index = mynum++;
603 (void) fwrite(ep->e_name, sizeof (ep->e_name[0]),
604 (size_t)allocsize(ep->e_namlen), fp);
608 * Convert e_name pointers to offsets, other pointers
609 * to indices, and output
611 tep = &temp;
612 stroff = 0;
613 for (i = ROOTINO; !ferror(fp) && i < maxino; i++) {
614 for (ep = lookupino(i);
615 !ferror(fp) && ep != NIL;
616 ep = ep->e_links) {
617 bcopy((char *)ep, (char *)tep, sizeof (*tep));
618 /* LINTED: type pun ok */
619 tep->e_name = (char *)stroff;
620 stroff += allocsize(ep->e_namlen);
621 tep->e_parent = (struct entry *)ep->e_parent->e_index;
622 if (ep->e_links != NIL)
623 tep->e_links =
624 (struct entry *)ep->e_links->e_index;
625 if (ep->e_sibling != NIL)
626 tep->e_sibling =
627 (struct entry *)ep->e_sibling->e_index;
628 if (ep->e_entries != NIL)
629 tep->e_entries =
630 (struct entry *)ep->e_entries->e_index;
631 if (ep->e_xattrs != NIL)
632 tep->e_xattrs =
633 (struct entry *)ep->e_xattrs->e_index;
634 if (ep->e_next != NIL)
635 tep->e_next =
636 (struct entry *)ep->e_next->e_index;
637 (void) fwrite((char *)tep, sizeof (*tep), 1, fp);
641 * Convert entry pointers to indices, and output
643 for (i = 0; !ferror(fp) && i < (ino_t)entrytblsize; i++) {
644 if (entry[i] == NIL)
645 tentry = NIL;
646 else
647 tentry = (struct entry *)entry[i]->e_index;
648 (void) fwrite((char *)&tentry, sizeof (tentry), 1, fp);
651 if (!ferror(fp)) {
652 /* Ought to have a checksum or magic number */
653 hdr.volno = checkpt;
654 hdr.maxino = maxino;
655 hdr.entrytblsize = entrytblsize;
656 hdr.stringsize = stroff;
657 hdr.dumptime = dumptime;
658 hdr.dumpdate = dumpdate;
659 hdr.ntrec = ntrec;
660 (void) fwrite((char *)&hdr, sizeof (hdr), 1, fp);
663 if (ferror(fp)) {
664 perror("fwrite");
665 panic(gettext("output error to file %s writing symbol table\n"),
666 filename);
668 (void) fclose(fp);
672 * Initialize a symbol table from a file
674 void
675 initsymtable(filename)
676 char *filename;
678 char *base;
679 off64_t tblsize;
680 struct entry *ep;
681 struct entry *baseep, *lep;
682 struct symtableheader hdr;
683 struct stat64 stbuf;
684 uint_t i;
685 int fd;
687 vprintf(stdout, gettext("Initialize symbol table.\n"));
688 if (filename == NULL) {
689 if ((maxino / HASHFACTOR) > UINT_MAX) {
690 (void) fprintf(stderr,
691 gettext("file system too large\n"));
692 done(1);
694 /* LINTED: result fits in entrytblsize */
695 entrytblsize = maxino / HASHFACTOR;
696 entry = (struct entry **)
697 /* LINTED entrytblsize fits in a size_t */
698 calloc((size_t)entrytblsize, sizeof (*entry));
699 if (entry == (struct entry **)NULL) {
700 (void) fprintf(stderr,
701 gettext("no memory for entry table\n"));
702 done(1);
704 ep = addentry(".", ROOTINO, NODE);
705 /* LINTED: result fits in a short */
706 ep->e_flags |= NEW;
707 return;
709 if ((fd = open(filename, O_RDONLY|O_LARGEFILE)) < 0) {
710 perror("open");
711 (void) fprintf(stderr,
712 gettext("cannot open symbol table file %s\n"), filename);
713 done(1);
715 if (fstat64(fd, &stbuf) < 0) {
716 perror("stat");
717 (void) fprintf(stderr,
718 gettext("cannot stat symbol table file %s\n"), filename);
719 (void) close(fd);
720 done(1);
723 * The symbol table file is too small so say we can't read it.
725 if (stbuf.st_size < sizeof (hdr)) {
726 (void) fprintf(stderr,
727 gettext("cannot read symbol table file %s\n"), filename);
728 (void) close(fd);
729 done(1);
731 tblsize = stbuf.st_size - sizeof (hdr);
732 if (tblsize > ULONG_MAX) {
733 (void) fprintf(stderr,
734 gettext("symbol table file too large\n"));
735 (void) close(fd);
736 done(1);
738 /* LINTED tblsize fits in a size_t */
739 base = calloc((size_t)sizeof (char), (size_t)tblsize);
740 if (base == NULL) {
741 (void) fprintf(stderr,
742 gettext("cannot allocate space for symbol table\n"));
743 (void) close(fd);
744 done(1);
746 /* LINTED tblsize fits in a size_t */
747 if (read(fd, base, (size_t)tblsize) < 0 ||
748 read(fd, (char *)&hdr, sizeof (hdr)) < 0) {
749 perror("read");
750 (void) fprintf(stderr,
751 gettext("cannot read symbol table file %s\n"), filename);
752 (void) close(fd);
753 done(1);
755 (void) close(fd);
756 switch (command) {
757 case 'r':
758 case 'M':
760 * For normal continuation, insure that we are using
761 * the next incremental tape
763 if (hdr.dumpdate != dumptime) {
764 if (hdr.dumpdate < dumptime)
765 (void) fprintf(stderr, gettext(
766 "Incremental volume too low\n"));
767 else
768 (void) fprintf(stderr, gettext(
769 "Incremental volume too high\n"));
770 done(1);
772 break;
773 case 'R':
775 * For restart, insure that we are using the same tape
777 curfile.action = SKIP;
778 dumptime = hdr.dumptime;
779 dumpdate = hdr.dumpdate;
780 if (!bflag)
781 newtapebuf(hdr.ntrec);
782 getvol(hdr.volno);
783 break;
784 default:
785 (void) fprintf(stderr,
786 gettext("initsymtable called from command %c\n"),
787 (uchar_t)command);
788 done(1);
789 /*NOTREACHED*/
791 maxino = hdr.maxino;
792 entrytblsize = hdr.entrytblsize;
793 /*LINTED [pointer cast alignment]*/
794 entry = (struct entry **)
795 (base + tblsize - (entrytblsize * sizeof (*entry)));
796 if (((ulong_t)entry % 4) != 0) {
797 (void) fprintf(stderr,
798 gettext("Symbol table file corrupted\n"));
799 done(1);
801 /*LINTED [rvalue % 4 == 0] */
802 baseep = (struct entry *)
803 (base + hdr.stringsize - sizeof (*baseep));
804 if (((ulong_t)baseep % 4) != 0) {
805 (void) fprintf(stderr,
806 gettext("Symbol table file corrupted\n"));
807 done(1);
809 lep = (struct entry *)entry;
810 for (i = 0; i < entrytblsize; i++) {
811 if (entry[i] == NIL)
812 continue;
813 entry[i] = &baseep[(long)entry[i]];
815 for (ep = &baseep[1]; ep < lep; ep++) {
816 ep->e_name = base + (long)ep->e_name;
817 ep->e_parent = &baseep[(long)ep->e_parent];
818 if (ep->e_sibling != NIL)
819 ep->e_sibling = &baseep[(long)ep->e_sibling];
820 if (ep->e_links != NIL)
821 ep->e_links = &baseep[(long)ep->e_links];
822 if (ep->e_entries != NIL)
823 ep->e_entries = &baseep[(long)ep->e_entries];
824 if (ep->e_xattrs != NIL)
825 ep->e_xattrs = &baseep[(long)ep->e_xattrs];
826 if (ep->e_next != NIL)
827 ep->e_next = &baseep[(long)ep->e_next];