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