1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #if 0 33 #ifndef lint 34 static const char copyright[] = 35 "@(#) Copyright (c) 1983, 1993\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #ifndef lint 40 static char sccsid[] = "@(#)tunefs.c 8.2 (Berkeley) 4/19/94"; 41 #endif /* not lint */ 42 #endif 43 #include <sys/cdefs.h> 44 __FBSDID("$FreeBSD$"); 45 46 /* 47 * tunefs: change layout parameters to an existing file system. 48 */ 49 #include <sys/param.h> 50 #include <sys/mount.h> 51 #include <sys/disklabel.h> 52 #include <sys/stat.h> 53 54 #include <ufs/ufs/ufsmount.h> 55 #include <ufs/ufs/dinode.h> 56 #include <ufs/ffs/fs.h> 57 #include <ufs/ufs/dir.h> 58 59 #include <ctype.h> 60 #include <err.h> 61 #include <fcntl.h> 62 #include <fstab.h> 63 #include <libufs.h> 64 #include <mntopts.h> 65 #include <paths.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <stdint.h> 69 #include <string.h> 70 #include <time.h> 71 #include <unistd.h> 72 73 /* the optimization warning string template */ 74 #define OPTWARN "should optimize for %s with minfree %s %d%%" 75 76 static int blocks; 77 static char clrbuf[MAXBSIZE]; 78 static struct uufsd disk; 79 #define sblock disk.d_fs 80 81 static void usage(void); 82 static void printfs(void); 83 static int journal_alloc(int64_t size); 84 static void journal_clear(void); 85 static void sbdirty(void); 86 87 int 88 main(int argc, char *argv[]) 89 { 90 const char *avalue, *jvalue, *Jvalue, *Lvalue, *lvalue, *Nvalue, *nvalue; 91 const char *tvalue; 92 const char *special, *on; 93 const char *name; 94 int active; 95 int Aflag, aflag, eflag, evalue, fflag, fvalue, jflag, Jflag, kflag; 96 int kvalue, Lflag, lflag, mflag, mvalue, Nflag, nflag, oflag, ovalue; 97 int pflag, sflag, svalue, Svalue, tflag; 98 int ch, found_arg, i; 99 int iovlen = 0; 100 const char *chg[2]; 101 struct statfs stfs; 102 struct iovec *iov = NULL; 103 char errmsg[255] = {0}; 104 105 if (argc < 3) 106 usage(); 107 Aflag = aflag = eflag = fflag = jflag = Jflag = kflag = Lflag = 0; 108 lflag = mflag = Nflag = nflag = oflag = pflag = sflag = tflag = 0; 109 avalue = jvalue = Jvalue = Lvalue = lvalue = Nvalue = nvalue = NULL; 110 evalue = fvalue = mvalue = ovalue = svalue = Svalue = 0; 111 active = 0; 112 found_arg = 0; /* At least one arg is required. */ 113 while ((ch = getopt(argc, argv, "Aa:e:f:j:J:k:L:l:m:N:n:o:ps:S:t:")) 114 != -1) 115 switch (ch) { 116 117 case 'A': 118 found_arg = 1; 119 Aflag++; 120 break; 121 122 case 'a': 123 found_arg = 1; 124 name = "POSIX.1e ACLs"; 125 avalue = optarg; 126 if (strcmp(avalue, "enable") && 127 strcmp(avalue, "disable")) { 128 errx(10, "bad %s (options are %s)", 129 name, "`enable' or `disable'"); 130 } 131 aflag = 1; 132 break; 133 134 case 'e': 135 found_arg = 1; 136 name = "maximum blocks per file in a cylinder group"; 137 evalue = atoi(optarg); 138 if (evalue < 1) 139 errx(10, "%s must be >= 1 (was %s)", 140 name, optarg); 141 eflag = 1; 142 break; 143 144 case 'f': 145 found_arg = 1; 146 name = "average file size"; 147 fvalue = atoi(optarg); 148 if (fvalue < 1) 149 errx(10, "%s must be >= 1 (was %s)", 150 name, optarg); 151 fflag = 1; 152 break; 153 154 case 'j': 155 found_arg = 1; 156 name = "softdep journaled file system"; 157 jvalue = optarg; 158 if (strcmp(jvalue, "enable") && 159 strcmp(jvalue, "disable")) { 160 errx(10, "bad %s (options are %s)", 161 name, "`enable' or `disable'"); 162 } 163 jflag = 1; 164 break; 165 166 case 'J': 167 found_arg = 1; 168 name = "gjournaled file system"; 169 Jvalue = optarg; 170 if (strcmp(Jvalue, "enable") && 171 strcmp(Jvalue, "disable")) { 172 errx(10, "bad %s (options are %s)", 173 name, "`enable' or `disable'"); 174 } 175 Jflag = 1; 176 break; 177 178 case 'k': 179 found_arg = 1; 180 name = "space to hold for metadata blocks"; 181 kvalue = atoi(optarg); 182 if (kvalue < 0) 183 errx(10, "bad %s (%s)", name, optarg); 184 kflag = 1; 185 break; 186 187 case 'L': 188 found_arg = 1; 189 name = "volume label"; 190 Lvalue = optarg; 191 i = -1; 192 while (isalnum(Lvalue[++i])); 193 if (Lvalue[i] != '\0') { 194 errx(10, 195 "bad %s. Valid characters are alphanumerics.", 196 name); 197 } 198 if (strlen(Lvalue) >= MAXVOLLEN) { 199 errx(10, "bad %s. Length is longer than %d.", 200 name, MAXVOLLEN - 1); 201 } 202 Lflag = 1; 203 break; 204 205 case 'l': 206 found_arg = 1; 207 name = "multilabel MAC file system"; 208 lvalue = optarg; 209 if (strcmp(lvalue, "enable") && 210 strcmp(lvalue, "disable")) { 211 errx(10, "bad %s (options are %s)", 212 name, "`enable' or `disable'"); 213 } 214 lflag = 1; 215 break; 216 217 case 'm': 218 found_arg = 1; 219 name = "minimum percentage of free space"; 220 mvalue = atoi(optarg); 221 if (mvalue < 0 || mvalue > 99) 222 errx(10, "bad %s (%s)", name, optarg); 223 mflag = 1; 224 break; 225 226 case 'N': 227 found_arg = 1; 228 name = "NFSv4 ACLs"; 229 Nvalue = optarg; 230 if (strcmp(Nvalue, "enable") && 231 strcmp(Nvalue, "disable")) { 232 errx(10, "bad %s (options are %s)", 233 name, "`enable' or `disable'"); 234 } 235 Nflag = 1; 236 break; 237 238 case 'n': 239 found_arg = 1; 240 name = "soft updates"; 241 nvalue = optarg; 242 if (strcmp(nvalue, "enable") != 0 && 243 strcmp(nvalue, "disable") != 0) { 244 errx(10, "bad %s (options are %s)", 245 name, "`enable' or `disable'"); 246 } 247 nflag = 1; 248 break; 249 250 case 'o': 251 found_arg = 1; 252 name = "optimization preference"; 253 if (strcmp(optarg, "space") == 0) 254 ovalue = FS_OPTSPACE; 255 else if (strcmp(optarg, "time") == 0) 256 ovalue = FS_OPTTIME; 257 else 258 errx(10, 259 "bad %s (options are `space' or `time')", 260 name); 261 oflag = 1; 262 break; 263 264 case 'p': 265 found_arg = 1; 266 pflag = 1; 267 break; 268 269 case 's': 270 found_arg = 1; 271 name = "expected number of files per directory"; 272 svalue = atoi(optarg); 273 if (svalue < 1) 274 errx(10, "%s must be >= 1 (was %s)", 275 name, optarg); 276 sflag = 1; 277 break; 278 279 case 'S': 280 found_arg = 1; 281 name = "Softdep Journal Size"; 282 Svalue = atoi(optarg); 283 if (Svalue < SUJ_MIN) 284 errx(10, "%s must be >= %d (was %s)", 285 name, SUJ_MIN, optarg); 286 break; 287 288 case 't': 289 found_arg = 1; 290 name = "trim"; 291 tvalue = optarg; 292 if (strcmp(tvalue, "enable") != 0 && 293 strcmp(tvalue, "disable") != 0) { 294 errx(10, "bad %s (options are %s)", 295 name, "`enable' or `disable'"); 296 } 297 tflag = 1; 298 break; 299 300 default: 301 usage(); 302 } 303 argc -= optind; 304 argv += optind; 305 if (found_arg == 0 || argc != 1) 306 usage(); 307 308 on = special = argv[0]; 309 if (ufs_disk_fillout(&disk, special) == -1) 310 goto err; 311 if (disk.d_name != special) { 312 if (statfs(special, &stfs) != 0) 313 warn("Can't stat %s", special); 314 if (strcmp(special, stfs.f_mntonname) == 0) 315 active = 1; 316 } 317 318 if (pflag) { 319 printfs(); 320 exit(0); 321 } 322 if (Lflag) { 323 name = "volume label"; 324 strncpy(sblock.fs_volname, Lvalue, MAXVOLLEN); 325 } 326 if (aflag) { 327 name = "POSIX.1e ACLs"; 328 if (strcmp(avalue, "enable") == 0) { 329 if (sblock.fs_flags & FS_ACLS) { 330 warnx("%s remains unchanged as enabled", name); 331 } else if (sblock.fs_flags & FS_NFS4ACLS) { 332 warnx("%s and NFSv4 ACLs are mutually " 333 "exclusive", name); 334 } else { 335 sblock.fs_flags |= FS_ACLS; 336 warnx("%s set", name); 337 } 338 } else if (strcmp(avalue, "disable") == 0) { 339 if ((~sblock.fs_flags & FS_ACLS) == 340 FS_ACLS) { 341 warnx("%s remains unchanged as disabled", 342 name); 343 } else { 344 sblock.fs_flags &= ~FS_ACLS; 345 warnx("%s cleared", name); 346 } 347 } 348 } 349 if (eflag) { 350 name = "maximum blocks per file in a cylinder group"; 351 if (sblock.fs_maxbpg == evalue) 352 warnx("%s remains unchanged as %d", name, evalue); 353 else { 354 warnx("%s changes from %d to %d", 355 name, sblock.fs_maxbpg, evalue); 356 sblock.fs_maxbpg = evalue; 357 } 358 } 359 if (fflag) { 360 name = "average file size"; 361 if (sblock.fs_avgfilesize == (unsigned)fvalue) { 362 warnx("%s remains unchanged as %d", name, fvalue); 363 } 364 else { 365 warnx("%s changes from %d to %d", 366 name, sblock.fs_avgfilesize, fvalue); 367 sblock.fs_avgfilesize = fvalue; 368 } 369 } 370 if (jflag) { 371 name = "soft updates journaling"; 372 if (strcmp(jvalue, "enable") == 0) { 373 if ((sblock.fs_flags & (FS_DOSOFTDEP | FS_SUJ)) == 374 (FS_DOSOFTDEP | FS_SUJ)) { 375 warnx("%s remains unchanged as enabled", name); 376 } else if (sblock.fs_clean == 0) { 377 warnx("%s cannot be enabled until fsck is run", 378 name); 379 } else if (journal_alloc(Svalue) != 0) { 380 warnx("%s cannot be enabled", name); 381 } else { 382 sblock.fs_flags |= FS_DOSOFTDEP | FS_SUJ; 383 warnx("%s set", name); 384 } 385 } else if (strcmp(jvalue, "disable") == 0) { 386 if ((~sblock.fs_flags & FS_SUJ) == FS_SUJ) { 387 warnx("%s remains unchanged as disabled", name); 388 } else { 389 journal_clear(); 390 sblock.fs_flags &= ~FS_SUJ; 391 sblock.fs_sujfree = 0; 392 warnx("%s cleared but soft updates still set.", 393 name); 394 395 warnx("remove .sujournal to reclaim space"); 396 } 397 } 398 } 399 if (Jflag) { 400 name = "gjournal"; 401 if (strcmp(Jvalue, "enable") == 0) { 402 if (sblock.fs_flags & FS_GJOURNAL) { 403 warnx("%s remains unchanged as enabled", name); 404 } else { 405 sblock.fs_flags |= FS_GJOURNAL; 406 warnx("%s set", name); 407 } 408 } else if (strcmp(Jvalue, "disable") == 0) { 409 if ((~sblock.fs_flags & FS_GJOURNAL) == 410 FS_GJOURNAL) { 411 warnx("%s remains unchanged as disabled", 412 name); 413 } else { 414 sblock.fs_flags &= ~FS_GJOURNAL; 415 warnx("%s cleared", name); 416 } 417 } 418 } 419 if (kflag) { 420 name = "space to hold for metadata blocks"; 421 if (sblock.fs_metaspace == kvalue) 422 warnx("%s remains unchanged as %d", name, kvalue); 423 else { 424 kvalue = blknum(&sblock, kvalue); 425 if (kvalue > sblock.fs_fpg / 2) { 426 kvalue = blknum(&sblock, sblock.fs_fpg / 2); 427 warnx("%s cannot exceed half the file system " 428 "space", name); 429 } 430 warnx("%s changes from %jd to %d", 431 name, sblock.fs_metaspace, kvalue); 432 sblock.fs_metaspace = kvalue; 433 } 434 } 435 if (lflag) { 436 name = "multilabel"; 437 if (strcmp(lvalue, "enable") == 0) { 438 if (sblock.fs_flags & FS_MULTILABEL) { 439 warnx("%s remains unchanged as enabled", name); 440 } else { 441 sblock.fs_flags |= FS_MULTILABEL; 442 warnx("%s set", name); 443 } 444 } else if (strcmp(lvalue, "disable") == 0) { 445 if ((~sblock.fs_flags & FS_MULTILABEL) == 446 FS_MULTILABEL) { 447 warnx("%s remains unchanged as disabled", 448 name); 449 } else { 450 sblock.fs_flags &= ~FS_MULTILABEL; 451 warnx("%s cleared", name); 452 } 453 } 454 } 455 if (mflag) { 456 name = "minimum percentage of free space"; 457 if (sblock.fs_minfree == mvalue) 458 warnx("%s remains unchanged as %d%%", name, mvalue); 459 else { 460 warnx("%s changes from %d%% to %d%%", 461 name, sblock.fs_minfree, mvalue); 462 sblock.fs_minfree = mvalue; 463 if (mvalue >= MINFREE && sblock.fs_optim == FS_OPTSPACE) 464 warnx(OPTWARN, "time", ">=", MINFREE); 465 if (mvalue < MINFREE && sblock.fs_optim == FS_OPTTIME) 466 warnx(OPTWARN, "space", "<", MINFREE); 467 } 468 } 469 if (Nflag) { 470 name = "NFSv4 ACLs"; 471 if (strcmp(Nvalue, "enable") == 0) { 472 if (sblock.fs_flags & FS_NFS4ACLS) { 473 warnx("%s remains unchanged as enabled", name); 474 } else if (sblock.fs_flags & FS_ACLS) { 475 warnx("%s and POSIX.1e ACLs are mutually " 476 "exclusive", name); 477 } else { 478 sblock.fs_flags |= FS_NFS4ACLS; 479 warnx("%s set", name); 480 } 481 } else if (strcmp(Nvalue, "disable") == 0) { 482 if ((~sblock.fs_flags & FS_NFS4ACLS) == 483 FS_NFS4ACLS) { 484 warnx("%s remains unchanged as disabled", 485 name); 486 } else { 487 sblock.fs_flags &= ~FS_NFS4ACLS; 488 warnx("%s cleared", name); 489 } 490 } 491 } 492 if (nflag) { 493 name = "soft updates"; 494 if (strcmp(nvalue, "enable") == 0) { 495 if (sblock.fs_flags & FS_DOSOFTDEP) 496 warnx("%s remains unchanged as enabled", name); 497 else if (sblock.fs_clean == 0) { 498 warnx("%s cannot be enabled until fsck is run", 499 name); 500 } else { 501 sblock.fs_flags |= FS_DOSOFTDEP; 502 warnx("%s set", name); 503 } 504 } else if (strcmp(nvalue, "disable") == 0) { 505 if ((~sblock.fs_flags & FS_DOSOFTDEP) == FS_DOSOFTDEP) 506 warnx("%s remains unchanged as disabled", name); 507 else { 508 sblock.fs_flags &= ~FS_DOSOFTDEP; 509 warnx("%s cleared", name); 510 } 511 } 512 } 513 if (oflag) { 514 name = "optimization preference"; 515 chg[FS_OPTSPACE] = "space"; 516 chg[FS_OPTTIME] = "time"; 517 if (sblock.fs_optim == ovalue) 518 warnx("%s remains unchanged as %s", name, chg[ovalue]); 519 else { 520 warnx("%s changes from %s to %s", 521 name, chg[sblock.fs_optim], chg[ovalue]); 522 sblock.fs_optim = ovalue; 523 if (sblock.fs_minfree >= MINFREE && 524 ovalue == FS_OPTSPACE) 525 warnx(OPTWARN, "time", ">=", MINFREE); 526 if (sblock.fs_minfree < MINFREE && ovalue == FS_OPTTIME) 527 warnx(OPTWARN, "space", "<", MINFREE); 528 } 529 } 530 if (sflag) { 531 name = "expected number of files per directory"; 532 if (sblock.fs_avgfpdir == (unsigned)svalue) { 533 warnx("%s remains unchanged as %d", name, svalue); 534 } 535 else { 536 warnx("%s changes from %d to %d", 537 name, sblock.fs_avgfpdir, svalue); 538 sblock.fs_avgfpdir = svalue; 539 } 540 } 541 if (tflag) { 542 name = "issue TRIM to the disk"; 543 if (strcmp(tvalue, "enable") == 0) { 544 if (sblock.fs_flags & FS_TRIM) 545 warnx("%s remains unchanged as enabled", name); 546 else { 547 sblock.fs_flags |= FS_TRIM; 548 warnx("%s set", name); 549 } 550 } else if (strcmp(tvalue, "disable") == 0) { 551 if ((~sblock.fs_flags & FS_TRIM) == FS_TRIM) 552 warnx("%s remains unchanged as disabled", name); 553 else { 554 sblock.fs_flags &= ~FS_TRIM; 555 warnx("%s cleared", name); 556 } 557 } 558 } 559 560 if (sbwrite(&disk, Aflag) == -1) 561 goto err; 562 ufs_disk_close(&disk); 563 if (active) { 564 build_iovec_argf(&iov, &iovlen, "fstype", "ufs"); 565 build_iovec_argf(&iov, &iovlen, "fspath", "%s", on); 566 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 567 if (nmount(iov, iovlen, 568 stfs.f_flags | MNT_UPDATE | MNT_RELOAD) < 0) { 569 if (errmsg[0]) 570 err(9, "%s: reload: %s", special, errmsg); 571 else 572 err(9, "%s: reload", special); 573 } 574 warnx("file system reloaded"); 575 } 576 exit(0); 577 err: 578 if (disk.d_error != NULL) 579 errx(11, "%s: %s", special, disk.d_error); 580 else 581 err(12, "%s", special); 582 } 583 584 static void 585 sbdirty(void) 586 { 587 disk.d_fs.fs_flags |= FS_UNCLEAN | FS_NEEDSFSCK; 588 disk.d_fs.fs_clean = 0; 589 } 590 591 static ufs2_daddr_t 592 journal_balloc(void) 593 { 594 ufs2_daddr_t blk; 595 struct cg *cgp; 596 int valid; 597 static int contig = 1; 598 599 cgp = &disk.d_cg; 600 for (;;) { 601 blk = cgballoc(&disk); 602 if (blk > 0) 603 break; 604 /* 605 * If we failed to allocate a block from this cg, move to 606 * the next. 607 */ 608 if (cgwrite(&disk) < 0) { 609 warn("Failed to write updated cg"); 610 return (-1); 611 } 612 while ((valid = cgread(&disk)) == 1) { 613 /* 614 * Try to minimize fragmentation by requiring a minimum 615 * number of blocks present. 616 */ 617 if (cgp->cg_cs.cs_nbfree > 256 * 1024) 618 break; 619 if (contig == 0 && cgp->cg_cs.cs_nbfree) 620 break; 621 } 622 if (valid) 623 continue; 624 /* 625 * Try once through looking only for large contiguous regions 626 * and again taking any space we can find. 627 */ 628 if (contig) { 629 contig = 0; 630 disk.d_ccg = 0; 631 warnx("Journal file fragmented."); 632 continue; 633 } 634 warnx("Failed to find sufficient free blocks for the journal"); 635 return -1; 636 } 637 if (bwrite(&disk, fsbtodb(&sblock, blk), clrbuf, 638 sblock.fs_bsize) <= 0) { 639 warn("Failed to initialize new block"); 640 return -1; 641 } 642 return (blk); 643 } 644 645 /* 646 * Search a directory block for the SUJ_FILE. 647 */ 648 static ino_t 649 dir_search(ufs2_daddr_t blk, int bytes) 650 { 651 char block[MAXBSIZE]; 652 struct direct *dp; 653 int off; 654 655 if (bread(&disk, fsbtodb(&sblock, blk), block, bytes) <= 0) { 656 warn("Failed to read dir block"); 657 return (-1); 658 } 659 for (off = 0; off < bytes; off += dp->d_reclen) { 660 dp = (struct direct *)&block[off]; 661 if (dp->d_reclen == 0) 662 break; 663 if (dp->d_ino == 0) 664 continue; 665 if (dp->d_namlen != strlen(SUJ_FILE)) 666 continue; 667 if (bcmp(dp->d_name, SUJ_FILE, dp->d_namlen) != 0) 668 continue; 669 return (dp->d_ino); 670 } 671 672 return (0); 673 } 674 675 /* 676 * Search in the UFS_ROOTINO for the SUJ_FILE. If it exists we can not enable 677 * journaling. 678 */ 679 static ino_t 680 journal_findfile(void) 681 { 682 struct ufs1_dinode *dp1; 683 struct ufs2_dinode *dp2; 684 ino_t ino; 685 int mode; 686 void *ip; 687 int i; 688 689 if (getino(&disk, &ip, UFS_ROOTINO, &mode) != 0) { 690 warn("Failed to get root inode"); 691 return (-1); 692 } 693 dp2 = ip; 694 dp1 = ip; 695 if (sblock.fs_magic == FS_UFS1_MAGIC) { 696 if ((off_t)dp1->di_size >= lblktosize(&sblock, UFS_NDADDR)) { 697 warnx("UFS_ROOTINO extends beyond direct blocks."); 698 return (-1); 699 } 700 for (i = 0; i < UFS_NDADDR; i++) { 701 if (dp1->di_db[i] == 0) 702 break; 703 if ((ino = dir_search(dp1->di_db[i], 704 sblksize(&sblock, (off_t)dp1->di_size, i))) != 0) 705 return (ino); 706 } 707 } else { 708 if ((off_t)dp2->di_size >= lblktosize(&sblock, UFS_NDADDR)) { 709 warnx("UFS_ROOTINO extends beyond direct blocks."); 710 return (-1); 711 } 712 for (i = 0; i < UFS_NDADDR; i++) { 713 if (dp2->di_db[i] == 0) 714 break; 715 if ((ino = dir_search(dp2->di_db[i], 716 sblksize(&sblock, (off_t)dp2->di_size, i))) != 0) 717 return (ino); 718 } 719 } 720 721 return (0); 722 } 723 724 static void 725 dir_clear_block(const char *block, off_t off) 726 { 727 struct direct *dp; 728 729 for (; off < sblock.fs_bsize; off += DIRBLKSIZ) { 730 dp = (struct direct *)&block[off]; 731 dp->d_ino = 0; 732 dp->d_reclen = DIRBLKSIZ; 733 dp->d_type = DT_UNKNOWN; 734 } 735 } 736 737 /* 738 * Insert the journal at inode 'ino' into directory blk 'blk' at the first 739 * free offset of 'off'. DIRBLKSIZ blocks after off are initialized as 740 * empty. 741 */ 742 static int 743 dir_insert(ufs2_daddr_t blk, off_t off, ino_t ino) 744 { 745 struct direct *dp; 746 char block[MAXBSIZE]; 747 748 if (bread(&disk, fsbtodb(&sblock, blk), block, sblock.fs_bsize) <= 0) { 749 warn("Failed to read dir block"); 750 return (-1); 751 } 752 bzero(&block[off], sblock.fs_bsize - off); 753 dp = (struct direct *)&block[off]; 754 dp->d_ino = ino; 755 dp->d_reclen = DIRBLKSIZ; 756 dp->d_type = DT_REG; 757 dp->d_namlen = strlen(SUJ_FILE); 758 bcopy(SUJ_FILE, &dp->d_name, strlen(SUJ_FILE)); 759 dir_clear_block(block, off + DIRBLKSIZ); 760 if (bwrite(&disk, fsbtodb(&sblock, blk), block, sblock.fs_bsize) <= 0) { 761 warn("Failed to write dir block"); 762 return (-1); 763 } 764 return (0); 765 } 766 767 /* 768 * Extend a directory block in 'blk' by copying it to a full size block 769 * and inserting the new journal inode into .sujournal. 770 */ 771 static int 772 dir_extend(ufs2_daddr_t blk, ufs2_daddr_t nblk, off_t size, ino_t ino) 773 { 774 char block[MAXBSIZE]; 775 776 if (bread(&disk, fsbtodb(&sblock, blk), block, 777 roundup(size, sblock.fs_fsize)) <= 0) { 778 warn("Failed to read dir block"); 779 return (-1); 780 } 781 dir_clear_block(block, size); 782 if (bwrite(&disk, fsbtodb(&sblock, nblk), block, sblock.fs_bsize) 783 <= 0) { 784 warn("Failed to write dir block"); 785 return (-1); 786 } 787 788 return (dir_insert(nblk, size, ino)); 789 } 790 791 /* 792 * Insert the journal file into the UFS_ROOTINO directory. We always extend the 793 * last frag 794 */ 795 static int 796 journal_insertfile(ino_t ino) 797 { 798 struct ufs1_dinode *dp1; 799 struct ufs2_dinode *dp2; 800 void *ip; 801 ufs2_daddr_t nblk; 802 ufs2_daddr_t blk; 803 ufs_lbn_t lbn; 804 int size; 805 int mode; 806 int off; 807 808 if (getino(&disk, &ip, UFS_ROOTINO, &mode) != 0) { 809 warn("Failed to get root inode"); 810 sbdirty(); 811 return (-1); 812 } 813 dp2 = ip; 814 dp1 = ip; 815 blk = 0; 816 size = 0; 817 nblk = journal_balloc(); 818 if (nblk <= 0) 819 return (-1); 820 /* 821 * For simplicity sake we aways extend the UFS_ROOTINO into a new 822 * directory block rather than searching for space and inserting 823 * into an existing block. However, if the rootino has frags 824 * have to free them and extend the block. 825 */ 826 if (sblock.fs_magic == FS_UFS1_MAGIC) { 827 lbn = lblkno(&sblock, dp1->di_size); 828 off = blkoff(&sblock, dp1->di_size); 829 blk = dp1->di_db[lbn]; 830 size = sblksize(&sblock, (off_t)dp1->di_size, lbn); 831 } else { 832 lbn = lblkno(&sblock, dp2->di_size); 833 off = blkoff(&sblock, dp2->di_size); 834 blk = dp2->di_db[lbn]; 835 size = sblksize(&sblock, (off_t)dp2->di_size, lbn); 836 } 837 if (off != 0) { 838 if (dir_extend(blk, nblk, off, ino) == -1) 839 return (-1); 840 } else { 841 blk = 0; 842 if (dir_insert(nblk, 0, ino) == -1) 843 return (-1); 844 } 845 if (sblock.fs_magic == FS_UFS1_MAGIC) { 846 dp1->di_blocks += (sblock.fs_bsize - size) / DEV_BSIZE; 847 dp1->di_db[lbn] = nblk; 848 dp1->di_size = lblktosize(&sblock, lbn+1); 849 } else { 850 dp2->di_blocks += (sblock.fs_bsize - size) / DEV_BSIZE; 851 dp2->di_db[lbn] = nblk; 852 dp2->di_size = lblktosize(&sblock, lbn+1); 853 } 854 if (putino(&disk) < 0) { 855 warn("Failed to write root inode"); 856 return (-1); 857 } 858 if (cgwrite(&disk) < 0) { 859 warn("Failed to write updated cg"); 860 sbdirty(); 861 return (-1); 862 } 863 if (blk) { 864 if (cgbfree(&disk, blk, size) < 0) { 865 warn("Failed to write cg"); 866 return (-1); 867 } 868 } 869 870 return (0); 871 } 872 873 static int 874 indir_fill(ufs2_daddr_t blk, int level, int *resid) 875 { 876 char indirbuf[MAXBSIZE]; 877 ufs1_daddr_t *bap1; 878 ufs2_daddr_t *bap2; 879 ufs2_daddr_t nblk; 880 int ncnt; 881 int cnt; 882 int i; 883 884 bzero(indirbuf, sizeof(indirbuf)); 885 bap1 = (ufs1_daddr_t *)indirbuf; 886 bap2 = (void *)bap1; 887 cnt = 0; 888 for (i = 0; i < NINDIR(&sblock) && *resid != 0; i++) { 889 nblk = journal_balloc(); 890 if (nblk <= 0) 891 return (-1); 892 cnt++; 893 if (sblock.fs_magic == FS_UFS1_MAGIC) 894 *bap1++ = nblk; 895 else 896 *bap2++ = nblk; 897 if (level != 0) { 898 ncnt = indir_fill(nblk, level - 1, resid); 899 if (ncnt <= 0) 900 return (-1); 901 cnt += ncnt; 902 } else 903 (*resid)--; 904 } 905 if (bwrite(&disk, fsbtodb(&sblock, blk), indirbuf, 906 sblock.fs_bsize) <= 0) { 907 warn("Failed to write indirect"); 908 return (-1); 909 } 910 return (cnt); 911 } 912 913 /* 914 * Clear the flag bits so the journal can be removed. 915 */ 916 static void 917 journal_clear(void) 918 { 919 struct ufs1_dinode *dp1; 920 struct ufs2_dinode *dp2; 921 ino_t ino; 922 int mode; 923 void *ip; 924 925 ino = journal_findfile(); 926 if (ino == (ino_t)-1 || ino == 0) { 927 warnx("Journal file does not exist"); 928 return; 929 } 930 printf("Clearing journal flags from inode %ju\n", (uintmax_t)ino); 931 if (getino(&disk, &ip, ino, &mode) != 0) { 932 warn("Failed to get journal inode"); 933 return; 934 } 935 dp2 = ip; 936 dp1 = ip; 937 if (sblock.fs_magic == FS_UFS1_MAGIC) 938 dp1->di_flags = 0; 939 else 940 dp2->di_flags = 0; 941 if (putino(&disk) < 0) { 942 warn("Failed to write journal inode"); 943 return; 944 } 945 } 946 947 static int 948 journal_alloc(int64_t size) 949 { 950 struct ufs1_dinode *dp1; 951 struct ufs2_dinode *dp2; 952 ufs2_daddr_t blk; 953 void *ip; 954 struct cg *cgp; 955 int resid; 956 ino_t ino; 957 int blks; 958 int mode; 959 time_t utime; 960 int i; 961 962 cgp = &disk.d_cg; 963 ino = 0; 964 965 /* 966 * If the journal file exists we can't allocate it. 967 */ 968 ino = journal_findfile(); 969 if (ino == (ino_t)-1) { 970 warnx("journal_findfile() failed."); 971 return (-1); 972 } 973 if (ino > 0) { 974 warnx("Journal file %s already exists, please remove.", 975 SUJ_FILE); 976 return (-1); 977 } 978 /* 979 * If the user didn't supply a size pick one based on the filesystem 980 * size constrained with hardcoded MIN and MAX values. We opt for 981 * 1/1024th of the filesystem up to MAX but not exceeding one CG and 982 * not less than the MIN. 983 */ 984 if (size == 0) { 985 size = (sblock.fs_size * sblock.fs_bsize) / 1024; 986 size = MIN(SUJ_MAX, size); 987 if (size / sblock.fs_fsize > sblock.fs_fpg) 988 size = sblock.fs_fpg * sblock.fs_fsize; 989 size = MAX(SUJ_MIN, size); 990 /* fsck does not support fragments in journal files. */ 991 size = roundup(size, sblock.fs_bsize); 992 } 993 resid = blocks = size / sblock.fs_bsize; 994 if (sblock.fs_cstotal.cs_nbfree < blocks) { 995 warn("Insufficient free space for %jd byte journal", size); 996 return (-1); 997 } 998 /* 999 * Find a cg with enough blocks to satisfy the journal 1000 * size. Presently the journal does not span cgs. 1001 */ 1002 while (cgread(&disk) == 1) { 1003 if (cgp->cg_cs.cs_nifree == 0) 1004 continue; 1005 ino = cgialloc(&disk); 1006 if (ino <= 0) 1007 break; 1008 printf("Using inode %ju in cg %d for %jd byte journal\n", 1009 (uintmax_t)ino, cgp->cg_cgx, size); 1010 if (getino(&disk, &ip, ino, &mode) != 0) { 1011 warn("Failed to get allocated inode"); 1012 sbdirty(); 1013 goto out; 1014 } 1015 /* 1016 * We leave fields unrelated to the number of allocated 1017 * blocks and size uninitialized. This causes legacy 1018 * fsck implementations to clear the inode. 1019 */ 1020 dp2 = ip; 1021 dp1 = ip; 1022 time(&utime); 1023 if (sblock.fs_magic == FS_UFS1_MAGIC) { 1024 bzero(dp1, sizeof(*dp1)); 1025 dp1->di_size = size; 1026 dp1->di_mode = IFREG | IREAD; 1027 dp1->di_nlink = 1; 1028 dp1->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP; 1029 dp1->di_atime = utime; 1030 dp1->di_mtime = utime; 1031 dp1->di_ctime = utime; 1032 } else { 1033 bzero(dp2, sizeof(*dp2)); 1034 dp2->di_size = size; 1035 dp2->di_mode = IFREG | IREAD; 1036 dp2->di_nlink = 1; 1037 dp2->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP; 1038 dp2->di_atime = utime; 1039 dp2->di_mtime = utime; 1040 dp2->di_ctime = utime; 1041 dp2->di_birthtime = utime; 1042 } 1043 for (i = 0; i < UFS_NDADDR && resid; i++, resid--) { 1044 blk = journal_balloc(); 1045 if (blk <= 0) 1046 goto out; 1047 if (sblock.fs_magic == FS_UFS1_MAGIC) { 1048 dp1->di_db[i] = blk; 1049 dp1->di_blocks++; 1050 } else { 1051 dp2->di_db[i] = blk; 1052 dp2->di_blocks++; 1053 } 1054 } 1055 for (i = 0; i < UFS_NIADDR && resid; i++) { 1056 blk = journal_balloc(); 1057 if (blk <= 0) 1058 goto out; 1059 blks = indir_fill(blk, i, &resid) + 1; 1060 if (blks <= 0) { 1061 sbdirty(); 1062 goto out; 1063 } 1064 if (sblock.fs_magic == FS_UFS1_MAGIC) { 1065 dp1->di_ib[i] = blk; 1066 dp1->di_blocks += blks; 1067 } else { 1068 dp2->di_ib[i] = blk; 1069 dp2->di_blocks += blks; 1070 } 1071 } 1072 if (sblock.fs_magic == FS_UFS1_MAGIC) 1073 dp1->di_blocks *= sblock.fs_bsize / disk.d_bsize; 1074 else 1075 dp2->di_blocks *= sblock.fs_bsize / disk.d_bsize; 1076 if (putino(&disk) < 0) { 1077 warn("Failed to write inode"); 1078 sbdirty(); 1079 return (-1); 1080 } 1081 if (cgwrite(&disk) < 0) { 1082 warn("Failed to write updated cg"); 1083 sbdirty(); 1084 return (-1); 1085 } 1086 if (journal_insertfile(ino) < 0) { 1087 sbdirty(); 1088 return (-1); 1089 } 1090 sblock.fs_sujfree = 0; 1091 return (0); 1092 } 1093 warnx("Insufficient free space for the journal."); 1094 out: 1095 return (-1); 1096 } 1097 1098 static void 1099 usage(void) 1100 { 1101 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", 1102 "usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]", 1103 " [-J enable | disable] [-j enable | disable] [-k metaspace]", 1104 " [-L volname] [-l enable | disable] [-m minfree]", 1105 " [-N enable | disable] [-n enable | disable]", 1106 " [-o space | time] [-p] [-s avgfpdir] [-t enable | disable]", 1107 " special | filesystem"); 1108 exit(2); 1109 } 1110 1111 static void 1112 printfs(void) 1113 { 1114 warnx("POSIX.1e ACLs: (-a) %s", 1115 (sblock.fs_flags & FS_ACLS)? "enabled" : "disabled"); 1116 warnx("NFSv4 ACLs: (-N) %s", 1117 (sblock.fs_flags & FS_NFS4ACLS)? "enabled" : "disabled"); 1118 warnx("MAC multilabel: (-l) %s", 1119 (sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled"); 1120 warnx("soft updates: (-n) %s", 1121 (sblock.fs_flags & FS_DOSOFTDEP)? "enabled" : "disabled"); 1122 warnx("soft update journaling: (-j) %s", 1123 (sblock.fs_flags & FS_SUJ)? "enabled" : "disabled"); 1124 warnx("gjournal: (-J) %s", 1125 (sblock.fs_flags & FS_GJOURNAL)? "enabled" : "disabled"); 1126 warnx("trim: (-t) %s", 1127 (sblock.fs_flags & FS_TRIM)? "enabled" : "disabled"); 1128 warnx("maximum blocks per file in a cylinder group: (-e) %d", 1129 sblock.fs_maxbpg); 1130 warnx("average file size: (-f) %d", 1131 sblock.fs_avgfilesize); 1132 warnx("average number of files in a directory: (-s) %d", 1133 sblock.fs_avgfpdir); 1134 warnx("minimum percentage of free space: (-m) %d%%", 1135 sblock.fs_minfree); 1136 warnx("space to hold for metadata blocks: (-k) %jd", 1137 sblock.fs_metaspace); 1138 warnx("optimization preference: (-o) %s", 1139 sblock.fs_optim == FS_OPTSPACE ? "space" : "time"); 1140 if (sblock.fs_minfree >= MINFREE && 1141 sblock.fs_optim == FS_OPTSPACE) 1142 warnx(OPTWARN, "time", ">=", MINFREE); 1143 if (sblock.fs_minfree < MINFREE && 1144 sblock.fs_optim == FS_OPTTIME) 1145 warnx(OPTWARN, "space", "<", MINFREE); 1146 warnx("volume label: (-L) %s", 1147 sblock.fs_volname); 1148 } 1149