1/* $NetBSD: traverse.c,v 1.56 2023/08/07 23:26:40 mrg Exp $ */ 2 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#include <sys/cdefs.h> 33#ifndef lint 34#if 0 35static char sccsid[] = "@(#)traverse.c 8.7 (Berkeley) 6/15/95"; 36#else 37__RCSID("$NetBSD: traverse.c,v 1.56 2023/08/07 23:26:40 mrg Exp $"); 38#endif 39#endif /* not lint */ 40 41#include <sys/param.h> 42#include <sys/time.h> 43#include <sys/stat.h> 44#include <ufs/ufs/dir.h> 45#include <ufs/ffs/fs.h> 46#include <ufs/ffs/ffs_extern.h> 47 48#include <assert.h> 49#include <ctype.h> 50#include <errno.h> 51#include <fts.h> 52#include <stdio.h> 53#include <stdlib.h> 54#include <string.h> 55#include <unistd.h> 56 57#include "dump.h" 58 59#define HASDUMPEDFILE 0x1 60#define HASSUBDIRS 0x2 61 62static int appendextdata(union dinode *dp); 63static void writeextdata(union dinode *dp, ino_t ino, int added); 64static int dirindir(ino_t, daddr_t, int, off_t *, u_int64_t *, int); 65static void dmpindir(union dinode *dp, ino_t, daddr_t, int, off_t *); 66static int searchdir(ino_t, daddr_t, long, off_t, u_int64_t *, int); 67 68/* 69 * This is an estimation of the number of TP_BSIZE blocks in the file. 70 * It estimates the number of blocks in files with holes by assuming 71 * that all of the blocks accounted for by di_blocks are data blocks 72 * (when some of the blocks are usually used for indirect pointers); 73 * hence the estimate may be high. 74 */ 75int64_t 76blockest(union dinode *dp) 77{ 78 int64_t blkest, sizeest; 79 80 /* 81 * dp->di_size is the size of the file in bytes. 82 * dp->di_blocks stores the number of sectors actually in the file. 83 * If there are more sectors than the size would indicate, this just 84 * means that there are indirect blocks in the file or unused 85 * sectors in the last file block; we can safely ignore these 86 * (blkest = sizeest below). 87 * If the file is bigger than the number of sectors would indicate, 88 * then the file has holes in it. In this case we must use the 89 * block count to estimate the number of data blocks used, but 90 * we use the actual size for estimating the number of indirect 91 * dump blocks (sizeest vs. blkest in the indirect block 92 * calculation). 93 */ 94 if (DIP(dp, flags) & SF_SNAPSHOT) 95 return (1); 96 blkest = howmany(ufs_fragroundup(ufsib, 97 dbtob((u_int64_t)DIP(dp, blocks))), TP_BSIZE); 98 sizeest = howmany(ufs_fragroundup(ufsib, DIP(dp, size)), TP_BSIZE); 99 if (blkest > sizeest) 100 blkest = sizeest; 101 if (DIP(dp, size) > ufsib->ufs_bsize * UFS_NDADDR) { 102 /* calculate the number of indirect blocks on the dump tape */ 103 blkest += 104 howmany(sizeest - UFS_NDADDR * ufsib->ufs_bsize / TP_BSIZE, 105 TP_NINDIR); 106 } 107 return (blkest + 1); 108} 109 110/* Auxiliary macro to pick up files changed since previous dump. */ 111#define CHANGEDSINCE(dp, t) \ 112 (DIP((dp), mtime) >= (t) || DIP((dp), ctime) >= (t)) 113 114/* The WANTTODUMP macro decides whether a file should be dumped. */ 115#ifdef UF_NODUMP 116#define WANTTODUMP(dp) \ 117 (CHANGEDSINCE(dp, iswap64(spcl.c_ddate)) && \ 118 (nonodump || (DIP((dp), flags) & UF_NODUMP) != UF_NODUMP)) 119#else 120#define WANTTODUMP(dp) CHANGEDSINCE(dp, iswap64(spcl.c_ddate)) 121#endif 122 123/* 124 * Determine if given inode should be dumped 125 */ 126void 127mapfileino(ino_t ino, u_int64_t *tape_size, int *dirskipped) 128{ 129 int mode; 130 union dinode *dp; 131 132 /* 133 * Skip inode if we've already marked it for dumping 134 */ 135 if (TSTINO(ino, usedinomap)) 136 return; 137 dp = getino(ino); 138 if (dp == NULL || (mode = (DIP(dp, mode) & IFMT)) == 0) 139 return; 140 /* 141 * Skip WAPBL log file inodes. 142 */ 143 if (DIP(dp, flags) & SF_LOG) 144 return; 145 /* 146 * Put all dirs in dumpdirmap, inodes that are to be dumped in the 147 * used map. All inode but dirs who have the nodump attribute go 148 * to the usedinomap. 149 */ 150 SETINO(ino, usedinomap); 151 if (mode == IFDIR) 152 SETINO(ino, dumpdirmap); 153 if (WANTTODUMP(dp)) { 154 SETINO(ino, dumpinomap); 155 if (mode != IFREG && mode != IFDIR && mode != IFLNK) 156 *tape_size += 1; 157 else 158 *tape_size += blockest(dp); 159 return; 160 } 161 if (mode == IFDIR) { 162#ifdef UF_NODUMP 163 if (!nonodump && (DIP(dp, flags) & UF_NODUMP)) 164 CLRINO(ino, usedinomap); 165#endif 166 *dirskipped = 1; 167 } 168} 169 170/* 171 * Dump pass 1. 172 * 173 * Walk the inode list for a file system to find all allocated inodes 174 * that have been modified since the previous dump time. Also, find all 175 * the directories in the file system. 176 * disk may be NULL if dirv is NULL. 177 */ 178int 179mapfiles(ino_t maxino, u_int64_t *tape_size, char *diskname, char * const *dirv) 180{ 181 int anydirskipped = 0; 182 183 if (dirv != NULL) { 184 char curdir[MAXPATHLEN]; 185 FTS *dirh; 186 FTSENT *entry; 187 int d; 188 189 if (getcwd(curdir, sizeof(curdir)) == NULL) { 190 msg("Can't determine cwd: %s\n", strerror(errno)); 191 dumpabort(0); 192 } 193 if ((dirh = fts_open(dirv, FTS_PHYSICAL|FTS_SEEDOT|FTS_XDEV, 194 NULL)) == NULL) { 195 msg("fts_open failed: %s\n", strerror(errno)); 196 dumpabort(0); 197 } 198 while ((entry = fts_read(dirh)) != NULL) { 199 switch (entry->fts_info) { 200 case FTS_DNR: /* an error */ 201 case FTS_ERR: 202 case FTS_NS: 203 msg("Can't fts_read %s: %s\n", entry->fts_path, 204 strerror(errno)); 205 /* FALLTHROUGH */ 206 case FTS_DP: /* already seen dir */ 207 continue; 208 } 209 mapfileino(entry->fts_statp->st_ino, tape_size, 210 &anydirskipped); 211 } 212 (void)fts_close(dirh); 213 214 /* 215 * Add any parent directories 216 */ 217 for (d = 0 ; dirv[d] != NULL ; d++) { 218 char path[MAXPATHLEN]; 219 220 if (dirv[d][0] != '/') 221 (void)snprintf(path, sizeof(path), "%s/%s", 222 curdir, dirv[d]); 223 else 224 (void)snprintf(path, sizeof(path), "%s", 225 dirv[d]); 226 while (strcmp(path, diskname) != 0) { 227 char *p; 228 struct stat sb; 229 230 if (*path == '\0') 231 break; 232 if ((p = strrchr(path, '/')) == NULL) 233 break; 234 if (p == path) 235 break; 236 *p = '\0'; 237 if (stat(path, &sb) == -1) { 238 msg("Can't stat %s: %s\n", path, 239 strerror(errno)); 240 break; 241 } 242 mapfileino(sb.st_ino, tape_size, &anydirskipped); 243 } 244 } 245 246 /* 247 * Ensure that the root inode actually appears in the 248 * file list for a subdir 249 */ 250 mapfileino(UFS_ROOTINO, tape_size, &anydirskipped); 251 } else { 252 fs_mapinodes(maxino, tape_size, &anydirskipped); 253 } 254 /* 255 * Restore gets very upset if the root is not dumped, 256 * so ensure that it always is dumped. 257 */ 258 SETINO(UFS_ROOTINO, dumpinomap); 259 return (anydirskipped); 260} 261 262/* 263 * Dump pass 2. 264 * 265 * Scan each directory on the file system to see if it has any modified 266 * files in it. If it does, and has not already been added to the dump 267 * list (because it was itself modified), then add it. If a directory 268 * has not been modified itself, contains no modified files and has no 269 * subdirectories, then it can be deleted from the dump list and from 270 * the list of directories. By deleting it from the list of directories, 271 * its parent may now qualify for the same treatment on this or a later 272 * pass using this algorithm. 273 */ 274int 275mapdirs(ino_t maxino, u_int64_t *tape_size) 276{ 277 union dinode *dp, di; 278 int i, isdir, nodump; 279 char *map; 280 ino_t ino; 281 off_t filesize; 282 int ret, change = 0; 283 daddr_t blk; 284 285 isdir = 0; /* XXX just to get gcc to shut up */ 286 for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { 287 if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ 288 isdir = *map++; 289 else 290 isdir >>= 1; 291 /* 292 * If dir has been removed from the used map, it's either 293 * because it had the nodump flag, or it inherited it from 294 * its parent. A directory can't be in dumpinomap if 295 * not in usedinomap, but we have to go throuh it anyway 296 * to propagate the nodump attribute. 297 */ 298 nodump = (TSTINO(ino, usedinomap) == 0); 299 if ((isdir & 1) == 0 || 300 (TSTINO(ino, dumpinomap) && nodump == 0)) 301 continue; 302 303 dp = getino(ino); 304 /* 305 * inode buf may be changed in searchdir(). 306 */ 307 if (is_ufs2) 308 di.dp2 = dp->dp2; 309 else 310 di.dp1 = dp->dp1; 311 filesize = DIP(&di, size); 312 for (ret = 0, i = 0; filesize > 0 && i < UFS_NDADDR; i++) { 313 if (is_ufs2) 314 blk = iswap64(di.dp2.di_db[i]); 315 else 316 blk = iswap32(di.dp1.di_db[i]); 317 if (blk != 0) 318 ret |= searchdir(ino, blk, 319 (long)ufs_dblksize(ufsib, &di, i), 320 filesize, tape_size, nodump); 321 if (ret & HASDUMPEDFILE) 322 filesize = 0; 323 else 324 filesize -= ufsib->ufs_bsize; 325 } 326 for (i = 0; filesize > 0 && i < UFS_NIADDR; i++) { 327 if (is_ufs2) 328 blk = iswap64(di.dp2.di_ib[i]); 329 else 330 blk = iswap32(di.dp1.di_ib[i]); 331 if (blk == 0) 332 continue; 333 ret |= dirindir(ino, blk, i, &filesize, 334 tape_size, nodump); 335 } 336 if (ret & HASDUMPEDFILE) { 337 SETINO(ino, dumpinomap); 338 *tape_size += blockest(&di); 339 change = 1; 340 continue; 341 } 342 if (nodump) { 343 if (ret & HASSUBDIRS) 344 change = 1; /* subdirs have inherited nodump */ 345 CLRINO(ino, dumpdirmap); 346 } else if ((ret & HASSUBDIRS) == 0) { 347 if (!TSTINO(ino, dumpinomap)) { 348 CLRINO(ino, dumpdirmap); 349 change = 1; 350 } 351 } 352 } 353 return (change); 354} 355 356/* 357 * Read indirect blocks, and pass the data blocks to be searched 358 * as directories. Quit as soon as any entry is found that will 359 * require the directory to be dumped. 360 */ 361static int 362dirindir(ino_t ino, daddr_t blkno, int ind_level, off_t *filesize, 363 u_int64_t *tape_size, int nodump) 364{ 365 int ret = 0; 366 int i; 367 union { 368 int64_t i64[MAXBSIZE / sizeof (int64_t)]; 369 int32_t i32[MAXBSIZE / sizeof (int32_t)]; 370 } idblk; 371 372 bread(fsatoda(ufsib, blkno), (char *)&idblk, (int)ufsib->ufs_bsize); 373 if (ind_level <= 0) { 374 for (i = 0; *filesize > 0 && i < ufsib->ufs_nindir; i++) { 375 if (is_ufs2) 376 blkno = iswap64(idblk.i64[i]); 377 else 378 blkno = iswap32(idblk.i32[i]); 379 if (blkno != 0) 380 ret |= searchdir(ino, blkno, 381 ufsib->ufs_bsize, *filesize, 382 tape_size, nodump); 383 if (ret & HASDUMPEDFILE) 384 *filesize = 0; 385 else 386 *filesize -= ufsib->ufs_bsize; 387 } 388 return (ret); 389 } 390 ind_level--; 391 for (i = 0; *filesize > 0 && i < ufsib->ufs_nindir; i++) { 392 if (is_ufs2) 393 blkno = iswap64(idblk.i64[i]); 394 else 395 blkno = iswap32(idblk.i32[i]); 396 if (blkno != 0) 397 ret |= dirindir(ino, blkno, ind_level, filesize, 398 tape_size, nodump); 399 } 400 return (ret); 401} 402 403/* 404 * Scan a disk block containing directory information looking to see if 405 * any of the entries are on the dump list and to see if the directory 406 * contains any subdirectories. 407 */ 408static int 409searchdir(ino_t dino, daddr_t blkno, long size, off_t filesize, 410 u_int64_t *tape_size, int nodump) 411{ 412 struct direct *dp; 413 union dinode *ip; 414 long loc, ret = 0; 415 char *dblk; 416 ino_t ino; 417 418 dblk = malloc(size); 419 if (dblk == NULL) 420 quit("%s: cannot allocate directory memory", __func__); 421 bread(fsatoda(ufsib, blkno), dblk, (int)size); 422 if (filesize < size) 423 size = filesize; 424 for (loc = 0; loc < size; ) { 425 dp = (struct direct *)(dblk + loc); 426 if (dp->d_reclen == 0) { 427 msg("corrupted directory, inumber %llu\n", 428 (unsigned long long)dino); 429 break; 430 } 431 loc += iswap16(dp->d_reclen); 432 if (dp->d_ino == 0) 433 continue; 434 if (dp->d_name[0] == '.') { 435 if (dp->d_name[1] == '\0') 436 continue; 437 if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') 438 continue; 439 } 440 ino = iswap32(dp->d_ino); 441 if (nodump) { 442 ip = getino(ino); 443 if (TSTINO(ino, dumpinomap)) { 444 CLRINO(ino, dumpinomap); 445 *tape_size -= blockest(ip); 446 } 447 /* 448 * Add back to dumpdirmap and remove from usedinomap 449 * to propagate nodump. 450 */ 451 if ((DIP(ip, mode) & IFMT) == IFDIR) { 452 SETINO(ino, dumpdirmap); 453 CLRINO(ino, usedinomap); 454 ret |= HASSUBDIRS; 455 } 456 } else { 457 if (TSTINO(ino, dumpinomap)) { 458 ret |= HASDUMPEDFILE; 459 if (ret & HASSUBDIRS) 460 break; 461 } 462 if (TSTINO(ino, dumpdirmap)) { 463 ret |= HASSUBDIRS; 464 if (ret & HASDUMPEDFILE) 465 break; 466 } 467 } 468 } 469 free(dblk); 470 return (ret); 471} 472 473/* 474 * Dump passes 3 and 4. 475 * 476 * Dump the contents of an inode to tape. 477 */ 478void 479dumpino(union dinode *dp, ino_t ino) 480{ 481 int ind_level, cnt, last, added; 482 off_t size; 483 char buf[TP_BSIZE]; 484 daddr_t blk; 485 void *shortlink; 486 487 if (newtape) { 488 newtape = 0; 489 dumpmap(dumpinomap, TS_BITS, ino); 490 } 491 CLRINO(ino, dumpinomap); 492 /* 493 * Zero out the size of a snapshot so that it will be dumped 494 * as a zero length file. 495 */ 496 if (DIP(dp, flags) & SF_SNAPSHOT) { 497 DIP_SET(dp, size, 0); 498 DIP_SET(dp, flags, DIP(dp, flags) & ~SF_SNAPSHOT); 499 } 500 if (!is_ufs2) { 501 if (needswap) 502 ffs_dinode1_swap(&dp->dp1, &spcl.c_dinode); 503 else 504 spcl.c_dinode = dp->dp1; 505 spcl.c_extsize = 0; 506 } else { 507 if (needswap) 508 ffs_dinode2_swap(&dp->dp2, &dp->dp2); 509 spcl.c_mode = dp->dp2.di_mode; 510 spcl.c_size = dp->dp2.di_size; 511 spcl.c_extsize = dp->dp2.di_extsize; 512 spcl.c_atime = dp->dp2.di_atime; 513 spcl.c_atimensec = dp->dp2.di_atimensec; 514 spcl.c_mtime = dp->dp2.di_mtime; 515 spcl.c_mtimensec = dp->dp2.di_mtimensec; 516 spcl.c_birthtime = dp->dp2.di_birthtime; 517 spcl.c_birthtimensec = dp->dp2.di_birthnsec; 518 spcl.c_rdev = dp->dp2.di_rdev; 519 spcl.c_file_flags = dp->dp2.di_flags; 520 spcl.c_uid = dp->dp2.di_uid; 521 spcl.c_gid = dp->dp2.di_gid; 522 } 523 spcl.c_type = iswap32(TS_INODE); 524 spcl.c_count = 0; 525 switch (DIP(dp, mode) & IFMT) { 526 527 case 0: 528 /* 529 * Freed inode. 530 */ 531 return; 532 533 case IFLNK: 534 /* 535 * Check for short symbolic link. 536 */ 537 if (DIP(dp, size) > 0 && 538#ifdef FS_44INODEFMT 539 (DIP(dp, size) < ufsib->ufs_maxsymlinklen || 540 (ufsib->ufs_maxsymlinklen == 0 && DIP(dp, blocks) == 0)) 541#else 542 DIP(dp, blocks) == 0 543#endif 544 ) { 545 spcl.c_addr[0] = 1; 546 spcl.c_count = iswap32(1); 547 added = appendextdata(dp); 548 writeheader(ino); 549 if (is_ufs2) 550 shortlink = dp->dp2.di_db; 551 else 552 shortlink = dp->dp1.di_db; 553 memmove(buf, shortlink, DIP(dp, size)); 554 buf[DIP(dp, size)] = '\0'; 555 writerec(buf, 0); 556 writeextdata(dp, ino, added); 557 return; 558 } 559 /* fall through */ 560 561 case IFDIR: 562 case IFREG: 563 if (DIP(dp, size) > 0) 564 break; 565 /* fall through */ 566 567 case IFIFO: 568 case IFSOCK: 569 case IFCHR: 570 case IFBLK: 571 added = appendextdata(dp); 572 writeheader(ino); 573 writeextdata(dp, ino, added); 574 return; 575 576 default: 577 msg("Warning: undefined file type 0%o\n", DIP(dp, mode) & IFMT); 578 return; 579 } 580 if (DIP(dp, size) > UFS_NDADDR * ufsib->ufs_bsize) { 581 cnt = UFS_NDADDR * ufsib->ufs_frag; 582 last = 0; 583 } else { 584 cnt = howmany(DIP(dp, size), ufsib->ufs_fsize); 585 last = 1; 586 } 587 if (is_ufs2) 588 blksout64(dp, &dp->dp2.di_db[0], cnt, ino, last); 589 else 590 blksout32(&dp->dp1.di_db[0], cnt, ino); 591 592 if ((size = DIP(dp, size) - UFS_NDADDR * ufsib->ufs_bsize) <= 0) 593 return; 594 for (ind_level = 0; ind_level < UFS_NIADDR; ind_level++) { 595 if (is_ufs2) 596 blk = iswap64(dp->dp2.di_ib[ind_level]); 597 else 598 blk = iswap32(dp->dp1.di_ib[ind_level]); 599 dmpindir(dp, ino, blk, ind_level, &size); 600 if (size <= 0) 601 return; 602 } 603} 604 605/* 606 * Read indirect blocks, and pass the data blocks to be dumped. 607 */ 608static void 609dmpindir(union dinode *dp, ino_t ino, daddr_t blk, int ind_level, off_t *size) 610{ 611 int i, cnt, last; 612 union { 613 int32_t i32[MAXBSIZE / sizeof (int32_t)]; 614 int64_t i64[MAXBSIZE / sizeof (int64_t)]; 615 } idblk; 616 daddr_t iblk; 617 618 619 if (blk != 0) 620 bread(fsatoda(ufsib, blk), (char *)&idblk, 621 (int) ufsib->ufs_bsize); 622 else 623 memset(&idblk, 0, (int)ufsib->ufs_bsize); 624 if (ind_level <= 0) { 625 if (*size < ufsib->ufs_nindir * ufsib->ufs_bsize) { 626 cnt = howmany(*size, ufsib->ufs_fsize); 627 last = 0; 628 } else { 629 cnt = ufsib->ufs_nindir * ufsib->ufs_frag; 630 last = 1; 631 } 632 *size -= ufsib->ufs_nindir * ufsib->ufs_bsize; 633 if (is_ufs2) 634 blksout64(dp, &idblk.i64[0], cnt, ino, last); 635 else 636 blksout32(&idblk.i32[0], cnt, ino); 637 return; 638 } 639 ind_level--; 640 for (i = 0; i < ufsib->ufs_nindir; i++) { 641 if (is_ufs2) 642 iblk = iswap64(idblk.i64[i]); 643 else 644 iblk = iswap32(idblk.i32[i]); 645 dmpindir(dp, ino, iblk, ind_level, size); 646 if (*size <= 0) 647 return; 648 } 649} 650 651/* 652 * Collect up the data into tape record sized buffers and output them. 653 */ 654void 655blksout32(int32_t *blkp, int frags, ino_t ino) 656{ 657 int32_t *bp; 658 int i, j, count, blks, tbperdb; 659 660 blks = howmany(frags * ufsib->ufs_fsize, TP_BSIZE); 661 tbperdb = ufsib->ufs_bsize >> tp_bshift; 662 for (i = 0; i < blks; i += TP_NINDIR) { 663 if (i + TP_NINDIR > blks) 664 count = blks; 665 else 666 count = i + TP_NINDIR; 667 for (j = i; j < count; j++) 668 if (blkp[j / tbperdb] != 0) 669 spcl.c_addr[j - i] = 1; 670 else 671 spcl.c_addr[j - i] = 0; 672 spcl.c_count = iswap32(count - i); 673 writeheader(ino); 674 bp = &blkp[i / tbperdb]; 675 for (j = i; j < count; j += tbperdb, bp++) 676 if (*bp != 0) { 677 if (j + tbperdb <= count) 678 dumpblock(iswap32(*bp), (int)ufsib->ufs_bsize); 679 else 680 dumpblock(iswap32(*bp), (count - j) * TP_BSIZE); 681 } 682 spcl.c_type = iswap32(TS_ADDR); 683 } 684} 685 686void 687blksout64(union dinode *dp, int64_t *blkp, int frags, ino_t ino, int last) 688{ 689 int64_t *bp; 690 int i, j, count, blks, tbperdb, added = 0; 691 static int writingextdata = 0; 692 693 blks = howmany(frags * ufsib->ufs_fsize, TP_BSIZE); 694#if 0 695 if (last) { 696 int resid; 697 int extsize = iswap32(spcl.c_extsize); 698 if (writingextdata) 699 resid = howmany(ufsib->ufs_qfmask & extsize, 700 TP_BSIZE); 701 else 702 resid = howmany(ufsib->ufs_qfmask & dp->dp2.di_size, 703 TP_BSIZE); 704 if (resid > 0) 705 blks -= howmany(ufsib->ufs_fsize, TP_BSIZE) - resid; 706 } 707#endif 708 tbperdb = ufsib->ufs_bsize >> tp_bshift; 709 for (i = 0; i < blks; i += TP_NINDIR) { 710 if (i + TP_NINDIR > blks) 711 count = blks; 712 else 713 count = i + TP_NINDIR; 714 assert(count <= TP_NINDIR + i); 715 for (j = i; j < count; j++) 716 if (blkp[j / tbperdb] != 0) 717 spcl.c_addr[j - i] = 1; 718 else 719 spcl.c_addr[j - i] = 0; 720 spcl.c_count = iswap32(count - i); 721 if (last && count == blks && !writingextdata) 722 added = appendextdata(dp); 723 writeheader(ino); 724 bp = &blkp[i / tbperdb]; 725 for (j = i; j < count; j += tbperdb, bp++) 726 if (*bp != 0) { 727 if (j + tbperdb <= count) 728 dumpblock(iswap64(*bp), (int)ufsib->ufs_bsize); 729 else 730 dumpblock(iswap64(*bp), (count - j) * TP_BSIZE); 731 } 732 spcl.c_type = iswap32(TS_ADDR); 733 spcl.c_count = 0; 734 if (last && count == blks && !writingextdata) { 735 writingextdata = 1; 736 writeextdata(dp, ino, added); 737 writingextdata = 0; 738 } 739 } 740} 741 742/* 743 * If there is room in the current block for the extended attributes 744 * as well as the file data, update the header to reflect the added 745 * attribute data at the end. Attributes are placed at the end so that 746 * old versions of restore will correctly restore the file and simply 747 * discard the extra data at the end that it does not understand. 748 * The attribute data is dumped following the file data by the 749 * writeextdata() function (below). 750 */ 751static int 752appendextdata(union dinode *dp) 753{ 754 int i, blks, tbperdb, count, extsize; 755 756 count = iswap32(spcl.c_count); 757 extsize = iswap32(spcl.c_extsize); 758 /* 759 * If no extended attributes, there is nothing to do. 760 */ 761 if (extsize == 0) 762 return (0); 763 /* 764 * If there is not enough room at the end of this block 765 * to add the extended attributes, then rather than putting 766 * part of them here, we simply push them entirely into a 767 * new block rather than putting some here and some later. 768 */ 769 if (extsize > UFS_NXADDR * ufsib->ufs_bsize) 770 blks = howmany(UFS_NXADDR * ufsib->ufs_bsize, TP_BSIZE); 771 else 772 blks = howmany(extsize, TP_BSIZE); 773 if (count + blks > TP_NINDIR) 774 return (0); 775 /* 776 * Update the block map in the header to indicate the added 777 * extended attribute. They will be appended after the file 778 * data by the writeextdata() routine. 779 */ 780 tbperdb = ufsib->ufs_bsize >> tp_bshift; 781 assert(count + blks < TP_NINDIR); 782 for (i = 0; i < blks; i++) 783 if (dp->dp2.di_extb[i / tbperdb] != 0) 784 spcl.c_addr[count + i] = 1; 785 else 786 spcl.c_addr[count + i] = 0; 787 spcl.c_count = iswap32(count + blks); 788 return (blks); 789} 790 791/* 792 * Dump the extended attribute data. If there was room in the file 793 * header, then all we need to do is output the data blocks. If there 794 * was not room in the file header, then an additional TS_ADDR header 795 * is created to hold the attribute data. 796 */ 797static void 798writeextdata(union dinode *dp, ino_t ino, int added) 799{ 800 int i, frags, blks, tbperdb, last, extsize; 801 int64_t *bp; 802 off_t size; 803 804 extsize = iswap32(spcl.c_extsize); 805 806 /* 807 * If no extended attributes, there is nothing to do. 808 */ 809 if (extsize == 0) 810 return; 811 /* 812 * If there was no room in the file block for the attributes, 813 * dump them out in a new block, otherwise just dump the data. 814 */ 815 if (added == 0) { 816 if (extsize > UFS_NXADDR * ufsib->ufs_bsize) { 817 frags = UFS_NXADDR * ufsib->ufs_frag; 818 last = 0; 819 } else { 820 frags = howmany(extsize, ufsib->ufs_fsize); 821 last = 1; 822 } 823 blksout64(dp, &dp->dp2.di_extb[0], frags, ino, last); 824 } else { 825 if (extsize > UFS_NXADDR * ufsib->ufs_bsize) 826 blks = howmany(UFS_NXADDR * ufsib->ufs_bsize, TP_BSIZE); 827 else 828 blks = howmany(extsize, TP_BSIZE); 829 tbperdb = ufsib->ufs_bsize >> tp_bshift; 830 for (i = 0; i < blks; i += tbperdb) { 831 bp = &dp->dp2.di_extb[i / tbperdb]; 832 if (*bp != 0) { 833 if (i + tbperdb <= blks) 834 dumpblock(iswap64(*bp), (int)ufsib->ufs_bsize); 835 else 836 dumpblock(iswap64(*bp), (blks - i) * TP_BSIZE); 837 } 838 } 839 840 } 841 /* 842 * If an indirect block is added for extended attributes, then 843 * di_exti below should be changed to the structure element 844 * that references the extended attribute indirect block. This 845 * definition is here only to make it compile without complaint. 846 */ 847#define di_exti di_spare[0] 848 /* 849 * If the extended attributes fall into an indirect block, 850 * dump it as well. 851 */ 852 if ((size = extsize - UFS_NXADDR * ufsib->ufs_bsize) > 0) 853 dmpindir(dp, ino, dp->dp2.di_exti, 0, &size); 854} 855 856/* 857 * Dump a map to the tape. 858 */ 859void 860dumpmap(char *map, int type, ino_t ino) 861{ 862 int i; 863 char *cp; 864 865 spcl.c_type = iswap32(type); 866 spcl.c_count = iswap32(howmany(mapsize * sizeof(char), TP_BSIZE)); 867 writeheader(ino); 868 for (i = 0, cp = map; i < iswap32(spcl.c_count); i++, cp += TP_BSIZE) 869 writerec(cp, 0); 870} 871 872/* 873 * Write a header record to the dump tape. 874 */ 875void 876writeheader(ino_t ino) 877{ 878 int32_t sum, cnt, *lp; 879 880 spcl.c_inumber = iswap32(ino); 881 if (is_ufs2) 882 spcl.c_magic = iswap32(FS_UFS2_MAGIC); 883 else { 884 spcl.c_magic = iswap32(NFS_MAGIC); 885 spcl.c_old_date = iswap32(iswap64(spcl.c_date)); 886 spcl.c_old_ddate = iswap32(iswap64(spcl.c_ddate)); 887 spcl.c_old_tapea = iswap32(iswap64(spcl.c_tapea)); 888 spcl.c_old_firstrec = iswap32(iswap64(spcl.c_firstrec)); 889 } 890 spcl.c_checksum = 0; 891 lp = (int32_t *)&spcl; 892 sum = 0; 893 cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t)); 894 while (--cnt >= 0) { 895 sum += iswap32(*lp++); 896 sum += iswap32(*lp++); 897 sum += iswap32(*lp++); 898 sum += iswap32(*lp++); 899 } 900 spcl.c_checksum = iswap32(CHECKSUM - sum); 901 writerec((char *)&spcl, 1); 902} 903