sbin/hammer2/cmd_snapshot.c: Fix error handling
[dragonfly.git] / sbin / restore / dirs.c
blob719cc05a95f23f50dbf3165ba47ad50f5d250c86
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.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * @(#)dirs.c 8.7 (Berkeley) 5/1/95
35 * $FreeBSD: src/sbin/restore/dirs.c,v 1.14.2.5 2001/10/15 13:44:45 dd Exp $
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <sys/time.h>
42 #include <vfs/ufs/dinode.h>
43 #include <vfs/ufs/dir.h>
44 #include <protocols/dumprestore.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <paths.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
55 #include "restore.h"
56 #include "extern.h"
59 * Symbol table of directories read from tape.
61 #define HASHSIZE 1000
62 #define INOHASH(val) (val % HASHSIZE)
63 struct inotab {
64 struct inotab *t_next;
65 ufs1_ino_t t_ino;
66 int32_t t_seekpt;
67 int32_t t_size;
69 static struct inotab *inotab[HASHSIZE];
72 * Information retained about directories.
74 struct modeinfo {
75 ufs1_ino_t ino;
76 struct timeval timep[2];
77 mode_t mode;
78 uid_t uid;
79 gid_t gid;
80 int flags;
84 * Definitions for library routines operating on directories.
86 #undef DIRBLKSIZ
87 #define DIRBLKSIZ 1024
88 struct rstdirdesc {
89 int dd_fd;
90 int32_t dd_loc;
91 int32_t dd_size;
92 char dd_buf[DIRBLKSIZ];
96 * Global variables for this file.
98 static long seekptg;
99 static FILE *df, *mfg;
100 static RST_DIR *dirpg;
101 static char dirfile[MAXPATHLEN] = "#"; /* No file */
102 static char modefile[MAXPATHLEN] = "#"; /* No file */
103 static char dot[2] = "."; /* So it can be modified */
106 * Format of old style directories.
108 #define ODIRSIZ 14
109 struct odirect {
110 u_short d_ino;
111 char d_name[ODIRSIZ];
114 static struct inotab *allocinotab(ufs1_ino_t, struct ufs1_dinode *, long);
115 static void dcvt(struct odirect *, struct direct *);
116 static void flushent(void);
117 static struct inotab *inotablookup(ufs1_ino_t);
118 static RST_DIR *opendirfile(const char *);
119 static void putdir(char *, size_t);
120 static void putent(struct direct *);
121 static void rst_seekdir(RST_DIR *, long, long);
122 static long rst_telldir(RST_DIR *);
123 static struct direct *searchdir(ufs1_ino_t, char *);
126 * Extract directory contents, building up a directory structure
127 * on disk for extraction by name.
128 * If genmode is requested, save mode, owner, and times for all
129 * directories on the tape.
131 void
132 extractdirs(int genmode)
134 int i;
135 struct ufs1_dinode *ip;
136 struct inotab *itp;
137 struct direct nulldir;
138 int fd;
139 const char *tmpdir;
141 vprintf(stdout, "Extract directories from tape\n");
142 if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
143 tmpdir = _PATH_TMP;
144 sprintf(dirfile, "%s/rstdir%ld", tmpdir, (long)dumpdate);
145 if (command != 'r' && command != 'R') {
146 strcat(dirfile, "-XXXXXX");
147 fd = mkstemp(dirfile);
148 } else
149 fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666);
150 if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
151 if (fd != -1)
152 close(fd);
153 warn("%s - cannot create directory temporary\nfopen", dirfile);
154 done(1);
156 if (genmode != 0) {
157 sprintf(modefile, "%s/rstmode%ld", tmpdir, (long)dumpdate);
158 if (command != 'r' && command != 'R') {
159 strcat(modefile, "-XXXXXX");
160 fd = mkstemp(modefile);
161 } else
162 fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666);
163 if (fd == -1 || (mfg = fdopen(fd, "w")) == NULL) {
164 if (fd != -1)
165 close(fd);
166 warn("%s - cannot create modefile\nfopen", modefile);
167 done(1);
170 nulldir.d_ino = 0;
171 nulldir.d_type = DT_DIR;
172 nulldir.d_namlen = 1;
173 strcpy(nulldir.d_name, "/");
174 nulldir.d_reclen = DIRSIZ(0, &nulldir);
175 for (;;) {
176 curfile.name = "<directory file - name unknown>";
177 curfile.action = USING;
178 ip = curfile.dip;
179 if (ip == NULL || (ip->di_mode & IFMT) != IFDIR) {
180 fclose(df);
181 dirpg = opendirfile(dirfile);
182 if (dirpg == NULL)
183 fprintf(stderr, "opendirfile: %s\n",
184 strerror(errno));
185 if (mfg != NULL)
186 fclose(mfg);
187 i = dirlookup(dot);
188 if (i == 0)
189 panic("Root directory is not on tape\n");
190 return;
192 itp = allocinotab(curfile.ino, ip, seekptg);
193 getfile(putdir, xtrnull);
194 putent(&nulldir);
195 flushent();
196 itp->t_size = seekptg - itp->t_seekpt;
201 * skip over all the directories on the tape
203 void
204 skipdirs(void)
207 while (curfile.dip && (curfile.dip->di_mode & IFMT) == IFDIR) {
208 skipfile();
213 * Recursively find names and inumbers of all files in subtree
214 * pname and pass them off to be processed.
216 void
217 treescan(char *pname, ufs1_ino_t ino, long (*todo)(char *, ufs1_ino_t, int))
219 struct inotab *itp;
220 struct direct *dp;
221 int namelen;
222 long bpt;
223 char locname[MAXPATHLEN + 1];
225 itp = inotablookup(ino);
226 if (itp == NULL) {
228 * Pname is name of a simple file or an unchanged directory.
230 (*todo)(pname, ino, LEAF);
231 return;
234 * Pname is a dumped directory name.
236 if ((*todo)(pname, ino, NODE) == FAIL)
237 return;
239 * begin search through the directory
240 * skipping over "." and ".."
242 strncpy(locname, pname, sizeof(locname) - 1);
243 locname[sizeof(locname) - 1] = '\0';
244 strncat(locname, "/", sizeof(locname) - strlen(locname));
245 namelen = strlen(locname);
246 rst_seekdir(dirpg, itp->t_seekpt, itp->t_seekpt);
247 dp = rst_readdir(dirpg); /* "." */
248 if (dp != NULL && strcmp(dp->d_name, ".") == 0)
249 dp = rst_readdir(dirpg); /* ".." */
250 else
251 fprintf(stderr, "Warning: `.' missing from directory %s\n",
252 pname);
253 if (dp != NULL && strcmp(dp->d_name, "..") == 0)
254 dp = rst_readdir(dirpg); /* first real entry */
255 else
256 fprintf(stderr, "Warning: `..' missing from directory %s\n",
257 pname);
258 bpt = rst_telldir(dirpg);
260 * a zero inode signals end of directory
262 while (dp != NULL) {
263 locname[namelen] = '\0';
264 if ((size_t)(namelen + dp->d_namlen) >= sizeof(locname)) {
265 fprintf(stderr, "%s%s: name exceeds %zu char\n",
266 locname, dp->d_name, sizeof(locname) - 1);
267 } else {
268 strncat(locname, dp->d_name, (int)dp->d_namlen);
269 treescan(locname, dp->d_ino, todo);
270 rst_seekdir(dirpg, bpt, itp->t_seekpt);
272 dp = rst_readdir(dirpg);
273 bpt = rst_telldir(dirpg);
278 * Lookup a pathname which is always assumed to start from the UFS_ROOTINO.
280 struct direct *
281 pathsearch(const char *pathname)
283 ufs1_ino_t ino;
284 struct direct *dp;
285 char *path, *name, buffer[MAXPATHLEN];
287 strcpy(buffer, pathname);
288 path = buffer;
289 ino = UFS_ROOTINO;
290 while (*path == '/')
291 path++;
292 dp = NULL;
293 while ((name = strsep(&path, "/")) != NULL && *name != '\0') {
294 if ((dp = searchdir(ino, name)) == NULL)
295 return (NULL);
296 ino = dp->d_ino;
298 return (dp);
302 * Lookup the requested name in directory inum.
303 * Return its inode number if found, zero if it does not exist.
305 static struct direct *
306 searchdir(ufs1_ino_t inum, char *name)
308 struct direct *dp;
309 struct inotab *itp;
310 int len;
312 itp = inotablookup(inum);
313 if (itp == NULL)
314 return (NULL);
315 rst_seekdir(dirpg, itp->t_seekpt, itp->t_seekpt);
316 len = strlen(name);
317 do {
318 dp = rst_readdir(dirpg);
319 if (dp == NULL)
320 return (NULL);
321 } while (dp->d_namlen != len || strncmp(dp->d_name, name, len) != 0);
322 return (dp);
326 * Put the directory entries in the directory file
328 static void
329 putdir(char *buf, size_t size)
331 struct direct cvtbuf;
332 struct odirect *odp;
333 struct odirect *eodp;
334 struct direct *dp;
335 size_t loc, i;
337 if (cvtflag) {
338 eodp = (struct odirect *)&buf[size];
339 for (odp = (struct odirect *)buf; odp < eodp; odp++)
340 if (odp->d_ino != 0) {
341 dcvt(odp, &cvtbuf);
342 putent(&cvtbuf);
344 } else {
345 for (loc = 0; loc < size; ) {
346 dp = (struct direct *)(buf + loc);
347 if (Bcvt)
348 swabst("ls", (u_char *) dp);
349 if (oldinofmt && dp->d_ino != 0) {
350 # if BYTE_ORDER == BIG_ENDIAN
351 if (Bcvt)
352 dp->d_namlen = dp->d_type;
353 # else
354 if (!Bcvt)
355 dp->d_namlen = dp->d_type;
356 # endif
357 dp->d_type = DT_UNKNOWN;
359 i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1));
360 if ((dp->d_reclen & 0x3) != 0 ||
361 dp->d_reclen > i ||
362 dp->d_reclen < DIRSIZ(0, dp)
363 #if NAME_MAX < 255
364 || dp->d_namlen > NAME_MAX
365 #endif
367 vprintf(stdout, "Mangled directory: ");
368 if ((dp->d_reclen & 0x3) != 0)
369 vprintf(stdout,
370 "reclen not multiple of 4 ");
371 if (dp->d_reclen < DIRSIZ(0, dp))
372 vprintf(stdout,
373 "reclen less than DIRSIZ (%u < %zu) ",
374 dp->d_reclen, DIRSIZ(0, dp));
375 #if NAME_MAX < 255
376 if (dp->d_namlen > NAME_MAX)
377 vprintf(stdout,
378 "reclen name too big (%u > %u) ",
379 dp->d_namlen, NAME_MAX);
380 #endif
381 vprintf(stdout, "\n");
382 loc += i;
383 continue;
385 loc += dp->d_reclen;
386 if (dp->d_ino != 0) {
387 putent(dp);
394 * These variables are "local" to the following two functions.
396 char dirbuf[DIRBLKSIZ];
397 long dirloc = 0;
398 long prev = 0;
401 * add a new directory entry to a file.
403 static void
404 putent(struct direct *dp)
406 dp->d_reclen = DIRSIZ(0, dp);
407 if (dirloc + dp->d_reclen > DIRBLKSIZ) {
408 ((struct direct *)(dirbuf + prev))->d_reclen =
409 DIRBLKSIZ - prev;
410 fwrite(dirbuf, 1, DIRBLKSIZ, df);
411 dirloc = 0;
413 memmove(dirbuf + dirloc, dp, (long)dp->d_reclen);
414 prev = dirloc;
415 dirloc += dp->d_reclen;
419 * flush out a directory that is finished.
421 static void
422 flushent(void)
424 ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev;
425 fwrite(dirbuf, (int)dirloc, 1, df);
426 seekptg = ftell(df);
427 dirloc = 0;
430 static void
431 dcvt(struct odirect *odp, struct direct *ndp)
434 memset(ndp, 0, (long)(sizeof *ndp));
435 ndp->d_ino = odp->d_ino;
436 ndp->d_type = DT_UNKNOWN;
437 strncpy(ndp->d_name, odp->d_name, ODIRSIZ);
438 ndp->d_namlen = strlen(ndp->d_name);
439 ndp->d_reclen = DIRSIZ(0, ndp);
443 * Seek to an entry in a directory.
444 * Only values returned by rst_telldir should be passed to rst_seekdir.
445 * This routine handles many directories in a single file.
446 * It takes the base of the directory in the file, plus
447 * the desired seek offset into it.
449 static void
450 rst_seekdir(RST_DIR *dirpl, long loc, long base)
453 if (loc == rst_telldir(dirpl))
454 return;
455 loc -= base;
456 if (loc < 0)
457 fprintf(stderr, "bad seek pointer to rst_seekdir %ld\n", loc);
458 lseek(dirpl->dd_fd, base + rounddown2(loc, DIRBLKSIZ), SEEK_SET);
459 dirpl->dd_loc = loc & (DIRBLKSIZ - 1);
460 if (dirpl->dd_loc != 0)
461 dirpl->dd_size = read(dirpl->dd_fd, dirpl->dd_buf, DIRBLKSIZ);
465 * get next entry in a directory.
467 struct direct *
468 rst_readdir(RST_DIR *dirpl)
470 struct direct *dp;
472 for (;;) {
473 if (dirpl->dd_loc == 0) {
474 dirpl->dd_size = read(dirpl->dd_fd, dirpl->dd_buf,
475 DIRBLKSIZ);
476 if (dirpl->dd_size <= 0) {
477 dprintf(stderr, "error reading directory\n");
478 return (NULL);
481 if (dirpl->dd_loc >= dirpl->dd_size) {
482 dirpl->dd_loc = 0;
483 continue;
485 dp = (struct direct *)(dirpl->dd_buf + dirpl->dd_loc);
486 if (dp->d_reclen == 0 ||
487 dp->d_reclen > DIRBLKSIZ + 1 - dirpl->dd_loc) {
488 dprintf(stderr, "corrupted directory: bad reclen %d\n",
489 dp->d_reclen);
490 return (NULL);
492 dirpl->dd_loc += dp->d_reclen;
493 if (dp->d_ino == 0 && strcmp(dp->d_name, "/") == 0)
494 return (NULL);
495 if (dp->d_ino >= maxino) {
496 dprintf(stderr, "corrupted directory: bad inum %d\n",
497 dp->d_ino);
498 continue;
500 return (dp);
505 * Simulate the opening of a directory
507 RST_DIR *
508 rst_opendir(const char *name)
510 struct inotab *itp;
511 RST_DIR *dirpl;
512 ufs1_ino_t ino;
514 if ((ino = dirlookup(name)) > 0 &&
515 (itp = inotablookup(ino)) != NULL) {
516 dirpl = opendirfile(dirfile);
517 rst_seekdir(dirpl, itp->t_seekpt, itp->t_seekpt);
518 return (dirpl);
520 return (NULL);
524 * In our case, there is nothing to do when closing a directory.
526 void
527 rst_closedir(RST_DIR *dirpl)
530 close(dirpl->dd_fd);
531 free(dirpl);
532 return;
536 * Simulate finding the current offset in the directory.
538 static long
539 rst_telldir(RST_DIR *dirpl)
541 return ((long)lseek(dirpl->dd_fd,
542 (off_t)0, SEEK_CUR) - dirpl->dd_size + dirpl->dd_loc);
546 * Open a directory file.
548 static RST_DIR *
549 opendirfile(const char *name)
551 RST_DIR *dirpl;
552 int fd;
554 if ((fd = open(name, O_RDONLY)) == -1)
555 return (NULL);
556 if ((dirpl = malloc(sizeof(RST_DIR))) == NULL) {
557 close(fd);
558 return (NULL);
560 dirpl->dd_fd = fd;
561 dirpl->dd_loc = 0;
562 return (dirpl);
566 * Set the mode, owner, and times for all new or changed directories
568 void
569 setdirmodes(int flags)
571 FILE *mfl;
572 struct modeinfo node;
573 struct entry *ep;
574 char *cp;
575 const char *tmpdir;
577 vprintf(stdout, "Set directory mode, owner, and times.\n");
578 if ((tmpdir = getenv("TMPDIR")) == NULL || tmpdir[0] == '\0')
579 tmpdir = _PATH_TMP;
580 if (command == 'r' || command == 'R')
581 sprintf(modefile, "%s/rstmode%ld", tmpdir, (long)dumpdate);
582 if (modefile[0] == '#') {
583 panic("modefile not defined\n");
584 fprintf(stderr, "directory mode, owner, and times not set\n");
585 return;
587 mfl = fopen(modefile, "r");
588 if (mfl == NULL) {
589 fprintf(stderr, "fopen: %s\n", strerror(errno));
590 fprintf(stderr, "cannot open mode file %s\n", modefile);
591 fprintf(stderr, "directory mode, owner, and times not set\n");
592 return;
594 clearerr(mfl);
595 for (;;) {
596 fread((char *)&node, 1, sizeof(struct modeinfo), mfl);
597 if (feof(mfl))
598 break;
599 ep = lookupino(node.ino);
600 if (command == 'i' || command == 'x') {
601 if (ep == NULL)
602 continue;
603 if ((flags & FORCE) == 0 && ep->e_flags & EXISTED) {
604 ep->e_flags &= ~NEW;
605 continue;
607 if (node.ino == UFS_ROOTINO &&
608 reply("set owner/mode for '.'") == FAIL)
609 continue;
611 if (ep == NULL) {
612 panic("cannot find directory inode %d\n", node.ino);
613 } else {
614 cp = myname(ep);
615 if (!Nflag) {
616 chown(cp, node.uid, node.gid);
617 chmod(cp, node.mode);
618 utimes(cp, node.timep);
619 chflags(cp, node.flags);
621 ep->e_flags &= ~NEW;
624 if (ferror(mfl))
625 panic("error setting directory modes\n");
626 fclose(mfl);
630 * Generate a literal copy of a directory.
633 genliteraldir(char *name, ufs1_ino_t ino)
635 struct inotab *itp;
636 int ofile, dp, i, size;
637 char buf[BUFSIZ];
639 itp = inotablookup(ino);
640 if (itp == NULL)
641 panic("Cannot find directory inode %d named %s\n", ino, name);
642 if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
643 fprintf(stderr, "%s: ", name);
644 fflush(stderr);
645 fprintf(stderr, "cannot create file: %s\n", strerror(errno));
646 return (FAIL);
648 rst_seekdir(dirpg, itp->t_seekpt, itp->t_seekpt);
649 dp = dup(dirpg->dd_fd);
650 for (i = itp->t_size; i > 0; i -= BUFSIZ) {
651 size = i < BUFSIZ ? i : BUFSIZ;
652 if (read(dp, buf, (int) size) == -1) {
653 fprintf(stderr,
654 "write error extracting inode %d, name %s\n",
655 curfile.ino, curfile.name);
656 fprintf(stderr, "read: %s\n", strerror(errno));
657 done(1);
659 if (!Nflag && write(ofile, buf, (int) size) == -1) {
660 fprintf(stderr,
661 "write error extracting inode %d, name %s\n",
662 curfile.ino, curfile.name);
663 fprintf(stderr, "write: %s\n", strerror(errno));
664 done(1);
667 close(dp);
668 close(ofile);
669 return (GOOD);
673 * Determine the type of an inode
676 inodetype(ufs1_ino_t ino)
678 struct inotab *itp;
680 itp = inotablookup(ino);
681 if (itp == NULL)
682 return (LEAF);
683 return (NODE);
687 * Allocate and initialize a directory inode entry.
688 * If requested, save its pertinent mode, owner, and time info.
690 static struct inotab *
691 allocinotab(ufs1_ino_t ino, struct ufs1_dinode *dip, long seekptl)
693 struct inotab *itp;
694 struct modeinfo node;
696 itp = calloc(1, sizeof(struct inotab));
697 if (itp == NULL)
698 panic("no memory directory table\n");
699 itp->t_next = inotab[INOHASH(ino)];
700 inotab[INOHASH(ino)] = itp;
701 itp->t_ino = ino;
702 itp->t_seekpt = seekptl;
703 if (mfg == NULL)
704 return (itp);
705 node.ino = ino;
706 node.timep[0].tv_sec = dip->di_atime;
707 node.timep[0].tv_usec = dip->di_atimensec / 1000;
708 node.timep[1].tv_sec = dip->di_mtime;
709 node.timep[1].tv_usec = dip->di_mtimensec / 1000;
710 node.mode = dip->di_mode;
711 node.flags = dip->di_flags;
712 node.uid = dip->di_uid;
713 node.gid = dip->di_gid;
714 fwrite((char *)&node, 1, sizeof(struct modeinfo), mfg);
715 return (itp);
719 * Look up an inode in the table of directories
721 static struct inotab *
722 inotablookup(ufs1_ino_t ino)
724 struct inotab *itp;
726 for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next)
727 if (itp->t_ino == ino)
728 return (itp);
729 return (NULL);
733 * Clean up and exit
735 void
736 done(int exitcode)
739 closemt();
740 if (modefile[0] != '#')
741 unlink(modefile);
742 if (dirfile[0] != '#')
743 unlink(dirfile);
744 exit(exitcode);