Start development on 386BSD 0.1
[unix-history] / .ref-386BSD-0.0 / usr / src / sbin / restore / dirs.c
CommitLineData
40d03e52
WJ
1/*
2 * Copyright (c) 1983 The Regents of the University of California.
3 * 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 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char sccsid[] = "@(#)dirs.c 5.15 (Berkeley) 2/26/91";
36#endif /* not lint */
37
38#include "restore.h"
39#include <protocols/dumprestore.h>
40#include <sys/file.h>
41#include <ufs/dir.h>
42#include "pathnames.h"
43
44/*
45 * Symbol table of directories read from tape.
46 */
47#define HASHSIZE 1000
48#define INOHASH(val) (val % HASHSIZE)
49struct inotab {
50 struct inotab *t_next;
51 ino_t t_ino;
52 daddr_t t_seekpt;
53 long t_size;
54};
55static struct inotab *inotab[HASHSIZE];
56extern struct inotab *inotablookup();
57extern struct inotab *allocinotab();
58
59/*
60 * Information retained about directories.
61 */
62struct modeinfo {
63 ino_t ino;
64 struct timeval timep[2];
65 short mode;
66 short uid;
67 short gid;
68};
69
70/*
71 * Definitions for library routines operating on directories.
72 */
73#undef DIRBLKSIZ
74#define DIRBLKSIZ 1024
75struct dirdesc {
76 int dd_fd;
77 long dd_loc;
78 long dd_size;
79 char dd_buf[DIRBLKSIZ];
80};
81extern DIR *opendirfile();
82extern off_t rst_telldir();
83extern void rst_seekdir();
84
85/*
86 * Global variables for this file.
87 */
88static daddr_t seekpt;
89static FILE *df, *mf;
90static DIR *dirp;
91static char dirfile[32] = "#"; /* No file */
92static char modefile[32] = "#"; /* No file */
93static char dot[2] = "."; /* So it can be modified */
94extern ino_t search();
95struct direct *rst_readdir();
96extern void rst_seekdir();
97
98/*
99 * Format of old style directories.
100 */
101#define ODIRSIZ 14
102struct odirect {
103 u_short d_ino;
104 char d_name[ODIRSIZ];
105};
106
107/*
108 * Extract directory contents, building up a directory structure
109 * on disk for extraction by name.
110 * If genmode is requested, save mode, owner, and times for all
111 * directories on the tape.
112 */
113extractdirs(genmode)
114 int genmode;
115{
116 register int i;
117 register struct dinode *ip;
118 struct inotab *itp;
119 struct direct nulldir;
120 int putdir(), null();
121
122 vprintf(stdout, "Extract directories from tape\n");
123 (void) sprintf(dirfile, "%s/rstdir%d", _PATH_TMP, dumpdate);
124 df = fopen(dirfile, "w");
125 if (df == 0) {
126 fprintf(stderr,
127 "restore: %s - cannot create directory temporary\n",
128 dirfile);
129 perror("fopen");
130 done(1);
131 }
132 if (genmode != 0) {
133 (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate);
134 mf = fopen(modefile, "w");
135 if (mf == 0) {
136 fprintf(stderr,
137 "restore: %s - cannot create modefile \n",
138 modefile);
139 perror("fopen");
140 done(1);
141 }
142 }
143 nulldir.d_ino = 0;
144 nulldir.d_namlen = 1;
145 (void) strcpy(nulldir.d_name, "/");
146 nulldir.d_reclen = DIRSIZ(&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 perror("opendirfile");
156 if (mf != NULL)
157 (void) fclose(mf);
158 i = dirlookup(dot);
159 if (i == 0)
160 panic("Root directory is not on tape\n");
161 return;
162 }
163 itp = allocinotab(curfile.ino, ip, seekpt);
164 getfile(putdir, null);
165 putent(&nulldir);
166 flushent();
167 itp->t_size = seekpt - itp->t_seekpt;
168 }
169}
170
171/*
172 * skip over all the directories on the tape
173 */
174skipdirs()
175{
176
177 while ((curfile.dip->di_mode & IFMT) == IFDIR) {
178 skipfile();
179 }
180}
181
182/*
183 * Recursively find names and inumbers of all files in subtree
184 * pname and pass them off to be processed.
185 */
186treescan(pname, ino, todo)
187 char *pname;
188 ino_t ino;
189 long (*todo)();
190{
191 register struct inotab *itp;
192 register struct direct *dp;
193 register struct entry *np;
194 int namelen;
195 daddr_t bpt;
196 char locname[MAXPATHLEN + 1];
197
198 itp = inotablookup(ino);
199 if (itp == NULL) {
200 /*
201 * Pname is name of a simple file or an unchanged directory.
202 */
203 (void) (*todo)(pname, ino, LEAF);
204 return;
205 }
206 /*
207 * Pname is a dumped directory name.
208 */
209 if ((*todo)(pname, ino, NODE) == FAIL)
210 return;
211 /*
212 * begin search through the directory
213 * skipping over "." and ".."
214 */
215 (void) strncpy(locname, pname, MAXPATHLEN);
216 (void) strncat(locname, "/", MAXPATHLEN);
217 namelen = strlen(locname);
218 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
219 dp = rst_readdir(dirp); /* "." */
220 if (dp != NULL && strcmp(dp->d_name, ".") == 0)
221 dp = rst_readdir(dirp); /* ".." */
222 else
223 fprintf(stderr, "Warning: `.' missing from directory %s\n",
224 pname);
225 if (dp != NULL && strcmp(dp->d_name, "..") == 0)
226 dp = rst_readdir(dirp); /* first real entry */
227 else
228 fprintf(stderr, "Warning: `..' missing from directory %s\n",
229 pname);
230 bpt = rst_telldir(dirp);
231 /*
232 * a zero inode signals end of directory
233 */
234 while (dp != NULL && dp->d_ino != 0) {
235 locname[namelen] = '\0';
236 if (namelen + dp->d_namlen >= MAXPATHLEN) {
237 fprintf(stderr, "%s%s: name exceeds %d char\n",
238 locname, dp->d_name, MAXPATHLEN);
239 } else {
240 (void) strncat(locname, dp->d_name, (int)dp->d_namlen);
241 treescan(locname, dp->d_ino, todo);
242 rst_seekdir(dirp, bpt, itp->t_seekpt);
243 }
244 dp = rst_readdir(dirp);
245 bpt = rst_telldir(dirp);
246 }
247 if (dp == NULL)
248 fprintf(stderr, "corrupted directory: %s.\n", locname);
249}
250
251/*
252 * Search the directory tree rooted at inode ROOTINO
253 * for the path pointed at by n
254 */
255ino_t
256psearch(n)
257 char *n;
258{
259 register char *cp, *cp1;
260 ino_t ino;
261 char c;
262
263 ino = ROOTINO;
264 if (*(cp = n) == '/')
265 cp++;
266next:
267 cp1 = cp + 1;
268 while (*cp1 != '/' && *cp1)
269 cp1++;
270 c = *cp1;
271 *cp1 = 0;
272 ino = search(ino, cp);
273 if (ino == 0) {
274 *cp1 = c;
275 return(0);
276 }
277 *cp1 = c;
278 if (c == '/') {
279 cp = cp1+1;
280 goto next;
281 }
282 return(ino);
283}
284
285/*
286 * search the directory inode ino
287 * looking for entry cp
288 */
289ino_t
290search(inum, cp)
291 ino_t inum;
292 char *cp;
293{
294 register struct direct *dp;
295 register struct inotab *itp;
296 int len;
297
298 itp = inotablookup(inum);
299 if (itp == NULL)
300 return(0);
301 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
302 len = strlen(cp);
303 do {
304 dp = rst_readdir(dirp);
305 if (dp == NULL || dp->d_ino == 0)
306 return (0);
307 } while (dp->d_namlen != len || strncmp(dp->d_name, cp, len) != 0);
308 return(dp->d_ino);
309}
310
311/*
312 * Put the directory entries in the directory file
313 */
314putdir(buf, size)
315 char *buf;
316 int size;
317{
318 struct direct cvtbuf;
319 register struct odirect *odp;
320 struct odirect *eodp;
321 register struct direct *dp;
322 long loc, i;
323 extern int Bcvt;
324
325 if (cvtflag) {
326 eodp = (struct odirect *)&buf[size];
327 for (odp = (struct odirect *)buf; odp < eodp; odp++)
328 if (odp->d_ino != 0) {
329 dcvt(odp, &cvtbuf);
330 putent(&cvtbuf);
331 }
332 } else {
333 for (loc = 0; loc < size; ) {
334 dp = (struct direct *)(buf + loc);
335 if (Bcvt) {
336 swabst("l2s", (char *) dp);
337 }
338 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
339 if ((dp->d_reclen & 0x3) != 0 ||
340 dp->d_reclen > i ||
341 dp->d_reclen < DIRSIZ(dp) ||
342 dp->d_namlen > MAXNAMLEN) {
343 vprintf(stdout, "Mangled directory\n");
344 loc += i;
345 continue;
346 }
347 loc += dp->d_reclen;
348 if (dp->d_ino != 0) {
349 putent(dp);
350 }
351 }
352 }
353}
354
355/*
356 * These variables are "local" to the following two functions.
357 */
358char dirbuf[DIRBLKSIZ];
359long dirloc = 0;
360long prev = 0;
361
362/*
363 * add a new directory entry to a file.
364 */
365putent(dp)
366 struct direct *dp;
367{
368 dp->d_reclen = DIRSIZ(dp);
369 if (dirloc + dp->d_reclen > DIRBLKSIZ) {
370 ((struct direct *)(dirbuf + prev))->d_reclen =
371 DIRBLKSIZ - prev;
372 (void) fwrite(dirbuf, 1, DIRBLKSIZ, df);
373 dirloc = 0;
374 }
375 bcopy((char *)dp, dirbuf + dirloc, (long)dp->d_reclen);
376 prev = dirloc;
377 dirloc += dp->d_reclen;
378}
379
380/*
381 * flush out a directory that is finished.
382 */
383flushent()
384{
385
386 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
387 (void) fwrite(dirbuf, (int)dirloc, 1, df);
388 seekpt = ftell(df);
389 dirloc = 0;
390}
391
392dcvt(odp, ndp)
393 register struct odirect *odp;
394 register struct direct *ndp;
395{
396
397 bzero((char *)ndp, (long)(sizeof *ndp));
398 ndp->d_ino = odp->d_ino;
399 (void) strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
400 ndp->d_namlen = strlen(ndp->d_name);
401 ndp->d_reclen = DIRSIZ(ndp);
402}
403
404/*
405 * Seek to an entry in a directory.
406 * Only values returned by rst_telldir should be passed to rst_seekdir.
407 * This routine handles many directories in a single file.
408 * It takes the base of the directory in the file, plus
409 * the desired seek offset into it.
410 */
411void
412rst_seekdir(dirp, loc, base)
413 register DIR *dirp;
414 daddr_t loc, base;
415{
416
417 if (loc == rst_telldir(dirp))
418 return;
419 loc -= base;
420 if (loc < 0)
421 fprintf(stderr, "bad seek pointer to rst_seekdir %d\n", loc);
422 (void) lseek(dirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0);
423 dirp->dd_loc = loc & (DIRBLKSIZ - 1);
424 if (dirp->dd_loc != 0)
425 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ);
426}
427
428/*
429 * get next entry in a directory.
430 */
431struct direct *
432rst_readdir(dirp)
433 register DIR *dirp;
434{
435 register struct direct *dp;
436
437 for (;;) {
438 if (dirp->dd_loc == 0) {
439 dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
440 DIRBLKSIZ);
441 if (dirp->dd_size <= 0) {
442 dprintf(stderr, "error reading directory\n");
443 return NULL;
444 }
445 }
446 if (dirp->dd_loc >= dirp->dd_size) {
447 dirp->dd_loc = 0;
448 continue;
449 }
450 dp = (struct direct *)(dirp->dd_buf + dirp->dd_loc);
451 if (dp->d_reclen == 0 ||
452 dp->d_reclen > DIRBLKSIZ + 1 - dirp->dd_loc) {
453 dprintf(stderr, "corrupted directory: bad reclen %d\n",
454 dp->d_reclen);
455 return NULL;
456 }
457 dirp->dd_loc += dp->d_reclen;
458 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0)
459 continue;
460 if (dp->d_ino >= maxino) {
461 dprintf(stderr, "corrupted directory: bad inum %d\n",
462 dp->d_ino);
463 continue;
464 }
465 return (dp);
466 }
467}
468
469/*
470 * Simulate the opening of a directory
471 */
472DIR *
473rst_opendir(name)
474 char *name;
475{
476 struct inotab *itp;
477 ino_t ino;
478
479 if ((ino = dirlookup(name)) > 0 &&
480 (itp = inotablookup(ino)) != NULL) {
481 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
482 return (dirp);
483 }
484 return (0);
485}
486
487/*
488 * Simulate finding the current offset in the directory.
489 */
490off_t
491rst_telldir(dirp)
492 DIR *dirp;
493{
494 off_t lseek();
495
496 return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc);
497}
498
499/*
500 * Open a directory file.
501 */
502DIR *
503opendirfile(name)
504 char *name;
505{
506 register DIR *dirp;
507 register int fd;
508
509 if ((fd = open(name, 0)) == -1)
510 return NULL;
511 if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
512 close (fd);
513 return NULL;
514 }
515 dirp->dd_fd = fd;
516 dirp->dd_loc = 0;
517 return dirp;
518}
519
520/*
521 * Set the mode, owner, and times for all new or changed directories
522 */
523setdirmodes()
524{
525 FILE *mf;
526 struct modeinfo node;
527 struct entry *ep;
528 char *cp;
529
530 vprintf(stdout, "Set directory mode, owner, and times.\n");
531 (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate);
532 mf = fopen(modefile, "r");
533 if (mf == NULL) {
534 perror("fopen");
535 fprintf(stderr, "cannot open mode file %s\n", modefile);
536 fprintf(stderr, "directory mode, owner, and times not set\n");
537 return;
538 }
539 clearerr(mf);
540 for (;;) {
541 (void) fread((char *)&node, 1, sizeof(struct modeinfo), mf);
542 if (feof(mf))
543 break;
544 ep = lookupino(node.ino);
545 if (command == 'i' || command == 'x') {
546 if (ep == NIL)
547 continue;
548 if (ep->e_flags & EXISTED) {
549 ep->e_flags &= ~NEW;
550 continue;
551 }
552 if (node.ino == ROOTINO &&
553 reply("set owner/mode for '.'") == FAIL)
554 continue;
555 }
556 if (ep == NIL) {
557 panic("cannot find directory inode %d\n", node.ino);
558 } else {
559 cp = myname(ep);
560 (void) chown(cp, node.uid, node.gid);
561 (void) chmod(cp, node.mode);
562 utimes(cp, node.timep);
563 ep->e_flags &= ~NEW;
564 }
565 }
566 if (ferror(mf))
567 panic("error setting directory modes\n");
568 (void) fclose(mf);
569}
570
571/*
572 * Generate a literal copy of a directory.
573 */
574genliteraldir(name, ino)
575 char *name;
576 ino_t ino;
577{
578 register struct inotab *itp;
579 int ofile, dp, i, size;
580 char buf[BUFSIZ];
581
582 itp = inotablookup(ino);
583 if (itp == NULL)
584 panic("Cannot find directory inode %d named %s\n", ino, name);
585 if ((ofile = creat(name, 0666)) < 0) {
586 fprintf(stderr, "%s: ", name);
587 (void) fflush(stderr);
588 perror("cannot create file");
589 return (FAIL);
590 }
591 rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt);
592 dp = dup(dirp->dd_fd);
593 for (i = itp->t_size; i > 0; i -= BUFSIZ) {
594 size = i < BUFSIZ ? i : BUFSIZ;
595 if (read(dp, buf, (int) size) == -1) {
596 fprintf(stderr,
597 "write error extracting inode %d, name %s\n",
598 curfile.ino, curfile.name);
599 perror("read");
600 done(1);
601 }
602 if (!Nflag && write(ofile, buf, (int) size) == -1) {
603 fprintf(stderr,
604 "write error extracting inode %d, name %s\n",
605 curfile.ino, curfile.name);
606 perror("write");
607 done(1);
608 }
609 }
610 (void) close(dp);
611 (void) close(ofile);
612 return (GOOD);
613}
614
615/*
616 * Determine the type of an inode
617 */
618inodetype(ino)
619 ino_t ino;
620{
621 struct inotab *itp;
622
623 itp = inotablookup(ino);
624 if (itp == NULL)
625 return (LEAF);
626 return (NODE);
627}
628
629/*
630 * Allocate and initialize a directory inode entry.
631 * If requested, save its pertinent mode, owner, and time info.
632 */
633struct inotab *
634allocinotab(ino, dip, seekpt)
635 ino_t ino;
636 struct dinode *dip;
637 daddr_t seekpt;
638{
639 register struct inotab *itp;
640 struct modeinfo node;
641
642 itp = (struct inotab *)calloc(1, sizeof(struct inotab));
643 if (itp == 0)
644 panic("no memory directory table\n");
645 itp->t_next = inotab[INOHASH(ino)];
646 inotab[INOHASH(ino)] = itp;
647 itp->t_ino = ino;
648 itp->t_seekpt = seekpt;
649 if (mf == NULL)
650 return(itp);
651 node.ino = ino;
652 node.timep[0].tv_sec = dip->di_atime;
653 node.timep[0].tv_usec = 0;
654 node.timep[1].tv_sec = dip->di_mtime;
655 node.timep[1].tv_usec = 0;
656 node.mode = dip->di_mode;
657 node.uid = dip->di_uid;
658 node.gid = dip->di_gid;
659 (void) fwrite((char *)&node, 1, sizeof(struct modeinfo), mf);
660 return(itp);
661}
662
663/*
664 * Look up an inode in the table of directories
665 */
666struct inotab *
667inotablookup(ino)
668 ino_t ino;
669{
670 register struct inotab *itp;
671
672 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
673 if (itp->t_ino == ino)
674 return(itp);
675 return ((struct inotab *)0);
676}
677
678/*
679 * Clean up and exit
680 */
681done(exitcode)
682 int exitcode;
683{
684
685 closemt();
686 if (modefile[0] != '#')
687 (void) unlink(modefile);
688 if (dirfile[0] != '#')
689 (void) unlink(dirfile);
690 exit(exitcode);
691}