1 |
|
|
/* $OpenBSD: restore.c,v 1.17 2013/04/24 13:46:29 deraadt Exp $ */ |
2 |
|
|
/* $NetBSD: restore.c,v 1.9 1997/06/18 07:10:16 lukem Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1983, 1993 |
6 |
|
|
* The Regents of the University of California. All rights reserved. |
7 |
|
|
* |
8 |
|
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
|
* modification, are permitted provided that the following conditions |
10 |
|
|
* are met: |
11 |
|
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
|
* documentation and/or other materials provided with the distribution. |
16 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
17 |
|
|
* may be used to endorse or promote products derived from this software |
18 |
|
|
* without specific prior written permission. |
19 |
|
|
* |
20 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
21 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
24 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 |
|
|
* SUCH DAMAGE. |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
#include <sys/types.h> |
34 |
|
|
#include <sys/stat.h> |
35 |
|
|
|
36 |
|
|
#include <ufs/ufs/dinode.h> |
37 |
|
|
|
38 |
|
|
#include <stdio.h> |
39 |
|
|
#include <string.h> |
40 |
|
|
|
41 |
|
|
#include "restore.h" |
42 |
|
|
#include "extern.h" |
43 |
|
|
|
44 |
|
|
static char *keyval(int); |
45 |
|
|
|
46 |
|
|
/* |
47 |
|
|
* This implements the 't' option. |
48 |
|
|
* List entries on the tape. |
49 |
|
|
*/ |
50 |
|
|
long |
51 |
|
|
listfile(char *name, ino_t ino, int type) |
52 |
|
|
{ |
53 |
|
|
long descend = hflag ? GOOD : FAIL; |
54 |
|
|
|
55 |
|
|
if (TSTINO(ino, dumpmap) == 0) |
56 |
|
|
return (descend); |
57 |
|
|
Vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir "); |
58 |
|
|
fprintf(stdout, "%10llu\t%s\n", (unsigned long long)ino, name); |
59 |
|
|
return (descend); |
60 |
|
|
} |
61 |
|
|
|
62 |
|
|
/* |
63 |
|
|
* This implements the 'x' option. |
64 |
|
|
* Request that new entries be extracted. |
65 |
|
|
*/ |
66 |
|
|
long |
67 |
|
|
addfile(char *name, ino_t ino, int type) |
68 |
|
|
{ |
69 |
|
|
struct entry *ep; |
70 |
|
|
long descend = hflag ? GOOD : FAIL; |
71 |
|
|
char buf[100]; |
72 |
|
|
|
73 |
|
|
if (TSTINO(ino, dumpmap) == 0) { |
74 |
|
|
Dprintf(stdout, "%s: not on the tape\n", name); |
75 |
|
|
return (descend); |
76 |
|
|
} |
77 |
|
|
if (!mflag) { |
78 |
|
|
(void)snprintf(buf, sizeof(buf), "./%llu", |
79 |
|
|
(unsigned long long)ino); |
80 |
|
|
name = buf; |
81 |
|
|
if (type == NODE) { |
82 |
|
|
(void)genliteraldir(name, ino); |
83 |
|
|
return (descend); |
84 |
|
|
} |
85 |
|
|
} |
86 |
|
|
ep = lookupino(ino); |
87 |
|
|
if (ep != NULL) { |
88 |
|
|
if (strcmp(name, myname(ep)) == 0) { |
89 |
|
|
ep->e_flags |= NEW; |
90 |
|
|
return (descend); |
91 |
|
|
} |
92 |
|
|
type |= LINK; |
93 |
|
|
} |
94 |
|
|
ep = addentry(name, ino, type); |
95 |
|
|
if (type == NODE) |
96 |
|
|
newnode(ep); |
97 |
|
|
ep->e_flags |= NEW; |
98 |
|
|
return (descend); |
99 |
|
|
} |
100 |
|
|
|
101 |
|
|
/* |
102 |
|
|
* This is used by the 'i' option to undo previous requests made by addfile. |
103 |
|
|
* Delete entries from the request queue. |
104 |
|
|
*/ |
105 |
|
|
/* ARGSUSED */ |
106 |
|
|
long |
107 |
|
|
deletefile(char *name, ino_t ino, int type) |
108 |
|
|
{ |
109 |
|
|
long descend = hflag ? GOOD : FAIL; |
110 |
|
|
struct entry *ep; |
111 |
|
|
|
112 |
|
|
if (TSTINO(ino, dumpmap) == 0) |
113 |
|
|
return (descend); |
114 |
|
|
ep = lookupname(name); |
115 |
|
|
if (ep != NULL) { |
116 |
|
|
ep->e_flags &= ~NEW; |
117 |
|
|
ep->e_flags |= REMOVED; |
118 |
|
|
if (ep->e_type != NODE) |
119 |
|
|
freeentry(ep); |
120 |
|
|
} |
121 |
|
|
return (descend); |
122 |
|
|
} |
123 |
|
|
|
124 |
|
|
/* |
125 |
|
|
* The following four routines implement the incremental |
126 |
|
|
* restore algorithm. The first removes old entries, the second |
127 |
|
|
* does renames and calculates the extraction list, the third |
128 |
|
|
* cleans up link names missed by the first two, and the final |
129 |
|
|
* one deletes old directories. |
130 |
|
|
* |
131 |
|
|
* Directories cannot be immediately deleted, as they may have |
132 |
|
|
* other files in them which need to be moved out first. As |
133 |
|
|
* directories to be deleted are found, they are put on the |
134 |
|
|
* following deletion list. After all deletions and renames |
135 |
|
|
* are done, this list is actually deleted. |
136 |
|
|
*/ |
137 |
|
|
static struct entry *removelist; |
138 |
|
|
|
139 |
|
|
/* |
140 |
|
|
* Remove unneeded leaves from the old tree. |
141 |
|
|
* Remove directories from the lookup chains. |
142 |
|
|
*/ |
143 |
|
|
void |
144 |
|
|
removeoldleaves(void) |
145 |
|
|
{ |
146 |
|
|
struct entry *ep; |
147 |
|
|
ino_t i; |
148 |
|
|
|
149 |
|
|
Vprintf(stdout, "Mark entries to be removed.\n"); |
150 |
|
|
for (i = ROOTINO + 1; i < maxino; i++) { |
151 |
|
|
ep = lookupino(i); |
152 |
|
|
if (ep == NULL) |
153 |
|
|
continue; |
154 |
|
|
if (TSTINO(i, usedinomap)) |
155 |
|
|
continue; |
156 |
|
|
for ( ; ep != NULL; ep = ep->e_links) { |
157 |
|
|
Dprintf(stdout, "%s: REMOVE\n", myname(ep)); |
158 |
|
|
if (ep->e_type == LEAF) { |
159 |
|
|
removeleaf(ep); |
160 |
|
|
freeentry(ep); |
161 |
|
|
} else { |
162 |
|
|
mktempname(ep); |
163 |
|
|
deleteino(ep->e_ino); |
164 |
|
|
ep->e_next = removelist; |
165 |
|
|
removelist = ep; |
166 |
|
|
} |
167 |
|
|
} |
168 |
|
|
} |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
/* |
172 |
|
|
* For each directory entry on the incremental tape, determine which |
173 |
|
|
* category it falls into as follows: |
174 |
|
|
* KEEP - entries that are to be left alone. |
175 |
|
|
* NEW - new entries to be added. |
176 |
|
|
* EXTRACT - files that must be updated with new contents. |
177 |
|
|
* LINK - new links to be added. |
178 |
|
|
* Renames are done at the same time. |
179 |
|
|
*/ |
180 |
|
|
long |
181 |
|
|
nodeupdates(char *name, ino_t ino, int type) |
182 |
|
|
{ |
183 |
|
|
struct entry *ep, *np, *ip; |
184 |
|
|
long descend = GOOD; |
185 |
|
|
int lookuptype = 0; |
186 |
|
|
int key = 0; |
187 |
|
|
/* key values */ |
188 |
|
|
# define ONTAPE 0x1 /* inode is on the tape */ |
189 |
|
|
# define INOFND 0x2 /* inode already exists */ |
190 |
|
|
# define NAMEFND 0x4 /* name already exists */ |
191 |
|
|
# define MODECHG 0x8 /* mode of inode changed */ |
192 |
|
|
|
193 |
|
|
/* |
194 |
|
|
* This routine is called once for each element in the |
195 |
|
|
* directory hierarchy, with a full path name. |
196 |
|
|
* The "type" value is incorrectly specified as LEAF for |
197 |
|
|
* directories that are not on the dump tape. |
198 |
|
|
* |
199 |
|
|
* Check to see if the file is on the tape. |
200 |
|
|
*/ |
201 |
|
|
if (TSTINO(ino, dumpmap)) |
202 |
|
|
key |= ONTAPE; |
203 |
|
|
/* |
204 |
|
|
* Check to see if the name exists, and if the name is a link. |
205 |
|
|
*/ |
206 |
|
|
np = lookupname(name); |
207 |
|
|
if (np != NULL) { |
208 |
|
|
key |= NAMEFND; |
209 |
|
|
ip = lookupino(np->e_ino); |
210 |
|
|
if (ip == NULL) |
211 |
|
|
panic("corrupted symbol table\n"); |
212 |
|
|
if (ip != np) |
213 |
|
|
lookuptype = LINK; |
214 |
|
|
} |
215 |
|
|
/* |
216 |
|
|
* Check to see if the inode exists, and if one of its links |
217 |
|
|
* corresponds to the name (if one was found). |
218 |
|
|
*/ |
219 |
|
|
ip = lookupino(ino); |
220 |
|
|
if (ip != NULL) { |
221 |
|
|
key |= INOFND; |
222 |
|
|
for (ep = ip->e_links; ep != NULL; ep = ep->e_links) { |
223 |
|
|
if (ep == np) { |
224 |
|
|
ip = ep; |
225 |
|
|
break; |
226 |
|
|
} |
227 |
|
|
} |
228 |
|
|
} |
229 |
|
|
/* |
230 |
|
|
* If both a name and an inode are found, but they do not |
231 |
|
|
* correspond to the same file, then both the inode that has |
232 |
|
|
* been found and the inode corresponding to the name that |
233 |
|
|
* has been found need to be renamed. The current pathname |
234 |
|
|
* is the new name for the inode that has been found. Since |
235 |
|
|
* all files to be deleted have already been removed, the |
236 |
|
|
* named file is either a now unneeded link, or it must live |
237 |
|
|
* under a new name in this dump level. If it is a link, it |
238 |
|
|
* can be removed. If it is not a link, it is given a |
239 |
|
|
* temporary name in anticipation that it will be renamed |
240 |
|
|
* when it is later found by inode number. |
241 |
|
|
*/ |
242 |
|
|
if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) { |
243 |
|
|
if (lookuptype == LINK) { |
244 |
|
|
removeleaf(np); |
245 |
|
|
freeentry(np); |
246 |
|
|
} else { |
247 |
|
|
Dprintf(stdout, "name/inode conflict, mktempname %s\n", |
248 |
|
|
myname(np)); |
249 |
|
|
mktempname(np); |
250 |
|
|
} |
251 |
|
|
np = NULL; |
252 |
|
|
key &= ~NAMEFND; |
253 |
|
|
} |
254 |
|
|
if ((key & ONTAPE) && |
255 |
|
|
(((key & INOFND) && ip->e_type != type) || |
256 |
|
|
((key & NAMEFND) && np->e_type != type))) |
257 |
|
|
key |= MODECHG; |
258 |
|
|
|
259 |
|
|
/* |
260 |
|
|
* Decide on the disposition of the file based on its flags. |
261 |
|
|
* Note that we have already handled the case in which |
262 |
|
|
* a name and inode are found that correspond to different files. |
263 |
|
|
* Thus if both NAMEFND and INOFND are set then ip == np. |
264 |
|
|
*/ |
265 |
|
|
switch (key) { |
266 |
|
|
|
267 |
|
|
/* |
268 |
|
|
* A previously existing file has been found. |
269 |
|
|
* Mark it as KEEP so that other links to the inode can be |
270 |
|
|
* detected, and so that it will not be reclaimed by the search |
271 |
|
|
* for unreferenced names. |
272 |
|
|
*/ |
273 |
|
|
case INOFND|NAMEFND: |
274 |
|
|
ip->e_flags |= KEEP; |
275 |
|
|
Dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, |
276 |
|
|
flagvalues(ip)); |
277 |
|
|
break; |
278 |
|
|
|
279 |
|
|
/* |
280 |
|
|
* A file on the tape has a name which is the same as a name |
281 |
|
|
* corresponding to a different file in the previous dump. |
282 |
|
|
* Since all files to be deleted have already been removed, |
283 |
|
|
* this file is either a now unneeded link, or it must live |
284 |
|
|
* under a new name in this dump level. If it is a link, it |
285 |
|
|
* can simply be removed. If it is not a link, it is given a |
286 |
|
|
* temporary name in anticipation that it will be renamed |
287 |
|
|
* when it is later found by inode number (see INOFND case |
288 |
|
|
* below). The entry is then treated as a new file. |
289 |
|
|
*/ |
290 |
|
|
case ONTAPE|NAMEFND: |
291 |
|
|
case ONTAPE|NAMEFND|MODECHG: |
292 |
|
|
if (lookuptype == LINK) { |
293 |
|
|
removeleaf(np); |
294 |
|
|
freeentry(np); |
295 |
|
|
} else { |
296 |
|
|
mktempname(np); |
297 |
|
|
} |
298 |
|
|
/* fall through */ |
299 |
|
|
|
300 |
|
|
/* |
301 |
|
|
* A previously non-existent file. |
302 |
|
|
* Add it to the file system, and request its extraction. |
303 |
|
|
* If it is a directory, create it immediately. |
304 |
|
|
* (Since the name is unused there can be no conflict) |
305 |
|
|
*/ |
306 |
|
|
case ONTAPE: |
307 |
|
|
ep = addentry(name, ino, type); |
308 |
|
|
if (type == NODE) |
309 |
|
|
newnode(ep); |
310 |
|
|
ep->e_flags |= NEW|KEEP; |
311 |
|
|
Dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, |
312 |
|
|
flagvalues(ep)); |
313 |
|
|
break; |
314 |
|
|
|
315 |
|
|
/* |
316 |
|
|
* A file with the same inode number, but a different |
317 |
|
|
* name has been found. If the other name has not already |
318 |
|
|
* been found (indicated by the KEEP flag, see above) then |
319 |
|
|
* this must be a new name for the file, and it is renamed. |
320 |
|
|
* If the other name has been found then this must be a |
321 |
|
|
* link to the file. Hard links to directories are not |
322 |
|
|
* permitted, and are either deleted or converted to |
323 |
|
|
* symbolic links. Finally, if the file is on the tape, |
324 |
|
|
* a request is made to extract it. |
325 |
|
|
*/ |
326 |
|
|
case ONTAPE|INOFND: |
327 |
|
|
if (type == LEAF && (ip->e_flags & KEEP) == 0) |
328 |
|
|
ip->e_flags |= EXTRACT; |
329 |
|
|
/* fall through */ |
330 |
|
|
case INOFND: |
331 |
|
|
if ((ip->e_flags & KEEP) == 0) { |
332 |
|
|
renameit(myname(ip), name); |
333 |
|
|
moveentry(ip, name); |
334 |
|
|
ip->e_flags |= KEEP; |
335 |
|
|
Dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, |
336 |
|
|
flagvalues(ip)); |
337 |
|
|
break; |
338 |
|
|
} |
339 |
|
|
if (ip->e_type == NODE) { |
340 |
|
|
descend = FAIL; |
341 |
|
|
fprintf(stderr, |
342 |
|
|
"deleted hard link %s to directory %s\n", |
343 |
|
|
name, myname(ip)); |
344 |
|
|
break; |
345 |
|
|
} |
346 |
|
|
ep = addentry(name, ino, type|LINK); |
347 |
|
|
ep->e_flags |= NEW; |
348 |
|
|
Dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name, |
349 |
|
|
flagvalues(ep)); |
350 |
|
|
break; |
351 |
|
|
|
352 |
|
|
/* |
353 |
|
|
* A previously known file which is to be updated. If it is a link, |
354 |
|
|
* then all names referring to the previous file must be removed |
355 |
|
|
* so that the subset of them that remain can be recreated. |
356 |
|
|
*/ |
357 |
|
|
case ONTAPE|INOFND|NAMEFND: |
358 |
|
|
if (lookuptype == LINK) { |
359 |
|
|
removeleaf(np); |
360 |
|
|
freeentry(np); |
361 |
|
|
ep = addentry(name, ino, type|LINK); |
362 |
|
|
if (type == NODE) |
363 |
|
|
newnode(ep); |
364 |
|
|
ep->e_flags |= NEW|KEEP; |
365 |
|
|
Dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name, |
366 |
|
|
flagvalues(ep)); |
367 |
|
|
break; |
368 |
|
|
} |
369 |
|
|
if (type == LEAF && lookuptype != LINK) |
370 |
|
|
np->e_flags |= EXTRACT; |
371 |
|
|
np->e_flags |= KEEP; |
372 |
|
|
Dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, |
373 |
|
|
flagvalues(np)); |
374 |
|
|
break; |
375 |
|
|
|
376 |
|
|
/* |
377 |
|
|
* An inode is being reused in a completely different way. |
378 |
|
|
* Normally an extract can simply do an "unlink" followed |
379 |
|
|
* by a "creat". Here we must do effectively the same |
380 |
|
|
* thing. The complications arise because we cannot really |
381 |
|
|
* delete a directory since it may still contain files |
382 |
|
|
* that we need to rename, so we delete it from the symbol |
383 |
|
|
* table, and put it on the list to be deleted eventually. |
384 |
|
|
* Conversely if a directory is to be created, it must be |
385 |
|
|
* done immediately, rather than waiting until the |
386 |
|
|
* extraction phase. |
387 |
|
|
*/ |
388 |
|
|
case ONTAPE|INOFND|MODECHG: |
389 |
|
|
case ONTAPE|INOFND|NAMEFND|MODECHG: |
390 |
|
|
if (ip->e_flags & KEEP) { |
391 |
|
|
badentry(ip, "cannot KEEP and change modes"); |
392 |
|
|
break; |
393 |
|
|
} |
394 |
|
|
if (ip->e_type == LEAF) { |
395 |
|
|
/* changing from leaf to node */ |
396 |
|
|
for ( ; ip != NULL; ip = ip->e_links) { |
397 |
|
|
if (ip->e_type != LEAF) |
398 |
|
|
badentry(ip, |
399 |
|
|
"NODE and LEAF links to same inode"); |
400 |
|
|
removeleaf(ip); |
401 |
|
|
freeentry(ip); |
402 |
|
|
} |
403 |
|
|
ip = addentry(name, ino, type); |
404 |
|
|
newnode(ip); |
405 |
|
|
} else { |
406 |
|
|
/* changing from node to leaf */ |
407 |
|
|
if ((ip->e_flags & TMPNAME) == 0) |
408 |
|
|
mktempname(ip); |
409 |
|
|
deleteino(ip->e_ino); |
410 |
|
|
ip->e_next = removelist; |
411 |
|
|
removelist = ip; |
412 |
|
|
ip = addentry(name, ino, type); |
413 |
|
|
} |
414 |
|
|
ip->e_flags |= NEW|KEEP; |
415 |
|
|
Dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, |
416 |
|
|
flagvalues(ip)); |
417 |
|
|
break; |
418 |
|
|
|
419 |
|
|
/* |
420 |
|
|
* A hard link to a diirectory that has been removed. |
421 |
|
|
* Ignore it. |
422 |
|
|
*/ |
423 |
|
|
case NAMEFND: |
424 |
|
|
Dprintf(stdout, "[%s] %s: Extraneous name\n", keyval(key), |
425 |
|
|
name); |
426 |
|
|
descend = FAIL; |
427 |
|
|
break; |
428 |
|
|
|
429 |
|
|
/* |
430 |
|
|
* If we find a directory entry for a file that is not on |
431 |
|
|
* the tape, then we must have found a file that was created |
432 |
|
|
* while the dump was in progress. Since we have no contents |
433 |
|
|
* for it, we discard the name knowing that it will be on the |
434 |
|
|
* next incremental tape. |
435 |
|
|
*/ |
436 |
|
|
case 0: |
437 |
|
|
fprintf(stderr, "%s: (inode %llu) not found on tape\n", |
438 |
|
|
name, (unsigned long long)ino); |
439 |
|
|
break; |
440 |
|
|
|
441 |
|
|
/* |
442 |
|
|
* If any of these arise, something is grievously wrong with |
443 |
|
|
* the current state of the symbol table. |
444 |
|
|
*/ |
445 |
|
|
case INOFND|NAMEFND|MODECHG: |
446 |
|
|
case NAMEFND|MODECHG: |
447 |
|
|
case INOFND|MODECHG: |
448 |
|
|
fprintf(stderr, "[%s] %s: inconsistent state\n", keyval(key), |
449 |
|
|
name); |
450 |
|
|
break; |
451 |
|
|
|
452 |
|
|
/* |
453 |
|
|
* These states "cannot" arise for any state of the symbol table. |
454 |
|
|
*/ |
455 |
|
|
case ONTAPE|MODECHG: |
456 |
|
|
case MODECHG: |
457 |
|
|
default: |
458 |
|
|
panic("[%s] %s: impossible state\n", keyval(key), name); |
459 |
|
|
break; |
460 |
|
|
} |
461 |
|
|
return (descend); |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
/* |
465 |
|
|
* Calculate the active flags in a key. |
466 |
|
|
*/ |
467 |
|
|
static char * |
468 |
|
|
keyval(int key) |
469 |
|
|
{ |
470 |
|
|
static char keybuf[32]; |
471 |
|
|
|
472 |
|
|
(void)strlcpy(keybuf, "|NIL", sizeof keybuf); |
473 |
|
|
keybuf[0] = '\0'; |
474 |
|
|
if (key & ONTAPE) |
475 |
|
|
(void)strlcat(keybuf, "|ONTAPE", sizeof keybuf); |
476 |
|
|
if (key & INOFND) |
477 |
|
|
(void)strlcat(keybuf, "|INOFND", sizeof keybuf); |
478 |
|
|
if (key & NAMEFND) |
479 |
|
|
(void)strlcat(keybuf, "|NAMEFND", sizeof keybuf); |
480 |
|
|
if (key & MODECHG) |
481 |
|
|
(void)strlcat(keybuf, "|MODECHG", sizeof keybuf); |
482 |
|
|
return (&keybuf[1]); |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
/* |
486 |
|
|
* Find unreferenced link names. |
487 |
|
|
*/ |
488 |
|
|
void |
489 |
|
|
findunreflinks(void) |
490 |
|
|
{ |
491 |
|
|
struct entry *ep, *np; |
492 |
|
|
ino_t i; |
493 |
|
|
|
494 |
|
|
Vprintf(stdout, "Find unreferenced names.\n"); |
495 |
|
|
for (i = ROOTINO; i < maxino; i++) { |
496 |
|
|
ep = lookupino(i); |
497 |
|
|
if (ep == NULL || ep->e_type == LEAF || TSTINO(i, dumpmap) == 0) |
498 |
|
|
continue; |
499 |
|
|
for (np = ep->e_entries; np != NULL; np = np->e_sibling) { |
500 |
|
|
if (np->e_flags == 0) { |
501 |
|
|
Dprintf(stdout, |
502 |
|
|
"%s: remove unreferenced name\n", |
503 |
|
|
myname(np)); |
504 |
|
|
removeleaf(np); |
505 |
|
|
freeentry(np); |
506 |
|
|
} |
507 |
|
|
} |
508 |
|
|
} |
509 |
|
|
/* |
510 |
|
|
* Any leaves remaining in removed directories is unreferenced. |
511 |
|
|
*/ |
512 |
|
|
for (ep = removelist; ep != NULL; ep = ep->e_next) { |
513 |
|
|
for (np = ep->e_entries; np != NULL; np = np->e_sibling) { |
514 |
|
|
if (np->e_type == LEAF) { |
515 |
|
|
if (np->e_flags != 0) |
516 |
|
|
badentry(np, "unreferenced with flags"); |
517 |
|
|
Dprintf(stdout, |
518 |
|
|
"%s: remove unreferenced name\n", |
519 |
|
|
myname(np)); |
520 |
|
|
removeleaf(np); |
521 |
|
|
freeentry(np); |
522 |
|
|
} |
523 |
|
|
} |
524 |
|
|
} |
525 |
|
|
} |
526 |
|
|
|
527 |
|
|
/* |
528 |
|
|
* Remove old nodes (directories). |
529 |
|
|
* Note that this routine runs in O(N*D) where: |
530 |
|
|
* N is the number of directory entries to be removed. |
531 |
|
|
* D is the maximum depth of the tree. |
532 |
|
|
* If N == D this can be quite slow. If the list were |
533 |
|
|
* topologically sorted, the deletion could be done in |
534 |
|
|
* time O(N). |
535 |
|
|
*/ |
536 |
|
|
void |
537 |
|
|
removeoldnodes(void) |
538 |
|
|
{ |
539 |
|
|
struct entry *ep, **prev; |
540 |
|
|
long change; |
541 |
|
|
|
542 |
|
|
Vprintf(stdout, "Remove old nodes (directories).\n"); |
543 |
|
|
do { |
544 |
|
|
change = 0; |
545 |
|
|
prev = &removelist; |
546 |
|
|
for (ep = removelist; ep != NULL; ep = *prev) { |
547 |
|
|
if (ep->e_entries != NULL) { |
548 |
|
|
prev = &ep->e_next; |
549 |
|
|
continue; |
550 |
|
|
} |
551 |
|
|
*prev = ep->e_next; |
552 |
|
|
removenode(ep); |
553 |
|
|
freeentry(ep); |
554 |
|
|
change++; |
555 |
|
|
} |
556 |
|
|
} while (change); |
557 |
|
|
for (ep = removelist; ep != NULL; ep = ep->e_next) |
558 |
|
|
badentry(ep, "cannot remove, non-empty"); |
559 |
|
|
} |
560 |
|
|
|
561 |
|
|
/* |
562 |
|
|
* This is the routine used to extract files for the 'r' command. |
563 |
|
|
* Extract new leaves. |
564 |
|
|
*/ |
565 |
|
|
void |
566 |
|
|
createleaves(char *symtabfile) |
567 |
|
|
{ |
568 |
|
|
struct entry *ep; |
569 |
|
|
ino_t first; |
570 |
|
|
long curvol; |
571 |
|
|
|
572 |
|
|
if (command == 'R') { |
573 |
|
|
Vprintf(stdout, "Continue extraction of new leaves\n"); |
574 |
|
|
} else { |
575 |
|
|
Vprintf(stdout, "Extract new leaves.\n"); |
576 |
|
|
dumpsymtable(symtabfile, volno); |
577 |
|
|
} |
578 |
|
|
first = lowerbnd(ROOTINO); |
579 |
|
|
curvol = volno; |
580 |
|
|
while (curfile.ino < maxino) { |
581 |
|
|
first = lowerbnd(first); |
582 |
|
|
/* |
583 |
|
|
* If the next available file is not the one which we |
584 |
|
|
* expect then we have missed one or more files. Since |
585 |
|
|
* we do not request files that were not on the tape, |
586 |
|
|
* the lost files must have been due to a tape read error, |
587 |
|
|
* or a file that was removed while the dump was in progress. |
588 |
|
|
*/ |
589 |
|
|
while (first < curfile.ino) { |
590 |
|
|
ep = lookupino(first); |
591 |
|
|
if (ep == NULL) |
592 |
|
|
panic("%llu: bad first\n", |
593 |
|
|
(unsigned long long)first); |
594 |
|
|
fprintf(stderr, "%s: not found on tape\n", myname(ep)); |
595 |
|
|
ep->e_flags &= ~(NEW|EXTRACT); |
596 |
|
|
first = lowerbnd(first); |
597 |
|
|
} |
598 |
|
|
/* |
599 |
|
|
* If we find files on the tape that have no corresponding |
600 |
|
|
* directory entries, then we must have found a file that |
601 |
|
|
* was created while the dump was in progress. Since we have |
602 |
|
|
* no name for it, we discard it knowing that it will be |
603 |
|
|
* on the next incremental tape. |
604 |
|
|
*/ |
605 |
|
|
if (first != curfile.ino) { |
606 |
|
|
fprintf(stderr, "expected next file %llu, got %llu\n", |
607 |
|
|
(unsigned long long)first, |
608 |
|
|
(unsigned long long)curfile.ino); |
609 |
|
|
skipfile(); |
610 |
|
|
goto next; |
611 |
|
|
} |
612 |
|
|
ep = lookupino(curfile.ino); |
613 |
|
|
if (ep == NULL) |
614 |
|
|
panic("unknown file on tape\n"); |
615 |
|
|
if ((ep->e_flags & (NEW|EXTRACT)) == 0) |
616 |
|
|
badentry(ep, "unexpected file on tape"); |
617 |
|
|
/* |
618 |
|
|
* If the file is to be extracted, then the old file must |
619 |
|
|
* be removed since its type may change from one leaf type |
620 |
|
|
* to another (eg "file" to "character special"). |
621 |
|
|
*/ |
622 |
|
|
if ((ep->e_flags & EXTRACT) != 0) { |
623 |
|
|
removeleaf(ep); |
624 |
|
|
ep->e_flags &= ~REMOVED; |
625 |
|
|
} |
626 |
|
|
(void)extractfile(myname(ep)); |
627 |
|
|
ep->e_flags &= ~(NEW|EXTRACT); |
628 |
|
|
/* |
629 |
|
|
* We checkpoint the restore after every tape reel, so |
630 |
|
|
* as to simplify the amount of work re quired by the |
631 |
|
|
* 'R' command. |
632 |
|
|
*/ |
633 |
|
|
next: |
634 |
|
|
if (curvol != volno) { |
635 |
|
|
dumpsymtable(symtabfile, volno); |
636 |
|
|
skipmaps(); |
637 |
|
|
curvol = volno; |
638 |
|
|
} |
639 |
|
|
} |
640 |
|
|
} |
641 |
|
|
|
642 |
|
|
/* |
643 |
|
|
* This is the routine used to extract files for the 'x' and 'i' commands. |
644 |
|
|
* Efficiently extract a subset of the files on a tape. |
645 |
|
|
*/ |
646 |
|
|
void |
647 |
|
|
createfiles(void) |
648 |
|
|
{ |
649 |
|
|
ino_t first, next, last; |
650 |
|
|
struct entry *ep; |
651 |
|
|
long curvol; |
652 |
|
|
|
653 |
|
|
Vprintf(stdout, "Extract requested files\n"); |
654 |
|
|
curfile.action = SKIP; |
655 |
|
|
getvol((long)1); |
656 |
|
|
skipmaps(); |
657 |
|
|
skipdirs(); |
658 |
|
|
first = lowerbnd(ROOTINO); |
659 |
|
|
last = upperbnd(maxino - 1); |
660 |
|
|
for (;;) { |
661 |
|
|
first = lowerbnd(first); |
662 |
|
|
last = upperbnd(last); |
663 |
|
|
/* |
664 |
|
|
* Check to see if any files remain to be extracted |
665 |
|
|
*/ |
666 |
|
|
if (first > last) |
667 |
|
|
return; |
668 |
|
|
/* |
669 |
|
|
* Reject any volumes with inodes greater |
670 |
|
|
* than the last one needed |
671 |
|
|
*/ |
672 |
|
|
while (curfile.ino > last) { |
673 |
|
|
curfile.action = SKIP; |
674 |
|
|
getvol((long)0); |
675 |
|
|
skipmaps(); |
676 |
|
|
skipdirs(); |
677 |
|
|
} |
678 |
|
|
/* |
679 |
|
|
* Decide on the next inode needed. |
680 |
|
|
* Skip across the inodes until it is found |
681 |
|
|
* or an out of order volume change is encountered |
682 |
|
|
*/ |
683 |
|
|
next = lowerbnd(curfile.ino); |
684 |
|
|
do { |
685 |
|
|
curvol = volno; |
686 |
|
|
while (next > curfile.ino && volno == curvol) |
687 |
|
|
skipfile(); |
688 |
|
|
skipmaps(); |
689 |
|
|
skipdirs(); |
690 |
|
|
} while (volno == curvol + 1); |
691 |
|
|
/* |
692 |
|
|
* If volume change out of order occurred the |
693 |
|
|
* current state must be recalculated |
694 |
|
|
*/ |
695 |
|
|
if (volno != curvol) |
696 |
|
|
continue; |
697 |
|
|
/* |
698 |
|
|
* If the current inode is greater than the one we were |
699 |
|
|
* looking for then we missed the one we were looking for. |
700 |
|
|
* Since we only attempt to extract files listed in the |
701 |
|
|
* dump map, the lost files must have been due to a tape |
702 |
|
|
* read error, or a file that was removed while the dump |
703 |
|
|
* was in progress. Thus we report all requested files |
704 |
|
|
* between the one we were looking for, and the one we |
705 |
|
|
* found as missing, and delete their request flags. |
706 |
|
|
*/ |
707 |
|
|
while (next < curfile.ino) { |
708 |
|
|
ep = lookupino(next); |
709 |
|
|
if (ep == NULL) |
710 |
|
|
panic("corrupted symbol table\n"); |
711 |
|
|
fprintf(stderr, "%s: not found on tape\n", myname(ep)); |
712 |
|
|
ep->e_flags &= ~NEW; |
713 |
|
|
next = lowerbnd(next); |
714 |
|
|
} |
715 |
|
|
/* |
716 |
|
|
* The current inode is the one that we are looking for, |
717 |
|
|
* so extract it per its requested name. |
718 |
|
|
*/ |
719 |
|
|
if (next == curfile.ino && next <= last) { |
720 |
|
|
ep = lookupino(next); |
721 |
|
|
if (ep == NULL) |
722 |
|
|
panic("corrupted symbol table\n"); |
723 |
|
|
(void)extractfile(myname(ep)); |
724 |
|
|
ep->e_flags &= ~NEW; |
725 |
|
|
if (volno != curvol) |
726 |
|
|
skipmaps(); |
727 |
|
|
} |
728 |
|
|
} |
729 |
|
|
} |
730 |
|
|
|
731 |
|
|
/* |
732 |
|
|
* Add links. |
733 |
|
|
*/ |
734 |
|
|
void |
735 |
|
|
createlinks(void) |
736 |
|
|
{ |
737 |
|
|
struct entry *np, *ep; |
738 |
|
|
ino_t i; |
739 |
|
|
char name[BUFSIZ]; |
740 |
|
|
|
741 |
|
|
Vprintf(stdout, "Add links\n"); |
742 |
|
|
for (i = ROOTINO; i < maxino; i++) { |
743 |
|
|
ep = lookupino(i); |
744 |
|
|
if (ep == NULL) |
745 |
|
|
continue; |
746 |
|
|
for (np = ep->e_links; np != NULL; np = np->e_links) { |
747 |
|
|
if ((np->e_flags & NEW) == 0) |
748 |
|
|
continue; |
749 |
|
|
(void)strlcpy(name, myname(ep), sizeof name); |
750 |
|
|
if (ep->e_type == NODE) { |
751 |
|
|
(void)linkit(name, myname(np), SYMLINK); |
752 |
|
|
} else { |
753 |
|
|
(void)linkit(name, myname(np), HARDLINK); |
754 |
|
|
} |
755 |
|
|
np->e_flags &= ~NEW; |
756 |
|
|
} |
757 |
|
|
} |
758 |
|
|
} |
759 |
|
|
|
760 |
|
|
/* |
761 |
|
|
* Check the symbol table. |
762 |
|
|
* We do this to insure that all the requested work was done, and |
763 |
|
|
* that no temporary names remain. |
764 |
|
|
*/ |
765 |
|
|
void |
766 |
|
|
checkrestore(void) |
767 |
|
|
{ |
768 |
|
|
struct entry *ep; |
769 |
|
|
ino_t i; |
770 |
|
|
|
771 |
|
|
Vprintf(stdout, "Check the symbol table.\n"); |
772 |
|
|
for (i = ROOTINO; i < maxino; i++) { |
773 |
|
|
for (ep = lookupino(i); ep != NULL; ep = ep->e_links) { |
774 |
|
|
ep->e_flags &= ~KEEP; |
775 |
|
|
if (ep->e_type == NODE) |
776 |
|
|
ep->e_flags &= ~(NEW|EXISTED); |
777 |
|
|
if (ep->e_flags != 0) |
778 |
|
|
badentry(ep, "incomplete operations"); |
779 |
|
|
} |
780 |
|
|
} |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
/* |
784 |
|
|
* Compare with the directory structure on the tape |
785 |
|
|
* A paranoid check that things are as they should be. |
786 |
|
|
*/ |
787 |
|
|
long |
788 |
|
|
verifyfile(char *name, ino_t ino, int type) |
789 |
|
|
{ |
790 |
|
|
struct entry *np, *ep; |
791 |
|
|
long descend = GOOD; |
792 |
|
|
|
793 |
|
|
ep = lookupname(name); |
794 |
|
|
if (ep == NULL) { |
795 |
|
|
fprintf(stderr, "Warning: missing name %s\n", name); |
796 |
|
|
return (FAIL); |
797 |
|
|
} |
798 |
|
|
np = lookupino(ino); |
799 |
|
|
if (np != ep) |
800 |
|
|
descend = FAIL; |
801 |
|
|
for ( ; np != NULL; np = np->e_links) |
802 |
|
|
if (np == ep) |
803 |
|
|
break; |
804 |
|
|
if (np == NULL) |
805 |
|
|
panic("missing inumber %llu\n", (unsigned long long)ino); |
806 |
|
|
if (ep->e_type == LEAF && type != LEAF) |
807 |
|
|
badentry(ep, "type should be LEAF"); |
808 |
|
|
return (descend); |
809 |
|
|
} |