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