]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sbin/restore/symtab.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sbin / restore / symtab.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #ifndef lint
31 #if 0
32 static char sccsid[] = "@(#)symtab.c    8.3 (Berkeley) 4/28/95";
33 #endif
34 static const char rcsid[] =
35   "$FreeBSD$";
36 #endif /* not lint */
37
38 /*
39  * These routines maintain the symbol table which tracks the state
40  * of the file system being restored. They provide lookup by either
41  * name or inode number. They also provide for creation, deletion,
42  * and renaming of entries. Because of the dynamic nature of pathnames,
43  * names should not be saved, but always constructed just before they
44  * are needed, by calling "myname".
45  */
46
47 #include <sys/param.h>
48 #include <sys/stat.h>
49
50 #include <ufs/ufs/dinode.h>
51
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <limits.h>
55 #include <stdint.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #include "restore.h"
62 #include "extern.h"
63
64 /*
65  * The following variables define the inode symbol table.
66  * The primary hash table is dynamically allocated based on
67  * the number of inodes in the file system (maxino), scaled by
68  * HASHFACTOR. The variable "entry" points to the hash table;
69  * the variable "entrytblsize" indicates its size (in entries).
70  */
71 #define HASHFACTOR 5
72 static struct entry **entry;
73 static long entrytblsize;
74
75 static void              addino(ino_t, struct entry *);
76 static struct entry     *lookupparent(char *);
77 static void              removeentry(struct entry *);
78
79 /*
80  * Look up an entry by inode number
81  */
82 struct entry *
83 lookupino(ino_t inum)
84 {
85         struct entry *ep;
86
87         if (inum < WINO || inum >= maxino)
88                 return (NULL);
89         for (ep = entry[inum % entrytblsize]; ep != NULL; ep = ep->e_next)
90                 if (ep->e_ino == inum)
91                         return (ep);
92         return (NULL);
93 }
94
95 /*
96  * Add an entry into the entry table
97  */
98 static void
99 addino(ino_t inum, struct entry *np)
100 {
101         struct entry **epp;
102
103         if (inum < WINO || inum >= maxino)
104                 panic("addino: out of range %ju\n", (uintmax_t)inum);
105         epp = &entry[inum % entrytblsize];
106         np->e_ino = inum;
107         np->e_next = *epp;
108         *epp = np;
109         if (dflag)
110                 for (np = np->e_next; np != NULL; np = np->e_next)
111                         if (np->e_ino == inum)
112                                 badentry(np, "duplicate inum");
113 }
114
115 /*
116  * Delete an entry from the entry table
117  */
118 void
119 deleteino(ino_t inum)
120 {
121         struct entry *next;
122         struct entry **prev;
123
124         if (inum < WINO || inum >= maxino)
125                 panic("deleteino: out of range %ju\n", (uintmax_t)inum);
126         prev = &entry[inum % entrytblsize];
127         for (next = *prev; next != NULL; next = next->e_next) {
128                 if (next->e_ino == inum) {
129                         next->e_ino = 0;
130                         *prev = next->e_next;
131                         return;
132                 }
133                 prev = &next->e_next;
134         }
135         panic("deleteino: %ju not found\n", (uintmax_t)inum);
136 }
137
138 /*
139  * Look up an entry by name
140  */
141 struct entry *
142 lookupname(char *name)
143 {
144         struct entry *ep;
145         char *np, *cp;
146         char buf[MAXPATHLEN];
147
148         cp = name;
149         for (ep = lookupino(ROOTINO); ep != NULL; ep = ep->e_entries) {
150                 for (np = buf; *cp != '/' && *cp != '\0' &&
151                                 np < &buf[sizeof(buf)]; )
152                         *np++ = *cp++;
153                 if (np == &buf[sizeof(buf)])
154                         break;
155                 *np = '\0';
156                 for ( ; ep != NULL; ep = ep->e_sibling)
157                         if (strcmp(ep->e_name, buf) == 0)
158                                 break;
159                 if (ep == NULL)
160                         break;
161                 if (*cp++ == '\0')
162                         return (ep);
163         }
164         return (NULL);
165 }
166
167 /*
168  * Look up the parent of a pathname
169  */
170 static struct entry *
171 lookupparent(char *name)
172 {
173         struct entry *ep;
174         char *tailindex;
175
176         tailindex = strrchr(name, '/');
177         if (tailindex == NULL)
178                 return (NULL);
179         *tailindex = '\0';
180         ep = lookupname(name);
181         *tailindex = '/';
182         if (ep == NULL)
183                 return (NULL);
184         if (ep->e_type != NODE)
185                 panic("%s is not a directory\n", name);
186         return (ep);
187 }
188
189 /*
190  * Determine the current pathname of a node or leaf
191  */
192 char *
193 myname(struct entry *ep)
194 {
195         char *cp;
196         static char namebuf[MAXPATHLEN];
197
198         for (cp = &namebuf[MAXPATHLEN - 2]; cp > &namebuf[ep->e_namlen]; ) {
199                 cp -= ep->e_namlen;
200                 memmove(cp, ep->e_name, (long)ep->e_namlen);
201                 if (ep == lookupino(ROOTINO))
202                         return (cp);
203                 *(--cp) = '/';
204                 ep = ep->e_parent;
205         }
206         panic("%s: pathname too long\n", cp);
207         return(cp);
208 }
209
210 /*
211  * Unused symbol table entries are linked together on a free list
212  * headed by the following pointer.
213  */
214 static struct entry *freelist = NULL;
215
216 /*
217  * add an entry to the symbol table
218  */
219 struct entry *
220 addentry(char *name, ino_t inum, int type)
221 {
222         struct entry *np, *ep;
223
224         if (freelist != NULL) {
225                 np = freelist;
226                 freelist = np->e_next;
227                 memset(np, 0, (long)sizeof(struct entry));
228         } else {
229                 np = (struct entry *)calloc(1, sizeof(struct entry));
230                 if (np == NULL)
231                         panic("no memory to extend symbol table\n");
232         }
233         np->e_type = type & ~LINK;
234         ep = lookupparent(name);
235         if (ep == NULL) {
236                 if (inum != ROOTINO || lookupino(ROOTINO) != NULL)
237                         panic("bad name to addentry %s\n", name);
238                 np->e_name = savename(name);
239                 np->e_namlen = strlen(name);
240                 np->e_parent = np;
241                 addino(ROOTINO, np);
242                 return (np);
243         }
244         np->e_name = savename(strrchr(name, '/') + 1);
245         np->e_namlen = strlen(np->e_name);
246         np->e_parent = ep;
247         np->e_sibling = ep->e_entries;
248         ep->e_entries = np;
249         if (type & LINK) {
250                 ep = lookupino(inum);
251                 if (ep == NULL)
252                         panic("link to non-existent name\n");
253                 np->e_ino = inum;
254                 np->e_links = ep->e_links;
255                 ep->e_links = np;
256         } else if (inum != 0) {
257                 if (lookupino(inum) != NULL)
258                         panic("duplicate entry\n");
259                 addino(inum, np);
260         }
261         return (np);
262 }
263
264 /*
265  * delete an entry from the symbol table
266  */
267 void
268 freeentry(struct entry *ep)
269 {
270         struct entry *np;
271         ino_t inum;
272
273         if (ep->e_flags != REMOVED)
274                 badentry(ep, "not marked REMOVED");
275         if (ep->e_type == NODE) {
276                 if (ep->e_links != NULL)
277                         badentry(ep, "freeing referenced directory");
278                 if (ep->e_entries != NULL)
279                         badentry(ep, "freeing non-empty directory");
280         }
281         if (ep->e_ino != 0) {
282                 np = lookupino(ep->e_ino);
283                 if (np == NULL)
284                         badentry(ep, "lookupino failed");
285                 if (np == ep) {
286                         inum = ep->e_ino;
287                         deleteino(inum);
288                         if (ep->e_links != NULL)
289                                 addino(inum, ep->e_links);
290                 } else {
291                         for (; np != NULL; np = np->e_links) {
292                                 if (np->e_links == ep) {
293                                         np->e_links = ep->e_links;
294                                         break;
295                                 }
296                         }
297                         if (np == NULL)
298                                 badentry(ep, "link not found");
299                 }
300         }
301         removeentry(ep);
302         freename(ep->e_name);
303         ep->e_next = freelist;
304         freelist = ep;
305 }
306
307 /*
308  * Relocate an entry in the tree structure
309  */
310 void
311 moveentry(struct entry *ep, char *newname)
312 {
313         struct entry *np;
314         char *cp;
315
316         np = lookupparent(newname);
317         if (np == NULL)
318                 badentry(ep, "cannot move ROOT");
319         if (np != ep->e_parent) {
320                 removeentry(ep);
321                 ep->e_parent = np;
322                 ep->e_sibling = np->e_entries;
323                 np->e_entries = ep;
324         }
325         cp = strrchr(newname, '/') + 1;
326         freename(ep->e_name);
327         ep->e_name = savename(cp);
328         ep->e_namlen = strlen(cp);
329         if (strcmp(gentempname(ep), ep->e_name) == 0)
330                 ep->e_flags |= TMPNAME;
331         else
332                 ep->e_flags &= ~TMPNAME;
333 }
334
335 /*
336  * Remove an entry in the tree structure
337  */
338 static void
339 removeentry(struct entry *ep)
340 {
341         struct entry *np;
342
343         np = ep->e_parent;
344         if (np->e_entries == ep) {
345                 np->e_entries = ep->e_sibling;
346         } else {
347                 for (np = np->e_entries; np != NULL; np = np->e_sibling) {
348                         if (np->e_sibling == ep) {
349                                 np->e_sibling = ep->e_sibling;
350                                 break;
351                         }
352                 }
353                 if (np == NULL)
354                         badentry(ep, "cannot find entry in parent list");
355         }
356 }
357
358 /*
359  * Table of unused string entries, sorted by length.
360  *
361  * Entries are allocated in STRTBLINCR sized pieces so that names
362  * of similar lengths can use the same entry. The value of STRTBLINCR
363  * is chosen so that every entry has at least enough space to hold
364  * a "struct strtbl" header. Thus every entry can be linked onto an
365  * appropriate free list.
366  *
367  * NB. The macro "allocsize" below assumes that "struct strhdr"
368  *     has a size that is a power of two.
369  */
370 struct strhdr {
371         struct strhdr *next;
372 };
373
374 #define STRTBLINCR      (sizeof(struct strhdr))
375 #define allocsize(size) (((size) + 1 + STRTBLINCR - 1) & ~(STRTBLINCR - 1))
376
377 static struct strhdr strtblhdr[allocsize(NAME_MAX) / STRTBLINCR];
378
379 /*
380  * Allocate space for a name. It first looks to see if it already
381  * has an appropriate sized entry, and if not allocates a new one.
382  */
383 char *
384 savename(char *name)
385 {
386         struct strhdr *np;
387         long len;
388         char *cp;
389
390         if (name == NULL)
391                 panic("bad name\n");
392         len = strlen(name);
393         np = strtblhdr[len / STRTBLINCR].next;
394         if (np != NULL) {
395                 strtblhdr[len / STRTBLINCR].next = np->next;
396                 cp = (char *)np;
397         } else {
398                 cp = malloc((unsigned)allocsize(len));
399                 if (cp == NULL)
400                         panic("no space for string table\n");
401         }
402         (void) strcpy(cp, name);
403         return (cp);
404 }
405
406 /*
407  * Free space for a name. The resulting entry is linked onto the
408  * appropriate free list.
409  */
410 void
411 freename(char *name)
412 {
413         struct strhdr *tp, *np;
414
415         tp = &strtblhdr[strlen(name) / STRTBLINCR];
416         np = (struct strhdr *)name;
417         np->next = tp->next;
418         tp->next = np;
419 }
420
421 /*
422  * Useful quantities placed at the end of a dumped symbol table.
423  */
424 struct symtableheader {
425         int32_t volno;
426         int32_t stringsize;
427         int32_t entrytblsize;
428         time_t  dumptime;
429         time_t  dumpdate;
430         ino_t   maxino;
431         int32_t ntrec;
432 };
433
434 /*
435  * dump a snapshot of the symbol table
436  */
437 void
438 dumpsymtable(char *filename, long checkpt)
439 {
440         struct entry *ep, *tep;
441         ino_t i;
442         struct entry temp, *tentry;
443         long mynum = 1, stroff = 0;
444         FILE *fd;
445         struct symtableheader hdr;
446
447         vprintf(stdout, "Checkpointing the restore\n");
448         if (Nflag)
449                 return;
450         if ((fd = fopen(filename, "w")) == NULL) {
451                 fprintf(stderr, "fopen: %s\n", strerror(errno));
452                 panic("cannot create save file %s for symbol table\n",
453                         filename);
454                 done(1);
455         }
456         clearerr(fd);
457         /*
458          * Assign indices to each entry
459          * Write out the string entries
460          */
461         for (i = WINO; i <= maxino; i++) {
462                 for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
463                         ep->e_index = mynum++;
464                         (void) fwrite(ep->e_name, sizeof(char),
465                                (int)allocsize(ep->e_namlen), fd);
466                 }
467         }
468         /*
469          * Convert pointers to indexes, and output
470          */
471         tep = &temp;
472         stroff = 0;
473         for (i = WINO; i <= maxino; i++) {
474                 for (ep = lookupino(i); ep != NULL; ep = ep->e_links) {
475                         memmove(tep, ep, (long)sizeof(struct entry));
476                         tep->e_name = (char *)stroff;
477                         stroff += allocsize(ep->e_namlen);
478                         tep->e_parent = (struct entry *)ep->e_parent->e_index;
479                         if (ep->e_links != NULL)
480                                 tep->e_links =
481                                         (struct entry *)ep->e_links->e_index;
482                         if (ep->e_sibling != NULL)
483                                 tep->e_sibling =
484                                         (struct entry *)ep->e_sibling->e_index;
485                         if (ep->e_entries != NULL)
486                                 tep->e_entries =
487                                         (struct entry *)ep->e_entries->e_index;
488                         if (ep->e_next != NULL)
489                                 tep->e_next =
490                                         (struct entry *)ep->e_next->e_index;
491                         (void) fwrite((char *)tep, sizeof(struct entry), 1, fd);
492                 }
493         }
494         /*
495          * Convert entry pointers to indexes, and output
496          */
497         for (i = 0; i < entrytblsize; i++) {
498                 if (entry[i] == NULL)
499                         tentry = NULL;
500                 else
501                         tentry = (struct entry *)entry[i]->e_index;
502                 (void) fwrite((char *)&tentry, sizeof(struct entry *), 1, fd);
503         }
504         hdr.volno = checkpt;
505         hdr.maxino = maxino;
506         hdr.entrytblsize = entrytblsize;
507         hdr.stringsize = stroff;
508         hdr.dumptime = dumptime;
509         hdr.dumpdate = dumpdate;
510         hdr.ntrec = ntrec;
511         (void) fwrite((char *)&hdr, sizeof(struct symtableheader), 1, fd);
512         if (ferror(fd)) {
513                 fprintf(stderr, "fwrite: %s\n", strerror(errno));
514                 panic("output error to file %s writing symbol table\n",
515                         filename);
516         }
517         (void) fclose(fd);
518 }
519
520 /*
521  * Initialize a symbol table from a file
522  */
523 void
524 initsymtable(char *filename)
525 {
526         char *base;
527         long tblsize;
528         struct entry *ep;
529         struct entry *baseep, *lep;
530         struct symtableheader hdr;
531         struct stat stbuf;
532         long i;
533         int fd;
534
535         vprintf(stdout, "Initialize symbol table.\n");
536         if (filename == NULL) {
537                 entrytblsize = maxino / HASHFACTOR;
538                 entry = (struct entry **)
539                         calloc((unsigned)entrytblsize, sizeof(struct entry *));
540                 if (entry == (struct entry **)NULL)
541                         panic("no memory for entry table\n");
542                 ep = addentry(".", ROOTINO, NODE);
543                 ep->e_flags |= NEW;
544                 return;
545         }
546         if ((fd = open(filename, O_RDONLY, 0)) < 0) {
547                 fprintf(stderr, "open: %s\n", strerror(errno));
548                 panic("cannot open symbol table file %s\n", filename);
549         }
550         if (fstat(fd, &stbuf) < 0) {
551                 fprintf(stderr, "stat: %s\n", strerror(errno));
552                 panic("cannot stat symbol table file %s\n", filename);
553         }
554         tblsize = stbuf.st_size - sizeof(struct symtableheader);
555         base = calloc(sizeof(char), (unsigned)tblsize);
556         if (base == NULL)
557                 panic("cannot allocate space for symbol table\n");
558         if (read(fd, base, (int)tblsize) < 0 ||
559             read(fd, (char *)&hdr, sizeof(struct symtableheader)) < 0) {
560                 fprintf(stderr, "read: %s\n", strerror(errno));
561                 panic("cannot read symbol table file %s\n", filename);
562         }
563         switch (command) {
564         case 'r':
565                 /*
566                  * For normal continuation, insure that we are using
567                  * the next incremental tape
568                  */
569                 if (hdr.dumpdate != dumptime) {
570                         if (hdr.dumpdate < dumptime)
571                                 fprintf(stderr, "Incremental tape too low\n");
572                         else
573                                 fprintf(stderr, "Incremental tape too high\n");
574                         done(1);
575                 }
576                 break;
577         case 'R':
578                 /*
579                  * For restart, insure that we are using the same tape
580                  */
581                 curfile.action = SKIP;
582                 dumptime = hdr.dumptime;
583                 dumpdate = hdr.dumpdate;
584                 if (!bflag)
585                         newtapebuf(hdr.ntrec);
586                 getvol(hdr.volno);
587                 break;
588         default:
589                 panic("initsymtable called from command %c\n", command);
590                 break;
591         }
592         maxino = hdr.maxino;
593         entrytblsize = hdr.entrytblsize;
594         entry = (struct entry **)
595                 (base + tblsize - (entrytblsize * sizeof(struct entry *)));
596         baseep = (struct entry *)(base + hdr.stringsize - sizeof(struct entry));
597         lep = (struct entry *)entry;
598         for (i = 0; i < entrytblsize; i++) {
599                 if (entry[i] == NULL)
600                         continue;
601                 entry[i] = &baseep[(long)entry[i]];
602         }
603         for (ep = &baseep[1]; ep < lep; ep++) {
604                 ep->e_name = base + (long)ep->e_name;
605                 ep->e_parent = &baseep[(long)ep->e_parent];
606                 if (ep->e_sibling != NULL)
607                         ep->e_sibling = &baseep[(long)ep->e_sibling];
608                 if (ep->e_links != NULL)
609                         ep->e_links = &baseep[(long)ep->e_links];
610                 if (ep->e_entries != NULL)
611                         ep->e_entries = &baseep[(long)ep->e_entries];
612                 if (ep->e_next != NULL)
613                         ep->e_next = &baseep[(long)ep->e_next];
614         }
615 }