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 filesystem. 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/ffs/fs.h> 57 #include <ufs/ufs/ufsmount.h> 58 59 #include <err.h> 60 #include <fcntl.h> 61 #include <fstab.h> 62 #include <paths.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <unistd.h> 67 68 /* the optimization warning string template */ 69 #define OPTWARN "should optimize for %s with minfree %s %d%%" 70 71 union { 72 struct fs sb; 73 char pad[MAXBSIZE]; 74 } sbun; 75 #define sblock sbun.sb 76 77 int fi; 78 long dev_bsize = 1; 79 80 void bwrite(daddr_t, const char *, int); 81 int bread(daddr_t, char *, int); 82 void getsb(struct fs *, const char *); 83 void putsb(struct fs *, const char *, int); 84 void usage(void); 85 void printfs(void); 86 87 int 88 main(argc, argv) 89 int argc; 90 char *argv[]; 91 { 92 char *special; 93 const char *name; 94 struct stat st; 95 int Aflag = 0, active = 0; 96 int aflag = 0, dflag = 0, eflag = 0, fflag = 0, mflag = 0; 97 int nflag = 0, oflag = 0, pflag = 0, sflag = 0; 98 int avalue = 0, dvalue = 0, evalue = 0, fvalue = 0; 99 int mvalue = 0, ovalue = 0, svalue = 0; 100 char *nvalue = NULL; 101 struct fstab *fs; 102 const char *chg[2]; 103 char device[MAXPATHLEN]; 104 struct ufs_args args; 105 struct statfs stfs; 106 int found_arg, ch; 107 108 if (argc < 3) 109 usage(); 110 found_arg = 0; /* at least one arg is required */ 111 while ((ch = getopt(argc, argv, "Aa:d:e:f:m:n:o:ps:")) != -1) 112 switch (ch) { 113 case 'A': 114 found_arg = 1; 115 Aflag++; 116 break; 117 case 'a': 118 found_arg = 1; 119 name = "maximum contiguous block count"; 120 avalue = atoi(optarg); 121 if (avalue < 1) 122 errx(10, "%s must be >= 1 (was %s)", name, optarg); 123 aflag = 1; 124 break; 125 case 'd': 126 found_arg = 1; 127 name = "rotational delay between contiguous blocks"; 128 dvalue = atoi(optarg); 129 dflag = 1; 130 break; 131 case 'e': 132 found_arg = 1; 133 name = "maximum blocks per file in a cylinder group"; 134 evalue = atoi(optarg); 135 if (evalue < 1) 136 errx(10, "%s must be >= 1 (was %s)", name, optarg); 137 eflag = 1; 138 break; 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)", name, optarg); 145 fflag = 1; 146 break; 147 case 'm': 148 found_arg = 1; 149 name = "minimum percentage of free space"; 150 mvalue = atoi(optarg); 151 if (mvalue < 0 || mvalue > 99) 152 errx(10, "bad %s (%s)", name, optarg); 153 mflag = 1; 154 break; 155 case 'n': 156 found_arg = 1; 157 name = "soft updates"; 158 nvalue = optarg; 159 if (strcmp(nvalue, "enable") && strcmp(nvalue, "disable")) { 160 errx(10, "bad %s (options are %s)", 161 name, "`enable' or `disable'"); 162 } 163 nflag = 1; 164 break; 165 case 'o': 166 found_arg = 1; 167 name = "optimization preference"; 168 chg[FS_OPTSPACE] = "space"; 169 chg[FS_OPTTIME] = "time"; 170 if (strcmp(optarg, chg[FS_OPTSPACE]) == 0) 171 ovalue = FS_OPTSPACE; 172 else if (strcmp(optarg, chg[FS_OPTTIME]) == 0) 173 ovalue = FS_OPTTIME; 174 else 175 errx(10, "bad %s (options are `space' or `time')", 176 name); 177 oflag = 1; 178 break; 179 case 'p': 180 found_arg = 1; 181 pflag = 1; 182 break; 183 case 's': 184 found_arg = 1; 185 name = "expected number of files per directory"; 186 svalue = atoi(optarg); 187 if (svalue < 1) 188 errx(10, "%s must be >= 1 (was %s)", name, optarg); 189 sflag = 1; 190 break; 191 default: 192 usage(); 193 } 194 argc -= optind; 195 argv += optind; 196 197 if (found_arg == 0 || argc != 1) 198 usage(); 199 200 special = argv[0]; 201 fs = getfsfile(special); 202 if (fs) { 203 if (statfs(special, &stfs) == 0 && 204 strcmp(special, stfs.f_mntonname) == 0) { 205 active = 1; 206 } 207 special = fs->fs_spec; 208 } 209 again: 210 if (stat(special, &st) < 0) { 211 if (*special != '/') { 212 if (*special == 'r') 213 special++; 214 (void)snprintf(device, sizeof(device), "%s%s", 215 _PATH_DEV, special); 216 special = device; 217 goto again; 218 } 219 err(1, "%s", special); 220 } 221 if (fs == NULL && (st.st_mode & S_IFMT) == S_IFDIR) 222 errx(10, "%s: unknown filesystem", special); 223 getsb(&sblock, special); 224 225 if (pflag) { 226 printfs(); 227 exit(0); 228 } 229 if (aflag) { 230 name = "maximum contiguous block count"; 231 if (sblock.fs_maxcontig == avalue) { 232 warnx("%s remains unchanged as %d", name, avalue); 233 } 234 else { 235 warnx("%s changes from %d to %d", 236 name, sblock.fs_maxcontig, avalue); 237 sblock.fs_maxcontig = avalue; 238 } 239 } 240 if (dflag) { 241 name = "rotational delay between contiguous blocks"; 242 if (sblock.fs_rotdelay == dvalue) { 243 warnx("%s remains unchanged as %dms", name, dvalue); 244 } 245 else { 246 warnx("%s changes from %dms to %dms", 247 name, sblock.fs_rotdelay, dvalue); 248 sblock.fs_rotdelay = dvalue; 249 } 250 } 251 if (eflag) { 252 name = "maximum blocks per file in a cylinder group"; 253 if (sblock.fs_maxbpg == evalue) { 254 warnx("%s remains unchanged as %d", name, evalue); 255 } 256 else { 257 warnx("%s changes from %d to %d", 258 name, sblock.fs_maxbpg, evalue); 259 sblock.fs_maxbpg = evalue; 260 } 261 } 262 if (fflag) { 263 name = "average file size"; 264 if (sblock.fs_avgfilesize == fvalue) { 265 warnx("%s remains unchanged as %d", name, fvalue); 266 } 267 else { 268 warnx("%s changes from %d to %d", 269 name, sblock.fs_avgfilesize, fvalue); 270 sblock.fs_avgfilesize = fvalue; 271 } 272 } 273 if (mflag) { 274 name = "minimum percentage of free space"; 275 if (sblock.fs_minfree == mvalue) { 276 warnx("%s remains unchanged as %d%%", name, mvalue); 277 } 278 else { 279 warnx("%s changes from %d%% to %d%%", 280 name, sblock.fs_minfree, mvalue); 281 sblock.fs_minfree = mvalue; 282 if (mvalue >= MINFREE && sblock.fs_optim == FS_OPTSPACE) 283 warnx(OPTWARN, "time", ">=", MINFREE); 284 if (mvalue < MINFREE && sblock.fs_optim == FS_OPTTIME) 285 warnx(OPTWARN, "space", "<", MINFREE); 286 } 287 } 288 if (nflag) { 289 name = "soft updates"; 290 if (strcmp(nvalue, "enable") == 0) { 291 if (sblock.fs_flags & FS_DOSOFTDEP) { 292 warnx("%s remains unchanged as enabled", name); 293 } else if (sblock.fs_clean == 0) { 294 warnx("%s cannot be enabled until fsck is run", 295 name); 296 } else { 297 sblock.fs_flags |= FS_DOSOFTDEP; 298 warnx("%s set", name); 299 } 300 } else if (strcmp(nvalue, "disable") == 0) { 301 if ((~sblock.fs_flags & FS_DOSOFTDEP) == FS_DOSOFTDEP) { 302 warnx("%s remains unchanged as disabled", name); 303 } else { 304 sblock.fs_flags &= ~FS_DOSOFTDEP; 305 warnx("%s cleared", name); 306 } 307 } 308 } 309 if (oflag) { 310 name = "optimization preference"; 311 chg[FS_OPTSPACE] = "space"; 312 chg[FS_OPTTIME] = "time"; 313 if (sblock.fs_optim == ovalue) { 314 warnx("%s remains unchanged as %s", name, chg[ovalue]); 315 } 316 else { 317 warnx("%s changes from %s to %s", 318 name, chg[sblock.fs_optim], chg[ovalue]); 319 sblock.fs_optim = ovalue; 320 if (sblock.fs_minfree >= MINFREE && 321 ovalue == FS_OPTSPACE) 322 warnx(OPTWARN, "time", ">=", MINFREE); 323 if (sblock.fs_minfree < MINFREE && 324 ovalue == FS_OPTTIME) 325 warnx(OPTWARN, "space", "<", MINFREE); 326 } 327 } 328 if (sflag) { 329 name = "expected number of files per directory"; 330 if (sblock.fs_avgfpdir == svalue) { 331 warnx("%s remains unchanged as %d", name, svalue); 332 } 333 else { 334 warnx("%s changes from %d to %d", 335 name, sblock.fs_avgfpdir, svalue); 336 sblock.fs_avgfpdir = svalue; 337 } 338 } 339 340 putsb(&sblock, special, Aflag); 341 if (active) { 342 bzero(&args, sizeof(args)); 343 if (mount("ufs", fs->fs_file, 344 stfs.f_flags | MNT_UPDATE | MNT_RELOAD, &args) < 0) 345 err(9, "%s: reload", special); 346 warnx("filesystem reloaded"); 347 } 348 exit(0); 349 } 350 351 void 352 usage() 353 { 354 fprintf(stderr, "%s\n%s\n%s\n", 355 "usage: tunefs [-A] [-a maxcontig] [-d rotdelay] [-e maxbpg] [-f avgfilesize]", 356 " [-m minfree] [-p] [-n enable | disable] [-o space | time]", 357 " [-s filesperdir] special | filesystem"); 358 exit(2); 359 } 360 361 void 362 getsb(fs, file) 363 struct fs *fs; 364 const char *file; 365 { 366 367 fi = open(file, O_RDONLY); 368 if (fi < 0) 369 err(3, "cannot open %s", file); 370 if (bread((daddr_t)SBOFF, (char *)fs, SBSIZE)) 371 err(4, "%s: bad super block", file); 372 if (fs->fs_magic != FS_MAGIC) 373 errx(5, "%s: bad magic number", file); 374 dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); 375 } 376 377 void 378 putsb(fs, file, all) 379 struct fs *fs; 380 const char *file; 381 int all; 382 { 383 int i; 384 385 /* 386 * Re-open the device read-write. Use the read-only file 387 * descriptor as an interlock to prevent the device from 388 * being mounted while we are switching mode. 389 */ 390 i = fi; 391 fi = open(file, O_RDWR); 392 close(i); 393 if (fi < 0) 394 err(3, "cannot open %s", file); 395 bwrite((daddr_t)SBOFF / dev_bsize, (const char *)fs, SBSIZE); 396 if (all) 397 for (i = 0; i < fs->fs_ncg; i++) 398 bwrite(fsbtodb(fs, cgsblock(fs, i)), 399 (const char *)fs, SBSIZE); 400 close(fi); 401 } 402 403 void 404 printfs() 405 { 406 warnx("soft updates: (-n) %s", 407 (sblock.fs_flags & FS_DOSOFTDEP)? "enabled" : "disabled"); 408 warnx("maximum contiguous block count: (-a) %d", 409 sblock.fs_maxcontig); 410 warnx("rotational delay between contiguous blocks: (-d) %d ms", 411 sblock.fs_rotdelay); 412 warnx("maximum blocks per file in a cylinder group: (-e) %d", 413 sblock.fs_maxbpg); 414 warnx("average file size: (-f) %d", 415 sblock.fs_avgfilesize); 416 warnx("average number of files in a directory: (-s) %d", 417 sblock.fs_avgfpdir); 418 warnx("minimum percentage of free space: (-m) %d%%", 419 sblock.fs_minfree); 420 warnx("optimization preference: (-o) %s", 421 sblock.fs_optim == FS_OPTSPACE ? "space" : "time"); 422 if (sblock.fs_minfree >= MINFREE && 423 sblock.fs_optim == FS_OPTSPACE) 424 warnx(OPTWARN, "time", ">=", MINFREE); 425 if (sblock.fs_minfree < MINFREE && 426 sblock.fs_optim == FS_OPTTIME) 427 warnx(OPTWARN, "space", "<", MINFREE); 428 } 429 430 void 431 bwrite(blk, buf, size) 432 daddr_t blk; 433 const char *buf; 434 int size; 435 { 436 437 if (lseek(fi, (off_t)blk * dev_bsize, SEEK_SET) < 0) 438 err(6, "FS SEEK"); 439 if (write(fi, buf, size) != size) 440 err(7, "FS WRITE"); 441 } 442 443 int 444 bread(bno, buf, cnt) 445 daddr_t bno; 446 char *buf; 447 int cnt; 448 { 449 int i; 450 451 if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0) 452 return(1); 453 if ((i = read(fi, buf, cnt)) != cnt) { 454 for(i=0; i<sblock.fs_bsize; i++) 455 buf[i] = 0; 456 return (1); 457 } 458 return (0); 459 } 460