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