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