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