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