1 /* $NetBSD: tunefs.c,v 1.60 2026/05/03 17:49:58 kre Exp $ */ 2 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/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)tunefs.c 8.3 (Berkeley) 5/3/95"; 41 #else 42 __RCSID("$NetBSD: tunefs.c,v 1.60 2026/05/03 17:49:58 kre Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 /* 47 * tunefs: change layout parameters to an existing file system. 48 */ 49 #include <sys/param.h> 50 #include <sys/statvfs.h> 51 52 #include <ufs/ffs/fs.h> 53 #include <ufs/ffs/ffs_extern.h> 54 #include <ufs/ufs/ufs_wapbl.h> 55 #include <ufs/ufs/ufsmount.h> 56 #include <ufs/ufs/quota2.h> 57 58 #include <machine/bswap.h> 59 60 #include <err.h> 61 #include <errno.h> 62 #include <fcntl.h> 63 #include <fstab.h> 64 #include <paths.h> 65 #include <stdbool.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <unistd.h> 70 #include <util.h> 71 72 #include "partutil.h" 73 74 /* the optimization warning string template */ 75 #define OPTWARN "should optimize for %s with minfree %s %d%%" 76 77 union { 78 struct fs sb; 79 char data[MAXBSIZE]; 80 } sbun, buf; 81 #define sblock sbun.sb 82 83 int fi; 84 long dev_bsize = 512; 85 int needswap = 0; 86 int is_ufs2 = 0; 87 int extattr = 0; 88 off_t sblockloc; 89 int userquota = 0; 90 int groupquota = 0; 91 #define Q2_EN (1) 92 #define Q2_IGN (0) 93 #define Q2_DIS (-1) 94 95 static off_t sblock_try[] = SBLOCKSEARCH; 96 97 static void bwrite(daddr_t, char *, int, const char *); 98 static void bread(daddr_t, char *, int, const char *); 99 static void change_log_info(long long); 100 static void getsb(struct fs *, const char *); 101 static int isactive(int, struct statvfs *); 102 static void show_log_info(void); 103 __dead static void usage(void); 104 105 int 106 main(int argc, char *argv[]) 107 { 108 int ch, aflag, pflag, Aflag, Fflag, Nflag, openflags; 109 const char *special, *chg[2]; 110 char device[MAXPATHLEN]; 111 int maxbpg, minfree, optim, secsize; 112 uint32_t i, avgfilesize, avgfpdir; 113 bool active; 114 long long logfilesize; 115 int secshift, fsbtodb; 116 const char *avalue, *pvalue, *name; 117 struct statvfs sfs; 118 119 aflag = pflag = Aflag = Fflag = Nflag = 0; 120 avalue = pvalue = NULL; 121 maxbpg = minfree = optim = secsize = -1; 122 avgfilesize = avgfpdir = -1; 123 logfilesize = -1; 124 secshift = 0; 125 chg[FS_OPTSPACE] = "space"; 126 chg[FS_OPTTIME] = "time"; 127 128 while ((ch = getopt(argc, argv, "AFNa:e:g:h:l:m:o:p:q:S:")) != -1) { 129 switch (ch) { 130 131 case 'A': 132 Aflag++; 133 break; 134 135 case 'F': 136 Fflag++; 137 break; 138 139 case 'N': 140 Nflag++; 141 break; 142 143 case 'a': 144 name = "ACLs"; 145 avalue = optarg; 146 if (strcmp(avalue, "enable") && 147 strcmp(avalue, "disable")) { 148 errx(10, "bad %s (options are %s)", 149 name, "`enable' or `disable'"); 150 } 151 aflag = 1; 152 break; 153 154 case 'e': 155 maxbpg = strsuftoll( 156 "maximum blocks per file in a cylinder group", 157 optarg, 1, INT_MAX); 158 break; 159 160 case 'g': 161 avgfilesize = strsuftoll("average file size", optarg, 162 1, INT_MAX); 163 break; 164 165 case 'h': 166 avgfpdir = strsuftoll( 167 "expected number of files per directory", 168 optarg, 1, INT_MAX); 169 break; 170 171 case 'l': 172 logfilesize = strsuftoll("journal log file size", 173 optarg, 0, INT_MAX); 174 break; 175 176 case 'm': 177 minfree = strsuftoll("minimum percentage of free space", 178 optarg, 0, 99); 179 break; 180 181 case 'o': 182 if (strcmp(optarg, chg[FS_OPTSPACE]) == 0) 183 optim = FS_OPTSPACE; 184 else if (strcmp(optarg, chg[FS_OPTTIME]) == 0) 185 optim = FS_OPTTIME; 186 else 187 errx(10, 188 "bad %s (options are `space' or `time')", 189 "optimization preference"); 190 break; 191 192 case 'p': 193 name = "POSIX1e ACLs"; 194 pvalue = optarg; 195 if (strcmp(pvalue, "enable") && 196 strcmp(pvalue, "disable")) { 197 errx(10, "bad %s (options are %s)", 198 name, "`enable' or `disable'"); 199 } 200 pflag = 1; 201 break; 202 203 case 'q': 204 if (strcmp(optarg, "user") == 0) 205 userquota = Q2_EN; 206 else if (strcmp(optarg, "group") == 0) 207 groupquota = Q2_EN; 208 else if (strcmp(optarg, "nouser") == 0) 209 userquota = Q2_DIS; 210 else if (strcmp(optarg, "nogroup") == 0) 211 groupquota = Q2_DIS; 212 else 213 errx(11, "invalid quota type %s", optarg); 214 break; 215 216 case 'S': 217 secsize = strsuftoll("physical sector size", 218 optarg, 0, INT_MAX); 219 secshift = ffs(secsize) - 1; 220 if (secsize != 0 && 1 << secshift != secsize) 221 errx(12, "sector size %d is not a power of two", secsize); 222 break; 223 224 default: 225 usage(); 226 } 227 } 228 argc -= optind; 229 argv += optind; 230 if (argc != 1) 231 usage(); 232 233 special = argv[0]; 234 openflags = Nflag ? O_RDONLY : O_RDWR; 235 if (Fflag) 236 fi = open(special, openflags); 237 else { 238 special = findspecial(special, 1); 239 fi = opendisk(special, openflags, device, sizeof(device), 0); 240 if (fi >= 0 || errno != ENOENT) 241 special = device; 242 } 243 if (fi == -1) 244 err(1, "%s", special); 245 active = !Fflag && isactive(fi, &sfs); 246 getsb(&sblock, special); 247 248 #define CHANGEVAL(old, new, type, suffix) do \ 249 if ((uint32_t)(new) != (uint32_t)-1) { \ 250 if ((new) == (old)) \ 251 warnx("%s remains unchanged at %d%s", \ 252 (type), (old), (suffix)); \ 253 else { \ 254 warnx("%s changes from %d%s to %d%s", \ 255 (type), (old), (suffix), (new), (suffix)); \ 256 (old) = (new); \ 257 } \ 258 } while (/* CONSTCOND */0) 259 260 warnx("tuning %s", special); 261 CHANGEVAL(sblock.fs_maxbpg, maxbpg, 262 "maximum blocks per file in a cylinder group", ""); 263 CHANGEVAL(sblock.fs_minfree, minfree, 264 "minimum percentage of free space", "%"); 265 if (minfree != -1) { 266 if (minfree >= MINFREE && 267 sblock.fs_optim == FS_OPTSPACE) 268 warnx(OPTWARN, "time", ">=", MINFREE); 269 if (minfree < MINFREE && 270 sblock.fs_optim == FS_OPTTIME) 271 warnx(OPTWARN, "space", "<", MINFREE); 272 } 273 if (optim != -1) { 274 if (sblock.fs_optim == optim) { 275 warnx("%s remains unchanged as %s", 276 "optimization preference", 277 chg[optim]); 278 } else { 279 warnx("%s changes from %s to %s", 280 "optimization preference", 281 chg[sblock.fs_optim], chg[optim]); 282 sblock.fs_optim = optim; 283 if (sblock.fs_minfree >= MINFREE && 284 optim == FS_OPTSPACE) 285 warnx(OPTWARN, "time", ">=", MINFREE); 286 if (sblock.fs_minfree < MINFREE && 287 optim == FS_OPTTIME) 288 warnx(OPTWARN, "space", "<", MINFREE); 289 } 290 } 291 if (secsize != -1) { 292 if (secsize == 0) { 293 secsize = sblock.fs_fsize / FFS_FSBTODB(&sblock, 1); 294 secshift = ffs(secsize) - 1; 295 } 296 297 if (secshift < DEV_BSHIFT) 298 warnx("sector size must be at least %d", DEV_BSIZE); 299 else if (secshift > sblock.fs_fshift) 300 warnx("sector size %d cannot be larger than fragment size %d", 301 secsize, sblock.fs_fsize); 302 else { 303 fsbtodb = sblock.fs_fshift - secshift; 304 if (fsbtodb == sblock.fs_fsbtodb) { 305 warnx("sector size remains unchanged as %d", 306 sblock.fs_fsize / FFS_FSBTODB(&sblock, 1)); 307 } else { 308 warnx("sector size changed from %d to %d", 309 sblock.fs_fsize / FFS_FSBTODB(&sblock, 1), 310 secsize); 311 sblock.fs_fsbtodb = fsbtodb; 312 } 313 } 314 } 315 CHANGEVAL(sblock.fs_avgfilesize, avgfilesize, 316 "average file size", ""); 317 CHANGEVAL(sblock.fs_avgfpdir, avgfpdir, 318 "expected number of files per directory", ""); 319 320 if (logfilesize >= 0) 321 change_log_info(logfilesize); 322 if (userquota == Q2_EN || groupquota == Q2_EN) 323 sblock.fs_flags |= FS_DOQUOTA2; 324 if (sblock.fs_flags & FS_DOQUOTA2) { 325 sblock.fs_quota_magic = Q2_HEAD_MAGIC; 326 switch(userquota) { 327 case Q2_EN: 328 if ((sblock.fs_quota_flags & FS_Q2_DO_TYPE(USRQUOTA)) 329 == 0) { 330 printf("enabling user quotas\n"); 331 sblock.fs_quota_flags |= 332 FS_Q2_DO_TYPE(USRQUOTA); 333 sblock.fs_quotafile[USRQUOTA] = 0; 334 } 335 break; 336 case Q2_DIS: 337 if ((sblock.fs_quota_flags & FS_Q2_DO_TYPE(USRQUOTA)) 338 != 0) { 339 printf("disabling user quotas\n"); 340 sblock.fs_quota_flags &= 341 ~FS_Q2_DO_TYPE(USRQUOTA); 342 } 343 } 344 switch(groupquota) { 345 case Q2_EN: 346 if ((sblock.fs_quota_flags & FS_Q2_DO_TYPE(GRPQUOTA)) 347 == 0) { 348 printf("enabling group quotas\n"); 349 sblock.fs_quota_flags |= 350 FS_Q2_DO_TYPE(GRPQUOTA); 351 sblock.fs_quotafile[GRPQUOTA] = 0; 352 } 353 break; 354 case Q2_DIS: 355 if ((sblock.fs_quota_flags & FS_Q2_DO_TYPE(GRPQUOTA)) 356 != 0) { 357 printf("disabling group quotas\n"); 358 sblock.fs_quota_flags &= 359 ~FS_Q2_DO_TYPE(GRPQUOTA); 360 } 361 } 362 } 363 /* 364 * if we disabled all quotas, FS_DOQUOTA2 and associated inode(s) will 365 * be cleared by kernel or fsck. 366 */ 367 if (aflag) { 368 name = "NFSv4 ACLs"; 369 if (strcmp(avalue, "enable") == 0) { 370 if (is_ufs2 && !extattr) { 371 warnx("%s not supported by this fs", name); 372 } else if (sblock.fs_flags & FS_NFS4ACLS) { 373 warnx("%s remains unchanged as enabled", name); 374 } else if (sblock.fs_flags & FS_POSIX1EACLS) { 375 warnx("%s and POSIX.1e ACLs are mutually " 376 "exclusive", name); 377 } else { 378 sblock.fs_flags |= FS_NFS4ACLS; 379 printf("%s set\n", name); 380 } 381 } else if (strcmp(avalue, "disable") == 0) { 382 if ((~sblock.fs_flags & FS_NFS4ACLS) == FS_NFS4ACLS) { 383 warnx("%s remains unchanged as disabled", 384 name); 385 } else { 386 sblock.fs_flags &= ~FS_NFS4ACLS; 387 printf("%s cleared\n", name); 388 } 389 } 390 } 391 392 if (pflag) { 393 name = "POSIX1e ACLs"; 394 if (strcmp(pvalue, "enable") == 0) { 395 if (is_ufs2 && !extattr) { 396 warnx("%s not supported by this fs", name); 397 } else if (sblock.fs_flags & FS_POSIX1EACLS) { 398 warnx("%s remains unchanged as enabled", name); 399 } else if (sblock.fs_flags & FS_NFS4ACLS) { 400 warnx("%s and ACLs are mutually " 401 "exclusive", name); 402 } else { 403 sblock.fs_flags |= FS_POSIX1EACLS; 404 printf("%s set\n", name); 405 } 406 } else if (strcmp(pvalue, "disable") == 0) { 407 if ((~sblock.fs_flags & FS_POSIX1EACLS) == 408 FS_POSIX1EACLS) { 409 warnx("%s remains unchanged as disabled", 410 name); 411 } else { 412 sblock.fs_flags &= ~FS_POSIX1EACLS; 413 printf("%s cleared\n", name); 414 } 415 } 416 } 417 418 if (Nflag) { 419 printf("%s: current settings of %s\n", getprogname(), special); 420 printf("\tmaximum contiguous block count %d\n", 421 sblock.fs_maxcontig); 422 printf("\tmaximum blocks per file in a cylinder group %d\n", 423 sblock.fs_maxbpg); 424 printf("\tminimum percentage of free space %d%%\n", 425 sblock.fs_minfree); 426 printf("\toptimization preference: %s\n", chg[sblock.fs_optim]); 427 printf("\taverage file size: %d\n", sblock.fs_avgfilesize); 428 printf("\texpected number of files per directory: %d\n", 429 sblock.fs_avgfpdir); 430 show_log_info(); 431 printf("\tquotas"); 432 if (sblock.fs_flags & FS_DOQUOTA2) { 433 if (sblock.fs_quota_flags & FS_Q2_DO_TYPE(USRQUOTA)) { 434 printf(" user"); 435 if (sblock.fs_quota_flags & 436 FS_Q2_DO_TYPE(GRPQUOTA)) 437 printf(","); 438 } 439 if (sblock.fs_quota_flags & FS_Q2_DO_TYPE(GRPQUOTA)) 440 printf(" group"); 441 printf(" enabled\n"); 442 } else { 443 printf(" disabled\n"); 444 } 445 printf("\tPOSIX.1e ACLs %s\n", 446 (sblock.fs_flags & FS_POSIX1EACLS) ? "enabled" : "disabled"); 447 printf("\tNFS4 ACLs %s\n", 448 (sblock.fs_flags & FS_NFS4ACLS) ? "enabled" : "disabled"); 449 printf("%s: no changes made\n", getprogname()); 450 return 0; 451 } 452 453 memcpy(&buf, (char *)&sblock, SBLOCKSIZE); 454 if (needswap) 455 ffs_sb_swap((struct fs*)&buf, (struct fs*)&buf); 456 457 /* write superblock to original coordinates (use old dev_bsize!) */ 458 bwrite(sblockloc, buf.data, SBLOCKSIZE, special); 459 460 if (active) { 461 struct ufs_args args; 462 args.fspec = sfs.f_mntfromname; 463 if (mount(MOUNT_FFS, sfs.f_mntonname, sfs.f_flag | MNT_UPDATE, 464 &args, sizeof args) == -1) 465 warn("mount"); 466 else 467 printf("%s: mount of %s on %s updated\n", 468 getprogname(), sfs.f_mntfromname, sfs.f_mntonname); 469 } 470 471 472 /* correct dev_bsize from possibly changed superblock data */ 473 dev_bsize = sblock.fs_fsize / FFS_FSBTODB(&sblock, 1); 474 475 if (Aflag) 476 for (i = 0; i < sblock.fs_ncg; i++) 477 bwrite(FFS_FSBTODB(&sblock, cgsblock(&sblock, i)), 478 buf.data, SBLOCKSIZE, special); 479 close(fi); 480 exit(0); 481 } 482 483 static int 484 isactive(int fd, struct statvfs *rsfs) 485 { 486 struct stat st0, st; 487 struct statvfs *sfs; 488 int n; 489 490 if (fstat(fd, &st0) == -1) { 491 warn("stat"); 492 return 0; 493 } 494 495 if ((n = getmntinfo(&sfs, 0)) == -1) { 496 warn("getmntinfo"); 497 return 0; 498 } 499 500 for (int i = 0; i < n; i++) { 501 if (stat(sfs[i].f_mntfromname, &st) == -1) 502 continue; 503 if (st.st_rdev != st0.st_rdev) 504 continue; 505 *rsfs = sfs[i]; 506 return 1; 507 508 } 509 return 0; 510 } 511 512 static void 513 show_log_info(void) 514 { 515 const char *loc; 516 uint64_t size, blksize, logsize; 517 int print; 518 519 switch (sblock.fs_journal_location) { 520 case UFS_WAPBL_JOURNALLOC_NONE: 521 print = blksize = 0; 522 /* nothing */ 523 break; 524 case UFS_WAPBL_JOURNALLOC_END_PARTITION: 525 loc = "end of partition"; 526 size = sblock.fs_journallocs[UFS_WAPBL_EPART_COUNT]; 527 blksize = sblock.fs_journallocs[UFS_WAPBL_EPART_BLKSZ]; 528 print = 1; 529 break; 530 case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM: 531 loc = "in filesystem"; 532 size = sblock.fs_journallocs[UFS_WAPBL_INFS_COUNT]; 533 blksize = sblock.fs_journallocs[UFS_WAPBL_INFS_BLKSZ]; 534 print = 1; 535 break; 536 default: 537 loc = "unknown"; 538 size = blksize = 0; 539 print = 1; 540 break; 541 } 542 543 if (print) { 544 logsize = size * blksize; 545 546 printf("\tjournal log file location: %s\n", loc); 547 printf("\tjournal log file size: "); 548 if (logsize == 0) 549 printf("0\n"); 550 else { 551 char sizebuf[8]; 552 humanize_number(sizebuf, 6, size * blksize, "B", 553 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 554 printf("%s (%" PRId64 " bytes)", sizebuf, logsize); 555 } 556 printf("\n"); 557 printf("\tjournal log flags:"); 558 if (sblock.fs_journal_flags & UFS_WAPBL_FLAGS_CREATE_LOG) 559 printf(" create-log"); 560 if (sblock.fs_journal_flags & UFS_WAPBL_FLAGS_CLEAR_LOG) 561 printf(" clear-log"); 562 printf("\n"); 563 } 564 } 565 566 static void 567 change_log_info(long long logfilesize) 568 { 569 /* 570 * NOTES: 571 * - only operate on in-filesystem log sizes 572 * - can't change size of existing log 573 * - if current is same, no action 574 * - if current is zero and new is non-zero, set flag to create log 575 * on next mount 576 * - if current is non-zero and new is zero, set flag to clear log 577 * on next mount 578 */ 579 int in_fs_log; 580 uint64_t old_size; 581 582 old_size = 0; 583 switch (sblock.fs_journal_location) { 584 case UFS_WAPBL_JOURNALLOC_END_PARTITION: 585 in_fs_log = 0; 586 old_size = sblock.fs_journallocs[UFS_WAPBL_EPART_COUNT] * 587 sblock.fs_journallocs[UFS_WAPBL_EPART_BLKSZ]; 588 break; 589 590 case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM: 591 in_fs_log = 1; 592 old_size = sblock.fs_journallocs[UFS_WAPBL_INFS_COUNT] * 593 sblock.fs_journallocs[UFS_WAPBL_INFS_BLKSZ]; 594 break; 595 596 case UFS_WAPBL_JOURNALLOC_NONE: 597 default: 598 in_fs_log = 0; 599 old_size = 0; 600 break; 601 } 602 603 if (logfilesize == 0) { 604 /* 605 * Don't clear out the locators - the kernel might need 606 * these to find the log! Just set the "clear the log" 607 * flag and let the kernel do the rest. 608 */ 609 sblock.fs_journal_flags |= UFS_WAPBL_FLAGS_CLEAR_LOG; 610 sblock.fs_journal_flags &= ~UFS_WAPBL_FLAGS_CREATE_LOG; 611 warnx("log file size cleared from %" PRIu64 "", old_size); 612 return; 613 } 614 615 if (!in_fs_log && logfilesize > 0 && old_size > 0) 616 errx(1, "Can't change size of non-in-filesystem log"); 617 618 if (old_size == (uint64_t)logfilesize && logfilesize > 0) { 619 /* no action */ 620 warnx("log file size remains unchanged at %lld", logfilesize); 621 return; 622 } 623 624 if (old_size == 0) { 625 /* create new log of desired size next mount */ 626 sblock.fs_journal_location = UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM; 627 sblock.fs_journallocs[UFS_WAPBL_INFS_ADDR] = 0; 628 sblock.fs_journallocs[UFS_WAPBL_INFS_COUNT] = logfilesize; 629 sblock.fs_journallocs[UFS_WAPBL_INFS_BLKSZ] = 0; 630 sblock.fs_journallocs[UFS_WAPBL_INFS_INO] = 0; 631 sblock.fs_journal_flags |= UFS_WAPBL_FLAGS_CREATE_LOG; 632 sblock.fs_journal_flags &= ~UFS_WAPBL_FLAGS_CLEAR_LOG; 633 warnx("log file size set to %lld", logfilesize); 634 } else { 635 errx(1, 636 "Can't change existing log size from %" PRIu64 " to %lld", 637 old_size, logfilesize); 638 } 639 } 640 641 static void 642 usage(void) 643 { 644 645 fprintf(stderr, "usage: tunefs [-AFN] tuneup-options special-device\n"); 646 fprintf(stderr, "where tuneup-options are:\n"); 647 fprintf(stderr, "\t-a ACLS: `enable' or `disable'\n"); 648 fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n"); 649 fprintf(stderr, "\t-g average file size\n"); 650 fprintf(stderr, "\t-h expected number of files per directory\n"); 651 fprintf(stderr, "\t-l journal log file size (`0' to clear journal)\n"); 652 fprintf(stderr, "\t-m minimum percentage of free space\n"); 653 fprintf(stderr, "\t-p POSIX.1e ACLS: `enable' or `disable'\n"); 654 fprintf(stderr, "\t-o optimization preference (`space' or `time')\n"); 655 fprintf(stderr, "\t-q quota type (`[no]user' or `[no]group')\n"); 656 fprintf(stderr, "\t-S sector size\n"); 657 exit(2); 658 } 659 660 static void 661 getsb(struct fs *fs, const char *file) 662 { 663 int i; 664 665 for (i = 0; ; i++) { 666 if (sblock_try[i] == -1) 667 errx(5, "cannot find filesystem superblock"); 668 bread(sblock_try[i] / dev_bsize, (char *)fs, SBLOCKSIZE, file); 669 switch(fs->fs_magic) { 670 case FS_UFS2EA_MAGIC: 671 extattr = 1; 672 /*FALLTHROUGH*/ 673 case FS_UFS2_MAGIC: 674 is_ufs2 = 1; 675 /*FALLTHROUGH*/ 676 case FS_UFS1_MAGIC: 677 break; 678 case FS_UFS2EA_MAGIC_SWAPPED: 679 extattr = 1; 680 /*FALLTHROUGH*/ 681 case FS_UFS2_MAGIC_SWAPPED: 682 is_ufs2 = 1; 683 /*FALLTHROUGH*/ 684 case FS_UFS1_MAGIC_SWAPPED: 685 warnx("%s: swapping byte order", file); 686 needswap = 1; 687 ffs_sb_swap(fs, fs); 688 break; 689 default: 690 continue; 691 } 692 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2) 693 continue; 694 if ((is_ufs2 || fs->fs_old_flags & FS_FLAGS_UPDATED) 695 && fs->fs_sblockloc != sblock_try[i]) 696 continue; 697 break; 698 } 699 700 dev_bsize = fs->fs_fsize / FFS_FSBTODB(fs, 1); 701 sblockloc = sblock_try[i] / dev_bsize; 702 } 703 704 static void 705 bwrite(daddr_t blk, char *buffer, int size, const char *file) 706 { 707 off_t offset; 708 709 offset = (off_t)blk * dev_bsize; 710 if (lseek(fi, offset, SEEK_SET) == -1) 711 err(6, "%s: seeking to %lld", file, (long long)offset); 712 if (write(fi, buffer, size) != size) 713 err(7, "%s: writing %d bytes", file, size); 714 } 715 716 static void 717 bread(daddr_t blk, char *buffer, int cnt, const char *file) 718 { 719 off_t offset; 720 int i; 721 722 offset = (off_t)blk * dev_bsize; 723 if (lseek(fi, offset, SEEK_SET) == -1) 724 err(4, "%s: seeking to %lld", file, (long long)offset); 725 if ((i = read(fi, buffer, cnt)) != cnt) 726 errx(5, "%s: short read", file); 727 } 728