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