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 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, 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 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 static 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 %ju\n", (uintmax_t)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 time_t utime; 927 int i; 928 929 cgp = &disk.d_cg; 930 ino = 0; 931 932 /* 933 * If the journal file exists we can't allocate it. 934 */ 935 ino = journal_findfile(); 936 if (ino == (ino_t)-1) 937 return (-1); 938 if (ino > 0) { 939 warnx("Journal file %s already exists, please remove.", 940 SUJ_FILE); 941 return (-1); 942 } 943 /* 944 * If the user didn't supply a size pick one based on the filesystem 945 * size constrained with hardcoded MIN and MAX values. We opt for 946 * 1/1024th of the filesystem up to MAX but not exceeding one CG and 947 * not less than the MIN. 948 */ 949 if (size == 0) { 950 size = (sblock.fs_size * sblock.fs_bsize) / 1024; 951 size = MIN(SUJ_MAX, size); 952 if (size / sblock.fs_fsize > sblock.fs_fpg) 953 size = sblock.fs_fpg * sblock.fs_fsize; 954 size = MAX(SUJ_MIN, size); 955 /* fsck does not support fragments in journal files. */ 956 size = roundup(size, sblock.fs_bsize); 957 } 958 resid = blocks = size / sblock.fs_bsize; 959 if (sblock.fs_cstotal.cs_nbfree < blocks) { 960 warn("Insufficient free space for %jd byte journal", size); 961 return (-1); 962 } 963 /* 964 * Find a cg with enough blocks to satisfy the journal 965 * size. Presently the journal does not span cgs. 966 */ 967 while (cgread(&disk) == 1) { 968 if (cgp->cg_cs.cs_nifree == 0) 969 continue; 970 ino = cgialloc(&disk); 971 if (ino <= 0) 972 break; 973 printf("Using inode %ju in cg %d for %jd byte journal\n", 974 (uintmax_t)ino, cgp->cg_cgx, size); 975 if (getino(&disk, &ip, ino, &mode) != 0) { 976 warn("Failed to get allocated inode"); 977 sbdirty(); 978 goto out; 979 } 980 /* 981 * We leave fields unrelated to the number of allocated 982 * blocks and size uninitialized. This causes legacy 983 * fsck implementations to clear the inode. 984 */ 985 dp2 = ip; 986 dp1 = ip; 987 time(&utime); 988 if (sblock.fs_magic == FS_UFS1_MAGIC) { 989 bzero(dp1, sizeof(*dp1)); 990 dp1->di_size = size; 991 dp1->di_mode = IFREG | IREAD; 992 dp1->di_nlink = 1; 993 dp1->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP; 994 dp1->di_atime = utime; 995 dp1->di_mtime = utime; 996 dp1->di_ctime = utime; 997 } else { 998 bzero(dp2, sizeof(*dp2)); 999 dp2->di_size = size; 1000 dp2->di_mode = IFREG | IREAD; 1001 dp2->di_nlink = 1; 1002 dp2->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP; 1003 dp2->di_atime = utime; 1004 dp2->di_mtime = utime; 1005 dp2->di_ctime = utime; 1006 dp2->di_birthtime = utime; 1007 } 1008 for (i = 0; i < NDADDR && resid; i++, resid--) { 1009 blk = journal_balloc(); 1010 if (blk <= 0) 1011 goto out; 1012 if (sblock.fs_magic == FS_UFS1_MAGIC) { 1013 dp1->di_db[i] = blk; 1014 dp1->di_blocks++; 1015 } else { 1016 dp2->di_db[i] = blk; 1017 dp2->di_blocks++; 1018 } 1019 } 1020 for (i = 0; i < NIADDR && resid; i++) { 1021 blk = journal_balloc(); 1022 if (blk <= 0) 1023 goto out; 1024 blks = indir_fill(blk, i, &resid) + 1; 1025 if (blks <= 0) { 1026 sbdirty(); 1027 goto out; 1028 } 1029 if (sblock.fs_magic == FS_UFS1_MAGIC) { 1030 dp1->di_ib[i] = blk; 1031 dp1->di_blocks += blks; 1032 } else { 1033 dp2->di_ib[i] = blk; 1034 dp2->di_blocks += blks; 1035 } 1036 } 1037 if (sblock.fs_magic == FS_UFS1_MAGIC) 1038 dp1->di_blocks *= sblock.fs_bsize / disk.d_bsize; 1039 else 1040 dp2->di_blocks *= sblock.fs_bsize / disk.d_bsize; 1041 if (putino(&disk) < 0) { 1042 warn("Failed to write inode"); 1043 sbdirty(); 1044 return (-1); 1045 } 1046 if (cgwrite(&disk) < 0) { 1047 warn("Failed to write updated cg"); 1048 sbdirty(); 1049 return (-1); 1050 } 1051 if (journal_insertfile(ino) < 0) { 1052 sbdirty(); 1053 return (-1); 1054 } 1055 sblock.fs_sujfree = 0; 1056 return (0); 1057 } 1058 warnx("Insufficient free space for the journal."); 1059 out: 1060 return (-1); 1061 } 1062 1063 void 1064 usage(void) 1065 { 1066 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", 1067 "usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]", 1068 " [-J enable | disable] [-j enable | disable]", 1069 " [-L volname] [-l enable | disable] [-m minfree]", 1070 " [-N enable | disable] [-n enable | disable]", 1071 " [-o space | time] [-p] [-s avgfpdir] [-t enable | disable]", 1072 " special | filesystem"); 1073 exit(2); 1074 } 1075 1076 void 1077 printfs(void) 1078 { 1079 warnx("POSIX.1e ACLs: (-a) %s", 1080 (sblock.fs_flags & FS_ACLS)? "enabled" : "disabled"); 1081 warnx("NFSv4 ACLs: (-N) %s", 1082 (sblock.fs_flags & FS_NFS4ACLS)? "enabled" : "disabled"); 1083 warnx("MAC multilabel: (-l) %s", 1084 (sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled"); 1085 warnx("soft updates: (-n) %s", 1086 (sblock.fs_flags & FS_DOSOFTDEP)? "enabled" : "disabled"); 1087 warnx("soft update journaling: (-j) %s", 1088 (sblock.fs_flags & FS_SUJ)? "enabled" : "disabled"); 1089 warnx("gjournal: (-J) %s", 1090 (sblock.fs_flags & FS_GJOURNAL)? "enabled" : "disabled"); 1091 warnx("trim: (-t) %s", 1092 (sblock.fs_flags & FS_TRIM)? "enabled" : "disabled"); 1093 warnx("maximum blocks per file in a cylinder group: (-e) %d", 1094 sblock.fs_maxbpg); 1095 warnx("average file size: (-f) %d", 1096 sblock.fs_avgfilesize); 1097 warnx("average number of files in a directory: (-s) %d", 1098 sblock.fs_avgfpdir); 1099 warnx("minimum percentage of free space: (-m) %d%%", 1100 sblock.fs_minfree); 1101 warnx("optimization preference: (-o) %s", 1102 sblock.fs_optim == FS_OPTSPACE ? "space" : "time"); 1103 if (sblock.fs_minfree >= MINFREE && 1104 sblock.fs_optim == FS_OPTSPACE) 1105 warnx(OPTWARN, "time", ">=", MINFREE); 1106 if (sblock.fs_minfree < MINFREE && 1107 sblock.fs_optim == FS_OPTTIME) 1108 warnx(OPTWARN, "space", "<", MINFREE); 1109 warnx("volume label: (-L) %s", 1110 sblock.fs_volname); 1111 } 1112