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