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