1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1980, 1988, 1991, 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
34 static char sccsid[] = "@(#)traverse.c 8.7 (Berkeley) 6/15/95";
35 #endif
36 #endif /* not lint */
37
38 #include <sys/param.h>
39 #include <sys/stat.h>
40
41 #include <ufs/ufs/dir.h>
42 #include <ufs/ufs/dinode.h>
43 #include <ufs/ffs/fs.h>
44
45 #include <protocols/dumprestore.h>
46
47 #include <assert.h>
48 #include <ctype.h>
49 #include <errno.h>
50 #include <inttypes.h>
51 #include <limits.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <timeconv.h>
56 #include <unistd.h>
57
58 #include "dump.h"
59
60 #define DIP(dp, field) \
61 ((sblock->fs_magic == FS_UFS1_MAGIC) ? \
62 (dp)->dp1.field : (dp)->dp2.field)
63 #define DIP_SET(dp, field, val) do {\
64 if (sblock->fs_magic == FS_UFS1_MAGIC) \
65 (dp)->dp1.field = (val); \
66 else \
67 (dp)->dp2.field = (val); \
68 } while (0)
69
70 #define HASDUMPEDFILE 0x1
71 #define HASSUBDIRS 0x2
72
73 static int dirindir(ino_t ino, ufs2_daddr_t blkno, int level, long *size,
74 long *tapesize, int nodump, ino_t maxino);
75 static void dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int level,
76 off_t *size);
77 static void ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino);
78 static void ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags,
79 ino_t ino, int last);
80 static int appendextdata(union dinode *dp);
81 static void writeextdata(union dinode *dp, ino_t ino, int added);
82 static int searchdir(ino_t ino, ufs2_daddr_t blkno, long size, long filesize,
83 long *tapesize, int nodump, ino_t maxino);
84 static long blockest(union dinode *dp);
85
86 /*
87 * This is an estimation of the number of TP_BSIZE blocks in the file.
88 * It estimates the number of blocks in files with holes by assuming
89 * that all of the blocks accounted for by di_blocks are data blocks
90 * (when some of the blocks are usually used for indirect pointers);
91 * hence the estimate may be high.
92 */
93 static long
blockest(union dinode * dp)94 blockest(union dinode *dp)
95 {
96 long blkest, sizeest;
97
98 /*
99 * dp->di_size is the size of the file in bytes.
100 * dp->di_blocks stores the number of sectors actually in the file.
101 * If there are more sectors than the size would indicate, this just
102 * means that there are indirect blocks in the file or unused
103 * sectors in the last file block; we can safely ignore these
104 * (blkest = sizeest below).
105 * If the file is bigger than the number of sectors would indicate,
106 * then the file has holes in it. In this case we must use the
107 * block count to estimate the number of data blocks used, but
108 * we use the actual size for estimating the number of indirect
109 * dump blocks (sizeest vs. blkest in the indirect block
110 * calculation).
111 */
112 if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0)
113 return (1);
114 blkest = howmany(dbtob(DIP(dp, di_blocks)), TP_BSIZE);
115 sizeest = howmany(DIP(dp, di_size), TP_BSIZE);
116 if (blkest > sizeest)
117 blkest = sizeest;
118 if (DIP(dp, di_size) > sblock->fs_bsize * UFS_NDADDR) {
119 /* calculate the number of indirect blocks on the dump tape */
120 blkest += howmany(sizeest -
121 UFS_NDADDR * sblock->fs_bsize / TP_BSIZE, TP_NINDIR);
122 }
123 return (blkest + 1);
124 }
125
126 /* Auxiliary macro to pick up files changed since previous dump. */
127 #define CHANGEDSINCE(dp, t) \
128 (DIP(dp, di_mtime) >= (t) || DIP(dp, di_ctime) >= (t))
129
130 /* The WANTTODUMP macro decides whether a file should be dumped. */
131 #ifdef UF_NODUMP
132 #define WANTTODUMP(dp) \
133 (CHANGEDSINCE(dp, spcl.c_ddate) && \
134 (nonodump || (DIP(dp, di_flags) & UF_NODUMP) != UF_NODUMP))
135 #else
136 #define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
137 #endif
138
139 /*
140 * Dump pass 1.
141 *
142 * Walk the inode list for a file system to find all allocated inodes
143 * that have been modified since the previous dump time. Also, find all
144 * the directories in the file system.
145 */
146 int
mapfiles(ino_t maxino,long * tapesize)147 mapfiles(ino_t maxino, long *tapesize)
148 {
149 int i, cg, mode, inosused;
150 int anydirskipped = 0;
151 union dinode *dp;
152 struct cg *cgp;
153 ino_t ino;
154 u_char *cp;
155
156 if ((cgp = malloc(sblock->fs_cgsize)) == NULL)
157 quit("mapfiles: cannot allocate memory.\n");
158 for (cg = 0; cg < sblock->fs_ncg; cg++) {
159 ino = cg * sblock->fs_ipg;
160 blkread(fsbtodb(sblock, cgtod(sblock, cg)), (char *)cgp,
161 sblock->fs_cgsize);
162 if (sblock->fs_magic == FS_UFS2_MAGIC)
163 inosused = cgp->cg_initediblk;
164 else
165 inosused = sblock->fs_ipg;
166 /*
167 * If we are using soft updates, then we can trust the
168 * cylinder group inode allocation maps to tell us which
169 * inodes are allocated. We will scan the used inode map
170 * to find the inodes that are really in use, and then
171 * read only those inodes in from disk.
172 */
173 if (sblock->fs_flags & FS_DOSOFTDEP) {
174 if (!cg_chkmagic(cgp))
175 quit("mapfiles: cg %d: bad magic number\n", cg);
176 cp = &cg_inosused(cgp)[(inosused - 1) / CHAR_BIT];
177 for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) {
178 if (*cp == 0)
179 continue;
180 for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) {
181 if (*cp & i)
182 break;
183 inosused--;
184 }
185 break;
186 }
187 if (inosused <= 0)
188 continue;
189 }
190 for (i = 0; i < inosused; i++, ino++) {
191 if (ino < UFS_ROOTINO ||
192 (dp = getino(ino, &mode)) == NULL ||
193 (mode & IFMT) == 0)
194 continue;
195 if (ino >= maxino) {
196 msg("Skipping inode %ju >= maxino %ju\n",
197 (uintmax_t)ino, (uintmax_t)maxino);
198 continue;
199 }
200 /*
201 * Everything must go in usedinomap so that a check
202 * for "in dumpdirmap but not in usedinomap" to detect
203 * dirs with nodump set has a chance of succeeding
204 * (this is used in mapdirs()).
205 */
206 SETINO(ino, usedinomap);
207 if (mode == IFDIR)
208 SETINO(ino, dumpdirmap);
209 if (WANTTODUMP(dp)) {
210 SETINO(ino, dumpinomap);
211 if (mode != IFREG &&
212 mode != IFDIR &&
213 mode != IFLNK)
214 *tapesize += 1;
215 else
216 *tapesize += blockest(dp);
217 continue;
218 }
219 if (mode == IFDIR) {
220 if (!nonodump &&
221 (DIP(dp, di_flags) & UF_NODUMP))
222 CLRINO(ino, usedinomap);
223 anydirskipped = 1;
224 }
225 }
226 }
227 /*
228 * Restore gets very upset if the root is not dumped,
229 * so ensure that it always is dumped.
230 */
231 SETINO(UFS_ROOTINO, dumpinomap);
232 return (anydirskipped);
233 }
234
235 /*
236 * Dump pass 2.
237 *
238 * Scan each directory on the file system to see if it has any modified
239 * files in it. If it does, and has not already been added to the dump
240 * list (because it was itself modified), then add it. If a directory
241 * has not been modified itself, contains no modified files and has no
242 * subdirectories, then it can be deleted from the dump list and from
243 * the list of directories. By deleting it from the list of directories,
244 * its parent may now qualify for the same treatment on this or a later
245 * pass using this algorithm.
246 */
247 int
mapdirs(ino_t maxino,long * tapesize)248 mapdirs(ino_t maxino, long *tapesize)
249 {
250 union dinode *dp;
251 int i, isdir, nodump;
252 char *map;
253 ino_t ino;
254 union dinode di;
255 long filesize;
256 int ret, change = 0;
257
258 isdir = 0; /* XXX just to get gcc to shut up */
259 for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
260 if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */
261 isdir = *map++;
262 else
263 isdir >>= 1;
264 /*
265 * If a directory has been removed from usedinomap, it
266 * either has the nodump flag set, or has inherited
267 * it. Although a directory can't be in dumpinomap if
268 * it isn't in usedinomap, we have to go through it to
269 * propagate the nodump flag.
270 */
271 nodump = !nonodump && (TSTINO(ino, usedinomap) == 0);
272 if ((isdir & 1) == 0 || (TSTINO(ino, dumpinomap) && !nodump))
273 continue;
274 dp = getino(ino, &i);
275 /*
276 * inode buf may change in searchdir().
277 */
278 if (sblock->fs_magic == FS_UFS1_MAGIC)
279 di.dp1 = dp->dp1;
280 else
281 di.dp2 = dp->dp2;
282 filesize = DIP(&di, di_size);
283 for (ret = 0, i = 0; filesize > 0 && i < UFS_NDADDR; i++) {
284 if (DIP(&di, di_db[i]) != 0)
285 ret |= searchdir(ino, DIP(&di, di_db[i]),
286 (long)sblksize(sblock, DIP(&di, di_size),
287 i), filesize, tapesize, nodump, maxino);
288 if (ret & HASDUMPEDFILE)
289 filesize = 0;
290 else
291 filesize -= sblock->fs_bsize;
292 }
293 for (i = 0; filesize > 0 && i < UFS_NIADDR; i++) {
294 if (DIP(&di, di_ib[i]) == 0)
295 continue;
296 ret |= dirindir(ino, DIP(&di, di_ib[i]), i, &filesize,
297 tapesize, nodump, maxino);
298 }
299 if (ret & HASDUMPEDFILE) {
300 SETINO(ino, dumpinomap);
301 *tapesize += blockest(&di);
302 change = 1;
303 continue;
304 }
305 if (nodump) {
306 if (ret & HASSUBDIRS)
307 change = 1; /* subdirs inherit nodump */
308 CLRINO(ino, dumpdirmap);
309 } else if ((ret & HASSUBDIRS) == 0)
310 if (!TSTINO(ino, dumpinomap)) {
311 CLRINO(ino, dumpdirmap);
312 change = 1;
313 }
314 }
315 return (change);
316 }
317
318 /*
319 * Read indirect blocks, and pass the data blocks to be searched
320 * as directories. Quit as soon as any entry is found that will
321 * require the directory to be dumped.
322 */
323 static int
dirindir(ino_t ino,ufs2_daddr_t blkno,int ind_level,long * filesize,long * tapesize,int nodump,ino_t maxino)324 dirindir(
325 ino_t ino,
326 ufs2_daddr_t blkno,
327 int ind_level,
328 long *filesize,
329 long *tapesize,
330 int nodump,
331 ino_t maxino)
332 {
333 union {
334 ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
335 ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
336 } idblk;
337 int ret = 0;
338 int i;
339
340 blkread(fsbtodb(sblock, blkno), (char *)&idblk, (int)sblock->fs_bsize);
341 if (ind_level <= 0) {
342 for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
343 if (sblock->fs_magic == FS_UFS1_MAGIC)
344 blkno = idblk.ufs1[i];
345 else
346 blkno = idblk.ufs2[i];
347 if (blkno != 0)
348 ret |= searchdir(ino, blkno, sblock->fs_bsize,
349 *filesize, tapesize, nodump, maxino);
350 if (ret & HASDUMPEDFILE)
351 *filesize = 0;
352 else
353 *filesize -= sblock->fs_bsize;
354 }
355 return (ret);
356 }
357 ind_level--;
358 for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
359 if (sblock->fs_magic == FS_UFS1_MAGIC)
360 blkno = idblk.ufs1[i];
361 else
362 blkno = idblk.ufs2[i];
363 if (blkno != 0)
364 ret |= dirindir(ino, blkno, ind_level, filesize,
365 tapesize, nodump, maxino);
366 }
367 return (ret);
368 }
369
370 /*
371 * Scan a disk block containing directory information looking to see if
372 * any of the entries are on the dump list and to see if the directory
373 * contains any subdirectories.
374 */
375 static int
searchdir(ino_t ino,ufs2_daddr_t blkno,long size,long filesize,long * tapesize,int nodump,ino_t maxino)376 searchdir(
377 ino_t ino,
378 ufs2_daddr_t blkno,
379 long size,
380 long filesize,
381 long *tapesize,
382 int nodump,
383 ino_t maxino)
384 {
385 int mode;
386 struct direct *dp;
387 union dinode *ip;
388 long loc, ret = 0;
389 static caddr_t dblk;
390
391 if (dblk == NULL && (dblk = malloc(sblock->fs_bsize)) == NULL)
392 quit("searchdir: cannot allocate indirect memory.\n");
393 blkread(fsbtodb(sblock, blkno), dblk, (int)size);
394 if (filesize < size)
395 size = filesize;
396 for (loc = 0; loc < size; ) {
397 dp = (struct direct *)(dblk + loc);
398 if (dp->d_reclen == 0) {
399 msg("corrupted directory, inumber %ju\n",
400 (uintmax_t)ino);
401 break;
402 }
403 loc += dp->d_reclen;
404 if (dp->d_ino == 0)
405 continue;
406 if (dp->d_ino >= maxino) {
407 msg("corrupted directory entry, d_ino %ju >= %ju\n",
408 (uintmax_t)dp->d_ino, (uintmax_t)maxino);
409 break;
410 }
411 if (dp->d_name[0] == '.') {
412 if (dp->d_name[1] == '\0')
413 continue;
414 if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
415 continue;
416 }
417 if (nodump) {
418 ip = getino(dp->d_ino, &mode);
419 if (TSTINO(dp->d_ino, dumpinomap)) {
420 CLRINO(dp->d_ino, dumpinomap);
421 *tapesize -= blockest(ip);
422 }
423 /*
424 * Add back to dumpdirmap and remove from usedinomap
425 * to propagate nodump.
426 */
427 if (mode == IFDIR) {
428 SETINO(dp->d_ino, dumpdirmap);
429 CLRINO(dp->d_ino, usedinomap);
430 ret |= HASSUBDIRS;
431 }
432 } else {
433 if (TSTINO(dp->d_ino, dumpinomap)) {
434 ret |= HASDUMPEDFILE;
435 if (ret & HASSUBDIRS)
436 break;
437 }
438 if (TSTINO(dp->d_ino, dumpdirmap)) {
439 ret |= HASSUBDIRS;
440 if (ret & HASDUMPEDFILE)
441 break;
442 }
443 }
444 }
445 return (ret);
446 }
447
448 /*
449 * Dump passes 3 and 4.
450 *
451 * Dump the contents of an inode to tape.
452 */
453 void
dumpino(union dinode * dp,ino_t ino)454 dumpino(union dinode *dp, ino_t ino)
455 {
456 int ind_level, cnt, last, added;
457 off_t size;
458 char buf[TP_BSIZE];
459
460 if (newtape) {
461 newtape = 0;
462 dumpmap(dumpinomap, TS_BITS, ino);
463 }
464 CLRINO(ino, dumpinomap);
465 /*
466 * Zero out the size of a snapshot so that it will be dumped
467 * as a zero length file.
468 */
469 if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) {
470 DIP_SET(dp, di_size, 0);
471 DIP_SET(dp, di_flags, DIP(dp, di_flags) & ~SF_SNAPSHOT);
472 }
473 if (sblock->fs_magic == FS_UFS1_MAGIC) {
474 spcl.c_mode = dp->dp1.di_mode;
475 spcl.c_size = dp->dp1.di_size;
476 spcl.c_extsize = 0;
477 spcl.c_atime = _time32_to_time(dp->dp1.di_atime);
478 spcl.c_atimensec = dp->dp1.di_atimensec;
479 spcl.c_mtime = _time32_to_time(dp->dp1.di_mtime);
480 spcl.c_mtimensec = dp->dp1.di_mtimensec;
481 spcl.c_birthtime = 0;
482 spcl.c_birthtimensec = 0;
483 spcl.c_rdev = dp->dp1.di_rdev;
484 spcl.c_file_flags = dp->dp1.di_flags;
485 spcl.c_uid = dp->dp1.di_uid;
486 spcl.c_gid = dp->dp1.di_gid;
487 } else {
488 spcl.c_mode = dp->dp2.di_mode;
489 spcl.c_size = dp->dp2.di_size;
490 spcl.c_extsize = dp->dp2.di_extsize;
491 spcl.c_atime = _time64_to_time(dp->dp2.di_atime);
492 spcl.c_atimensec = dp->dp2.di_atimensec;
493 spcl.c_mtime = _time64_to_time(dp->dp2.di_mtime);
494 spcl.c_mtimensec = dp->dp2.di_mtimensec;
495 spcl.c_birthtime = _time64_to_time(dp->dp2.di_birthtime);
496 spcl.c_birthtimensec = dp->dp2.di_birthnsec;
497 spcl.c_rdev = dp->dp2.di_rdev;
498 spcl.c_file_flags = dp->dp2.di_flags;
499 spcl.c_uid = dp->dp2.di_uid;
500 spcl.c_gid = dp->dp2.di_gid;
501 }
502 spcl.c_type = TS_INODE;
503 spcl.c_count = 0;
504 switch (DIP(dp, di_mode) & S_IFMT) {
505
506 case 0:
507 /*
508 * Freed inode.
509 */
510 return;
511
512 case S_IFLNK:
513 /*
514 * Check for short symbolic link.
515 */
516 if (DIP(dp, di_size) > 0 &&
517 DIP(dp, di_size) < sblock->fs_maxsymlinklen) {
518 spcl.c_addr[0] = 1;
519 spcl.c_count = 1;
520 added = appendextdata(dp);
521 writeheader(ino);
522 memmove(buf, DIP(dp, di_shortlink),
523 (u_long)DIP(dp, di_size));
524 buf[DIP(dp, di_size)] = '\0';
525 writerec(buf, 0);
526 writeextdata(dp, ino, added);
527 return;
528 }
529 /* FALLTHROUGH */
530
531 case S_IFDIR:
532 case S_IFREG:
533 if (DIP(dp, di_size) > 0)
534 break;
535 /* FALLTHROUGH */
536
537 case S_IFIFO:
538 case S_IFSOCK:
539 case S_IFCHR:
540 case S_IFBLK:
541 added = appendextdata(dp);
542 writeheader(ino);
543 writeextdata(dp, ino, added);
544 return;
545
546 default:
547 msg("Warning: undefined file type 0%o\n",
548 DIP(dp, di_mode) & IFMT);
549 return;
550 }
551 if (DIP(dp, di_size) > UFS_NDADDR * sblock->fs_bsize) {
552 cnt = UFS_NDADDR * sblock->fs_frag;
553 last = 0;
554 } else {
555 cnt = howmany(DIP(dp, di_size), sblock->fs_fsize);
556 last = 1;
557 }
558 if (sblock->fs_magic == FS_UFS1_MAGIC)
559 ufs1_blksout(&dp->dp1.di_db[0], cnt, ino);
560 else
561 ufs2_blksout(dp, &dp->dp2.di_db[0], cnt, ino, last);
562 if ((size = DIP(dp, di_size) - UFS_NDADDR * sblock->fs_bsize) <= 0)
563 return;
564 for (ind_level = 0; ind_level < UFS_NIADDR; ind_level++) {
565 dmpindir(dp, ino, DIP(dp, di_ib[ind_level]), ind_level, &size);
566 if (size <= 0)
567 return;
568 }
569 }
570
571 /*
572 * Read indirect blocks, and pass the data blocks to be dumped.
573 */
574 static void
dmpindir(union dinode * dp,ino_t ino,ufs2_daddr_t blk,int ind_level,off_t * size)575 dmpindir(union dinode *dp, ino_t ino, ufs2_daddr_t blk, int ind_level,
576 off_t *size)
577 {
578 union {
579 ufs1_daddr_t ufs1[MAXBSIZE / sizeof(ufs1_daddr_t)];
580 ufs2_daddr_t ufs2[MAXBSIZE / sizeof(ufs2_daddr_t)];
581 } idblk;
582 int i, cnt, last;
583
584 if (blk != 0)
585 blkread(fsbtodb(sblock, blk), (char *)&idblk,
586 (int)sblock->fs_bsize);
587 else
588 memset(&idblk, 0, sblock->fs_bsize);
589 if (ind_level <= 0) {
590 if (*size > NINDIR(sblock) * sblock->fs_bsize) {
591 cnt = NINDIR(sblock) * sblock->fs_frag;
592 last = 0;
593 } else {
594 cnt = howmany(*size, sblock->fs_fsize);
595 last = 1;
596 }
597 *size -= NINDIR(sblock) * sblock->fs_bsize;
598 if (sblock->fs_magic == FS_UFS1_MAGIC)
599 ufs1_blksout(idblk.ufs1, cnt, ino);
600 else
601 ufs2_blksout(dp, idblk.ufs2, cnt, ino, last);
602 return;
603 }
604 ind_level--;
605 for (i = 0; i < NINDIR(sblock); i++) {
606 if (sblock->fs_magic == FS_UFS1_MAGIC)
607 dmpindir(dp, ino, idblk.ufs1[i], ind_level, size);
608 else
609 dmpindir(dp, ino, idblk.ufs2[i], ind_level, size);
610 if (*size <= 0)
611 return;
612 }
613 }
614
615 /*
616 * Collect up the data into tape record sized buffers and output them.
617 */
618 static void
ufs1_blksout(ufs1_daddr_t * blkp,int frags,ino_t ino)619 ufs1_blksout(ufs1_daddr_t *blkp, int frags, ino_t ino)
620 {
621 ufs1_daddr_t *bp;
622 int i, j, count, blks, tbperdb;
623
624 blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
625 tbperdb = sblock->fs_bsize >> tp_bshift;
626 for (i = 0; i < blks; i += TP_NINDIR) {
627 if (i + TP_NINDIR > blks)
628 count = blks;
629 else
630 count = i + TP_NINDIR;
631 assert(count <= TP_NINDIR + i);
632 for (j = i; j < count; j++)
633 if (blkp[j / tbperdb] != 0)
634 spcl.c_addr[j - i] = 1;
635 else
636 spcl.c_addr[j - i] = 0;
637 spcl.c_count = count - i;
638 writeheader(ino);
639 bp = &blkp[i / tbperdb];
640 for (j = i; j < count; j += tbperdb, bp++)
641 if (*bp != 0) {
642 if (j + tbperdb <= count)
643 dumpblock(*bp, (int)sblock->fs_bsize);
644 else
645 dumpblock(*bp, (count - j) * TP_BSIZE);
646 }
647 spcl.c_type = TS_ADDR;
648 }
649 }
650
651 /*
652 * Collect up the data into tape record sized buffers and output them.
653 */
654 static void
ufs2_blksout(union dinode * dp,ufs2_daddr_t * blkp,int frags,ino_t ino,int last)655 ufs2_blksout(union dinode *dp, ufs2_daddr_t *blkp, int frags, ino_t ino,
656 int last)
657 {
658 ufs2_daddr_t *bp;
659 int i, j, count, resid, blks, tbperdb, added;
660 static int writingextdata = 0;
661
662 /*
663 * Calculate the number of TP_BSIZE blocks to be dumped.
664 * For filesystems with a fragment size bigger than TP_BSIZE,
665 * only part of the final fragment may need to be dumped.
666 */
667 blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
668 if (last) {
669 if (writingextdata)
670 resid = howmany(fragoff(sblock, spcl.c_extsize),
671 TP_BSIZE);
672 else
673 resid = howmany(fragoff(sblock, dp->dp2.di_size),
674 TP_BSIZE);
675 if (resid > 0)
676 blks -= howmany(sblock->fs_fsize, TP_BSIZE) - resid;
677 }
678 tbperdb = sblock->fs_bsize >> tp_bshift;
679 for (i = 0; i < blks; i += TP_NINDIR) {
680 if (i + TP_NINDIR > blks)
681 count = blks;
682 else
683 count = i + TP_NINDIR;
684 assert(count <= TP_NINDIR + i);
685 for (j = i; j < count; j++)
686 if (blkp[j / tbperdb] != 0)
687 spcl.c_addr[j - i] = 1;
688 else
689 spcl.c_addr[j - i] = 0;
690 spcl.c_count = count - i;
691 if (last && count == blks && !writingextdata)
692 added = appendextdata(dp);
693 writeheader(ino);
694 bp = &blkp[i / tbperdb];
695 for (j = i; j < count; j += tbperdb, bp++)
696 if (*bp != 0) {
697 if (j + tbperdb <= count)
698 dumpblock(*bp, (int)sblock->fs_bsize);
699 else
700 dumpblock(*bp, (count - j) * TP_BSIZE);
701 }
702 spcl.c_type = TS_ADDR;
703 spcl.c_count = 0;
704 if (last && count == blks && !writingextdata) {
705 writingextdata = 1;
706 writeextdata(dp, ino, added);
707 writingextdata = 0;
708 }
709 }
710 }
711
712 /*
713 * If there is room in the current block for the extended attributes
714 * as well as the file data, update the header to reflect the added
715 * attribute data at the end. Attributes are placed at the end so that
716 * old versions of restore will correctly restore the file and simply
717 * discard the extra data at the end that it does not understand.
718 * The attribute data is dumped following the file data by the
719 * writeextdata() function (below).
720 */
721 static int
appendextdata(union dinode * dp)722 appendextdata(union dinode *dp)
723 {
724 int i, blks, tbperdb;
725
726 /*
727 * If no extended attributes, there is nothing to do.
728 */
729 if (spcl.c_extsize == 0)
730 return (0);
731 /*
732 * If there is not enough room at the end of this block
733 * to add the extended attributes, then rather than putting
734 * part of them here, we simply push them entirely into a
735 * new block rather than putting some here and some later.
736 */
737 if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize)
738 blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE);
739 else
740 blks = howmany(spcl.c_extsize, TP_BSIZE);
741 if (spcl.c_count + blks > TP_NINDIR)
742 return (0);
743 /*
744 * Update the block map in the header to indicate the added
745 * extended attribute. They will be appended after the file
746 * data by the writeextdata() routine.
747 */
748 tbperdb = sblock->fs_bsize >> tp_bshift;
749 assert(spcl.c_count + blks <= TP_NINDIR);
750 for (i = 0; i < blks; i++)
751 if (&dp->dp2.di_extb[i / tbperdb] != 0)
752 spcl.c_addr[spcl.c_count + i] = 1;
753 else
754 spcl.c_addr[spcl.c_count + i] = 0;
755 spcl.c_count += blks;
756 return (blks);
757 }
758
759 /*
760 * Dump the extended attribute data. If there was room in the file
761 * header, then all we need to do is output the data blocks. If there
762 * was not room in the file header, then an additional TS_ADDR header
763 * is created to hold the attribute data.
764 */
765 static void
writeextdata(union dinode * dp,ino_t ino,int added)766 writeextdata(union dinode *dp, ino_t ino, int added)
767 {
768 int i, frags, blks, tbperdb, last;
769 ufs2_daddr_t *bp;
770 off_t size;
771
772 /*
773 * If no extended attributes, there is nothing to do.
774 */
775 if (spcl.c_extsize == 0)
776 return;
777 /*
778 * If there was no room in the file block for the attributes,
779 * dump them out in a new block, otherwise just dump the data.
780 */
781 if (added == 0) {
782 if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize) {
783 frags = UFS_NXADDR * sblock->fs_frag;
784 last = 0;
785 } else {
786 frags = howmany(spcl.c_extsize, sblock->fs_fsize);
787 last = 1;
788 }
789 ufs2_blksout(dp, &dp->dp2.di_extb[0], frags, ino, last);
790 } else {
791 if (spcl.c_extsize > UFS_NXADDR * sblock->fs_bsize)
792 blks = howmany(UFS_NXADDR * sblock->fs_bsize, TP_BSIZE);
793 else
794 blks = howmany(spcl.c_extsize, TP_BSIZE);
795 tbperdb = sblock->fs_bsize >> tp_bshift;
796 for (i = 0; i < blks; i += tbperdb) {
797 bp = &dp->dp2.di_extb[i / tbperdb];
798 if (*bp != 0) {
799 if (i + tbperdb <= blks)
800 dumpblock(*bp, (int)sblock->fs_bsize);
801 else
802 dumpblock(*bp, (blks - i) * TP_BSIZE);
803 }
804 }
805
806 }
807 /*
808 * If an indirect block is added for extended attributes, then
809 * di_exti below should be changed to the structure element
810 * that references the extended attribute indirect block. This
811 * definition is here only to make it compile without complaint.
812 */
813 #define di_exti di_spare[0]
814 /*
815 * If the extended attributes fall into an indirect block,
816 * dump it as well.
817 */
818 if ((size = spcl.c_extsize - UFS_NXADDR * sblock->fs_bsize) > 0)
819 dmpindir(dp, ino, dp->dp2.di_exti, 0, &size);
820 }
821
822 /*
823 * Dump a map to the tape.
824 */
825 void
dumpmap(char * map,int type,ino_t ino)826 dumpmap(char *map, int type, ino_t ino)
827 {
828 int i;
829 char *cp;
830
831 spcl.c_type = type;
832 spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
833 writeheader(ino);
834 for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
835 writerec(cp, 0);
836 }
837
838 /*
839 * Write a header record to the dump tape.
840 */
841 void
writeheader(ino_t ino)842 writeheader(ino_t ino)
843 {
844 int32_t sum, cnt, *lp;
845
846 if (rsync_friendly >= 2) {
847 /* don't track changes to access time */
848 spcl.c_atime = spcl.c_mtime;
849 spcl.c_atimensec = spcl.c_mtimensec;
850 }
851 spcl.c_inumber = ino;
852 spcl.c_magic = FS_UFS2_MAGIC;
853 spcl.c_checksum = 0;
854 lp = (int32_t *)&spcl;
855 sum = 0;
856 cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
857 while (--cnt >= 0) {
858 sum += *lp++;
859 sum += *lp++;
860 sum += *lp++;
861 sum += *lp++;
862 }
863 spcl.c_checksum = CHECKSUM - sum;
864 writerec((char *)&spcl, 1);
865 }
866
867 union dinode *
getino(ino_t inum,int * modep)868 getino(ino_t inum, int *modep)
869 {
870 static ino_t minino, maxino;
871 static caddr_t inoblock;
872 struct ufs1_dinode *dp1;
873 struct ufs2_dinode *dp2;
874
875 if (inoblock == NULL && (inoblock = malloc(sblock->fs_bsize)) == NULL)
876 quit("cannot allocate inode memory.\n");
877 curino = inum;
878 if (inum >= minino && inum < maxino)
879 goto gotit;
880 blkread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), inoblock,
881 (int)sblock->fs_bsize);
882 minino = inum - (inum % INOPB(sblock));
883 maxino = minino + INOPB(sblock);
884 gotit:
885 if (sblock->fs_magic == FS_UFS1_MAGIC) {
886 dp1 = &((struct ufs1_dinode *)inoblock)[inum - minino];
887 *modep = (dp1->di_mode & IFMT);
888 return ((union dinode *)dp1);
889 }
890 dp2 = &((struct ufs2_dinode *)inoblock)[inum - minino];
891 *modep = (dp2->di_mode & IFMT);
892 return ((union dinode *)dp2);
893 }
894
895 /*
896 * Read a chunk of data from the disk.
897 * Try to recover from hard errors by reading in sector sized pieces.
898 * Error recovery is attempted at most BREADEMAX times before seeking
899 * consent from the operator to continue.
900 */
901 int breaderrors = 0;
902 #define BREADEMAX 32
903
904 void
blkread(ufs2_daddr_t blkno,char * buf,int size)905 blkread(ufs2_daddr_t blkno, char *buf, int size)
906 {
907 int secsize, bytes, resid, xfer, base, cnt, i;
908 static char *tmpbuf;
909 off_t offset;
910
911 loop:
912 offset = blkno << dev_bshift;
913 secsize = sblock->fs_fsize;
914 base = offset % secsize;
915 resid = size % secsize;
916 /*
917 * If the transfer request starts or ends on a non-sector
918 * boundary, we must read the entire sector and copy out
919 * just the part that we need.
920 */
921 if (base == 0 && resid == 0) {
922 cnt = cread(diskfd, buf, size, offset);
923 if (cnt == size)
924 return;
925 } else {
926 if (tmpbuf == NULL && (tmpbuf = malloc(secsize)) == NULL)
927 quit("buffer malloc failed\n");
928 xfer = 0;
929 bytes = size;
930 if (base != 0) {
931 cnt = cread(diskfd, tmpbuf, secsize, offset - base);
932 if (cnt != secsize)
933 goto bad;
934 xfer = MIN(secsize - base, size);
935 offset += xfer;
936 bytes -= xfer;
937 resid = bytes % secsize;
938 memcpy(buf, &tmpbuf[base], xfer);
939 }
940 if (bytes >= secsize) {
941 cnt = cread(diskfd, &buf[xfer], bytes - resid, offset);
942 if (cnt != bytes - resid)
943 goto bad;
944 xfer += cnt;
945 offset += cnt;
946 }
947 if (resid == 0)
948 return;
949 cnt = cread(diskfd, tmpbuf, secsize, offset);
950 if (cnt == secsize) {
951 memcpy(&buf[xfer], tmpbuf, resid);
952 return;
953 }
954 }
955 bad:
956 if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
957 /*
958 * Trying to read the final fragment.
959 *
960 * NB - dump only works in TP_BSIZE blocks, hence
961 * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
962 * It should be smarter about not actually trying to
963 * read more than it can get, but for the time being
964 * we punt and scale back the read only when it gets
965 * us into trouble. (mkm 9/25/83)
966 */
967 size -= dev_bsize;
968 goto loop;
969 }
970 if (cnt == -1)
971 msg("read error from %s: %s: [block %jd]: count=%d\n",
972 disk, strerror(errno), (intmax_t)blkno, size);
973 else
974 msg("short read error from %s: [block %jd]: count=%d, got=%d\n",
975 disk, (intmax_t)blkno, size, cnt);
976 if (++breaderrors > BREADEMAX) {
977 msg("More than %d block read errors from %s\n",
978 BREADEMAX, disk);
979 broadcast("DUMP IS AILING!\n");
980 msg("This is an unrecoverable error.\n");
981 if (!query("Do you want to attempt to continue?")){
982 dumpabort(0);
983 /*NOTREACHED*/
984 } else
985 breaderrors = 0;
986 }
987 /*
988 * Zero buffer, then try to read each sector of buffer separately,
989 * and bypass the cache.
990 */
991 memset(buf, 0, size);
992 for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
993 if ((cnt = pread(diskfd, buf, (int)dev_bsize,
994 ((off_t)blkno << dev_bshift))) == dev_bsize)
995 continue;
996 if (cnt == -1) {
997 msg("read error from %s: %s: [sector %jd]: count=%ld\n",
998 disk, strerror(errno), (intmax_t)blkno, dev_bsize);
999 continue;
1000 }
1001 msg("short read from %s: [sector %jd]: count=%ld, got=%d\n",
1002 disk, (intmax_t)blkno, dev_bsize, cnt);
1003 }
1004 }
1005