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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static const char copyright[] = 36 "@(#) Copyright (c) 1983, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)tunefs.c 8.2 (Berkeley) 4/19/94"; 43 #endif 44 static const char rcsid[] = 45 "$FreeBSD$"; 46 #endif /* not lint */ 47 48 /* 49 * tunefs: change layout parameters to an existing file system. 50 */ 51 #include <sys/param.h> 52 #include <sys/mount.h> 53 #include <sys/disklabel.h> 54 #include <sys/stat.h> 55 56 #include <ufs/ufs/ufsmount.h> 57 #include <ufs/ufs/dinode.h> 58 #include <ufs/ffs/fs.h> 59 60 #include <err.h> 61 #include <fcntl.h> 62 #include <fstab.h> 63 #include <paths.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <unistd.h> 68 69 /* the optimization warning string template */ 70 #define OPTWARN "should optimize for %s with minfree %s %d%%" 71 72 union { 73 struct fs sb; 74 char pad[MAXBSIZE]; 75 } sbun; 76 #define sblock sbun.sb 77 78 int fi; 79 long dev_bsize = 1; 80 81 void bwrite(ufs2_daddr_t, const char *, int); 82 int bread(ufs2_daddr_t, char *, int); 83 void getsb(struct fs *, const char *); 84 void putsb(struct fs *, const char *, int); 85 void usage(void); 86 void printfs(void); 87 88 int 89 main(argc, argv) 90 int argc; 91 char *argv[]; 92 { 93 char *special; 94 const char *name; 95 struct stat st; 96 int Aflag = 0, active = 0, aflag = 0; 97 int eflag = 0, fflag = 0, lflag = 0, mflag = 0; 98 int nflag = 0, oflag = 0, pflag = 0, sflag = 0; 99 int evalue = 0, fvalue = 0; 100 int mvalue = 0, ovalue = 0, svalue = 0; 101 char *avalue = NULL, *lvalue = NULL, *nvalue = NULL; 102 struct fstab *fs; 103 const char *chg[2]; 104 char device[MAXPATHLEN]; 105 struct ufs_args args; 106 struct statfs stfs; 107 int found_arg, ch; 108 109 if (argc < 3) 110 usage(); 111 found_arg = 0; /* at least one arg is required */ 112 while ((ch = getopt(argc, argv, "Aa:e:f:l:m:n:o:ps:")) != -1) 113 switch (ch) { 114 case 'A': 115 found_arg = 1; 116 Aflag++; 117 break; 118 case 'a': 119 found_arg = 1; 120 name = "ACLs"; 121 avalue = optarg; 122 if (strcmp(avalue, "enable") && strcmp(avalue, "disable")) { 123 errx(10, "bad %s (options are %s)", name, 124 "`enable' or `disable'"); 125 } 126 aflag = 1; 127 break; 128 case 'e': 129 found_arg = 1; 130 name = "maximum blocks per file in a cylinder group"; 131 evalue = atoi(optarg); 132 if (evalue < 1) 133 errx(10, "%s must be >= 1 (was %s)", name, optarg); 134 eflag = 1; 135 break; 136 case 'f': 137 found_arg = 1; 138 name = "average file size"; 139 fvalue = atoi(optarg); 140 if (fvalue < 1) 141 errx(10, "%s must be >= 1 (was %s)", name, optarg); 142 fflag = 1; 143 break; 144 case 'l': 145 found_arg = 1; 146 name = "multilabel MAC file system"; 147 lvalue = optarg; 148 if (strcmp(lvalue, "enable") && strcmp(lvalue, "disable")) { 149 errx(10, "bad %s (options are %s)", name, 150 "`enable' or `disable'"); 151 } 152 lflag = 1; 153 break; 154 case 'm': 155 found_arg = 1; 156 name = "minimum percentage of free space"; 157 mvalue = atoi(optarg); 158 if (mvalue < 0 || mvalue > 99) 159 errx(10, "bad %s (%s)", name, optarg); 160 mflag = 1; 161 break; 162 case 'n': 163 found_arg = 1; 164 name = "soft updates"; 165 nvalue = optarg; 166 if (strcmp(nvalue, "enable") && strcmp(nvalue, "disable")) { 167 errx(10, "bad %s (options are %s)", 168 name, "`enable' or `disable'"); 169 } 170 nflag = 1; 171 break; 172 case 'o': 173 found_arg = 1; 174 name = "optimization preference"; 175 chg[FS_OPTSPACE] = "space"; 176 chg[FS_OPTTIME] = "time"; 177 if (strcmp(optarg, chg[FS_OPTSPACE]) == 0) 178 ovalue = FS_OPTSPACE; 179 else if (strcmp(optarg, chg[FS_OPTTIME]) == 0) 180 ovalue = FS_OPTTIME; 181 else 182 errx(10, "bad %s (options are `space' or `time')", 183 name); 184 oflag = 1; 185 break; 186 case 'p': 187 found_arg = 1; 188 pflag = 1; 189 break; 190 case 's': 191 found_arg = 1; 192 name = "expected number of files per directory"; 193 svalue = atoi(optarg); 194 if (svalue < 1) 195 errx(10, "%s must be >= 1 (was %s)", name, optarg); 196 sflag = 1; 197 break; 198 default: 199 usage(); 200 } 201 argc -= optind; 202 argv += optind; 203 204 if (found_arg == 0 || argc != 1) 205 usage(); 206 207 special = argv[0]; 208 fs = getfsfile(special); 209 if (fs) { 210 if (statfs(special, &stfs) == 0 && 211 strcmp(special, stfs.f_mntonname) == 0) { 212 active = 1; 213 } 214 special = fs->fs_spec; 215 } 216 again: 217 if (stat(special, &st) < 0) { 218 if (*special != '/') { 219 if (*special == 'r') 220 special++; 221 (void)snprintf(device, sizeof(device), "%s%s", 222 _PATH_DEV, special); 223 special = device; 224 goto again; 225 } 226 err(1, "%s", special); 227 } 228 if (fs == NULL && (st.st_mode & S_IFMT) == S_IFDIR) 229 errx(10, "%s: unknown file system", special); 230 getsb(&sblock, special); 231 232 if (pflag) { 233 printfs(); 234 exit(0); 235 } 236 if (aflag) { 237 name = "ACLs"; 238 if (strcmp(avalue, "enable") == 0) { 239 if (sblock.fs_flags & FS_ACLS) { 240 warnx("%s remains unchanged as enabled", name); 241 } else { 242 sblock.fs_flags |= FS_ACLS; 243 warnx("%s set", name); 244 } 245 } else if (strcmp(avalue, "disable") == 0) { 246 if ((~sblock.fs_flags & FS_ACLS) == 247 FS_ACLS) { 248 warnx("%s remains unchanged as disabled", 249 name); 250 } else { 251 sblock.fs_flags &= ~FS_ACLS; 252 warnx("%s cleared", name); 253 } 254 } 255 } 256 if (eflag) { 257 name = "maximum blocks per file in a cylinder group"; 258 if (sblock.fs_maxbpg == evalue) { 259 warnx("%s remains unchanged as %d", name, evalue); 260 } 261 else { 262 warnx("%s changes from %d to %d", 263 name, sblock.fs_maxbpg, evalue); 264 sblock.fs_maxbpg = evalue; 265 } 266 } 267 if (fflag) { 268 name = "average file size"; 269 if (sblock.fs_avgfilesize == fvalue) { 270 warnx("%s remains unchanged as %d", name, fvalue); 271 } 272 else { 273 warnx("%s changes from %d to %d", 274 name, sblock.fs_avgfilesize, fvalue); 275 sblock.fs_avgfilesize = fvalue; 276 } 277 } 278 if (lflag) { 279 name = "multilabel"; 280 if (strcmp(lvalue, "enable") == 0) { 281 if (sblock.fs_flags & FS_MULTILABEL) { 282 warnx("%s remains unchanged as enabled", name); 283 } else { 284 sblock.fs_flags |= FS_MULTILABEL; 285 warnx("%s set", name); 286 } 287 } else if (strcmp(lvalue, "disable") == 0) { 288 if ((~sblock.fs_flags & FS_MULTILABEL) == 289 FS_MULTILABEL) { 290 warnx("%s remains unchanged as disabled", 291 name); 292 } else { 293 sblock.fs_flags &= ~FS_MULTILABEL; 294 warnx("%s cleared", name); 295 } 296 } 297 } 298 if (mflag) { 299 name = "minimum percentage of free space"; 300 if (sblock.fs_minfree == mvalue) { 301 warnx("%s remains unchanged as %d%%", name, mvalue); 302 } 303 else { 304 warnx("%s changes from %d%% to %d%%", 305 name, sblock.fs_minfree, mvalue); 306 sblock.fs_minfree = mvalue; 307 if (mvalue >= MINFREE && sblock.fs_optim == FS_OPTSPACE) 308 warnx(OPTWARN, "time", ">=", MINFREE); 309 if (mvalue < MINFREE && sblock.fs_optim == FS_OPTTIME) 310 warnx(OPTWARN, "space", "<", MINFREE); 311 } 312 } 313 if (nflag) { 314 name = "soft updates"; 315 if (strcmp(nvalue, "enable") == 0) { 316 if (sblock.fs_flags & FS_DOSOFTDEP) { 317 warnx("%s remains unchanged as enabled", name); 318 } else if (sblock.fs_clean == 0) { 319 warnx("%s cannot be enabled until fsck is run", 320 name); 321 } else { 322 sblock.fs_flags |= FS_DOSOFTDEP; 323 warnx("%s set", name); 324 } 325 } else if (strcmp(nvalue, "disable") == 0) { 326 if ((~sblock.fs_flags & FS_DOSOFTDEP) == FS_DOSOFTDEP) { 327 warnx("%s remains unchanged as disabled", name); 328 } else { 329 sblock.fs_flags &= ~FS_DOSOFTDEP; 330 warnx("%s cleared", name); 331 } 332 } 333 } 334 if (oflag) { 335 name = "optimization preference"; 336 chg[FS_OPTSPACE] = "space"; 337 chg[FS_OPTTIME] = "time"; 338 if (sblock.fs_optim == ovalue) { 339 warnx("%s remains unchanged as %s", name, chg[ovalue]); 340 } 341 else { 342 warnx("%s changes from %s to %s", 343 name, chg[sblock.fs_optim], chg[ovalue]); 344 sblock.fs_optim = ovalue; 345 if (sblock.fs_minfree >= MINFREE && 346 ovalue == FS_OPTSPACE) 347 warnx(OPTWARN, "time", ">=", MINFREE); 348 if (sblock.fs_minfree < MINFREE && 349 ovalue == FS_OPTTIME) 350 warnx(OPTWARN, "space", "<", MINFREE); 351 } 352 } 353 if (sflag) { 354 name = "expected number of files per directory"; 355 if (sblock.fs_avgfpdir == svalue) { 356 warnx("%s remains unchanged as %d", name, svalue); 357 } 358 else { 359 warnx("%s changes from %d to %d", 360 name, sblock.fs_avgfpdir, svalue); 361 sblock.fs_avgfpdir = svalue; 362 } 363 } 364 365 putsb(&sblock, special, Aflag); 366 if (active) { 367 bzero(&args, sizeof(args)); 368 if (mount("ufs", fs->fs_file, 369 stfs.f_flags | MNT_UPDATE | MNT_RELOAD, &args) < 0) 370 err(9, "%s: reload", special); 371 warnx("file system reloaded"); 372 } 373 exit(0); 374 } 375 376 void 377 usage() 378 { 379 fprintf(stderr, "%s\n%s\n%s\n", 380 "usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]", 381 " [-l enable | disable] [-m minfree] [-n enable | disable]", 382 " [-o space | time] [-p] [-s avgfpdir] special | filesystem"); 383 exit(2); 384 } 385 386 /* 387 * Possible superblock locations ordered from most to least likely. 388 */ 389 static int sblock_try[] = SBLOCKSEARCH; 390 static ufs2_daddr_t sblockloc; 391 392 void 393 getsb(fs, file) 394 struct fs *fs; 395 const char *file; 396 { 397 int i; 398 399 fi = open(file, O_RDONLY); 400 if (fi < 0) 401 err(3, "cannot open %s", file); 402 for (i = 0; sblock_try[i] != -1; i++) { 403 if (bread(sblock_try[i], (char *)fs, SBLOCKSIZE)) 404 err(4, "%s: bad super block", file); 405 if ((fs->fs_magic == FS_UFS1_MAGIC || 406 (fs->fs_magic == FS_UFS2_MAGIC && 407 fs->fs_sblockloc == numfrags(fs, sblock_try[i]))) && 408 fs->fs_bsize <= MAXBSIZE && 409 fs->fs_bsize >= sizeof(struct fs)) 410 break; 411 } 412 if (sblock_try[i] == -1) 413 err(5, "Cannot find file system superblock"); 414 dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); 415 sblockloc = sblock_try[i] / dev_bsize; 416 } 417 418 void 419 putsb(fs, file, all) 420 struct fs *fs; 421 const char *file; 422 int all; 423 { 424 int i; 425 426 /* 427 * Re-open the device read-write. Use the read-only file 428 * descriptor as an interlock to prevent the device from 429 * being mounted while we are switching mode. 430 */ 431 i = fi; 432 fi = open(file, O_RDWR); 433 close(i); 434 if (fi < 0) 435 err(3, "cannot open %s", file); 436 bwrite(sblockloc, (const char *)fs, SBLOCKSIZE); 437 if (all) 438 for (i = 0; i < fs->fs_ncg; i++) 439 bwrite(fsbtodb(fs, cgsblock(fs, i)), 440 (const char *)fs, SBLOCKSIZE); 441 close(fi); 442 } 443 444 void 445 printfs() 446 { 447 warnx("ACLs: (-a) %s", 448 (sblock.fs_flags & FS_ACLS)? "enabled" : "disabled"); 449 warnx("MAC multilabel: (-l) %s", 450 (sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled"); 451 warnx("soft updates: (-n) %s", 452 (sblock.fs_flags & FS_DOSOFTDEP)? "enabled" : "disabled"); 453 warnx("maximum blocks per file in a cylinder group: (-e) %d", 454 sblock.fs_maxbpg); 455 warnx("average file size: (-f) %d", 456 sblock.fs_avgfilesize); 457 warnx("average number of files in a directory: (-s) %d", 458 sblock.fs_avgfpdir); 459 warnx("minimum percentage of free space: (-m) %d%%", 460 sblock.fs_minfree); 461 warnx("optimization preference: (-o) %s", 462 sblock.fs_optim == FS_OPTSPACE ? "space" : "time"); 463 if (sblock.fs_minfree >= MINFREE && 464 sblock.fs_optim == FS_OPTSPACE) 465 warnx(OPTWARN, "time", ">=", MINFREE); 466 if (sblock.fs_minfree < MINFREE && 467 sblock.fs_optim == FS_OPTTIME) 468 warnx(OPTWARN, "space", "<", MINFREE); 469 } 470 471 void 472 bwrite(blk, buf, size) 473 ufs2_daddr_t blk; 474 const char *buf; 475 int size; 476 { 477 478 if (lseek(fi, (off_t)blk * dev_bsize, SEEK_SET) < 0) 479 err(6, "FS SEEK"); 480 if (write(fi, buf, size) != size) 481 err(7, "FS WRITE"); 482 } 483 484 int 485 bread(bno, buf, cnt) 486 ufs2_daddr_t bno; 487 char *buf; 488 int cnt; 489 { 490 int i; 491 492 if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0) 493 return(1); 494 if ((i = read(fi, buf, cnt)) != cnt) { 495 for(i=0; i<sblock.fs_bsize; i++) 496 buf[i] = 0; 497 return (1); 498 } 499 return (0); 500 } 501