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