1 /* $NetBSD: tunefs.c,v 1.27 2003/04/02 10:39:32 fvdl Exp $ */ 2 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\ 39 The Regents of the University of California. All rights reserved.\n"); 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)tunefs.c 8.3 (Berkeley) 5/3/95"; 45 #else 46 __RCSID("$NetBSD: tunefs.c,v 1.27 2003/04/02 10:39:32 fvdl Exp $"); 47 #endif 48 #endif /* not lint */ 49 50 /* 51 * tunefs: change layout parameters to an existing file system. 52 */ 53 #include <sys/param.h> 54 55 #include <ufs/ufs/dinode.h> 56 #include <ufs/ffs/fs.h> 57 #include <ufs/ffs/ffs_extern.h> 58 59 #include <machine/bswap.h> 60 61 #include <err.h> 62 #include <errno.h> 63 #include <fcntl.h> 64 #include <fstab.h> 65 #include <paths.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <unistd.h> 70 #include <util.h> 71 72 /* the optimization warning string template */ 73 #define OPTWARN "should optimize for %s with minfree %s %d%%" 74 75 union { 76 struct fs sb; 77 char pad[MAXBSIZE]; 78 } sbun; 79 #define sblock sbun.sb 80 char buf[MAXBSIZE]; 81 82 int fi; 83 long dev_bsize = 512; 84 int needswap = 0; 85 int is_ufs2 = 0; 86 off_t sblockloc; 87 88 static off_t sblock_try[] = SBLOCKSEARCH; 89 90 static void bwrite(daddr_t, char *, int, const char *); 91 static void bread(daddr_t, char *, int, const char *); 92 static int getnum(const char *, const char *, int, int); 93 static void getsb(struct fs *, const char *); 94 static int openpartition(const char *, int, char *, size_t); 95 static void usage(void); 96 int main(int, char *[]); 97 98 int 99 main(int argc, char *argv[]) 100 { 101 #define OPTSTRINGBASE "AFNe:g:h:m:o:" 102 #ifdef TUNEFS_SOFTDEP 103 int softdep; 104 #define OPTSTRING OPTSTRINGBASE ## "n:" 105 #else 106 #define OPTSTRING OPTSTRINGBASE 107 #endif 108 int i, ch, Aflag, Fflag, Nflag, openflags; 109 const char *special, *chg[2]; 110 char device[MAXPATHLEN]; 111 int maxbpg, minfree, optim; 112 int avgfilesize, avgfpdir; 113 114 Aflag = Fflag = Nflag = 0; 115 maxbpg = minfree = optim = -1; 116 avgfilesize = avgfpdir = -1; 117 #ifdef TUNEFS_SOFTDEP 118 softdep = -1; 119 #endif 120 chg[FS_OPTSPACE] = "space"; 121 chg[FS_OPTTIME] = "time"; 122 123 while ((ch = getopt(argc, argv, OPTSTRING)) != -1) { 124 switch (ch) { 125 126 case 'A': 127 Aflag++; 128 break; 129 130 case 'F': 131 Fflag++; 132 break; 133 134 case 'N': 135 Nflag++; 136 break; 137 138 case 'e': 139 maxbpg = getnum(optarg, 140 "maximum blocks per file in a cylinder group", 141 1, INT_MAX); 142 break; 143 144 case 'g': 145 avgfilesize = getnum(optarg, 146 "average file size", 1, INT_MAX); 147 break; 148 149 case 'h': 150 avgfpdir = getnum(optarg, 151 "expected number of files per directory", 152 1, INT_MAX); 153 break; 154 155 case 'm': 156 minfree = getnum(optarg, 157 "minimum percentage of free space", 0, 99); 158 break; 159 160 #ifdef TUNEFS_SOFTDEP 161 case 'n': 162 if (strcmp(optarg, "enable") == 0) 163 softdep = 1; 164 else if (strcmp(optarg, "disable") == 0) 165 softdep = 0; 166 else { 167 errx(10, "bad soft dependencies " 168 "(options are `enable' or `disable')"); 169 } 170 break; 171 #endif 172 173 case 'o': 174 if (strcmp(optarg, chg[FS_OPTSPACE]) == 0) 175 optim = FS_OPTSPACE; 176 else if (strcmp(optarg, chg[FS_OPTTIME]) == 0) 177 optim = FS_OPTTIME; 178 else 179 errx(10, 180 "bad %s (options are `space' or `time')", 181 "optimization preference"); 182 break; 183 184 default: 185 usage(); 186 } 187 } 188 argc -= optind; 189 argv += optind; 190 if (argc != 1) 191 usage(); 192 193 special = argv[0]; 194 openflags = Nflag ? O_RDONLY : O_RDWR; 195 if (Fflag) 196 fi = open(special, openflags); 197 else { 198 fi = openpartition(special, openflags, device, sizeof(device)); 199 special = device; 200 } 201 if (fi == -1) 202 err(1, "%s", special); 203 getsb(&sblock, special); 204 205 #define CHANGEVAL(old, new, type, suffix) do \ 206 if ((new) != -1) { \ 207 if ((new) == (old)) \ 208 warnx("%s remains unchanged at %d%s", \ 209 (type), (old), (suffix)); \ 210 else { \ 211 warnx("%s changes from %d%s to %d%s", \ 212 (type), (old), (suffix), (new), (suffix)); \ 213 (old) = (new); \ 214 } \ 215 } while (/* CONSTCOND */0) 216 217 warnx("tuning %s", special); 218 CHANGEVAL(sblock.fs_maxbpg, maxbpg, 219 "maximum blocks per file in a cylinder group", ""); 220 CHANGEVAL(sblock.fs_minfree, minfree, 221 "minimum percentage of free space", "%"); 222 if (minfree != -1) { 223 if (minfree >= MINFREE && 224 sblock.fs_optim == FS_OPTSPACE) 225 warnx(OPTWARN, "time", ">=", MINFREE); 226 if (minfree < MINFREE && 227 sblock.fs_optim == FS_OPTTIME) 228 warnx(OPTWARN, "space", "<", MINFREE); 229 } 230 #ifdef TUNEFS_SOFTDEP 231 if (softdep == 1) { 232 sblock.fs_flags |= FS_DOSOFTDEP; 233 warnx("soft dependencies set"); 234 } else if (softdep == 0) { 235 sblock.fs_flags &= ~FS_DOSOFTDEP; 236 warnx("soft dependencies cleared"); 237 } 238 #endif 239 if (optim != -1) { 240 if (sblock.fs_optim == optim) { 241 warnx("%s remains unchanged as %s", 242 "optimization preference", 243 chg[optim]); 244 } else { 245 warnx("%s changes from %s to %s", 246 "optimization preference", 247 chg[sblock.fs_optim], chg[optim]); 248 sblock.fs_optim = optim; 249 if (sblock.fs_minfree >= MINFREE && 250 optim == FS_OPTSPACE) 251 warnx(OPTWARN, "time", ">=", MINFREE); 252 if (sblock.fs_minfree < MINFREE && 253 optim == FS_OPTTIME) 254 warnx(OPTWARN, "space", "<", MINFREE); 255 } 256 } 257 CHANGEVAL(sblock.fs_avgfilesize, avgfilesize, 258 "average file size", ""); 259 CHANGEVAL(sblock.fs_avgfpdir, avgfpdir, 260 "expected number of files per directory", ""); 261 262 if (Nflag) { 263 fprintf(stdout, "tunefs: current settings of %s\n", special); 264 fprintf(stdout, "\tmaximum contiguous block count %d\n", 265 sblock.fs_maxcontig); 266 fprintf(stdout, 267 "\tmaximum blocks per file in a cylinder group %d\n", 268 sblock.fs_maxbpg); 269 fprintf(stdout, "\tminimum percentage of free space %d%%\n", 270 sblock.fs_minfree); 271 #ifdef TUNEFS_SOFTDEP 272 fprintf(stdout, "\tsoft dependencies: %s\n", 273 (sblock.fs_flags & FS_DOSOFTDEP) ? "on" : "off"); 274 #endif 275 fprintf(stdout, "\toptimization preference: %s\n", 276 chg[sblock.fs_optim]); 277 fprintf(stdout, "\taverage file size: %d\n", 278 sblock.fs_avgfilesize); 279 fprintf(stdout, 280 "\texpected number of files per directory: %d\n", 281 sblock.fs_avgfpdir); 282 fprintf(stdout, "tunefs: no changes made\n"); 283 exit(0); 284 } 285 286 memcpy(buf, (char *)&sblock, SBLOCKSIZE); 287 if (needswap) 288 ffs_sb_swap((struct fs*)buf, (struct fs*)buf); 289 bwrite(sblockloc, buf, SBLOCKSIZE, special); 290 if (Aflag) 291 for (i = 0; i < sblock.fs_ncg; i++) 292 bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)), 293 buf, SBLOCKSIZE, special); 294 close(fi); 295 exit(0); 296 } 297 298 static int 299 getnum(const char *num, const char *desc, int min, int max) 300 { 301 long n; 302 char *ep; 303 304 n = strtol(num, &ep, 10); 305 if (ep[0] != '\0') 306 errx(1, "Invalid number `%s' for %s", num, desc); 307 if ((int) n < min) 308 errx(1, "%s `%s' too small (minimum is %d)", desc, num, min); 309 if ((int) n > max) 310 errx(1, "%s `%s' too large (maximum is %d)", desc, num, max); 311 return ((int)n); 312 } 313 314 static void 315 usage(void) 316 { 317 318 fprintf(stderr, "Usage: tunefs [-AFN] tuneup-options special-device\n"); 319 fprintf(stderr, "where tuneup-options are:\n"); 320 fprintf(stderr, "\t-a maximum contiguous blocks\n"); 321 fprintf(stderr, "\t-d rotational delay between contiguous blocks\n"); 322 fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n"); 323 fprintf(stderr, "\t-g average file size\n"); 324 fprintf(stderr, "\t-h expected number of files per directory\n"); 325 fprintf(stderr, "\t-k track skew in sectors\n"); 326 fprintf(stderr, "\t-m minimum percentage of free space\n"); 327 #ifdef TUNEFS_SOFTDEP 328 fprintf(stderr, "\t-n soft dependencies (`enable' or `disable')\n"); 329 #endif 330 fprintf(stderr, "\t-o optimization preference (`space' or `time')\n"); 331 exit(2); 332 } 333 334 static void 335 getsb(struct fs *fs, const char *file) 336 { 337 int i; 338 339 for (i = 0; sblock_try[i] != -1; i++) { 340 bread(sblock_try[i] / dev_bsize, (char *)fs, SBLOCKSIZE, file); 341 switch(fs->fs_magic) { 342 case FS_UFS2_MAGIC: 343 is_ufs2 = 1; 344 /*FALLTHROUGH*/ 345 case FS_UFS1_MAGIC: 346 goto found; 347 case FS_UFS2_MAGIC_SWAPPED: 348 is_ufs2 = 1; 349 /*FALLTHROUGH*/ 350 case FS_UFS1_MAGIC_SWAPPED: 351 warnx("%s: swapping byte order", file); 352 needswap = 1; 353 ffs_sb_swap(fs, fs); 354 goto found; 355 default: 356 break; 357 } 358 } 359 errx(5, "cannot find filesystem superblock"); 360 found: 361 if (is_ufs2 && fs->fs_sblockloc != sblock_try[i]) 362 errx(5, "bad super block"); 363 dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); 364 sblockloc = sblock_try[i] / dev_bsize; 365 } 366 367 static void 368 bwrite(daddr_t blk, char *buffer, int size, const char *file) 369 { 370 off_t offset; 371 372 offset = (off_t)blk * dev_bsize; 373 if (lseek(fi, offset, SEEK_SET) == -1) 374 err(6, "%s: seeking to %lld", file, (long long)offset); 375 if (write(fi, buffer, size) != size) 376 err(7, "%s: writing %d bytes", file, size); 377 } 378 379 static void 380 bread(daddr_t blk, char *buffer, int cnt, const char *file) 381 { 382 off_t offset; 383 int i; 384 385 offset = (off_t)blk * dev_bsize; 386 if (lseek(fi, offset, SEEK_SET) == -1) 387 err(4, "%s: seeking to %lld", file, (long long)offset); 388 if ((i = read(fi, buffer, cnt)) != cnt) 389 errx(5, "%s: short read", file); 390 } 391 392 static int 393 openpartition(const char *name, int flags, char *device, size_t devicelen) 394 { 395 char rawspec[MAXPATHLEN], *p; 396 struct fstab *fs; 397 int fd, oerrno; 398 399 fs = getfsfile(name); 400 if (fs) { 401 if ((p = strrchr(fs->fs_spec, '/')) != NULL) { 402 snprintf(rawspec, sizeof(rawspec), "%.*s/r%s", 403 (int)(p - fs->fs_spec), fs->fs_spec, p + 1); 404 name = rawspec; 405 } else 406 name = fs->fs_spec; 407 } 408 fd = opendisk(name, flags, device, devicelen, 0); 409 if (fd == -1 && errno == ENOENT) { 410 oerrno = errno; 411 strlcpy(device, name, devicelen); 412 errno = oerrno; 413 } 414 return (fd); 415 } 416