Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sbin / restore / utilities.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  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
35 #if 0
36 static char sccsid[] = "@(#)utilities.c 8.5 (Berkeley) 4/28/95";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD: src/sbin/restore/utilities.c,v 1.8.2.2 2001/07/30 10:30:08 dd Exp $";
40 #endif /* not lint */
41
42 #include <sys/param.h>
43 #include <sys/stat.h>
44
45 #include <ufs/ufs/dinode.h>
46 #include <ufs/ufs/dir.h>
47
48 #include <errno.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 #include "restore.h"
55 #include "extern.h"
56
57 /*
58  * Insure that all the components of a pathname exist.
59  */
60 void
61 pathcheck(name)
62         char *name;
63 {
64         register char *cp;
65         struct entry *ep;
66         char *start;
67
68         start = strchr(name, '/');
69         if (start == 0)
70                 return;
71         for (cp = start; *cp != '\0'; cp++) {
72                 if (*cp != '/')
73                         continue;
74                 *cp = '\0';
75                 ep = lookupname(name);
76                 if (ep == NULL) {
77                         /* Safe; we know the pathname exists in the dump. */
78                         ep = addentry(name, pathsearch(name)->d_ino, NODE);
79                         newnode(ep);
80                 }
81                 ep->e_flags |= NEW|KEEP;
82                 *cp = '/';
83         }
84 }
85
86 /*
87  * Change a name to a unique temporary name.
88  */
89 void
90 mktempname(ep)
91         register struct entry *ep;
92 {
93         char oldname[MAXPATHLEN];
94
95         if (ep->e_flags & TMPNAME)
96                 badentry(ep, "mktempname: called with TMPNAME");
97         ep->e_flags |= TMPNAME;
98         (void) strcpy(oldname, myname(ep));
99         freename(ep->e_name);
100         ep->e_name = savename(gentempname(ep));
101         ep->e_namlen = strlen(ep->e_name);
102         renameit(oldname, myname(ep));
103 }
104
105 /*
106  * Generate a temporary name for an entry.
107  */
108 char *
109 gentempname(ep)
110         struct entry *ep;
111 {
112         static char name[MAXPATHLEN];
113         struct entry *np;
114         long i = 0;
115
116         for (np = lookupino(ep->e_ino);
117             np != NULL && np != ep; np = np->e_links)
118                 i++;
119         if (np == NULL)
120                 badentry(ep, "not on ino list");
121         (void) sprintf(name, "%s%ld%lu", TMPHDR, i, (u_long)ep->e_ino);
122         return (name);
123 }
124
125 /*
126  * Rename a file or directory.
127  */
128 void
129 renameit(from, to)
130         char *from, *to;
131 {
132         if (!Nflag && rename(from, to) < 0) {
133                 fprintf(stderr, "warning: cannot rename %s to %s: %s\n",
134                     from, to, strerror(errno));
135                 return;
136         }
137         vprintf(stdout, "rename %s to %s\n", from, to);
138 }
139
140 /*
141  * Create a new node (directory).
142  */
143 void
144 newnode(np)
145         struct entry *np;
146 {
147         char *cp;
148
149         if (np->e_type != NODE)
150                 badentry(np, "newnode: not a node");
151         cp = myname(np);
152         if (!Nflag && mkdir(cp, 0777) < 0 && !uflag) {
153                 np->e_flags |= EXISTED;
154                 fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
155                 return;
156         }
157         vprintf(stdout, "Make node %s\n", cp);
158 }
159
160 /*
161  * Remove an old node (directory).
162  */
163 void
164 removenode(ep)
165         register struct entry *ep;
166 {
167         char *cp;
168
169         if (ep->e_type != NODE)
170                 badentry(ep, "removenode: not a node");
171         if (ep->e_entries != NULL)
172                 badentry(ep, "removenode: non-empty directory");
173         ep->e_flags |= REMOVED;
174         ep->e_flags &= ~TMPNAME;
175         cp = myname(ep);
176         if (!Nflag && rmdir(cp) < 0) {
177                 fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
178                 return;
179         }
180         vprintf(stdout, "Remove node %s\n", cp);
181 }
182
183 /*
184  * Remove a leaf.
185  */
186 void
187 removeleaf(ep)
188         register struct entry *ep;
189 {
190         char *cp;
191
192         if (ep->e_type != LEAF)
193                 badentry(ep, "removeleaf: not a leaf");
194         ep->e_flags |= REMOVED;
195         ep->e_flags &= ~TMPNAME;
196         cp = myname(ep);
197         if (!Nflag && unlink(cp) < 0) {
198                 fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
199                 return;
200         }
201         vprintf(stdout, "Remove leaf %s\n", cp);
202 }
203
204 /*
205  * Create a link.
206  */
207 int
208 linkit(existing, new, type)
209         char *existing, *new;
210         int type;
211 {
212
213         /* if we want to unlink first, do it now so *link() won't fail */
214         if (uflag && !Nflag)
215                 (void)unlink(new);
216
217         if (type == SYMLINK) {
218                 if (!Nflag && symlink(existing, new) < 0) {
219                         fprintf(stderr,
220                             "warning: cannot create symbolic link %s->%s: %s\n",
221                             new, existing, strerror(errno));
222                         return (FAIL);
223                 }
224         } else if (type == HARDLINK) {
225                 int ret;
226
227                 if (!Nflag && (ret = link(existing, new)) < 0) {
228                         struct stat s;
229
230                         /*
231                          * Most likely, the schg flag is set.  Clear the
232                          * flags and try again.
233                          */
234                         if (stat(existing, &s) == 0 && s.st_flags != 0 &&
235                             chflags(existing, 0) == 0) {
236                                 ret = link(existing, new);
237                                 chflags(existing, s.st_flags);
238                         }
239                         if (ret < 0) {
240                                 fprintf(stderr, "warning: cannot create "
241                                     "hard link %s->%s: %s\n",
242                                     new, existing, strerror(errno));
243                                 return (FAIL);
244                         }
245                 }
246         } else {
247                 panic("linkit: unknown type %d\n", type);
248                 return (FAIL);
249         }
250         vprintf(stdout, "Create %s link %s->%s\n",
251                 type == SYMLINK ? "symbolic" : "hard", new, existing);
252         return (GOOD);
253 }
254
255 /*
256  * Create a whiteout.
257  */
258 int
259 addwhiteout(name)
260         char *name;
261 {
262
263         if (!Nflag && mknod(name, S_IFWHT, 0) < 0) {
264                 fprintf(stderr, "warning: cannot create whiteout %s: %s\n",
265                     name, strerror(errno));
266                 return (FAIL);
267         }
268         vprintf(stdout, "Create whiteout %s\n", name);
269         return (GOOD);
270 }
271
272 /*
273  * Delete a whiteout.
274  */
275 void
276 delwhiteout(ep)
277         register struct entry *ep;
278 {
279         char *name;
280
281         if (ep->e_type != LEAF)
282                 badentry(ep, "delwhiteout: not a leaf");
283         ep->e_flags |= REMOVED;
284         ep->e_flags &= ~TMPNAME;
285         name = myname(ep);
286         if (!Nflag && undelete(name) < 0) {
287                 fprintf(stderr, "warning: cannot delete whiteout %s: %s\n",
288                     name, strerror(errno));
289                 return;
290         }
291         vprintf(stdout, "Delete whiteout %s\n", name);
292 }
293
294 /*
295  * find lowest number file (above "start") that needs to be extracted
296  */
297 ino_t
298 lowerbnd(start)
299         ino_t start;
300 {
301         register struct entry *ep;
302
303         for ( ; start < maxino; start++) {
304                 ep = lookupino(start);
305                 if (ep == NULL || ep->e_type == NODE)
306                         continue;
307                 if (ep->e_flags & (NEW|EXTRACT))
308                         return (start);
309         }
310         return (start);
311 }
312
313 /*
314  * find highest number file (below "start") that needs to be extracted
315  */
316 ino_t
317 upperbnd(start)
318         ino_t start;
319 {
320         register struct entry *ep;
321
322         for ( ; start > ROOTINO; start--) {
323                 ep = lookupino(start);
324                 if (ep == NULL || ep->e_type == NODE)
325                         continue;
326                 if (ep->e_flags & (NEW|EXTRACT))
327                         return (start);
328         }
329         return (start);
330 }
331
332 /*
333  * report on a badly formed entry
334  */
335 void
336 badentry(ep, msg)
337         register struct entry *ep;
338         char *msg;
339 {
340
341         fprintf(stderr, "bad entry: %s\n", msg);
342         fprintf(stderr, "name: %s\n", myname(ep));
343         fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
344         if (ep->e_sibling != NULL)
345                 fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
346         if (ep->e_entries != NULL)
347                 fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries));
348         if (ep->e_links != NULL)
349                 fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
350         if (ep->e_next != NULL)
351                 fprintf(stderr,
352                     "next hashchain name: %s\n", myname(ep->e_next));
353         fprintf(stderr, "entry type: %s\n",
354                 ep->e_type == NODE ? "NODE" : "LEAF");
355         fprintf(stderr, "inode number: %lu\n", (u_long)ep->e_ino);
356         panic("flags: %s\n", flagvalues(ep));
357 }
358
359 /*
360  * Construct a string indicating the active flag bits of an entry.
361  */
362 char *
363 flagvalues(ep)
364         register struct entry *ep;
365 {
366         static char flagbuf[BUFSIZ];
367
368         (void) strcpy(flagbuf, "|NIL");
369         flagbuf[0] = '\0';
370         if (ep->e_flags & REMOVED)
371                 (void) strcat(flagbuf, "|REMOVED");
372         if (ep->e_flags & TMPNAME)
373                 (void) strcat(flagbuf, "|TMPNAME");
374         if (ep->e_flags & EXTRACT)
375                 (void) strcat(flagbuf, "|EXTRACT");
376         if (ep->e_flags & NEW)
377                 (void) strcat(flagbuf, "|NEW");
378         if (ep->e_flags & KEEP)
379                 (void) strcat(flagbuf, "|KEEP");
380         if (ep->e_flags & EXISTED)
381                 (void) strcat(flagbuf, "|EXISTED");
382         return (&flagbuf[1]);
383 }
384
385 /*
386  * Check to see if a name is on a dump tape.
387  */
388 ino_t
389 dirlookup(name)
390         const char *name;
391 {
392         struct direct *dp;
393         ino_t ino;
394
395         ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino;
396
397         if (ino == 0 || TSTINO(ino, dumpmap) == 0)
398                 fprintf(stderr, "%s is not on the tape\n", name);
399         return (ino);
400 }
401
402 /*
403  * Elicit a reply.
404  */
405 int
406 reply(question)
407         char *question;
408 {
409         int c;
410
411         do      {
412                 fprintf(stderr, "%s? [yn] ", question);
413                 (void) fflush(stderr);
414                 c = getc(terminal);
415                 while (c != '\n' && getc(terminal) != '\n')
416                         if (c == EOF)
417                                 return (FAIL);
418         } while (c != 'y' && c != 'n');
419         if (c == 'y')
420                 return (GOOD);
421         return (FAIL);
422 }
423
424 /*
425  * handle unexpected inconsistencies
426  */
427 #if __STDC__
428 #include <stdarg.h>
429 #else
430 #include <varargs.h>
431 #endif
432
433 void
434 #if __STDC__
435 panic(const char *fmt, ...)
436 #else
437 panic(fmt, va_alist)
438         char *fmt;
439         va_dcl
440 #endif
441 {
442         va_list ap;
443 #if __STDC__
444         va_start(ap, fmt);
445 #else
446         va_start(ap);
447 #endif
448
449         vfprintf(stderr, fmt, ap);
450         if (yflag)
451                 return;
452         if (reply("abort") == GOOD) {
453                 if (reply("dump core") == GOOD)
454                         abort();
455                 done(1);
456         }
457 }