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