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