1 /*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
4 * (c) UNIX System Laboratories, Inc.
5 * All or some portions of this file are derived from material licensed
6 * to the University of California by American Telephone and Telegraph
7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8 * the permission of UNIX System Laboratories, Inc.
9 *
10 * %sccs.include.redist.c%
11 */
12
13 #ifndef lint
14 static char sccsid[] = "@(#)dirs.c 8.7 (Berkeley) 05/01/95";
15 #endif /* not lint */
16
17 #include <sys/param.h>
18 #include <sys/file.h>
19 #include <sys/stat.h>
20 #include <sys/time.h>
21
22 #include <ufs/ufs/dinode.h>
23 #include <ufs/ufs/dir.h>
24 #include <ufs/ffs/fs.h>
25 #include <protocols/dumprestore.h>
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include <machine/endian.h>
34
35 #include "pathnames.h"
36 #include "restore.h"
37 #include "extern.h"
38
39 /*
40 * Symbol table of directories read from tape.
41 */
42 #define HASHSIZE 1000
43 #define INOHASH(val) (val % HASHSIZE)
44 struct inotab {
45 struct inotab *t_next;
46 ino_t t_ino;
47 long t_seekpt;
48 long t_size;
49 };
50 static struct inotab *inotab[HASHSIZE];
51
52 /*
53 * Information retained about directories.
54 */
55 struct modeinfo {
56 ino_t ino;
57 struct timeval timep[2];
58 mode_t mode;
59 uid_t uid;
60 gid_t gid;
61 int flags;
62 };
63
64 /*
65 * Definitions for library routines operating on directories.
66 */
67 #undef DIRBLKSIZ
68 #define DIRBLKSIZ 1024
69 struct rstdirdesc {
70 int dd_fd;
71 long dd_loc;
72 long dd_size;
73 char dd_buf[DIRBLKSIZ];
74 };
75
76 /*
77 * Global variables for this file.
78 */
79 static long seekpt;
80 static FILE *df, *mf;
81 static RST_DIR *dirp;
82 static char dirfile[32] = "#"; /* No file */
83 static char modefile[32] = "#"; /* No file */
84 static char dot[2] = "."; /* So it can be modified */
85
86 /*
87 * Format of old style directories.
88 */
89 #define ODIRSIZ 14
90 struct odirect {
91 u_short d_ino;
92 char d_name[ODIRSIZ];
93 };
94
95 static struct inotab *allocinotab __P((ino_t, struct dinode *, long));
96 static void dcvt __P((struct odirect *, struct direct *));
97 static void flushent __P((void));
98 static struct inotab *inotablookup __P((ino_t));
99 static RST_DIR *opendirfile __P((const char *));
100 static void putdir __P((char *, long));
101 static void putent __P((struct direct *));
102 static void rst_seekdir __P((RST_DIR *, long, long));
103 static long rst_telldir __P((RST_DIR *));
104 static struct direct *searchdir __P((ino_t, char *));
105
106 /*
107 * Extract directory contents, building up a directory structure
108 * on disk for extraction by name.
109 * If genmode is requested, save mode, owner, and times for all
110 * directories on the tape.
111 */
112 void
extractdirs(genmode)113 extractdirs(genmode)
114 int genmode;
115 {
116 register int i;
117 register struct dinode *ip;
118 struct inotab *itp;
119 struct direct nulldir;
120
121 vprintf(stdout, "Extract directories from tape\n");
122 (void) sprintf(dirfile, "%s/rstdir%d", _PATH_TMP, dumpdate);
123 df = fopen(dirfile, "w");
124 if (df == NULL) {
125 fprintf(stderr,
126 "restore: %s - cannot create directory temporary\n",
127 dirfile);
128 fprintf(stderr, "fopen: %s\n", strerror(errno));
129 done(1);
130 }
131 if (genmode != 0) {
132 (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate);
133 mf = fopen(modefile, "w");
134 if (mf == NULL) {
135 fprintf(stderr,
136 "restore: %s - cannot create modefile \n",
137 modefile);
138 fprintf(stderr, "fopen: %s\n", strerror(errno));
139 done(1);
140 }
141 }
142 nulldir.d_ino = 0;
143 nulldir.d_type = DT_DIR;
144 nulldir.d_namlen = 1;
145 (void) strcpy(nulldir.d_name, "/");
146 nulldir.d_reclen = DIRSIZ(0, &nulldir);
147 for (;;) {
148 curfile.name = "<directory file - name unknown>";
149 curfile.action = USING;
150 ip = curfile.dip;
151 if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) {
152 (void) fclose(df);
153 dirp = opendirfile(dirfile);
154 if (dirp == NULL)
155 fprintf(stderr, "opendirfile: %s\n",
156 strerror(errno));
157 if (mf != NULL)
158 (void) fclose(mf);
159 i = dirlookup(dot);
160 if (i == 0)
161 panic("Root directory is not on tape\n");
162 return;
163 }
164 itp = allocinotab(curfile.ino, ip, seekpt);
165 getfile(putdir, xtrnull);
166 putent(&nulldir);
167 flushent();
168 itp->t_size = seekpt - itp->t_seekpt;
169 }
170 }
171
172 /*
173 * skip over all the directories on the tape
174 */
175 void
skipdirs()176 skipdirs()
177 {
178
179 while ((curfile.dip->di_mode & IFMT) == IFDIR) {
180 skipfile();
181 }
182 }
183
184 /*
185 * Recursively find names and inumbers of all files in subtree
186 * pname and pass them off to be processed.
187 */
188 void
treescan(pname,ino,todo)189 treescan(pname, ino, todo)
190 char *pname;
191 ino_t ino;
192 long (*todo) __P((char *, ino_t, int));
193 {
194 register struct inotab *itp;
195 register struct direct *dp;
196 int namelen;
197 long bpt;
198 char locname[MAXPATHLEN + 1];
199
200 itp = inotablookup(ino);
201 if (itp == NULL) {
202 /*
203 * Pname is name of a simple file or an unchanged directory.
204 */
205 (void) (*todo)(pname, ino, LEAF);
206 return;
207 }
208 /*
209 * Pname is a dumped directory name.
210 */
211 if ((*todo)(pname, ino, NODE) == FAIL)
212 return;
213 /*
214 * begin search through the directory
215 * skipping over "." and ".."
216 */
217 (void) strncpy(locname, pname, MAXPATHLEN);
218 (void) strncat(locname, "/", MAXPATHLEN);
219 namelen = strlen(locname);
220 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
221 dp = rst_readdir(dirp); /* "." */
222 if (dp != NULL && strcmp(dp->d_name, ".") == 0)
223 dp = rst_readdir(dirp); /* ".." */
224 else
225 fprintf(stderr, "Warning: `.' missing from directory %s\n",
226 pname);
227 if (dp != NULL && strcmp(dp->d_name, "..") == 0)
228 dp = rst_readdir(dirp); /* first real entry */
229 else
230 fprintf(stderr, "Warning: `..' missing from directory %s\n",
231 pname);
232 bpt = rst_telldir(dirp);
233 /*
234 * a zero inode signals end of directory
235 */
236 while (dp != NULL) {
237 locname[namelen] = '\0';
238 if (namelen + dp->d_namlen >= MAXPATHLEN) {
239 fprintf(stderr, "%s%s: name exceeds %d char\n",
240 locname, dp->d_name, MAXPATHLEN);
241 } else {
242 (void) strncat(locname, dp->d_name, (int)dp->d_namlen);
243 treescan(locname, dp->d_ino, todo);
244 rst_seekdir(dirp, bpt, itp->t_seekpt);
245 }
246 dp = rst_readdir(dirp);
247 bpt = rst_telldir(dirp);
248 }
249 }
250
251 /*
252 * Lookup a pathname which is always assumed to start from the ROOTINO.
253 */
254 struct direct *
pathsearch(pathname)255 pathsearch(pathname)
256 const char *pathname;
257 {
258 ino_t ino;
259 struct direct *dp;
260 char *path, *name, buffer[MAXPATHLEN];
261
262 strcpy(buffer, pathname);
263 path = buffer;
264 ino = ROOTINO;
265 while (*path == '/')
266 path++;
267 dp = NULL;
268 while ((name = strsep(&path, "/")) != NULL && *name != NULL) {
269 if ((dp = searchdir(ino, name)) == NULL)
270 return (NULL);
271 ino = dp->d_ino;
272 }
273 return (dp);
274 }
275
276 /*
277 * Lookup the requested name in directory inum.
278 * Return its inode number if found, zero if it does not exist.
279 */
280 static struct direct *
searchdir(inum,name)281 searchdir(inum, name)
282 ino_t inum;
283 char *name;
284 {
285 register struct direct *dp;
286 register struct inotab *itp;
287 int len;
288
289 itp = inotablookup(inum);
290 if (itp == NULL)
291 return (NULL);
292 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
293 len = strlen(name);
294 do {
295 dp = rst_readdir(dirp);
296 if (dp == NULL)
297 return (NULL);
298 } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
299 return (dp);
300 }
301
302 /*
303 * Put the directory entries in the directory file
304 */
305 static void
putdir(buf,size)306 putdir(buf, size)
307 char *buf;
308 long size;
309 {
310 struct direct cvtbuf;
311 register struct odirect *odp;
312 struct odirect *eodp;
313 register struct direct *dp;
314 long loc, i;
315
316 if (cvtflag) {
317 eodp = (struct odirect *)&buf[size];
318 for (odp = (struct odirect *)buf; odp < eodp; odp++)
319 if (odp->d_ino != 0) {
320 dcvt(odp, &cvtbuf);
321 putent(&cvtbuf);
322 }
323 } else {
324 for (loc = 0; loc < size; ) {
325 dp = (struct direct *)(buf + loc);
326 if (Bcvt)
327 swabst((u_char *)"ls", (u_char *) dp);
328 if (oldinofmt && dp->d_ino != 0) {
329 # if BYTE_ORDER == BIG_ENDIAN
330 if (Bcvt)
331 dp->d_namlen = dp->d_type;
332 # else
333 if (!Bcvt)
334 dp->d_namlen = dp->d_type;
335 # endif
336 dp->d_type = DT_UNKNOWN;
337 }
338 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
339 if ((dp->d_reclen & 0x3) != 0 ||
340 dp->d_reclen > i ||
341 dp->d_reclen < DIRSIZ(0, dp) ||
342 dp->d_namlen > NAME_MAX) {
343 vprintf(stdout, "Mangled directory: ");
344 if ((dp->d_reclen & 0x3) != 0)
345 vprintf(stdout,
346 "reclen not multiple of 4 ");
347 if (dp->d_reclen < DIRSIZ(0, dp))
348 vprintf(stdout,
349 "reclen less than DIRSIZ (%d < %d) ",
350 dp->d_reclen, DIRSIZ(0, dp));
351 if (dp->d_namlen > NAME_MAX)
352 vprintf(stdout,
353 "reclen name too big (%d > %d) ",
354 dp->d_namlen, NAME_MAX);
355 vprintf(stdout, "\n");
356 loc += i;
357 continue;
358 }
359 loc += dp->d_reclen;
360 if (dp->d_ino != 0) {
361 putent(dp);
362 }
363 }
364 }
365 }
366
367 /*
368 * These variables are "local" to the following two functions.
369 */
370 char dirbuf[DIRBLKSIZ];
371 long dirloc = 0;
372 long prev = 0;
373
374 /*
375 * add a new directory entry to a file.
376 */
377 static void
putent(dp)378 putent(dp)
379 struct direct *dp;
380 {
381 dp->d_reclen = DIRSIZ(0, dp);
382 if (dirloc + dp->d_reclen > DIRBLKSIZ) {
383 ((struct direct *)(dirbuf + prev))->d_reclen =
384 DIRBLKSIZ - prev;
385 (void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
386 dirloc = 0;
387 }
388 memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
389 prev = dirloc;
390 dirloc += dp->d_reclen;
391 }
392
393 /*
394 * flush out a directory that is finished.
395 */
396 static void
flushent()397 flushent()
398 {
399 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
400 (void) fwrite(dirbuf, (int)dirloc, 1, df);
401 seekpt = ftell(df);
402 dirloc = 0;
403 }
404
405 static void
dcvt(odp,ndp)406 dcvt(odp, ndp)
407 register struct odirect *odp;
408 register struct direct *ndp;
409 {
410
411 memset(ndp, 0, (long)(sizeof *ndp));
412 ndp->d_ino = odp->d_ino;
413 ndp->d_type = DT_UNKNOWN;
414 (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
415 ndp->d_namlen = strlen(ndp->d_name);
416 ndp->d_reclen = DIRSIZ(0, ndp);
417 }
418
419 /*
420 * Seek to an entry in a directory.
421 * Only values returned by rst_telldir should be passed to rst_seekdir.
422 * This routine handles many directories in a single file.
423 * It takes the base of the directory in the file, plus
424 * the desired seek offset into it.
425 */
426 static void
rst_seekdir(dirp,loc,base)427 rst_seekdir(dirp, loc, base)
428 register RST_DIR *dirp;
429 long loc, base;
430 {
431
432 if (loc == rst_telldir(dirp))
433 return;
434 loc -= base;
435 if (loc < 0)
436 fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc);
437 (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), SEEK_SET);
438 dirp->dd_loc = loc & (DIRBLKSIZ - 1);
439 if (dirp->dd_loc != 0)
440 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
441 }
442
443 /*
444 * get next entry in a directory.
445 */
446 struct direct *
rst_readdir(dirp)447 rst_readdir(dirp)
448 register RST_DIR *dirp;
449 {
450 register struct direct *dp;
451
452 for (;;) {
453 if (dirp->dd_loc == 0) {
454 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
455 DIRBLKSIZ);
456 if (dirp->dd_size <= 0) {
457 dprintf(stderr, "error reading directory\n");
458 return (NULL);
459 }
460 }
461 if (dirp->dd_loc >= dirp->dd_size) {
462 dirp->dd_loc = 0;
463 continue;
464 }
465 dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
466 if (dp->d_reclen == 0 ||
467 dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
468 dprintf(stderr, "corrupted directory: bad reclen %d\n",
469 dp->d_reclen);
470 return (NULL);
471 }
472 dirp->dd_loc += dp->d_reclen;
473 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
474 return (NULL);
475 if (dp->d_ino >= maxino) {
476 dprintf(stderr, "corrupted directory: bad inum %d\n",
477 dp->d_ino);
478 continue;
479 }
480 return (dp);
481 }
482 }
483
484 /*
485 * Simulate the opening of a directory
486 */
487 RST_DIR *
rst_opendir(name)488 rst_opendir(name)
489 const char *name;
490 {
491 struct inotab *itp;
492 RST_DIR *dirp;
493 ino_t ino;
494
495 if ((ino = dirlookup(name)) > 0 &&
496 (itp = inotablookup(ino)) != NULL) {
497 dirp = opendirfile(dirfile);
498 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
499 return (dirp);
500 }
501 return (NULL);
502 }
503
504 /*
505 * In our case, there is nothing to do when closing a directory.
506 */
507 void
rst_closedir(dirp)508 rst_closedir(dirp)
509 RST_DIR *dirp;
510 {
511
512 (void)close(dirp->dd_fd);
513 free(dirp);
514 return;
515 }
516
517 /*
518 * Simulate finding the current offset in the directory.
519 */
520 static long
rst_telldir(dirp)521 rst_telldir(dirp)
522 RST_DIR *dirp;
523 {
524 return ((long)lseek(dirp->dd_fd,
525 (off_t)0, SEEK_CUR) - dirp->dd_size + dirp->dd_loc);
526 }
527
528 /*
529 * Open a directory file.
530 */
531 static RST_DIR *
opendirfile(name)532 opendirfile(name)
533 const char *name;
534 {
535 register RST_DIR *dirp;
536 register int fd;
537
538 if ((fd = open(name, O_RDONLY)) == -1)
539 return (NULL);
540 if ((dirp = malloc(sizeof(RST_DIR))) == NULL) {
541 (void)close(fd);
542 return (NULL);
543 }
544 dirp->dd_fd = fd;
545 dirp->dd_loc = 0;
546 return (dirp);
547 }
548
549 /*
550 * Set the mode, owner, and times for all new or changed directories
551 */
552 void
setdirmodes(flags)553 setdirmodes(flags)
554 int flags;
555 {
556 FILE *mf;
557 struct modeinfo node;
558 struct entry *ep;
559 char *cp;
560
561 vprintf(stdout, "Set directory mode, owner, and times.\n");
562 (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate);
563 mf = fopen(modefile, "r");
564 if (mf == NULL) {
565 fprintf(stderr, "fopen: %s\n", strerror(errno));
566 fprintf(stderr, "cannot open mode file %s\n", modefile);
567 fprintf(stderr, "directory mode, owner, and times not set\n");
568 return;
569 }
570 clearerr(mf);
571 for (;;) {
572 (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
573 if (feof(mf))
574 break;
575 ep = lookupino(node.ino);
576 if (command == 'i' || command == 'x') {
577 if (ep == NULL)
578 continue;
579 if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
580 ep->e_flags &= ~NEW;
581 continue;
582 }
583 if (node.ino == ROOTINO &&
584 reply("set owner/mode for '.'") == FAIL)
585 continue;
586 }
587 if (ep == NULL) {
588 panic("cannot find directory inode %d\n", node.ino);
589 } else {
590 cp = myname(ep);
591 (void) chown(cp, node.uid, node.gid);
592 (void) chmod(cp, node.mode);
593 (void) chflags(cp, node.flags);
594 utimes(cp, node.timep);
595 ep->e_flags &= ~NEW;
596 }
597 }
598 if (ferror(mf))
599 panic("error setting directory modes\n");
600 (void) fclose(mf);
601 }
602
603 /*
604 * Generate a literal copy of a directory.
605 */
606 int
genliteraldir(name,ino)607 genliteraldir(name, ino)
608 char *name;
609 ino_t ino;
610 {
611 register struct inotab *itp;
612 int ofile, dp, i, size;
613 char buf[BUFSIZ];
614
615 itp = inotablookup(ino);
616 if (itp == NULL)
617 panic("Cannot find directory inode %d named %s\n", ino, name);
618 if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
619 fprintf(stderr, "%s: ", name);
620 (void) fflush(stderr);
621 fprintf(stderr, "cannot create file: %s\n", strerror(errno));
622 return (FAIL);
623 }
624 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
625 dp = dup(dirp->dd_fd);
626 for (i = itp->t_size; i > 0; i -= BUFSIZ) {
627 size = i < BUFSIZ ? i : BUFSIZ;
628 if (read(dp, buf, (int) size) == -1) {
629 fprintf(stderr,
630 "write error extracting inode %d, name %s\n",
631 curfile.ino, curfile.name);
632 fprintf(stderr, "read: %s\n", strerror(errno));
633 done(1);
634 }
635 if (!Nflag && write(ofile, buf, (int) size) == -1) {
636 fprintf(stderr,
637 "write error extracting inode %d, name %s\n",
638 curfile.ino, curfile.name);
639 fprintf(stderr, "write: %s\n", strerror(errno));
640 done(1);
641 }
642 }
643 (void) close(dp);
644 (void) close(ofile);
645 return (GOOD);
646 }
647
648 /*
649 * Determine the type of an inode
650 */
651 int
inodetype(ino)652 inodetype(ino)
653 ino_t ino;
654 {
655 struct inotab *itp;
656
657 itp = inotablookup(ino);
658 if (itp == NULL)
659 return (LEAF);
660 return (NODE);
661 }
662
663 /*
664 * Allocate and initialize a directory inode entry.
665 * If requested, save its pertinent mode, owner, and time info.
666 */
667 static struct inotab *
allocinotab(ino,dip,seekpt)668 allocinotab(ino, dip, seekpt)
669 ino_t ino;
670 struct dinode *dip;
671 long seekpt;
672 {
673 register struct inotab *itp;
674 struct modeinfo node;
675
676 itp = calloc(1, sizeof(struct inotab));
677 if (itp == NULL)
678 panic("no memory directory table\n");
679 itp->t_next = inotab[INOHASH(ino)];
680 inotab[INOHASH(ino)] = itp;
681 itp->t_ino = ino;
682 itp->t_seekpt = seekpt;
683 if (mf == NULL)
684 return (itp);
685 node.ino = ino;
686 node.timep[0].tv_sec = dip->di_atime;
687 node.timep[0].tv_usec = dip->di_atimensec / 1000;
688 node.timep[1].tv_sec = dip->di_mtime;
689 node.timep[1].tv_usec = dip->di_mtimensec / 1000;
690 node.mode = dip->di_mode;
691 node.flags = dip->di_flags;
692 node.uid = dip->di_uid;
693 node.gid = dip->di_gid;
694 (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
695 return (itp);
696 }
697
698 /*
699 * Look up an inode in the table of directories
700 */
701 static struct inotab *
inotablookup(ino)702 inotablookup(ino)
703 ino_t ino;
704 {
705 register struct inotab *itp;
706
707 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
708 if (itp->t_ino == ino)
709 return (itp);
710 return (NULL);
711 }
712
713 /*
714 * Clean up and exit
715 */
716 __dead void
done(exitcode)717 done(exitcode)
718 int exitcode;
719 {
720
721 closemt();
722 if (modefile[0] != '#')
723 (void) unlink(modefile);
724 if (dirfile[0] != '#')
725 (void) unlink(dirfile);
726 exit(exitcode);
727 }
728