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: */
6:
7: #ifndef lint
8: static char sccsid[] = "@(#)dirs.c 5.4 (Berkeley) 4/23/86";
9: #endif not lint
10:
11: #include "restore.h"
12: #include <protocols/dumprestore.h>
13: #include <sys/file.h>
14:
15: /*
16: * Symbol table of directories read from tape.
17: */
18: #define HASHSIZE 1000
19: #define INOHASH(val) (val % HASHSIZE)
20: struct inotab {
21: struct inotab *t_next;
22: ino_t t_ino;
23: daddr_t t_seekpt;
24: long t_size;
25: };
26: static struct inotab *inotab[HASHSIZE];
27: extern struct inotab *inotablookup();
28: extern struct inotab *allocinotab();
29:
30: /*
31: * Information retained about directories.
32: */
33: struct modeinfo {
34: ino_t ino;
35: time_t timep[2];
36: short mode;
37: short uid;
38: short gid;
39: };
40:
41: /*
42: * Global variables for this file.
43: */
44: static daddr_t seekpt;
45: static FILE *df, *mf;
46: static DIR *dirp;
47: static char dirfile[32] = "#"; /* No file */
48: static char modefile[32] = "#"; /* No file */
49: extern ino_t search();
50: struct direct *rst_readdir();
51: extern void rst_seekdir();
52:
53: /*
54: * Format of old style directories.
55: */
56: #define ODIRSIZ 14
57: struct odirect {
58: u_short d_ino;
59: char d_name[ODIRSIZ];
60: };
61:
62: /*
63: * Extract directory contents, building up a directory structure
64: * on disk for extraction by name.
65: * If genmode is requested, save mode, owner, and times for all
66: * directories on the tape.
67: */
68: (genmode)
69: int genmode;
70: {
71: register int i;
72: register struct dinode *ip;
73: struct inotab *itp;
74: struct direct nulldir;
75: int putdir(), null();
76:
77: vprintf(stdout, "Extract directories from tape\n");
78: (void) sprintf(dirfile, "/tmp/rstdir%d", dumpdate);
79: df = fopen(dirfile, "w");
80: if (df == 0) {
81: fprintf(stderr,
82: "restore: %s - cannot create directory temporary\n",
83: dirfile);
84: perror("fopen");
85: done(1);
86: }
87: if (genmode != 0) {
88: (void) sprintf(modefile, "/tmp/rstmode%d", dumpdate);
89: mf = fopen(modefile, "w");
90: if (mf == 0) {
91: fprintf(stderr,
92: "restore: %s - cannot create modefile \n",
93: modefile);
94: perror("fopen");
95: done(1);
96: }
97: }
98: nulldir.d_ino = 0;
99: nulldir.d_namlen = 1;
100: (void) strcpy(nulldir.d_name, "/");
101: nulldir.d_reclen = DIRSIZ(&nulldir);
102: for (;;) {
103: curfile.name = "<directory file - name unknown>";
104: curfile.action = USING;
105: ip = curfile.dip;
106: if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) {
107: (void) fclose(df);
108: dirp = opendir(dirfile);
109: if (dirp == NULL)
110: perror("opendir");
111: if (mf != NULL)
112: (void) fclose(mf);
113: i = dirlookup(".");
114: if (i == 0)
115: panic("Root directory is not on tape\n");
116: return;
117: }
118: itp = allocinotab(curfile.ino, ip, seekpt);
119: getfile(putdir, null);
120: putent(&nulldir);
121: flushent();
122: itp->t_size = seekpt - itp->t_seekpt;
123: }
124: }
125:
126: /*
127: * skip over all the directories on the tape
128: */
129: skipdirs()
130: {
131:
132: while ((curfile.dip->di_mode & IFMT) == IFDIR) {
133: skipfile();
134: }
135: }
136:
137: /*
138: * Recursively find names and inumbers of all files in subtree
139: * pname and pass them off to be processed.
140: */
141: treescan(pname, ino, todo)
142: char *pname;
143: ino_t ino;
144: long (*todo)();
145: {
146: register struct inotab *itp;
147: register struct direct *dp;
148: register struct entry *np;
149: int namelen;
150: daddr_t bpt;
151: char locname[MAXPATHLEN + 1];
152:
153: itp = inotablookup(ino);
154: if (itp == NULL) {
155: /*
156: * Pname is name of a simple file or an unchanged directory.
157: */
158: (void) (*todo)(pname, ino, LEAF);
159: return;
160: }
161: /*
162: * Pname is a dumped directory name.
163: */
164: if ((*todo)(pname, ino, NODE) == FAIL)
165: return;
166: /*
167: * begin search through the directory
168: * skipping over "." and ".."
169: */
170: (void) strncpy(locname, pname, MAXPATHLEN);
171: (void) strncat(locname, "/", MAXPATHLEN);
172: namelen = strlen(locname);
173: rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
174: dp = rst_readdir(dirp); /* "." */
175: if (dp != NULL && strcmp(dp->d_name, ".") == 0)
176: dp = rst_readdir(dirp); /* ".." */
177: else
178: fprintf(stderr, "Warning: `.' missing from directory %s\n",
179: pname);
180: if (dp != NULL && strcmp(dp->d_name, "..") == 0)
181: dp = rst_readdir(dirp); /* first real entry */
182: else
183: fprintf(stderr, "Warning: `..' missing from directory %s\n",
184: pname);
185: bpt = telldir(dirp);
186: /*
187: * a zero inode signals end of directory
188: */
189: while (dp != NULL && dp->d_ino != 0) {
190: locname[namelen] = '\0';
191: if (namelen + dp->d_namlen >= MAXPATHLEN) {
192: fprintf(stderr, "%s%s: name exceeds %d char\n",
193: locname, dp->d_name, MAXPATHLEN);
194: } else {
195: (void) strncat(locname, dp->d_name, (int)dp->d_namlen);
196: treescan(locname, dp->d_ino, todo);
197: rst_seekdir(dirp, bpt, itp->t_seekpt);
198: }
199: dp = rst_readdir(dirp);
200: bpt = telldir(dirp);
201: }
202: if (dp == NULL)
203: fprintf(stderr, "corrupted directory: %s.\n", locname);
204: }
205:
206: /*
207: * Search the directory tree rooted at inode ROOTINO
208: * for the path pointed at by n
209: */
210: ino_t
211: psearch(n)
212: char *n;
213: {
214: register char *cp, *cp1;
215: ino_t ino;
216: char c;
217:
218: ino = ROOTINO;
219: if (*(cp = n) == '/')
220: cp++;
221: next:
222: cp1 = cp + 1;
223: while (*cp1 != '/' && *cp1)
224: cp1++;
225: c = *cp1;
226: *cp1 = 0;
227: ino = search(ino, cp);
228: if (ino == 0) {
229: *cp1 = c;
230: return(0);
231: }
232: *cp1 = c;
233: if (c == '/') {
234: cp = cp1+1;
235: goto next;
236: }
237: return(ino);
238: }
239:
240: /*
241: * search the directory inode ino
242: * looking for entry cp
243: */
244: ino_t
245: search(inum, cp)
246: ino_t inum;
247: char *cp;
248: {
249: register struct direct *dp;
250: register struct inotab *itp;
251: int len;
252:
253: itp = inotablookup(inum);
254: if (itp == NULL)
255: return(0);
256: rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
257: len = strlen(cp);
258: do {
259: dp = rst_readdir(dirp);
260: if (dp == NULL || dp->d_ino == 0)
261: return (0);
262: } while (dp->d_namlen != len || strncmp(dp->d_name, cp, len) != 0);
263: return(dp->d_ino);
264: }
265:
266: /*
267: * Put the directory entries in the directory file
268: */
269: putdir(buf, size)
270: char *buf;
271: int size;
272: {
273: struct direct cvtbuf;
274: register struct odirect *odp;
275: struct odirect *eodp;
276: register struct direct *dp;
277: long loc, i;
278: extern int Bcvt;
279:
280: if (cvtflag) {
281: eodp = (struct odirect *)&buf[size];
282: for (odp = (struct odirect *)buf; odp < eodp; odp++)
283: if (odp->d_ino != 0) {
284: dcvt(odp, &cvtbuf);
285: putent(&cvtbuf);
286: }
287: } else {
288: for (loc = 0; loc < size; ) {
289: dp = (struct direct *)(buf + loc);
290: if (Bcvt) {
291: swabst("l2s", (char *) dp);
292: }
293: i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
294: if (dp->d_reclen == 0 || dp->d_reclen > i) {
295: loc += i;
296: continue;
297: }
298: loc += dp->d_reclen;
299: if (dp->d_ino != 0) {
300: putent(dp);
301: }
302: }
303: }
304: }
305:
306: /*
307: * These variables are "local" to the following two functions.
308: */
309: char dirbuf[DIRBLKSIZ];
310: long dirloc = 0;
311: long prev = 0;
312:
313: /*
314: * add a new directory entry to a file.
315: */
316: putent(dp)
317: struct direct *dp;
318: {
319: dp->d_reclen = DIRSIZ(dp);
320: if (dirloc + dp->d_reclen > DIRBLKSIZ) {
321: ((struct direct *)(dirbuf + prev))->d_reclen =
322: DIRBLKSIZ - prev;
323: (void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
324: dirloc = 0;
325: }
326: bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen);
327: prev = dirloc;
328: dirloc += dp->d_reclen;
329: }
330:
331: /*
332: * flush out a directory that is finished.
333: */
334: flushent()
335: {
336:
337: ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
338: (void) fwrite(dirbuf, (int)dirloc, 1, df);
339: seekpt = ftell(df);
340: dirloc = 0;
341: }
342:
343: dcvt(odp, ndp)
344: register struct odirect *odp;
345: register struct direct *ndp;
346: {
347:
348: bzero((char *)ndp, (long)(sizeof *ndp));
349: ndp->d_ino = odp->d_ino;
350: (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
351: ndp->d_namlen = strlen(ndp->d_name);
352: ndp->d_reclen = DIRSIZ(ndp);
353: }
354:
355: /*
356: * Seek to an entry in a directory.
357: * Only values returned by ``telldir'' should be passed to rst_seekdir.
358: * This routine handles many directories in a single file.
359: * It takes the base of the directory in the file, plus
360: * the desired seek offset into it.
361: */
362: void
363: rst_seekdir(dirp, loc, base)
364: register DIR *dirp;
365: daddr_t loc, base;
366: {
367:
368: if (loc == telldir(dirp))
369: return;
370: loc -= base;
371: if (loc < 0)
372: fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc);
373: (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0);
374: dirp->dd_loc = loc & (DIRBLKSIZ - 1);
375: if (dirp->dd_loc != 0)
376: dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
377: }
378:
379: /*
380: * get next entry in a directory.
381: */
382: struct direct *
383: rst_readdir(dirp)
384: register DIR *dirp;
385: {
386: register struct direct *dp;
387:
388: for (;;) {
389: if (dirp->dd_loc == 0) {
390: dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
391: DIRBLKSIZ);
392: if (dirp->dd_size <= 0) {
393: dprintf(stderr, "error reading directory\n");
394: return NULL;
395: }
396: }
397: if (dirp->dd_loc >= dirp->dd_size) {
398: dirp->dd_loc = 0;
399: continue;
400: }
401: dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
402: if (dp->d_reclen == 0 ||
403: dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
404: dprintf(stderr, "corrupted directory: bad reclen %d\n",
405: dp->d_reclen);
406: return NULL;
407: }
408: dirp->dd_loc += dp->d_reclen;
409: if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0)
410: continue;
411: if (dp->d_ino >= maxino) {
412: dprintf(stderr, "corrupted directory: bad inum %d\n",
413: dp->d_ino);
414: continue;
415: }
416: return (dp);
417: }
418: }
419:
420: /*
421: * Simulate the opening of a directory
422: */
423: DIR *
424: rst_opendir(name)
425: char *name;
426: {
427: struct inotab *itp;
428: ino_t ino;
429:
430: if ((ino = dirlookup(name)) > 0 &&
431: (itp = inotablookup(ino)) != NULL) {
432: rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
433: return (dirp);
434: }
435: return (0);
436: }
437:
438: /*
439: * Set the mode, owner, and times for all new or changed directories
440: */
441: setdirmodes()
442: {
443: FILE *mf;
444: struct modeinfo node;
445: struct entry *ep;
446: char *cp;
447:
448: vprintf(stdout, "Set directory mode, owner, and times.\n");
449: (void) sprintf(modefile, "/tmp/rstmode%d", dumpdate);
450: mf = fopen(modefile, "r");
451: if (mf == NULL) {
452: perror("fopen");
453: fprintf(stderr, "cannot open mode file %s\n", modefile);
454: fprintf(stderr, "directory mode, owner, and times not set\n");
455: return;
456: }
457: clearerr(mf);
458: for (;;) {
459: (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
460: if (feof(mf))
461: break;
462: ep = lookupino(node.ino);
463: if (command == 'i' || command == 'x') {
464: if (ep == NIL)
465: continue;
466: if (ep->e_flags & EXISTED) {
467: ep->e_flags &= ~NEW;
468: continue;
469: }
470: if (node.ino == ROOTINO &&
471: reply("set owner/mode for '.'") == FAIL)
472: continue;
473: }
474: if (ep == NIL)
475: panic("cannot find directory inode %d\n", node.ino);
476: cp = myname(ep);
477: (void) chown(cp, node.uid, node.gid);
478: (void) chmod(cp, node.mode);
479: utime(cp, node.timep);
480: ep->e_flags &= ~NEW;
481: }
482: if (ferror(mf))
483: panic("error setting directory modes\n");
484: (void) fclose(mf);
485: }
486:
487: /*
488: * Generate a literal copy of a directory.
489: */
490: genliteraldir(name, ino)
491: char *name;
492: ino_t ino;
493: {
494: register struct inotab *itp;
495: int ofile, dp, i, size;
496: char buf[BUFSIZ];
497:
498: itp = inotablookup(ino);
499: if (itp == NULL)
500: panic("Cannot find directory inode %d named %s\n", ino, name);
501: if ((ofile = creat(name, 0666)) < 0) {
502: fprintf(stderr, "%s: ", name);
503: (void) fflush(stderr);
504: perror("cannot create file");
505: return (FAIL);
506: }
507: rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
508: dp = dup(dirp->dd_fd);
509: for (i = itp->t_size; i > 0; i -= BUFSIZ) {
510: size = i < BUFSIZ ? i : BUFSIZ;
511: if (read(dp, buf, (int) size) == -1) {
512: fprintf(stderr,
513: "write error extracting inode %d, name %s\n",
514: curfile.ino, curfile.name);
515: perror("read");
516: done(1);
517: }
518: if (write(ofile, buf, (int) size) == -1) {
519: fprintf(stderr,
520: "write error extracting inode %d, name %s\n",
521: curfile.ino, curfile.name);
522: perror("write");
523: done(1);
524: }
525: }
526: (void) close(dp);
527: (void) close(ofile);
528: return (GOOD);
529: }
530:
531: /*
532: * Determine the type of an inode
533: */
534: inodetype(ino)
535: ino_t ino;
536: {
537: struct inotab *itp;
538:
539: itp = inotablookup(ino);
540: if (itp == NULL)
541: return (LEAF);
542: return (NODE);
543: }
544:
545: /*
546: * Allocate and initialize a directory inode entry.
547: * If requested, save its pertinent mode, owner, and time info.
548: */
549: struct inotab *
550: allocinotab(ino, dip, seekpt)
551: ino_t ino;
552: struct dinode *dip;
553: daddr_t seekpt;
554: {
555: register struct inotab *itp;
556: struct modeinfo node;
557:
558: itp = (struct inotab *)calloc(1, sizeof(struct inotab));
559: if (itp == 0)
560: panic("no memory directory table\n");
561: itp->t_next = inotab[INOHASH(ino)];
562: inotab[INOHASH(ino)] = itp;
563: itp->t_ino = ino;
564: itp->t_seekpt = seekpt;
565: if (mf == NULL)
566: return(itp);
567: node.ino = ino;
568: node.timep[0] = dip->di_atime;
569: node.timep[1] = dip->di_mtime;
570: node.mode = dip->di_mode;
571: node.uid = dip->di_uid;
572: node.gid = dip->di_gid;
573: (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
574: return(itp);
575: }
576:
577: /*
578: * Look up an inode in the table of directories
579: */
580: struct inotab *
581: inotablookup(ino)
582: ino_t ino;
583: {
584: register struct inotab *itp;
585:
586: for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
587: if (itp->t_ino == ino)
588: return(itp);
589: return ((struct inotab *)0);
590: }
591:
592: /*
593: * Clean up and exit
594: */
595: done(exitcode)
596: int exitcode;
597: {
598:
599: closemt();
600: if (modefile[0] != '#')
601: (void) unlink(modefile);
602: if (dirfile[0] != '#')
603: (void) unlink(dirfile);
604: exit(exitcode);
605: }