]> git.wh0rd.org Git - dump.git/blob - restore/utilities.c
Relicensed dump/restore under the revised BSD license, as per ftp://ftp.cs.berkeley...
[dump.git] / restore / utilities.c
1 /*
2  *      Ported to Linux's Second Extended File System as part of the
3  *      dump and restore backup suit
4  *      Remy Card <card@Linux.EU.Org>, 1994-1997
5  *      Stelian Pop <stelian@popies.net>, 1999-2000
6  *      Stelian Pop <stelian@popies.net> - AlcĂ´ve <www.alcove.com>, 2000-2002
7  */
8
9 /*
10  * Copyright (c) 1983, 1993
11  *      The Regents of the University of California.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37
38 #ifndef lint
39 static const char rcsid[] =
40         "$Id: utilities.c,v 1.22 2003/03/30 15:40:40 stelian Exp $";
41 #endif /* not lint */
42
43 #include <config.h>
44 #include <errno.h>
45 #include <compaterr.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include <sys/param.h>
51 #include <sys/stat.h>
52
53 #ifdef  __linux__
54 #include <sys/time.h>
55 #include <time.h>
56 #ifdef HAVE_EXT2FS_EXT2_FS_H
57 #include <ext2fs/ext2_fs.h>
58 #else
59 #include <linux/ext2_fs.h>
60 #endif
61 #include <ext2fs/ext2fs.h>
62 #include <bsdcompat.h>
63 #else   /* __linux__ */
64 #include <ufs/ufs/dinode.h>
65 #include <ufs/ufs/dir.h>
66 #endif  /* __linux__ */
67
68 #include "restore.h"
69 #include "extern.h"
70
71 /*
72  * Insure that all the components of a pathname exist.
73  */
74 void
75 pathcheck(char *name)
76 {
77         char *cp;
78         struct entry *ep;
79         char *start;
80
81         start = strchr(name, '/');
82         if (start == 0)
83                 return;
84         for (cp = start; *cp != '\0'; cp++) {
85                 if (*cp != '/')
86                         continue;
87                 *cp = '\0';
88                 ep = lookupname(name);
89                 if (ep == NULL) {
90                         /* Safe; we know the pathname exists in the dump. */
91                         ep = addentry(name, pathsearch(name)->d_ino, NODE);
92                         newnode(ep);
93                 }
94                 ep->e_flags |= NEW|KEEP;
95                 *cp = '/';
96         }
97 }
98
99 /*
100  * Change a name to a unique temporary name.
101  */
102 void
103 mktempname(struct entry *ep)
104 {
105         char oldname[MAXPATHLEN];
106
107         if (ep->e_flags & TMPNAME)
108                 badentry(ep, "mktempname: called with TMPNAME");
109         ep->e_flags |= TMPNAME;
110         (void) strcpy(oldname, myname(ep));
111         freename(ep->e_name);
112         ep->e_name = savename(gentempname(ep));
113         ep->e_namlen = strlen(ep->e_name);
114         renameit(oldname, myname(ep));
115 }
116
117 /*
118  * Generate a temporary name for an entry.
119  */
120 char *
121 gentempname(struct entry *ep)
122 {
123         static char name[MAXPATHLEN];
124         struct entry *np;
125         long i = 0;
126
127         for (np = lookupino(ep->e_ino);
128             np != NULL && np != ep; np = np->e_links)
129                 i++;
130         if (np == NULL)
131                 badentry(ep, "not on ino list");
132         (void) snprintf(name, sizeof(name), "%s%ld%lu", TMPHDR, i, (unsigned long)ep->e_ino);
133         return (name);
134 }
135
136 /*
137  * Rename a file or directory.
138  */
139 void
140 renameit(char *from, char *to)
141 {
142         if (!Nflag && rename(from, to) < 0) {
143                 warn("cannot rename %s to %s", from, to);
144                 return;
145         }
146         Vprintf(stdout, "rename %s to %s\n", from, to);
147 }
148
149 /*
150  * Create a new node (directory).
151  */
152 void
153 newnode(struct entry *np)
154 {
155         char *cp;
156         if (np->e_type != NODE)
157                 badentry(np, "newnode: not a node");
158         cp = myname(np);
159         if (command == 'C') return;
160
161         if (!Nflag && mkdir(cp, 0777) < 0 && !uflag) {
162                 np->e_flags |= EXISTED;
163                 warn("%s", cp);
164                 return;
165         }
166         Vprintf(stdout, "Make node %s\n", cp);
167 }
168
169 /*
170  * Remove an old node (directory).
171  */
172 void
173 removenode(struct entry *ep)
174 {
175         char *cp;
176
177         if (ep->e_type != NODE)
178                 badentry(ep, "removenode: not a node");
179         if (ep->e_entries != NULL)
180                 badentry(ep, "removenode: non-empty directory");
181         ep->e_flags |= REMOVED;
182         ep->e_flags &= ~TMPNAME;
183         cp = myname(ep);
184         if (!Nflag && rmdir(cp) < 0) {
185                 warn("%s", cp);
186                 return;
187         }
188         Vprintf(stdout, "Remove node %s\n", cp);
189 }
190
191 /*
192  * Remove a leaf.
193  */
194 void
195 removeleaf(struct entry *ep)
196 {
197         char *cp;
198
199         if (command == 'C') return;
200
201         if (ep->e_type != LEAF)
202                 badentry(ep, "removeleaf: not a leaf");
203         ep->e_flags |= REMOVED;
204         ep->e_flags &= ~TMPNAME;
205         cp = myname(ep);
206         if (!Nflag && unlink(cp) < 0) {
207                 warn("%s", cp);
208                 return;
209         }
210         Vprintf(stdout, "Remove leaf %s\n", cp);
211 }
212
213 /*
214  * Create a link.
215  */
216 int
217 linkit(char *existing, char *new, int type)
218 {
219
220         /* if we want to unlink first, do it now so *link() won't fail */
221         if (uflag && !Nflag)
222                 (void)unlink(new);
223
224         if (type == SYMLINK) {
225                 if (!Nflag && symlink(existing, new) < 0) {
226                         warn("cannot create symbolic link %s->%s",
227                             new, existing);
228                         return (FAIL);
229                 }
230         } else if (type == HARDLINK) {
231                 int ret;
232
233                 if (!Nflag && (ret = link(existing, new)) < 0) {
234
235 #if !defined(__linux__) && !defined(sunos)
236                         struct stat s;
237
238                         /*
239                          * Most likely, the schg flag is set.  Clear the
240                          * flags and try again.
241                          */
242                         if (stat(existing, &s) == 0 && s.st_flags != 0 &&
243                             chflags(existing, 0) == 0) {
244                                 ret = link(existing, new);
245                                 chflags(existing, s.st_flags);
246                         }
247 #else
248                         unsigned long s;
249
250                         /*
251                          * Most likely, the immutable or append-only attribute
252                          * is set. Clear the attributes and try again.
253                          */
254                         if (fgetflags (existing, &s) != -1 &&
255                             fsetflags (existing, 0) != -1) {
256                                 ret = link(existing, new);
257                                 fsetflags(existing, s);
258                         }
259 #endif
260                         if (ret < 0) {
261                                 warn("warning: cannot create hard link %s->%s",
262                                     new, existing);
263                                 return (FAIL);
264                         }
265                 }
266         } else {
267                 panic("linkit: unknown type %d\n", type);
268                 return (FAIL);
269         }
270         Vprintf(stdout, "Create %s link %s->%s\n",
271                 type == SYMLINK ? "symbolic" : "hard", new, existing);
272         return (GOOD);
273 }
274
275 #if !defined(__linux__) && !defined(sunos)
276 /*
277  * Create a whiteout.
278  */
279 int
280 addwhiteout(char *name)
281 {
282
283         if (!Nflag && mknod(name, S_IFWHT, 0) < 0) {
284                 warn("cannot create whiteout %s", name);
285                 return (FAIL);
286         }
287         Vprintf(stdout, "Create whiteout %s\n", name);
288         return (GOOD);
289 }
290
291 /*
292  * Delete a whiteout.
293  */
294 void
295 delwhiteout(struct entry *ep)
296 {
297         char *name;
298
299         if (ep->e_type != LEAF)
300                 badentry(ep, "delwhiteout: not a leaf");
301         ep->e_flags |= REMOVED;
302         ep->e_flags &= ~TMPNAME;
303         name = myname(ep);
304         if (!Nflag && undelete(name) < 0) {
305                 warn("cannot delete whiteout %s", name);
306                 return;
307         }
308         Vprintf(stdout, "Delete whiteout %s\n", name);
309 }
310 #endif
311
312 /*
313  * find lowest number file (above "start") that needs to be extracted
314  */
315 dump_ino_t
316 lowerbnd(dump_ino_t start)
317 {
318         struct entry *ep;
319
320         for ( ; start < maxino; start++) {
321                 ep = lookupino(start);
322                 if (ep == NULL || ep->e_type == NODE)
323                         continue;
324                 if (ep->e_flags & (NEW|EXTRACT))
325                         return (start);
326         }
327         return (start);
328 }
329
330 /*
331  * find highest number file (below "start") that needs to be extracted
332  */
333 dump_ino_t
334 upperbnd(dump_ino_t start)
335 {
336         struct entry *ep;
337
338         for ( ; start > ROOTINO; start--) {
339                 ep = lookupino(start);
340                 if (ep == NULL || ep->e_type == NODE)
341                         continue;
342                 if (ep->e_flags & (NEW|EXTRACT))
343                         return (start);
344         }
345         return (start);
346 }
347
348 /*
349  * report on a badly formed entry
350  */
351 void
352 badentry(struct entry *ep, const char *msg)
353 {
354
355         fprintf(stderr, "bad entry: %s\n", msg);
356         fprintf(stderr, "name: %s\n", myname(ep));
357         fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
358         if (ep->e_sibling != NULL)
359                 fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
360         if (ep->e_entries != NULL)
361                 fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries));
362         if (ep->e_links != NULL)
363                 fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
364         if (ep->e_next != NULL)
365                 fprintf(stderr,
366                     "next hashchain name: %s\n", myname(ep->e_next));
367         fprintf(stderr, "entry type: %s\n",
368                 ep->e_type == NODE ? "NODE" : "LEAF");
369         fprintf(stderr, "inode number: %lu\n", (unsigned long)ep->e_ino);
370         panic("flags: %s\n", flagvalues(ep));
371 }
372
373 /*
374  * Construct a string indicating the active flag bits of an entry.
375  */
376 char *
377 flagvalues(struct entry *ep)
378 {
379         static char flagbuf[BUFSIZ];
380
381         (void) strcpy(flagbuf, "|NIL");
382         flagbuf[0] = '\0';
383         if (ep->e_flags & REMOVED)
384                 (void) strcat(flagbuf, "|REMOVED");
385         if (ep->e_flags & TMPNAME)
386                 (void) strcat(flagbuf, "|TMPNAME");
387         if (ep->e_flags & EXTRACT)
388                 (void) strcat(flagbuf, "|EXTRACT");
389         if (ep->e_flags & NEW)
390                 (void) strcat(flagbuf, "|NEW");
391         if (ep->e_flags & KEEP)
392                 (void) strcat(flagbuf, "|KEEP");
393         if (ep->e_flags & EXISTED)
394                 (void) strcat(flagbuf, "|EXISTED");
395         return (&flagbuf[1]);
396 }
397
398 /*
399  * Check to see if a name is on a dump tape.
400  */
401 dump_ino_t
402 dirlookup(const char *name)
403 {
404         struct direct *dp;
405         dump_ino_t ino;
406
407         ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino;
408
409         if (ino == 0 || TSTINO(ino, dumpmap) == 0)
410                 fprintf(stderr, "%s is not on the tape\n", name);
411         return (ino);
412 }
413
414 /*
415  * Elicit a reply.
416  */
417 int
418 reply(const char *question)
419 {
420         char c;
421
422         do      {
423                 fprintf(stderr, "%s? [yn] ", question);
424                 (void) fflush(stderr);
425                 c = getc(terminal);
426                 while (c != '\n' && getc(terminal) != '\n')
427                         if (feof(terminal))
428                                 return (FAIL);
429         } while (c != 'y' && c != 'n');
430         if (c == 'y')
431                 return (GOOD);
432         return (FAIL);
433 }
434
435 /*
436  * handle unexpected inconsistencies
437  */
438 #ifdef __STDC__
439 #include <stdarg.h>
440 #else
441 #include <varargs.h>
442 #endif
443
444 void
445 #ifdef __STDC__
446 panic(const char *fmt, ...)
447 #else
448 panic(fmt, va_alist)
449         char *fmt;
450         va_dcl
451 #endif
452 {
453         va_list ap;
454 #ifdef __STDC__
455         va_start(ap, fmt);
456 #else
457         va_start(ap);
458 #endif
459
460         vfprintf(stderr, fmt, ap);
461         if (yflag)
462                 return;
463         if (reply("abort") == GOOD) {
464                 if (reply("dump core") == GOOD)
465                         abort();
466                 exit(1);
467         }
468 }
469
470 #ifdef USE_QFA
471 /*
472  * search for ino in QFA file
473  *
474  * if exactmatch:
475  * if ino found return tape number and tape position
476  * if ino not found return tnum=0 and tpos=0
477  *
478  * if not exactmatch:
479  * if ino found return tape number and tape position
480  * if ino not found return tape number and tape position of last smaller ino
481  * if no smaller inode found return tnum=0 and tpos=0
482  */
483 int
484 Inode2Tapepos(dump_ino_t ino, long *tnum, long long *tpos, int exactmatch)
485 {
486         char *p, *pp;
487         char numbuff[32];
488         unsigned long tmpino;
489         long tmptnum;
490         long long tmptpos;
491
492         *tpos = 0;
493         *tnum = 0;
494         if (fseek(gTapeposfp, gSeekstart, SEEK_SET) == -1)
495                 return errno;
496         while (fgets(gTps, sizeof(gTps), gTapeposfp) != NULL) {
497                 gTps[strlen(gTps) - 1] = 0;     /* delete end of line */
498                 p = gTps;
499                 bzero(numbuff, sizeof(numbuff));
500                 pp = numbuff;
501                 /* read inode */
502                 while ((*p != 0) && (*p != '\t'))
503                         *pp++ = *p++;
504                 tmpino = atol(numbuff);
505                 if (*p == 0)
506                         return 1;       /* may NOT happen */    
507                 p++;
508                 bzero(numbuff, sizeof(numbuff));
509                 pp = numbuff;
510                 /* read tapenum */
511                 while ((*p != 0) && (*p != '\t'))
512                         *pp++ = *p++;
513                 if (*p == 0)
514                         return 1;       /* may NOT happen */    
515                 tmptnum = atol(numbuff);
516                 p++;
517                 bzero(numbuff, sizeof(numbuff));
518                 pp = numbuff;
519                 /* read tapepos */
520                 while ((*p != 0) && (*p != '\t'))
521                         *pp++ = *p++;
522                 tmptpos = atoll(numbuff);
523
524                 if (exactmatch) {
525                         if (tmpino == ino)  {
526                                 *tnum = tmptnum;
527                                 *tpos = tmptpos;
528                                 return 0;
529                         }
530                 } else {
531                         if (tmpino > ino)  {
532                                 return 0;
533                         } else {
534                                 *tnum = tmptnum;
535                                 *tpos = tmptpos;
536                         }
537                 }
538         }
539         return 0;
540 }
541 #endif /* USE_QFA */
542
543 void resizemaps(dump_ino_t oldmax, dump_ino_t newmax)
544 {
545         char *map;
546
547         if (usedinomap) {
548                 map = calloc((unsigned)1, (unsigned)howmany(newmax, NBBY));
549                 if (map == NULL)
550                         errx(1, "no memory for active inode map");
551                 memcpy(map, usedinomap, howmany(oldmax, NBBY));
552                 free(usedinomap);
553                 usedinomap = map;
554         }
555         if (dumpmap) {
556                 map = calloc((unsigned)1, (unsigned)howmany(newmax, NBBY));
557                 if (map == NULL)
558                         errx(1, "no memory for file dump list");
559                 memcpy(map, dumpmap, howmany(oldmax, NBBY));
560                 free(dumpmap);
561                 dumpmap = map;
562         }
563 }