Imported Upstream version 0.4b43
[debian/dump] / 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.30 2010/06/11 11:19:17 stelian Exp $";
41 #endif /* not lint */
42
43 #include <config.h>
44 #include <compatlfs.h>
45 #include <sys/types.h>
46 #include <errno.h>
47 #include <compaterr.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #include <sys/param.h>
53 #include <sys/stat.h>
54
55 #ifdef  __linux__
56 #include <sys/time.h>
57 #include <time.h>
58 #include <fcntl.h>
59 #ifdef HAVE_EXT2FS_EXT2_FS_H
60 #include <ext2fs/ext2_fs.h>
61 #else
62 #include <linux/ext2_fs.h>
63 #endif
64 #include <ext2fs/ext2fs.h>
65 #include <bsdcompat.h>
66 #else   /* __linux__ */
67 #ifdef sunos
68 #include <sys/time.h>
69 #include <sys/fcntl.h>
70 #include <bsdcompat.h>
71 #else
72 #include <ufs/ufs/dinode.h>
73 #include <ufs/ufs/dir.h>
74 #endif
75 #endif  /* __linux__ */
76 #ifdef DUMP_MACOSX
77 #include "darwin.h"
78 #endif
79 #include "restore.h"
80 #include "extern.h"
81
82 /*
83  * Insure that all the components of a pathname exist.
84  */
85 void
86 pathcheck(char *name)
87 {
88         char *cp;
89         struct entry *ep;
90         char *start;
91
92         start = strchr(name, '/');
93         if (start == 0)
94                 return;
95         for (cp = start; *cp != '\0'; cp++) {
96                 if (*cp != '/')
97                         continue;
98                 *cp = '\0';
99                 ep = lookupname(name);
100                 if (ep == NULL) {
101                         /* Safe; we know the pathname exists in the dump. */
102                         ep = addentry(name, pathsearch(name)->d_ino, NODE);
103                         newnode(ep);
104                 }
105                 ep->e_flags |= NEW|KEEP;
106                 *cp = '/';
107         }
108 }
109
110 /*
111  * Change a name to a unique temporary name.
112  */
113 void
114 mktempname(struct entry *ep)
115 {
116         char oldname[MAXPATHLEN];
117
118         if (ep->e_flags & TMPNAME)
119                 badentry(ep, "mktempname: called with TMPNAME");
120         ep->e_flags |= TMPNAME;
121         (void) strcpy(oldname, myname(ep));
122         freename(ep->e_name);
123         ep->e_name = savename(gentempname(ep));
124         ep->e_namlen = strlen(ep->e_name);
125         renameit(oldname, myname(ep));
126 }
127
128 /*
129  * Generate a temporary name for an entry.
130  */
131 char *
132 gentempname(struct entry *ep)
133 {
134         static char name[MAXPATHLEN];
135         struct entry *np;
136         long i = 0;
137
138         for (np = lookupino(ep->e_ino);
139             np != NULL && np != ep; np = np->e_links)
140                 i++;
141         if (np == NULL)
142                 badentry(ep, "not on ino list");
143         (void) snprintf(name, sizeof(name), "%s%ld%lu", TMPHDR, i, (unsigned long)ep->e_ino);
144         return (name);
145 }
146
147 /*
148  * Rename a file or directory.
149  */
150 void
151 renameit(char *from, char *to)
152 {
153         if (!Nflag && rename(from, to) < 0) {
154                 warn("cannot rename %s to %s", from, to);
155                 return;
156         }
157         Vprintf(stdout, "rename %s to %s\n", from, to);
158 }
159
160 /*
161  * Create a new node (directory).
162  */
163 void
164 newnode(struct entry *np)
165 {
166         char *cp;
167         if (np->e_type != NODE)
168                 badentry(np, "newnode: not a node");
169         cp = myname(np);
170         if (command == 'C') return;
171
172         if (!Nflag && mkdir(cp, 0777) < 0 && !uflag) {
173                 np->e_flags |= EXISTED;
174                 warn("%s", cp);
175                 return;
176         }
177         Vprintf(stdout, "Make node %s\n", cp);
178 }
179
180 /*
181  * Remove an old node (directory).
182  */
183 void
184 removenode(struct entry *ep)
185 {
186         char *cp;
187
188         if (ep->e_type != NODE)
189                 badentry(ep, "removenode: not a node");
190         if (ep->e_entries != NULL) {
191                 int i;
192                 for (i = 0; i < dirhash_size; i++) {
193                         if (ep->e_entries[i] != NULL)
194                                 badentry(ep, "removenode: non-empty directory");
195                 }
196         }
197         ep->e_flags |= REMOVED;
198         ep->e_flags &= ~TMPNAME;
199         cp = myname(ep);
200         if (!Nflag && rmdir(cp) < 0) {
201                 warn("%s", cp);
202                 return;
203         }
204         Vprintf(stdout, "Remove node %s\n", cp);
205 }
206
207 /*
208  * Remove a leaf.
209  */
210 void
211 removeleaf(struct entry *ep)
212 {
213         char *cp;
214
215         if (command == 'C') return;
216
217         if (ep->e_type != LEAF)
218                 badentry(ep, "removeleaf: not a leaf");
219         ep->e_flags |= REMOVED;
220         ep->e_flags &= ~TMPNAME;
221         cp = myname(ep);
222         if (!Nflag && unlink(cp) < 0) {
223                 warn("%s", cp);
224                 return;
225         }
226         Vprintf(stdout, "Remove leaf %s\n", cp);
227 }
228
229 /*
230  * Create a link.
231  */
232 int
233 linkit(char *existing, char *new, int type)
234 {
235
236         /* if we want to unlink first, do it now so *link() won't fail */
237         if (uflag && !Nflag)
238                 (void)unlink(new);
239
240         if (type == SYMLINK) {
241                 if (!Nflag && symlink(existing, new) < 0) {
242                         warn("cannot create symbolic link %s->%s",
243                             new, existing);
244                         return (FAIL);
245                 }
246         } else if (type == HARDLINK) {
247                 int ret;
248
249                 if (!Nflag && (ret = link(existing, new)) < 0) {
250
251 #if !defined(__linux__) && !defined(sunos)
252                         struct stat s;
253
254                         /*
255                          * Most likely, the schg flag is set.  Clear the
256                          * flags and try again.
257                          */
258                         if (stat(existing, &s) == 0 && s.st_flags != 0 &&
259                             chflags(existing, 0) == 0) {
260                                 ret = link(existing, new);
261                                 chflags(existing, s.st_flags);
262                         }
263 #else
264                         unsigned long s;
265
266                         /*
267                          * Most likely, the immutable or append-only attribute
268                          * is set. Clear the attributes and try again.
269                          */
270 #ifdef sunos
271 #else
272                         if (lgetflags (existing, &s) != -1 &&
273                             lsetflags (existing, 0) != -1) {
274                                 ret = link(existing, new);
275                                 lsetflags(existing, s);
276                         }
277 #endif
278 #endif
279                         if (ret < 0) {
280                                 warn("warning: cannot create hard link %s->%s",
281                                     new, existing);
282                                 return (FAIL);
283                         }
284                 }
285         } else {
286                 panic("linkit: unknown type %d\n", type);
287                 return (FAIL);
288         }
289         Vprintf(stdout, "Create %s link %s->%s\n",
290                 type == SYMLINK ? "symbolic" : "hard", new, existing);
291         return (GOOD);
292 }
293
294 #if !defined(__linux__) && !defined(sunos)
295 /*
296  * Create a whiteout.
297  */
298 int
299 addwhiteout(char *name)
300 {
301
302         if (!Nflag && mknod(name, S_IFWHT, 0) < 0) {
303                 warn("cannot create whiteout %s", name);
304                 return (FAIL);
305         }
306         Vprintf(stdout, "Create whiteout %s\n", name);
307         return (GOOD);
308 }
309
310 /*
311  * Delete a whiteout.
312  */
313 void
314 delwhiteout(struct entry *ep)
315 {
316         char *name;
317
318         if (ep->e_type != LEAF)
319                 badentry(ep, "delwhiteout: not a leaf");
320         ep->e_flags |= REMOVED;
321         ep->e_flags &= ~TMPNAME;
322         name = myname(ep);
323         if (!Nflag && undelete(name) < 0) {
324                 warn("cannot delete whiteout %s", name);
325                 return;
326         }
327         Vprintf(stdout, "Delete whiteout %s\n", name);
328 }
329 #endif
330
331 /*
332  * find lowest number file (above "start") that needs to be extracted
333  */
334 dump_ino_t
335 lowerbnd(dump_ino_t start)
336 {
337         struct entry *ep;
338
339         for ( ; start < maxino; start++) {
340                 ep = lookupino(start);
341                 if (ep == NULL || ep->e_type == NODE)
342                         continue;
343                 if (ep->e_flags & (NEW|EXTRACT))
344                         return (start);
345         }
346         return (start);
347 }
348
349 /*
350  * find highest number file (below "start") that needs to be extracted
351  */
352 dump_ino_t
353 upperbnd(dump_ino_t start)
354 {
355         struct entry *ep;
356
357         for ( ; start > ROOTINO; start--) {
358                 ep = lookupino(start);
359                 if (ep == NULL || ep->e_type == NODE)
360                         continue;
361                 if (ep->e_flags & (NEW|EXTRACT))
362                         return (start);
363         }
364         return (start);
365 }
366
367 /*
368  * report on a badly formed entry
369  */
370 void
371 badentry(struct entry *ep, const char *msg)
372 {
373
374         fprintf(stderr, "bad entry: %s\n", msg);
375         fprintf(stderr, "name: %s\n", myname(ep));
376         fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
377         if (ep->e_sibling != NULL)
378                 fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
379         if (ep->e_entries != NULL) {
380                 int i;
381                 for (i = 0; i < dirhash_size; i++) {
382                         if (ep->e_entries[i] != NULL) {
383                                 fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries[i]));
384                                 break;
385                         }
386                 }
387         }
388         if (ep->e_links != NULL)
389                 fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
390         if (ep->e_next != NULL)
391                 fprintf(stderr,
392                     "next hashchain name: %s\n", myname(ep->e_next));
393         fprintf(stderr, "entry type: %s\n",
394                 ep->e_type == NODE ? "NODE" : "LEAF");
395         fprintf(stderr, "inode number: %lu\n", (unsigned long)ep->e_ino);
396         panic("flags: %s\n", flagvalues(ep));
397 }
398
399 /*
400  * Construct a string indicating the active flag bits of an entry.
401  */
402 char *
403 flagvalues(struct entry *ep)
404 {
405         static char flagbuf[BUFSIZ];
406
407         (void) strcpy(flagbuf, "|NIL");
408         flagbuf[0] = '\0';
409         if (ep->e_flags & REMOVED)
410                 (void) strcat(flagbuf, "|REMOVED");
411         if (ep->e_flags & TMPNAME)
412                 (void) strcat(flagbuf, "|TMPNAME");
413         if (ep->e_flags & EXTRACT)
414                 (void) strcat(flagbuf, "|EXTRACT");
415         if (ep->e_flags & NEW)
416                 (void) strcat(flagbuf, "|NEW");
417         if (ep->e_flags & KEEP)
418                 (void) strcat(flagbuf, "|KEEP");
419         if (ep->e_flags & EXISTED)
420                 (void) strcat(flagbuf, "|EXISTED");
421         return (&flagbuf[1]);
422 }
423
424 /*
425  * Check to see if a name is on a dump tape.
426  */
427 dump_ino_t
428 dirlookup(const char *name)
429 {
430         struct direct *dp;
431         dump_ino_t ino;
432
433         ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino;
434
435         if (ino == 0 || TSTINO(ino, dumpmap) == 0)
436                 fprintf(stderr, "%s is not on the tape\n", name);
437         return (ino);
438 }
439
440 /*
441  * Elicit a reply.
442  */
443 int
444 reply(const char *question)
445 {
446         char c;
447
448         do      {
449                 fprintf(stderr, "%s? [yn] ", question);
450                 (void) fflush(stderr);
451                 c = getc(terminal);
452                 while (c != '\n' && getc(terminal) != '\n')
453                         if (feof(terminal))
454                                 return (FAIL);
455         } while (c != 'y' && c != 'n');
456         if (c == 'y')
457                 return (GOOD);
458         return (FAIL);
459 }
460
461 /*
462  * handle unexpected inconsistencies
463  */
464 #ifdef __STDC__
465 #include <stdarg.h>
466 #else
467 #include <varargs.h>
468 #endif
469
470 void
471 #ifdef __STDC__
472 panic(const char *fmt, ...)
473 #else
474 panic(fmt, va_alist)
475         char *fmt;
476         va_dcl
477 #endif
478 {
479         va_list ap;
480 #ifdef __STDC__
481         va_start(ap, fmt);
482 #else
483         va_start(ap);
484 #endif
485
486         vfprintf(stderr, fmt, ap);
487         if (yflag)
488                 return;
489         if (reply("abort") == GOOD) {
490                 if (reply("dump core") == GOOD) {
491                         if (fchdir(wdfd) < 0)
492                                 warn("fchdir");
493                         abort();
494                 }
495                 exit(1);
496         }
497 }
498
499 void resizemaps(dump_ino_t oldmax, dump_ino_t newmax)
500 {
501         char *map;
502
503         if (usedinomap) {
504                 map = calloc((unsigned)1, (unsigned)howmany(newmax, NBBY));
505                 if (map == NULL)
506                         errx(1, "no memory for active inode map");
507                 memcpy(map, usedinomap, howmany(oldmax, NBBY));
508                 free(usedinomap);
509                 usedinomap = map;
510         }
511         if (dumpmap) {
512                 map = calloc((unsigned)1, (unsigned)howmany(newmax, NBBY));
513                 if (map == NULL)
514                         errx(1, "no memory for file dump list");
515                 memcpy(map, dumpmap, howmany(oldmax, NBBY));
516                 free(dumpmap);
517                 dumpmap = map;
518         }
519 }
520
521 void
522 GetPathFile(char *source, char *path, char *fname)
523 {
524         char *p, *s;
525
526         *path = 0;
527         *fname = 0;
528         p = s = source;
529         while (*s) {
530                 if (*s == '/') {
531                         p = s + 1;
532                 }
533                 s++;
534         }
535         if (p == source) {
536                 *path = 0;
537         } else {
538                 strncpy(path, source, p - source);
539                 path[p - source] = 0;
540         }
541         strcpy(fname, p);
542 }
543
544 #ifdef USE_QFA
545 /*
546  * search for ino in QFA file
547  *
548  * if exactmatch:
549  * if ino found return tape number and tape position
550  * if ino not found return tnum=0 and tpos=0
551  *
552  * if not exactmatch:
553  * if ino found return tape number and tape position
554  * if ino not found return tape number and tape position of last smaller ino
555  * if no smaller inode found return tnum=0 and tpos=0
556  */
557 int
558 Inode2Tapepos(dump_ino_t ino, long *tnum, long long *tpos, int exactmatch)
559 {
560         char *p, *pp;
561         char numbuff[32];
562         unsigned long tmpino;
563         long tmptnum;
564         long long tmptpos;
565
566         *tpos = 0;
567         *tnum = 0;
568         if (fseek(gTapeposfp, gSeekstart, SEEK_SET) == -1)
569                 return errno;
570         while (1) {
571                 gSeekstart = ftell(gTapeposfp); /* remember for later use */
572                 if (fgets(gTps, sizeof(gTps), gTapeposfp) == NULL) {
573                         return 0;
574                 }
575                 gTps[strlen(gTps) - 1] = 0;     /* delete end of line */
576                 p = gTps;
577                 bzero(numbuff, sizeof(numbuff));
578                 pp = numbuff;
579                 /* read inode */
580                 while ((*p != 0) && (*p != '\t'))
581                         *pp++ = *p++;
582                 tmpino = atol(numbuff);
583                 if (*p == 0)
584                         return 1;       /* may NOT happen */    
585                 p++;
586                 bzero(numbuff, sizeof(numbuff));
587                 pp = numbuff;
588                 /* read tapenum */
589                 while ((*p != 0) && (*p != '\t'))
590                         *pp++ = *p++;
591                 if (*p == 0)
592                         return 1;       /* may NOT happen */    
593                 tmptnum = atol(numbuff);
594                 p++;
595                 bzero(numbuff, sizeof(numbuff));
596                 pp = numbuff;
597                 /* read tapepos */
598                 while ((*p != 0) && (*p != '\t'))
599                         *pp++ = *p++;
600                 tmptpos = atoll(numbuff);
601
602                 if (exactmatch) {
603                         if (tmpino == ino)  {
604                                 *tnum = tmptnum;
605                                 *tpos = tmptpos;
606                                 return 0;
607                         }
608                 } else {
609                         if (tmpino > ino)  {
610                                 return 0;
611                         } else {
612                                 *tnum = tmptnum;
613                                 *tpos = tmptpos;
614                         }
615                 }
616         }
617         return 0;
618 }
619
620 #ifdef sunos
621 int
622 GetSCSIIDFromPath(char *devPath, long *id)
623 {
624         int     len;
625         char    fbuff[2048];
626         char    path[2048];
627         char    fname[2048];
628         char    *fpn = fname;
629         char    idstr[32];
630         char    *ip = idstr;
631
632         bzero(fbuff, sizeof(fbuff));
633         if ((len = readlink(devPath, fbuff, 2048)) == -1) {
634                 return errno;
635         }
636         fbuff[len] = 0;
637         GetPathFile(fbuff, path, fname);
638         (void)memset(idstr, 0, sizeof(idstr));
639         while (*fpn && (*fpn != ',')) {
640                 if (*fpn <= '9' && *fpn >= '0') {
641                         *ip = *fpn;
642                         ip++;
643                 }
644                 fpn++;
645         }
646         if (*idstr) {
647                 *id = atol(idstr);
648         } else {
649                 *id = -1;
650         }
651         return 0;
652 }
653 #endif
654 #endif /* USE_QFA */
655
656 #ifdef DUMP_MACOSX
657 int
658 CreateAppleDoubleFileRes(char *oFile, FndrFileInfo *finderinfo, mode_t mode, int flags,
659                 struct timeval *timep, u_int32_t uid, u_int32_t gid)
660 {
661         int             err = 0;
662         int             fdout;
663         char            *p;
664         char            *pp;
665         ASDHeaderPtr    hp;
666         ASDEntryPtr     ep;
667         long            thesize;
668         long            n;
669
670
671         n = 1;  /* number of entries in double file ._ only finderinfo */
672         /*
673         no data fork
674         n++;
675         currently no resource fork
676         n++;
677         */
678
679         thesize = sizeof(ASDHeader) + (n * sizeof(ASDEntry)) + INFOLEN;
680         if ((pp = p = (char *)malloc(thesize)) == NULL) {
681                 err = errno;
682                 return err;
683         }
684
685         hp = (ASDHeaderPtr)p;
686         p += sizeof(ASDHeader);
687         ep = (ASDEntryPtr)p;
688         p += sizeof(ASDEntry) * n;
689
690         hp->magic = ADOUBLE_MAGIC;
691         hp->version = ASD_VERSION2;
692
693         bzero(&hp->filler, sizeof(hp->filler));
694         hp->entries = (short)n;
695         
696         ep->entryID = EntryFinderInfo;
697         ep->offset = p - pp - CORRECT;
698         ep->len = INFOLEN; /*  sizeof(MacFInfo) + sizeof(FXInfo); */
699         bzero(p, ep->len);
700         bcopy(finderinfo, p, sizeof(FndrFileInfo));
701         p += ep->len;
702         ep++;
703
704         if ((fdout = open(oFile, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
705                 err = errno;
706                 free(pp);
707                 return err;
708         }
709
710         /* write the ASDHeader */
711         if (write(fdout, pp, sizeof(ASDHeader) - CORRECT) == -1) {
712                 err = errno;
713                 close(fdout);
714                 free(pp);
715                 unlink(oFile);
716                 return err;
717         }
718         /* write the ASDEntries */
719         if (write(fdout, pp + sizeof(ASDHeader), thesize - sizeof(ASDHeader)) == -1) {
720                 err = errno;
721                 close(fdout);
722                 free(pp);
723                 unlink(oFile);
724                 return err;
725         }
726
727         (void)fchown(fdout, uid, gid);
728         (void)fchmod(fdout, mode);
729         close(fdout);
730         (void)lsetflags(oFile, flags);
731         utimes(oFile, timep);
732         free(pp);
733         return err;
734 }
735 #endif /* DUMP_MACOSX */
736
737 int
738 lgetflags(const char *path, unsigned long *flags)
739 {
740         int err;
741         struct STAT sb;
742
743         err = LSTAT(path, &sb);
744         if (err < 0)
745                 return err;
746
747         if (S_ISLNK(sb.st_mode) || S_ISFIFO(sb.st_mode)) {
748                 // no way to get/set flags on a symlink
749                 *flags = 0;
750                 return 0;
751         }
752         else
753                 return fgetflags(path, flags);
754 }
755
756 int
757 lsetflags(const char *path, unsigned long flags)
758 {
759         int err;
760         struct STAT sb;
761
762         err = LSTAT(path, &sb);
763         if (err < 0)
764                 return err;
765
766         if (S_ISLNK(sb.st_mode) || S_ISFIFO(sb.st_mode)) {
767                 // no way to get/set flags on a symlink
768                 return 0;
769         }
770         else
771                 return fsetflags(path, flags);
772 }