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 special = disk.d_name; 284 if (statfs(special, &stfs) == 0 && 285 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_DOSOFTDEP | FS_SUJ); 362 sblock.fs_sujfree = 0; 363 warnx("%s cleared, " 364 "remove .sujournal to reclaim space", name); 365 } 366 } 367 } 368 if (Jflag) { 369 name = "gjournal"; 370 if (strcmp(Jvalue, "enable") == 0) { 371 if (sblock.fs_flags & FS_GJOURNAL) { 372 warnx("%s remains unchanged as enabled", name); 373 } else { 374 sblock.fs_flags |= FS_GJOURNAL; 375 warnx("%s set", name); 376 } 377 } else if (strcmp(Jvalue, "disable") == 0) { 378 if ((~sblock.fs_flags & FS_GJOURNAL) == 379 FS_GJOURNAL) { 380 warnx("%s remains unchanged as disabled", 381 name); 382 } else { 383 sblock.fs_flags &= ~FS_GJOURNAL; 384 warnx("%s cleared", name); 385 } 386 } 387 } 388 if (lflag) { 389 name = "multilabel"; 390 if (strcmp(lvalue, "enable") == 0) { 391 if (sblock.fs_flags & FS_MULTILABEL) { 392 warnx("%s remains unchanged as enabled", name); 393 } else { 394 sblock.fs_flags |= FS_MULTILABEL; 395 warnx("%s set", name); 396 } 397 } else if (strcmp(lvalue, "disable") == 0) { 398 if ((~sblock.fs_flags & FS_MULTILABEL) == 399 FS_MULTILABEL) { 400 warnx("%s remains unchanged as disabled", 401 name); 402 } else { 403 sblock.fs_flags &= ~FS_MULTILABEL; 404 warnx("%s cleared", name); 405 } 406 } 407 } 408 if (mflag) { 409 name = "minimum percentage of free space"; 410 if (sblock.fs_minfree == mvalue) 411 warnx("%s remains unchanged as %d%%", name, mvalue); 412 else { 413 warnx("%s changes from %d%% to %d%%", 414 name, sblock.fs_minfree, mvalue); 415 sblock.fs_minfree = mvalue; 416 if (mvalue >= MINFREE && sblock.fs_optim == FS_OPTSPACE) 417 warnx(OPTWARN, "time", ">=", MINFREE); 418 if (mvalue < MINFREE && sblock.fs_optim == FS_OPTTIME) 419 warnx(OPTWARN, "space", "<", MINFREE); 420 } 421 } 422 if (Nflag) { 423 name = "NFSv4 ACLs"; 424 if (strcmp(Nvalue, "enable") == 0) { 425 if (sblock.fs_flags & FS_NFS4ACLS) { 426 warnx("%s remains unchanged as enabled", name); 427 } else if (sblock.fs_flags & FS_ACLS) { 428 warnx("%s and POSIX.1e ACLs are mutually " 429 "exclusive", name); 430 } else { 431 sblock.fs_flags |= FS_NFS4ACLS; 432 warnx("%s set", name); 433 } 434 } else if (strcmp(Nvalue, "disable") == 0) { 435 if ((~sblock.fs_flags & FS_NFS4ACLS) == 436 FS_NFS4ACLS) { 437 warnx("%s remains unchanged as disabled", 438 name); 439 } else { 440 sblock.fs_flags &= ~FS_NFS4ACLS; 441 warnx("%s cleared", name); 442 } 443 } 444 } 445 if (nflag) { 446 name = "soft updates"; 447 if (strcmp(nvalue, "enable") == 0) { 448 if (sblock.fs_flags & FS_DOSOFTDEP) 449 warnx("%s remains unchanged as enabled", name); 450 else if (sblock.fs_clean == 0) { 451 warnx("%s cannot be enabled until fsck is run", 452 name); 453 } else { 454 sblock.fs_flags |= FS_DOSOFTDEP; 455 warnx("%s set", name); 456 } 457 } else if (strcmp(nvalue, "disable") == 0) { 458 if ((~sblock.fs_flags & FS_DOSOFTDEP) == FS_DOSOFTDEP) 459 warnx("%s remains unchanged as disabled", name); 460 else { 461 sblock.fs_flags &= ~FS_DOSOFTDEP; 462 warnx("%s cleared", name); 463 } 464 } 465 } 466 if (oflag) { 467 name = "optimization preference"; 468 chg[FS_OPTSPACE] = "space"; 469 chg[FS_OPTTIME] = "time"; 470 if (sblock.fs_optim == ovalue) 471 warnx("%s remains unchanged as %s", name, chg[ovalue]); 472 else { 473 warnx("%s changes from %s to %s", 474 name, chg[sblock.fs_optim], chg[ovalue]); 475 sblock.fs_optim = ovalue; 476 if (sblock.fs_minfree >= MINFREE && 477 ovalue == FS_OPTSPACE) 478 warnx(OPTWARN, "time", ">=", MINFREE); 479 if (sblock.fs_minfree < MINFREE && ovalue == FS_OPTTIME) 480 warnx(OPTWARN, "space", "<", MINFREE); 481 } 482 } 483 if (sflag) { 484 name = "expected number of files per directory"; 485 if (sblock.fs_avgfpdir == (unsigned)svalue) { 486 warnx("%s remains unchanged as %d", name, svalue); 487 } 488 else { 489 warnx("%s changes from %d to %d", 490 name, sblock.fs_avgfpdir, svalue); 491 sblock.fs_avgfpdir = svalue; 492 } 493 } 494 495 if (sbwrite(&disk, Aflag) == -1) 496 goto err; 497 ufs_disk_close(&disk); 498 if (active) { 499 bzero(&args, sizeof(args)); 500 if (mount("ufs", on, 501 stfs.f_flags | MNT_UPDATE | MNT_RELOAD, &args) < 0) 502 err(9, "%s: reload", special); 503 warnx("file system reloaded"); 504 } 505 exit(0); 506 err: 507 if (disk.d_error != NULL) 508 errx(11, "%s: %s", special, disk.d_error); 509 else 510 err(12, "%s", special); 511 } 512 513 void 514 sbdirty(void) 515 { 516 disk.d_fs.fs_flags |= FS_UNCLEAN | FS_NEEDSFSCK; 517 disk.d_fs.fs_clean = 0; 518 } 519 520 int blocks; 521 static char clrbuf[MAXBSIZE]; 522 523 static ufs2_daddr_t 524 journal_balloc(void) 525 { 526 ufs2_daddr_t blk; 527 struct cg *cgp; 528 int valid; 529 static int contig = 1; 530 531 cgp = &disk.d_cg; 532 for (;;) { 533 blk = cgballoc(&disk); 534 if (blk > 0) 535 break; 536 /* 537 * If we failed to allocate a block from this cg, move to 538 * the next. 539 */ 540 if (cgwrite(&disk) < 0) { 541 warn("Failed to write updated cg"); 542 return (-1); 543 } 544 while ((valid = cgread(&disk)) == 1) { 545 /* 546 * Try to minimize fragmentation by requiring a minimum 547 * number of blocks present. 548 */ 549 if (cgp->cg_cs.cs_nbfree > blocks / 8) 550 break; 551 if (contig == 0 && cgp->cg_cs.cs_nbfree) 552 break; 553 } 554 if (valid) 555 continue; 556 /* 557 * Try once through looking only for large contiguous regions 558 * and again taking any space we can find. 559 */ 560 if (contig) { 561 contig = 0; 562 disk.d_ccg = 0; 563 warnx("Journal file fragmented."); 564 continue; 565 } 566 warnx("Failed to find sufficient free blocks for the journal"); 567 return -1; 568 } 569 if (bwrite(&disk, fsbtodb(&sblock, blk), clrbuf, 570 sblock.fs_bsize) <= 0) { 571 warn("Failed to initialize new block"); 572 return -1; 573 } 574 return (blk); 575 } 576 577 /* 578 * Search a directory block for the SUJ_FILE. 579 */ 580 static ino_t 581 dir_search(ufs2_daddr_t blk, int bytes) 582 { 583 char block[MAXBSIZE]; 584 struct direct *dp; 585 int off; 586 587 if (bread(&disk, fsbtodb(&sblock, blk), block, bytes) <= 0) { 588 warn("Failed to read dir block"); 589 return (-1); 590 } 591 for (off = 0; off < bytes; off += dp->d_reclen) { 592 dp = (struct direct *)&block[off]; 593 if (dp->d_reclen == 0) 594 break; 595 if (dp->d_ino == 0) 596 continue; 597 if (dp->d_namlen != strlen(SUJ_FILE)) 598 continue; 599 if (bcmp(dp->d_name, SUJ_FILE, dp->d_namlen) != 0) 600 continue; 601 return (dp->d_ino); 602 } 603 604 return (0); 605 } 606 607 /* 608 * Search in the ROOTINO for the SUJ_FILE. If it exists we can not enable 609 * journaling. 610 */ 611 static ino_t 612 journal_findfile(void) 613 { 614 struct ufs1_dinode *dp1; 615 struct ufs2_dinode *dp2; 616 ino_t ino; 617 int mode; 618 void *ip; 619 int i; 620 621 if (getino(&disk, &ip, ROOTINO, &mode) != 0) { 622 warn("Failed to get root inode"); 623 return (-1); 624 } 625 dp2 = ip; 626 dp1 = ip; 627 if (sblock.fs_magic == FS_UFS1_MAGIC) { 628 if ((off_t)dp1->di_size >= lblktosize(&sblock, NDADDR)) { 629 warnx("ROOTINO extends beyond direct blocks."); 630 return (-1); 631 } 632 for (i = 0; i < NDADDR; i++) { 633 if (dp1->di_db[i] == 0) 634 break; 635 if ((ino = dir_search(dp1->di_db[i], 636 sblksize(&sblock, (off_t)dp1->di_size, i))) != 0) 637 return (ino); 638 } 639 } else { 640 if ((off_t)dp1->di_size >= lblktosize(&sblock, NDADDR)) { 641 warnx("ROOTINO extends beyond direct blocks."); 642 return (-1); 643 } 644 for (i = 0; i < NDADDR; i++) { 645 if (dp2->di_db[i] == 0) 646 break; 647 if ((ino = dir_search(dp2->di_db[i], 648 sblksize(&sblock, (off_t)dp2->di_size, i))) != 0) 649 return (ino); 650 } 651 } 652 653 return (0); 654 } 655 656 /* 657 * Insert the journal at inode 'ino' into directory blk 'blk' at the first 658 * free offset of 'off'. DIRBLKSIZ blocks after off are initialized as 659 * empty. 660 */ 661 static int 662 dir_insert(ufs2_daddr_t blk, off_t off, ino_t ino) 663 { 664 struct direct *dp; 665 char block[MAXBSIZE]; 666 667 if (bread(&disk, fsbtodb(&sblock, blk), block, sblock.fs_bsize) <= 0) { 668 warn("Failed to read dir block"); 669 return (-1); 670 } 671 bzero(&block[off], sblock.fs_bsize - off); 672 dp = (struct direct *)&block[off]; 673 dp->d_ino = ino; 674 dp->d_reclen = DIRBLKSIZ; 675 dp->d_type = DT_REG; 676 dp->d_namlen = strlen(SUJ_FILE); 677 bcopy(SUJ_FILE, &dp->d_name, strlen(SUJ_FILE)); 678 off += DIRBLKSIZ; 679 for (; off < sblock.fs_bsize; off += DIRBLKSIZ) { 680 dp = (struct direct *)&block[off]; 681 dp->d_ino = 0; 682 dp->d_reclen = DIRBLKSIZ; 683 dp->d_type = DT_UNKNOWN; 684 } 685 if (bwrite(&disk, fsbtodb(&sblock, blk), block, sblock.fs_bsize) <= 0) { 686 warn("Failed to write dir block"); 687 return (-1); 688 } 689 return (0); 690 } 691 692 /* 693 * Extend a directory block in 'blk' by copying it to a full size block 694 * and inserting the new journal inode into .sujournal. 695 */ 696 static int 697 dir_extend(ufs2_daddr_t blk, ufs2_daddr_t nblk, off_t size, ino_t ino) 698 { 699 char block[MAXBSIZE]; 700 701 if (bread(&disk, fsbtodb(&sblock, blk), block, size) <= 0) { 702 warn("Failed to read dir block"); 703 return (-1); 704 } 705 if (bwrite(&disk, fsbtodb(&sblock, nblk), block, size) <= 0) { 706 warn("Failed to write dir block"); 707 return (-1); 708 } 709 710 return dir_insert(nblk, size, ino); 711 } 712 713 /* 714 * Insert the journal file into the ROOTINO directory. We always extend the 715 * last frag 716 */ 717 static int 718 journal_insertfile(ino_t ino) 719 { 720 struct ufs1_dinode *dp1; 721 struct ufs2_dinode *dp2; 722 void *ip; 723 ufs2_daddr_t nblk; 724 ufs2_daddr_t blk; 725 ufs_lbn_t lbn; 726 int size; 727 int mode; 728 int off; 729 730 if (getino(&disk, &ip, ROOTINO, &mode) != 0) { 731 warn("Failed to get root inode"); 732 sbdirty(); 733 return (-1); 734 } 735 dp2 = ip; 736 dp1 = ip; 737 blk = 0; 738 size = 0; 739 nblk = journal_balloc(); 740 if (nblk <= 0) 741 return (-1); 742 /* 743 * For simplicity sake we aways extend the ROOTINO into a new 744 * directory block rather than searching for space and inserting 745 * into an existing block. However, if the rootino has frags 746 * have to free them and extend the block. 747 */ 748 if (sblock.fs_magic == FS_UFS1_MAGIC) { 749 lbn = lblkno(&sblock, dp1->di_size); 750 off = blkoff(&sblock, dp1->di_size); 751 blk = dp1->di_db[lbn]; 752 size = sblksize(&sblock, (off_t)dp1->di_size, lbn); 753 } else { 754 lbn = lblkno(&sblock, dp2->di_size); 755 off = blkoff(&sblock, dp2->di_size); 756 blk = dp2->di_db[lbn]; 757 size = sblksize(&sblock, (off_t)dp2->di_size, lbn); 758 } 759 if (off != 0) { 760 if (dir_extend(blk, nblk, off, ino) == -1) 761 return (-1); 762 } else { 763 blk = 0; 764 if (dir_insert(nblk, 0, ino) == -1) 765 return (-1); 766 } 767 if (sblock.fs_magic == FS_UFS1_MAGIC) { 768 dp1->di_blocks += (sblock.fs_bsize - size) / DEV_BSIZE; 769 dp1->di_db[lbn] = nblk; 770 dp1->di_size = lblktosize(&sblock, lbn+1); 771 } else { 772 dp2->di_blocks += (sblock.fs_bsize - size) / DEV_BSIZE; 773 dp2->di_db[lbn] = nblk; 774 dp2->di_size = lblktosize(&sblock, lbn+1); 775 } 776 if (putino(&disk) < 0) { 777 warn("Failed to write root inode"); 778 return (-1); 779 } 780 if (cgwrite(&disk) < 0) { 781 warn("Failed to write updated cg"); 782 sbdirty(); 783 return (-1); 784 } 785 if (blk) { 786 if (cgbfree(&disk, blk, size) < 0) { 787 warn("Failed to write cg"); 788 return (-1); 789 } 790 } 791 792 return (0); 793 } 794 795 static int 796 indir_fill(ufs2_daddr_t blk, int level, int *resid) 797 { 798 char indirbuf[MAXBSIZE]; 799 ufs1_daddr_t *bap1; 800 ufs2_daddr_t *bap2; 801 ufs2_daddr_t nblk; 802 int ncnt; 803 int cnt; 804 int i; 805 806 bzero(indirbuf, sizeof(indirbuf)); 807 bap1 = (ufs1_daddr_t *)indirbuf; 808 bap2 = (void *)bap1; 809 cnt = 0; 810 for (i = 0; i < NINDIR(&sblock) && *resid != 0; i++) { 811 nblk = journal_balloc(); 812 if (nblk <= 0) 813 return (-1); 814 cnt++; 815 if (sblock.fs_magic == FS_UFS1_MAGIC) 816 *bap1++ = nblk; 817 else 818 *bap2++ = nblk; 819 if (level != 0) { 820 ncnt = indir_fill(nblk, level - 1, resid); 821 if (ncnt <= 0) 822 return (-1); 823 cnt += ncnt; 824 } else 825 (*resid)--; 826 } 827 if (bwrite(&disk, fsbtodb(&sblock, blk), indirbuf, 828 sblock.fs_bsize) <= 0) { 829 warn("Failed to write indirect"); 830 return (-1); 831 } 832 return (cnt); 833 } 834 835 /* 836 * Clear the flag bits so the journal can be removed. 837 */ 838 void 839 journal_clear(void) 840 { 841 struct ufs1_dinode *dp1; 842 struct ufs2_dinode *dp2; 843 ino_t ino; 844 int mode; 845 void *ip; 846 847 ino = journal_findfile(); 848 if (ino == (ino_t)-1 || ino == 0) { 849 warnx("Journal file does not exist"); 850 return; 851 } 852 printf("Clearing journal flags from inode %d\n", ino); 853 if (getino(&disk, &ip, ino, &mode) != 0) { 854 warn("Failed to get journal inode"); 855 return; 856 } 857 dp2 = ip; 858 dp1 = ip; 859 if (sblock.fs_magic == FS_UFS1_MAGIC) 860 dp1->di_flags = 0; 861 else 862 dp2->di_flags = 0; 863 if (putino(&disk) < 0) { 864 warn("Failed to write journal inode"); 865 return; 866 } 867 } 868 869 int 870 journal_alloc(int64_t size) 871 { 872 struct ufs1_dinode *dp1; 873 struct ufs2_dinode *dp2; 874 ufs2_daddr_t blk; 875 void *ip; 876 struct cg *cgp; 877 int resid; 878 ino_t ino; 879 int blks; 880 int mode; 881 int i; 882 883 cgp = &disk.d_cg; 884 ino = 0; 885 886 /* 887 * If the journal file exists we can't allocate it. 888 */ 889 ino = journal_findfile(); 890 if (ino == (ino_t)-1) 891 return (-1); 892 if (ino > 0) { 893 warnx("Journal file %s already exists, please remove.", 894 SUJ_FILE); 895 return (-1); 896 } 897 /* 898 * If the user didn't supply a size pick one based on the filesystem 899 * size constrained with hardcoded MIN and MAX values. We opt for 900 * 1/1024th of the filesystem up to MAX but not exceeding one CG and 901 * not less than the MIN. 902 */ 903 if (size == 0) { 904 size = (sblock.fs_size * sblock.fs_bsize) / 1024; 905 size = MIN(SUJ_MAX, size); 906 if (size / sblock.fs_fsize > sblock.fs_fpg) 907 size = sblock.fs_fpg * sblock.fs_fsize; 908 size = MAX(SUJ_MIN, size); 909 } 910 resid = blocks = size / sblock.fs_bsize; 911 if (sblock.fs_cstotal.cs_nbfree < blocks) { 912 warn("Insufficient free space for %jd byte journal", size); 913 return (-1); 914 } 915 /* 916 * Find a cg with enough blocks to satisfy the journal 917 * size. Presently the journal does not span cgs. 918 */ 919 while (cgread(&disk) == 1) { 920 if (cgp->cg_cs.cs_nifree == 0) 921 continue; 922 ino = cgialloc(&disk); 923 if (ino <= 0) 924 break; 925 printf("Using inode %d in cg %d for %jd byte journal\n", 926 ino, cgp->cg_cgx, size); 927 if (getino(&disk, &ip, ino, &mode) != 0) { 928 warn("Failed to get allocated inode"); 929 sbdirty(); 930 goto out; 931 } 932 /* 933 * We leave fields unrelated to the number of allocated 934 * blocks and size uninitialized. This causes legacy 935 * fsck implementations to clear the inode. 936 */ 937 dp2 = ip; 938 dp1 = ip; 939 if (sblock.fs_magic == FS_UFS1_MAGIC) { 940 bzero(dp1, sizeof(*dp1)); 941 dp1->di_size = size; 942 dp1->di_mode = IFREG | IREAD; 943 dp1->di_nlink = 1; 944 dp1->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP; 945 } else { 946 bzero(dp2, sizeof(*dp2)); 947 dp2->di_size = size; 948 dp2->di_mode = IFREG | IREAD; 949 dp2->di_nlink = 1; 950 dp2->di_flags = SF_IMMUTABLE | SF_NOUNLINK | UF_NODUMP; 951 } 952 for (i = 0; i < NDADDR && resid; i++, resid--) { 953 blk = journal_balloc(); 954 if (blk <= 0) 955 goto out; 956 if (sblock.fs_magic == FS_UFS1_MAGIC) { 957 dp1->di_db[i] = blk; 958 dp1->di_blocks++; 959 } else { 960 dp2->di_db[i] = blk; 961 dp2->di_blocks++; 962 } 963 } 964 for (i = 0; i < NIADDR && resid; i++) { 965 blk = journal_balloc(); 966 if (blk <= 0) 967 goto out; 968 blks = indir_fill(blk, i, &resid) + 1; 969 if (blks <= 0) { 970 sbdirty(); 971 goto out; 972 } 973 if (sblock.fs_magic == FS_UFS1_MAGIC) { 974 dp1->di_ib[i] = blk; 975 dp1->di_blocks += blks; 976 } else { 977 dp2->di_ib[i] = blk; 978 dp2->di_blocks += blks; 979 } 980 } 981 if (sblock.fs_magic == FS_UFS1_MAGIC) 982 dp1->di_blocks *= sblock.fs_bsize / disk.d_bsize; 983 else 984 dp2->di_blocks *= sblock.fs_bsize / disk.d_bsize; 985 if (putino(&disk) < 0) { 986 warn("Failed to write inode"); 987 sbdirty(); 988 return (-1); 989 } 990 if (cgwrite(&disk) < 0) { 991 warn("Failed to write updated cg"); 992 sbdirty(); 993 return (-1); 994 } 995 if (journal_insertfile(ino) < 0) { 996 sbdirty(); 997 return (-1); 998 } 999 sblock.fs_sujfree = 0; 1000 return (0); 1001 } 1002 warnx("Insufficient free space for the journal."); 1003 out: 1004 return (-1); 1005 } 1006 1007 void 1008 usage(void) 1009 { 1010 fprintf(stderr, "%s\n%s\n%s\n%s\n", 1011 "usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]", 1012 " [-J enable | disable ] [-L volname] [-l enable | disable]", 1013 " [-m minfree] [-N enable | disable] [-n enable | disable]", 1014 " [-o space | time] [-p] [-s avgfpdir] special | filesystem"); 1015 exit(2); 1016 } 1017 1018 void 1019 printfs(void) 1020 { 1021 warnx("POSIX.1e ACLs: (-a) %s", 1022 (sblock.fs_flags & FS_ACLS)? "enabled" : "disabled"); 1023 warnx("NFSv4 ACLs: (-N) %s", 1024 (sblock.fs_flags & FS_NFS4ACLS)? "enabled" : "disabled"); 1025 warnx("MAC multilabel: (-l) %s", 1026 (sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled"); 1027 warnx("soft updates: (-n) %s", 1028 (sblock.fs_flags & FS_DOSOFTDEP)? "enabled" : "disabled"); 1029 warnx("soft update journaling: (-j) %s", 1030 (sblock.fs_flags & FS_SUJ)? "enabled" : "disabled"); 1031 warnx("gjournal: (-J) %s", 1032 (sblock.fs_flags & FS_GJOURNAL)? "enabled" : "disabled"); 1033 warnx("maximum blocks per file in a cylinder group: (-e) %d", 1034 sblock.fs_maxbpg); 1035 warnx("average file size: (-f) %d", 1036 sblock.fs_avgfilesize); 1037 warnx("average number of files in a directory: (-s) %d", 1038 sblock.fs_avgfpdir); 1039 warnx("minimum percentage of free space: (-m) %d%%", 1040 sblock.fs_minfree); 1041 warnx("optimization preference: (-o) %s", 1042 sblock.fs_optim == FS_OPTSPACE ? "space" : "time"); 1043 if (sblock.fs_minfree >= MINFREE && 1044 sblock.fs_optim == FS_OPTSPACE) 1045 warnx(OPTWARN, "time", ">=", MINFREE); 1046 if (sblock.fs_minfree < MINFREE && 1047 sblock.fs_optim == FS_OPTTIME) 1048 warnx(OPTWARN, "space", "<", MINFREE); 1049 warnx("volume label: (-L) %s", 1050 sblock.fs_volname); 1051 } 1052