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