xref: /freebsd/sbin/restore/utilities.c (revision bdcbfde31e8e9b343f113a1956384bdf30d1ed62)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 #endif /* not lint */
34 
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 
38 #include <ufs/ufs/dinode.h>
39 #include <ufs/ufs/dir.h>
40 
41 #include <errno.h>
42 #include <limits.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 #include "restore.h"
49 #include "extern.h"
50 
51 /*
52  * Insure that all the components of a pathname exist.
53  */
54 void
55 pathcheck(char *name)
56 {
57 	char *cp;
58 	struct entry *ep;
59 	char *start;
60 
61 	start = strchr(name, '/');
62 	if (start == NULL)
63 		return;
64 	for (cp = start; *cp != '\0'; cp++) {
65 		if (*cp != '/')
66 			continue;
67 		*cp = '\0';
68 		ep = lookupname(name);
69 		if (ep == NULL) {
70 			/* Safe; we know the pathname exists in the dump. */
71 			ep = addentry(name, pathsearch(name)->d_ino, NODE);
72 			newnode(ep);
73 		}
74 		ep->e_flags |= NEW|KEEP;
75 		*cp = '/';
76 	}
77 }
78 
79 /*
80  * Change a name to a unique temporary name.
81  */
82 void
83 mktempname(struct entry *ep)
84 {
85 	char oldname[MAXPATHLEN];
86 
87 	if (ep->e_flags & TMPNAME)
88 		badentry(ep, "mktempname: called with TMPNAME");
89 	ep->e_flags |= TMPNAME;
90 	(void) strcpy(oldname, myname(ep));
91 	freename(ep->e_name);
92 	ep->e_name = savename(gentempname(ep));
93 	ep->e_namlen = strlen(ep->e_name);
94 	renameit(oldname, myname(ep));
95 }
96 
97 /*
98  * Generate a temporary name for an entry.
99  */
100 char *
101 gentempname(struct entry *ep)
102 {
103 	static char name[MAXPATHLEN];
104 	struct entry *np;
105 	long i = 0;
106 
107 	for (np = lookupino(ep->e_ino);
108 	    np != NULL && np != ep; np = np->e_links)
109 		i++;
110 	if (np == NULL)
111 		badentry(ep, "not on ino list");
112 	(void) sprintf(name, "%s%ld%lu", TMPHDR, i, (u_long)ep->e_ino);
113 	return (name);
114 }
115 
116 /*
117  * Rename a file or directory.
118  */
119 void
120 renameit(char *from, char *to)
121 {
122 	if (!Nflag && rename(from, to) < 0) {
123 		fprintf(stderr, "warning: cannot rename %s to %s: %s\n",
124 		    from, to, strerror(errno));
125 		return;
126 	}
127 	vprintf(stdout, "rename %s to %s\n", from, to);
128 }
129 
130 /*
131  * Create a new node (directory).
132  */
133 void
134 newnode(struct entry *np)
135 {
136 	char *cp;
137 
138 	if (np->e_type != NODE)
139 		badentry(np, "newnode: not a node");
140 	cp = myname(np);
141 	if (!Nflag && mkdir(cp, 0777) < 0 && !uflag) {
142 		np->e_flags |= EXISTED;
143 		fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
144 		return;
145 	}
146 	vprintf(stdout, "Make node %s\n", cp);
147 }
148 
149 /*
150  * Remove an old node (directory).
151  */
152 void
153 removenode(struct entry *ep)
154 {
155 	char *cp;
156 
157 	if (ep->e_type != NODE)
158 		badentry(ep, "removenode: not a node");
159 	if (ep->e_entries != NULL)
160 		badentry(ep, "removenode: non-empty directory");
161 	ep->e_flags |= REMOVED;
162 	ep->e_flags &= ~TMPNAME;
163 	cp = myname(ep);
164 	if (!Nflag && rmdir(cp) < 0) {
165 		fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
166 		return;
167 	}
168 	vprintf(stdout, "Remove node %s\n", cp);
169 }
170 
171 /*
172  * Remove a leaf.
173  */
174 void
175 removeleaf(struct entry *ep)
176 {
177 	char *cp;
178 
179 	if (ep->e_type != LEAF)
180 		badentry(ep, "removeleaf: not a leaf");
181 	ep->e_flags |= REMOVED;
182 	ep->e_flags &= ~TMPNAME;
183 	cp = myname(ep);
184 	if (!Nflag && unlink(cp) < 0) {
185 		fprintf(stderr, "warning: %s: %s\n", cp, strerror(errno));
186 		return;
187 	}
188 	vprintf(stdout, "Remove leaf %s\n", cp);
189 }
190 
191 /*
192  * Create a link.
193  */
194 int
195 linkit(char *existing, char *new, int type)
196 {
197 
198 	/* if we want to unlink first, do it now so *link() won't fail */
199 	if (uflag && !Nflag)
200 		(void)unlink(new);
201 
202 	if (type == SYMLINK) {
203 		if (!Nflag && symlink(existing, new) < 0) {
204 			fprintf(stderr,
205 			    "warning: cannot create symbolic link %s->%s: %s\n",
206 			    new, existing, strerror(errno));
207 			return (FAIL);
208 		}
209 	} else if (type == HARDLINK) {
210 		int ret;
211 
212 		if (!Nflag && (ret = link(existing, new)) < 0) {
213 			struct stat s;
214 
215 			/*
216 			 * Most likely, the schg flag is set.  Clear the
217 			 * flags and try again.
218 			 */
219 			if (stat(existing, &s) == 0 && s.st_flags != 0 &&
220 			    chflags(existing, 0) == 0) {
221 				ret = link(existing, new);
222 				chflags(existing, s.st_flags);
223 			}
224 			if (ret < 0) {
225 				fprintf(stderr, "warning: cannot create "
226 				    "hard link %s->%s: %s\n",
227 				    new, existing, strerror(errno));
228 				return (FAIL);
229 			}
230 		}
231 	} else {
232 		panic("linkit: unknown type %d\n", type);
233 		return (FAIL);
234 	}
235 	vprintf(stdout, "Create %s link %s->%s\n",
236 		type == SYMLINK ? "symbolic" : "hard", new, existing);
237 	return (GOOD);
238 }
239 
240 /*
241  * Create a whiteout.
242  */
243 int
244 addwhiteout(char *name)
245 {
246 
247 	if (!Nflag && mknod(name, S_IFWHT, 0) < 0) {
248 		fprintf(stderr, "warning: cannot create whiteout %s: %s\n",
249 		    name, strerror(errno));
250 		return (FAIL);
251 	}
252 	vprintf(stdout, "Create whiteout %s\n", name);
253 	return (GOOD);
254 }
255 
256 /*
257  * Delete a whiteout.
258  */
259 void
260 delwhiteout(struct entry *ep)
261 {
262 	char *name;
263 
264 	if (ep->e_type != LEAF)
265 		badentry(ep, "delwhiteout: not a leaf");
266 	ep->e_flags |= REMOVED;
267 	ep->e_flags &= ~TMPNAME;
268 	name = myname(ep);
269 	if (!Nflag && undelete(name) < 0) {
270 		fprintf(stderr, "warning: cannot delete whiteout %s: %s\n",
271 		    name, strerror(errno));
272 		return;
273 	}
274 	vprintf(stdout, "Delete whiteout %s\n", name);
275 }
276 
277 /*
278  * find lowest number file (above "start") that needs to be extracted
279  */
280 ino_t
281 lowerbnd(ino_t start)
282 {
283 	struct entry *ep;
284 
285 	for ( ; start < maxino; start++) {
286 		ep = lookupino(start);
287 		if (ep == NULL || ep->e_type == NODE)
288 			continue;
289 		if (ep->e_flags & (NEW|EXTRACT))
290 			return (start);
291 	}
292 	return (start);
293 }
294 
295 /*
296  * find highest number file (below "start") that needs to be extracted
297  */
298 ino_t
299 upperbnd(ino_t start)
300 {
301 	struct entry *ep;
302 
303 	for ( ; start > UFS_ROOTINO; 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  * report on a badly formed entry
315  */
316 void
317 badentry(struct entry *ep, char *msg)
318 {
319 
320 	fprintf(stderr, "bad entry: %s\n", msg);
321 	fprintf(stderr, "name: %s\n", myname(ep));
322 	fprintf(stderr, "parent name %s\n", myname(ep->e_parent));
323 	if (ep->e_sibling != NULL)
324 		fprintf(stderr, "sibling name: %s\n", myname(ep->e_sibling));
325 	if (ep->e_entries != NULL)
326 		fprintf(stderr, "next entry name: %s\n", myname(ep->e_entries));
327 	if (ep->e_links != NULL)
328 		fprintf(stderr, "next link name: %s\n", myname(ep->e_links));
329 	if (ep->e_next != NULL)
330 		fprintf(stderr,
331 		    "next hashchain name: %s\n", myname(ep->e_next));
332 	fprintf(stderr, "entry type: %s\n",
333 		ep->e_type == NODE ? "NODE" : "LEAF");
334 	fprintf(stderr, "inode number: %lu\n", (u_long)ep->e_ino);
335 	panic("flags: %s\n", flagvalues(ep));
336 }
337 
338 /*
339  * Construct a string indicating the active flag bits of an entry.
340  */
341 char *
342 flagvalues(struct entry *ep)
343 {
344 	static char flagbuf[BUFSIZ];
345 
346 	(void) strcpy(flagbuf, "|NIL");
347 	flagbuf[0] = '\0';
348 	if (ep->e_flags & REMOVED)
349 		(void) strcat(flagbuf, "|REMOVED");
350 	if (ep->e_flags & TMPNAME)
351 		(void) strcat(flagbuf, "|TMPNAME");
352 	if (ep->e_flags & EXTRACT)
353 		(void) strcat(flagbuf, "|EXTRACT");
354 	if (ep->e_flags & NEW)
355 		(void) strcat(flagbuf, "|NEW");
356 	if (ep->e_flags & KEEP)
357 		(void) strcat(flagbuf, "|KEEP");
358 	if (ep->e_flags & EXISTED)
359 		(void) strcat(flagbuf, "|EXISTED");
360 	return (&flagbuf[1]);
361 }
362 
363 /*
364  * Check to see if a name is on a dump tape.
365  */
366 ino_t
367 dirlookup(const char *name)
368 {
369 	struct direct *dp;
370 	ino_t ino;
371 
372 	ino = ((dp = pathsearch(name)) == NULL) ? 0 : dp->d_ino;
373 
374 	if (ino == 0 || TSTINO(ino, dumpmap) == 0)
375 		fprintf(stderr, "%s is not on the tape\n", name);
376 	return (ino);
377 }
378 
379 /*
380  * Elicit a reply.
381  */
382 int
383 reply(char *question)
384 {
385 	int c;
386 
387 	do	{
388 		fprintf(stderr, "%s? [yn] ", question);
389 		(void) fflush(stderr);
390 		c = getc(terminal);
391 		while (c != '\n' && getc(terminal) != '\n')
392 			if (c == EOF)
393 				return (FAIL);
394 	} while (c != 'y' && c != 'n');
395 	if (c == 'y')
396 		return (GOOD);
397 	return (FAIL);
398 }
399 
400 /*
401  * handle unexpected inconsistencies
402  */
403 #include <stdarg.h>
404 
405 void
406 panic(const char *fmt, ...)
407 {
408 	va_list ap;
409 	va_start(ap, fmt);
410 	vfprintf(stderr, fmt, ap);
411 	va_end(ap);
412 	if (yflag)
413 		return;
414 	if (reply("abort") == GOOD) {
415 		if (reply("dump core") == GOOD)
416 			abort();
417 		done(1);
418 	}
419 }
420