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 /* 692 * Insert the journal at inode 'ino' into directory blk 'blk' at the first 693 * free offset of 'off'. DIRBLKSIZ blocks after off are initialized as 694 * empty. 695 */ 696 static int 697 dir_insert(ufs2_daddr_t blk, off_t off, ino_t ino) 698 { 699 struct direct *dp; 700 char block[MAXBSIZE]; 701 702 if (bread(&disk, fsbtodb(&sblock, blk), block, sblock.fs_bsize) <= 0) { 703 warn("Failed to read dir block"); 704 return (-1); 705 } 706 bzero(&block[off], sblock.fs_bsize - off); 707 dp = (struct direct *)&block[off]; 708 dp->d_ino = ino; 709 dp->d_reclen = DIRBLKSIZ; 710 dp->d_type = DT_REG; 711 dp->d_namlen = strlen(SUJ_FILE); 712 bcopy(SUJ_FILE, &dp->d_name, strlen(SUJ_FILE)); 713 off += DIRBLKSIZ; 714 for (; off < sblock.fs_bsize; off += DIRBLKSIZ) { 715 dp = (struct direct *)&block[off]; 716 dp->d_ino = 0; 717 dp->d_reclen = DIRBLKSIZ; 718 dp->d_type = DT_UNKNOWN; 719 } 720 if (bwrite(&disk, fsbtodb(&sblock, blk), block, sblock.fs_bsize) <= 0) { 721 warn("Failed to write dir block"); 722 return (-1); 723 } 724 return (0); 725 } 726 727 /* 728 * Extend a directory block in 'blk' by copying it to a full size block 729 * and inserting the new journal inode into .sujournal. 730 */ 731 static int 732 dir_extend(ufs2_daddr_t blk, ufs2_daddr_t nblk, off_t size, ino_t ino) 733 { 734 char block[MAXBSIZE]; 735 736 if (bread(&disk, fsbtodb(&sblock, blk), block, size) <= 0) { 737 warn("Failed to read dir block"); 738 return (-1); 739 } 740 if (bwrite(&disk, fsbtodb(&sblock, nblk), block, size) <= 0) { 741 warn("Failed to write dir block"); 742 return (-1); 743 } 744 745 return dir_insert(nblk, size, ino); 746 } 747 748 /* 749 * Insert the journal file into the ROOTINO directory. We always extend the 750 * last frag 751 */ 752 static int 753 journal_insertfile(ino_t ino) 754 { 755 struct ufs1_dinode *dp1; 756 struct ufs2_dinode *dp2; 757 void *ip; 758 ufs2_daddr_t nblk; 759 ufs2_daddr_t blk; 760 ufs_lbn_t lbn; 761 int size; 762 int mode; 763 int off; 764 765 if (getino(&disk, &ip, ROOTINO, &mode) != 0) { 766 warn("Failed to get root inode"); 767 sbdirty(); 768 return (-1); 769 } 770 dp2 = ip; 771 dp1 = ip; 772 blk = 0; 773 size = 0; 774 nblk = journal_balloc(); 775 if (nblk <= 0) 776 return (-1); 777 /* 778 * For simplicity sake we aways extend the ROOTINO into a new 779 * directory block rather than searching for space and inserting 780 * into an existing block. However, if the rootino has frags 781 * have to free them and extend the block. 782 */ 783 if (sblock.fs_magic == FS_UFS1_MAGIC) { 784 lbn = lblkno(&sblock, dp1->di_size); 785 off = blkoff(&sblock, dp1->di_size); 786 blk = dp1->di_db[lbn]; 787 size = sblksize(&sblock, (off_t)dp1->di_size, lbn); 788 } else { 789 lbn = lblkno(&sblock, dp2->di_size); 790 off = blkoff(&sblock, dp2->di_size); 791 blk = dp2->di_db[lbn]; 792 size = sblksize(&sblock, (off_t)dp2->di_size, lbn); 793 } 794 if (off != 0) { 795 if (dir_extend(blk, nblk, off, ino) == -1) 796 return (-1); 797 } else { 798 blk = 0; 799 if (dir_insert(nblk, 0, ino) == -1) 800 return (-1); 801 } 802 if (sblock.fs_magic == FS_UFS1_MAGIC) { 803 dp1->di_blocks += (sblock.fs_bsize - size) / DEV_BSIZE; 804 dp1->di_db[lbn] = nblk; 805 dp1->di_size = lblktosize(&sblock, lbn+1); 806 } else { 807 dp2->di_blocks += (sblock.fs_bsize - size) / DEV_BSIZE; 808 dp2->di_db[lbn] = nblk; 809 dp2->di_size = lblktosize(&sblock, lbn+1); 810 } 811 if (putino(&disk) < 0) { 812 warn("Failed to write root inode"); 813 return (-1); 814 } 815 if (cgwrite(&disk) < 0) { 816 warn("Failed to write updated cg"); 817 sbdirty(); 818 return (-1); 819 } 820 if (blk) { 821 if (cgbfree(&disk, blk, size) < 0) { 822 warn("Failed to write cg"); 823 return (-1); 824 } 825 } 826 827 return (0); 828 } 829 830 static int 831 indir_fill(ufs2_daddr_t blk, int level, int *resid) 832 { 833 char indirbuf[MAXBSIZE]; 834 ufs1_daddr_t *bap1; 835 ufs2_daddr_t *bap2; 836 ufs2_daddr_t nblk; 837 int ncnt; 838 int cnt; 839 int i; 840 841 bzero(indirbuf, sizeof(indirbuf)); 842 bap1 = (ufs1_daddr_t *)indirbuf; 843 bap2 = (void *)bap1; 844 cnt = 0; 845 for (i = 0; i < NINDIR(&sblock) && *resid != 0; i++) { 846 nblk = journal_balloc(); 847 if (nblk <= 0) 848 return (-1); 849 cnt++; 850 if (sblock.fs_magic == FS_UFS1_MAGIC) 851 *bap1++ = nblk; 852 else 853 *bap2++ = nblk; 854 if (level != 0) { 855 ncnt = indir_fill(nblk, level - 1, resid); 856 if (ncnt <= 0) 857 return (-1); 858 cnt += ncnt; 859 } else 860 (*resid)--; 861 } 862 if (bwrite(&disk, fsbtodb(&sblock, blk), indirbuf, 863 sblock.fs_bsize) <= 0) { 864 warn("Failed to write indirect"); 865 return (-1); 866 } 867 return (cnt); 868 } 869 870 /* 871 * Clear the flag bits so the journal can be removed. 872 */ 873 void 874 journal_clear(void) 875 { 876 struct ufs1_dinode *dp1; 877 struct ufs2_dinode *dp2; 878 ino_t ino; 879 int mode; 880 void *ip; 881 882 ino = journal_findfile(); 883 if (ino == (ino_t)-1 || ino == 0) { 884 warnx("Journal file does not exist"); 885 return; 886 } 887 printf("Clearing journal flags from inode %d\n", ino); 888 if (getino(&disk, &ip, ino, &mode) != 0) { 889 warn("Failed to get journal inode"); 890 return; 891 } 892 dp2 = ip; 893 dp1 = ip; 894 if (sblock.fs_magic == FS_UFS1_MAGIC) 895 dp1->di_flags = 0; 896 else 897 dp2->di_flags = 0; 898 if (putino(&disk) < 0) { 899 warn("Failed to write journal inode"); 900 return; 901 } 902 } 903 904 int 905 journal_alloc(int64_t size) 906 { 907 struct ufs1_dinode *dp1; 908 struct ufs2_dinode *dp2; 909 ufs2_daddr_t blk; 910 void *ip; 911 struct cg *cgp; 912 int resid; 913 ino_t ino; 914 int blks; 915 int mode; 916 int i; 917 918 cgp = &disk.d_cg; 919 ino = 0; 920 921 /* 922 * If the journal file exists we can't allocate it. 923 */ 924 ino = journal_findfile(); 925 if (ino == (ino_t)-1) 926 return (-1); 927 if (ino > 0) { 928 warnx("Journal file %s already exists, please remove.", 929 SUJ_FILE); 930 return (-1); 931 } 932 /* 933 * If the user didn't supply a size pick one based on the filesystem 934 * size constrained with hardcoded MIN and MAX values. We opt for 935 * 1/1024th of the filesystem up to MAX but not exceeding one CG and 936 * not less than the MIN. 937 */ 938 if (size == 0) { 939 size = (sblock.fs_size * sblock.fs_bsize) / 1024; 940 size = MIN(SUJ_MAX, size); 941 if (size / sblock.fs_fsize > sblock.fs_fpg) 942 size = sblock.fs_fpg * sblock.fs_fsize; 943 size = MAX(SUJ_MIN, size); 944 /* fsck does not support fragments in journal files. */ 945 size = roundup(size, sblock.fs_bsize); 946 } 947 resid = blocks = size / sblock.fs_bsize; 948 if (sblock.fs_cstotal.cs_nbfree < blocks) { 949 warn("Insufficient free space for %jd byte journal", size); 950 return (-1); 951 } 952 /* 953 * Find a cg with enough blocks to satisfy the journal 954 * size. Presently the journal does not span cgs. 955 */ 956 while (cgread(&disk) == 1) { 957 if (cgp->cg_cs.cs_nifree == 0) 958 continue; 959 ino = cgialloc(&disk); 960 if (ino <= 0) 961 break; 962 printf("Using inode %d in cg %d for %jd byte journal\n", 963 ino, cgp->cg_cgx, size); 964 if (getino(&disk, &ip, ino, &mode) != 0) { 965 warn("Failed to get allocated inode"); 966 sbdirty(); 967 goto out; 968 } 969 /* 970 * We leave fields unrelated to the number of allocated 971 * blocks and size uninitialized. This causes legacy 972 * fsck implementations to clear the inode. 973 */ 974 dp2 = ip; 975 dp1 = ip; 976 if (sblock.fs_magic == FS_UFS1_MAGIC) { 977 bzero(dp1, sizeof(*dp1)); 978 dp1->di_size = size; 979 dp1->di_mode = IFREG | IREAD; 980 dp1->di_nlink = 1; 981 dp1->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP; 982 } else { 983 bzero(dp2, sizeof(*dp2)); 984 dp2->di_size = size; 985 dp2->di_mode = IFREG | IREAD; 986 dp2->di_nlink = 1; 987 dp2->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP; 988 } 989 for (i = 0; i < NDADDR && resid; i++, resid--) { 990 blk = journal_balloc(); 991 if (blk <= 0) 992 goto out; 993 if (sblock.fs_magic == FS_UFS1_MAGIC) { 994 dp1->di_db[i] = blk; 995 dp1->di_blocks++; 996 } else { 997 dp2->di_db[i] = blk; 998 dp2->di_blocks++; 999 } 1000 } 1001 for (i = 0; i < NIADDR && resid; i++) { 1002 blk = journal_balloc(); 1003 if (blk <= 0) 1004 goto out; 1005 blks = indir_fill(blk, i, &resid) + 1; 1006 if (blks <= 0) { 1007 sbdirty(); 1008 goto out; 1009 } 1010 if (sblock.fs_magic == FS_UFS1_MAGIC) { 1011 dp1->di_ib[i] = blk; 1012 dp1->di_blocks += blks; 1013 } else { 1014 dp2->di_ib[i] = blk; 1015 dp2->di_blocks += blks; 1016 } 1017 } 1018 if (sblock.fs_magic == FS_UFS1_MAGIC) 1019 dp1->di_blocks *= sblock.fs_bsize / disk.d_bsize; 1020 else 1021 dp2->di_blocks *= sblock.fs_bsize / disk.d_bsize; 1022 if (putino(&disk) < 0) { 1023 warn("Failed to write inode"); 1024 sbdirty(); 1025 return (-1); 1026 } 1027 if (cgwrite(&disk) < 0) { 1028 warn("Failed to write updated cg"); 1029 sbdirty(); 1030 return (-1); 1031 } 1032 if (journal_insertfile(ino) < 0) { 1033 sbdirty(); 1034 return (-1); 1035 } 1036 sblock.fs_sujfree = 0; 1037 return (0); 1038 } 1039 warnx("Insufficient free space for the journal."); 1040 out: 1041 return (-1); 1042 } 1043 1044 void 1045 usage(void) 1046 { 1047 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", 1048 "usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]", 1049 " [-J enable | disable] [-j enable | disable]", 1050 " [-L volname] [-l enable | disable] [-m minfree]", 1051 " [-N enable | disable] [-n enable | disable]", 1052 " [-o space | time] [-p] [-s avgfpdir] [-t enable | disable]", 1053 " special | filesystem"); 1054 exit(2); 1055 } 1056 1057 void 1058 printfs(void) 1059 { 1060 warnx("POSIX.1e ACLs: (-a) %s", 1061 (sblock.fs_flags & FS_ACLS)? "enabled" : "disabled"); 1062 warnx("NFSv4 ACLs: (-N) %s", 1063 (sblock.fs_flags & FS_NFS4ACLS)? "enabled" : "disabled"); 1064 warnx("MAC multilabel: (-l) %s", 1065 (sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled"); 1066 warnx("soft updates: (-n) %s", 1067 (sblock.fs_flags & FS_DOSOFTDEP)? "enabled" : "disabled"); 1068 warnx("soft update journaling: (-j) %s", 1069 (sblock.fs_flags & FS_SUJ)? "enabled" : "disabled"); 1070 warnx("gjournal: (-J) %s", 1071 (sblock.fs_flags & FS_GJOURNAL)? "enabled" : "disabled"); 1072 warnx("trim: (-t) %s", 1073 (sblock.fs_flags & FS_TRIM)? "enabled" : "disabled"); 1074 warnx("maximum blocks per file in a cylinder group: (-e) %d", 1075 sblock.fs_maxbpg); 1076 warnx("average file size: (-f) %d", 1077 sblock.fs_avgfilesize); 1078 warnx("average number of files in a directory: (-s) %d", 1079 sblock.fs_avgfpdir); 1080 warnx("minimum percentage of free space: (-m) %d%%", 1081 sblock.fs_minfree); 1082 warnx("optimization preference: (-o) %s", 1083 sblock.fs_optim == FS_OPTSPACE ? "space" : "time"); 1084 if (sblock.fs_minfree >= MINFREE && 1085 sblock.fs_optim == FS_OPTSPACE) 1086 warnx(OPTWARN, "time", ">=", MINFREE); 1087 if (sblock.fs_minfree < MINFREE && 1088 sblock.fs_optim == FS_OPTTIME) 1089 warnx(OPTWARN, "space", "<", MINFREE); 1090 warnx("volume label: (-L) %s", 1091 sblock.fs_volname); 1092 } 1093