tunefs.c revision 1.39
1/* $NetBSD: tunefs.c,v 1.39 2009/04/07 12:25:19 lukem 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__COPYRIGHT("@(#) Copyright (c) 1983, 1993\ 35 The Regents of the University of California. All rights reserved."); 36#endif /* not lint */ 37 38#ifndef lint 39#if 0 40static char sccsid[] = "@(#)tunefs.c 8.3 (Berkeley) 5/3/95"; 41#else 42__RCSID("$NetBSD: tunefs.c,v 1.39 2009/04/07 12:25:19 lukem Exp $"); 43#endif 44#endif /* not lint */ 45 46/* 47 * tunefs: change layout parameters to an existing file system. 48 */ 49#include <sys/param.h> 50 51#include <ufs/ffs/fs.h> 52#include <ufs/ffs/ffs_extern.h> 53#include <ufs/ufs/ufs_wapbl.h> 54 55#include <machine/bswap.h> 56 57#include <err.h> 58#include <errno.h> 59#include <fcntl.h> 60#include <fstab.h> 61#include <paths.h> 62#include <stdio.h> 63#include <stdlib.h> 64#include <string.h> 65#include <unistd.h> 66#include <util.h> 67 68/* the optimization warning string template */ 69#define OPTWARN "should optimize for %s with minfree %s %d%%" 70 71union { 72 struct fs sb; 73 char pad[MAXBSIZE]; 74} sbun; 75#define sblock sbun.sb 76char buf[MAXBSIZE]; 77 78int fi; 79long dev_bsize = 512; 80int needswap = 0; 81int is_ufs2 = 0; 82off_t sblockloc; 83 84static off_t sblock_try[] = SBLOCKSEARCH; 85 86static void bwrite(daddr_t, char *, int, const char *); 87static void bread(daddr_t, char *, int, const char *); 88static void change_log_info(long long); 89static void getsb(struct fs *, const char *); 90static int openpartition(const char *, int, char *, size_t); 91static void show_log_info(void); 92static void usage(void); 93 94int 95main(int argc, char *argv[]) 96{ 97 int i, ch, Aflag, Fflag, Nflag, openflags; 98 const char *special, *chg[2]; 99 char device[MAXPATHLEN]; 100 int maxbpg, minfree, optim; 101 int avgfilesize, avgfpdir; 102 long long logfilesize; 103 104 Aflag = Fflag = Nflag = 0; 105 maxbpg = minfree = optim = -1; 106 avgfilesize = avgfpdir = -1; 107 logfilesize = -1; 108 chg[FS_OPTSPACE] = "space"; 109 chg[FS_OPTTIME] = "time"; 110 111 while ((ch = getopt(argc, argv, "AFNe:g:h:l:m:o:")) != -1) { 112 switch (ch) { 113 114 case 'A': 115 Aflag++; 116 break; 117 118 case 'F': 119 Fflag++; 120 break; 121 122 case 'N': 123 Nflag++; 124 break; 125 126 case 'e': 127 maxbpg = strsuftoll( 128 "maximum blocks per file in a cylinder group", 129 optarg, 1, INT_MAX); 130 break; 131 132 case 'g': 133 avgfilesize = strsuftoll("average file size", optarg, 134 1, INT_MAX); 135 break; 136 137 case 'h': 138 avgfpdir = strsuftoll( 139 "expected number of files per directory", 140 optarg, 1, INT_MAX); 141 break; 142 143 case 'l': 144 logfilesize = strsuftoll("journal log file size", 145 optarg, 0, INT_MAX); 146 break; 147 148 case 'm': 149 minfree = strsuftoll("minimum percentage of free space", 150 optarg, 0, 99); 151 break; 152 153 case 'o': 154 if (strcmp(optarg, chg[FS_OPTSPACE]) == 0) 155 optim = FS_OPTSPACE; 156 else if (strcmp(optarg, chg[FS_OPTTIME]) == 0) 157 optim = FS_OPTTIME; 158 else 159 errx(10, 160 "bad %s (options are `space' or `time')", 161 "optimization preference"); 162 break; 163 164 default: 165 usage(); 166 } 167 } 168 argc -= optind; 169 argv += optind; 170 if (argc != 1) 171 usage(); 172 173 special = argv[0]; 174 openflags = Nflag ? O_RDONLY : O_RDWR; 175 if (Fflag) 176 fi = open(special, openflags); 177 else { 178 fi = openpartition(special, openflags, device, sizeof(device)); 179 special = device; 180 } 181 if (fi == -1) 182 err(1, "%s", special); 183 getsb(&sblock, special); 184 185#define CHANGEVAL(old, new, type, suffix) do \ 186 if ((new) != -1) { \ 187 if ((new) == (old)) \ 188 warnx("%s remains unchanged at %d%s", \ 189 (type), (old), (suffix)); \ 190 else { \ 191 warnx("%s changes from %d%s to %d%s", \ 192 (type), (old), (suffix), (new), (suffix)); \ 193 (old) = (new); \ 194 } \ 195 } while (/* CONSTCOND */0) 196 197 warnx("tuning %s", special); 198 CHANGEVAL(sblock.fs_maxbpg, maxbpg, 199 "maximum blocks per file in a cylinder group", ""); 200 CHANGEVAL(sblock.fs_minfree, minfree, 201 "minimum percentage of free space", "%"); 202 if (minfree != -1) { 203 if (minfree >= MINFREE && 204 sblock.fs_optim == FS_OPTSPACE) 205 warnx(OPTWARN, "time", ">=", MINFREE); 206 if (minfree < MINFREE && 207 sblock.fs_optim == FS_OPTTIME) 208 warnx(OPTWARN, "space", "<", MINFREE); 209 } 210 if (optim != -1) { 211 if (sblock.fs_optim == optim) { 212 warnx("%s remains unchanged as %s", 213 "optimization preference", 214 chg[optim]); 215 } else { 216 warnx("%s changes from %s to %s", 217 "optimization preference", 218 chg[sblock.fs_optim], chg[optim]); 219 sblock.fs_optim = optim; 220 if (sblock.fs_minfree >= MINFREE && 221 optim == FS_OPTSPACE) 222 warnx(OPTWARN, "time", ">=", MINFREE); 223 if (sblock.fs_minfree < MINFREE && 224 optim == FS_OPTTIME) 225 warnx(OPTWARN, "space", "<", MINFREE); 226 } 227 } 228 CHANGEVAL(sblock.fs_avgfilesize, avgfilesize, 229 "average file size", ""); 230 CHANGEVAL(sblock.fs_avgfpdir, avgfpdir, 231 "expected number of files per directory", ""); 232 233 if (logfilesize >= 0) 234 change_log_info(logfilesize); 235 236 if (Nflag) { 237 printf("tunefs: current settings of %s\n", special); 238 printf("\tmaximum contiguous block count %d\n", 239 sblock.fs_maxcontig); 240 printf("\tmaximum blocks per file in a cylinder group %d\n", 241 sblock.fs_maxbpg); 242 printf("\tminimum percentage of free space %d%%\n", 243 sblock.fs_minfree); 244 printf("\toptimization preference: %s\n", chg[sblock.fs_optim]); 245 printf("\taverage file size: %d\n", sblock.fs_avgfilesize); 246 printf("\texpected number of files per directory: %d\n", 247 sblock.fs_avgfpdir); 248 show_log_info(); 249 printf("tunefs: no changes made\n"); 250 exit(0); 251 } 252 253 memcpy(buf, (char *)&sblock, SBLOCKSIZE); 254 if (needswap) 255 ffs_sb_swap((struct fs*)buf, (struct fs*)buf); 256 bwrite(sblockloc, buf, SBLOCKSIZE, special); 257 if (Aflag) 258 for (i = 0; i < sblock.fs_ncg; i++) 259 bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)), 260 buf, SBLOCKSIZE, special); 261 close(fi); 262 exit(0); 263} 264 265static void 266show_log_info(void) 267{ 268 const char *loc; 269 uint64_t size, blksize, logsize; 270 int print; 271 272 switch (sblock.fs_journal_location) { 273 case UFS_WAPBL_JOURNALLOC_NONE: 274 print = blksize = 0; 275 /* nothing */ 276 break; 277 case UFS_WAPBL_JOURNALLOC_END_PARTITION: 278 loc = "end of partition"; 279 size = sblock.fs_journallocs[UFS_WAPBL_EPART_COUNT]; 280 blksize = sblock.fs_journallocs[UFS_WAPBL_EPART_BLKSZ]; 281 print = 1; 282 break; 283 case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM: 284 loc = "in filesystem"; 285 size = sblock.fs_journallocs[UFS_WAPBL_INFS_COUNT]; 286 blksize = sblock.fs_journallocs[UFS_WAPBL_INFS_BLKSZ]; 287 print = 1; 288 break; 289 default: 290 loc = "unknown"; 291 size = blksize = 0; 292 print = 1; 293 break; 294 } 295 296 if (print) { 297 logsize = size * blksize; 298 299 printf("\tjournal log file location: %s\n", loc); 300 printf("\tjournal log file size: "); 301 if (logsize == 0) 302 printf("0\n"); 303 else { 304 char sizebuf[8]; 305 humanize_number(sizebuf, 6, size * blksize, "B", 306 HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 307 printf("%s (%" PRId64 " bytes)", sizebuf, logsize); 308 } 309 printf("\n"); 310 printf("\tjournal log flags:"); 311 if (sblock.fs_journal_flags & UFS_WAPBL_FLAGS_CREATE_LOG) 312 printf(" clear-log"); 313 if (sblock.fs_journal_flags & UFS_WAPBL_FLAGS_CLEAR_LOG) 314 printf(" clear-log"); 315 printf("\n"); 316 } 317} 318 319static void 320change_log_info(long long logfilesize) 321{ 322 /* 323 * NOTES: 324 * - only operate on in-filesystem log sizes 325 * - can't change size of existing log 326 * - if current is same, no action 327 * - if current is zero and new is non-zero, set flag to create log 328 * on next mount 329 * - if current is non-zero and new is zero, set flag to clear log 330 * on next mount 331 */ 332 int in_fs_log; 333 uint64_t old_size; 334 335 old_size = 0; 336 switch (sblock.fs_journal_location) { 337 case UFS_WAPBL_JOURNALLOC_END_PARTITION: 338 in_fs_log = 0; 339 old_size = sblock.fs_journallocs[UFS_WAPBL_EPART_COUNT] * 340 sblock.fs_journallocs[UFS_WAPBL_EPART_BLKSZ]; 341 break; 342 343 case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM: 344 in_fs_log = 1; 345 old_size = sblock.fs_journallocs[UFS_WAPBL_INFS_COUNT] * 346 sblock.fs_journallocs[UFS_WAPBL_INFS_BLKSZ]; 347 break; 348 349 case UFS_WAPBL_JOURNALLOC_NONE: 350 default: 351 in_fs_log = 0; 352 old_size = 0; 353 break; 354 } 355 356 if (!in_fs_log) 357 errx(1, "Can't change size of non-in-filesystem log"); 358 359 if (logfilesize > 0 && old_size == (uint64_t)logfilesize) { 360 /* no action */ 361 warnx("log file size remains unchanged at %lld", logfilesize); 362 return; 363 } 364 365 if (logfilesize == 0) { 366 /* 367 * Don't clear out the locators - the kernel might need 368 * these to find the log! Just set the "clear the log" 369 * flag and let the kernel do the rest. 370 */ 371 sblock.fs_journal_flags |= UFS_WAPBL_FLAGS_CLEAR_LOG; 372 sblock.fs_journal_flags &= ~UFS_WAPBL_FLAGS_CREATE_LOG; 373 warnx("log file size cleared from %" PRIu64 "", old_size); 374 return; 375 } 376 377 if (old_size == 0) { 378 /* create new log of desired size next mount */ 379 sblock.fs_journal_location = UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM; 380 sblock.fs_journallocs[UFS_WAPBL_INFS_ADDR] = 0; 381 sblock.fs_journallocs[UFS_WAPBL_INFS_COUNT] = logfilesize; 382 sblock.fs_journallocs[UFS_WAPBL_INFS_BLKSZ] = 0; 383 sblock.fs_journallocs[UFS_WAPBL_INFS_INO] = 0; 384 sblock.fs_journal_flags |= UFS_WAPBL_FLAGS_CREATE_LOG; 385 sblock.fs_journal_flags &= ~UFS_WAPBL_FLAGS_CLEAR_LOG; 386 warnx("log file size set to %lld", logfilesize); 387 } else { 388 errx(1, 389 "Can't change existing log size from %" PRIu64 " to %lld", 390 old_size, logfilesize); 391 } 392} 393 394static void 395usage(void) 396{ 397 398 fprintf(stderr, "usage: tunefs [-AFN] tuneup-options special-device\n"); 399 fprintf(stderr, "where tuneup-options are:\n"); 400 fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n"); 401 fprintf(stderr, "\t-g average file size\n"); 402 fprintf(stderr, "\t-h expected number of files per directory\n"); 403 fprintf(stderr, "\t-l journal log file size (`0' to clear journal)\n"); 404 fprintf(stderr, "\t-m minimum percentage of free space\n"); 405 fprintf(stderr, "\t-o optimization preference (`space' or `time')\n"); 406 exit(2); 407} 408 409static void 410getsb(struct fs *fs, const char *file) 411{ 412 int i; 413 414 for (i = 0; ; i++) { 415 if (sblock_try[i] == -1) 416 errx(5, "cannot find filesystem superblock"); 417 bread(sblock_try[i] / dev_bsize, (char *)fs, SBLOCKSIZE, file); 418 switch(fs->fs_magic) { 419 case FS_UFS2_MAGIC: 420 is_ufs2 = 1; 421 /*FALLTHROUGH*/ 422 case FS_UFS1_MAGIC: 423 break; 424 case FS_UFS2_MAGIC_SWAPPED: 425 is_ufs2 = 1; 426 /*FALLTHROUGH*/ 427 case FS_UFS1_MAGIC_SWAPPED: 428 warnx("%s: swapping byte order", file); 429 needswap = 1; 430 ffs_sb_swap(fs, fs); 431 break; 432 default: 433 continue; 434 } 435 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2) 436 continue; 437 if ((is_ufs2 || fs->fs_old_flags & FS_FLAGS_UPDATED) 438 && fs->fs_sblockloc != sblock_try[i]) 439 continue; 440 break; 441 } 442 443 dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); 444 sblockloc = sblock_try[i] / dev_bsize; 445} 446 447static void 448bwrite(daddr_t blk, char *buffer, int size, const char *file) 449{ 450 off_t offset; 451 452 offset = (off_t)blk * dev_bsize; 453 if (lseek(fi, offset, SEEK_SET) == -1) 454 err(6, "%s: seeking to %lld", file, (long long)offset); 455 if (write(fi, buffer, size) != size) 456 err(7, "%s: writing %d bytes", file, size); 457} 458 459static void 460bread(daddr_t blk, char *buffer, int cnt, const char *file) 461{ 462 off_t offset; 463 int i; 464 465 offset = (off_t)blk * dev_bsize; 466 if (lseek(fi, offset, SEEK_SET) == -1) 467 err(4, "%s: seeking to %lld", file, (long long)offset); 468 if ((i = read(fi, buffer, cnt)) != cnt) 469 errx(5, "%s: short read", file); 470} 471 472static int 473openpartition(const char *name, int flags, char *device, size_t devicelen) 474{ 475 char rawspec[MAXPATHLEN], *p; 476 struct fstab *fs; 477 int fd, oerrno; 478 479 fs = getfsfile(name); 480 if (fs) { 481 if ((p = strrchr(fs->fs_spec, '/')) != NULL) { 482 snprintf(rawspec, sizeof(rawspec), "%.*s/r%s", 483 (int)(p - fs->fs_spec), fs->fs_spec, p + 1); 484 name = rawspec; 485 } else 486 name = fs->fs_spec; 487 } 488 fd = opendisk(name, flags, device, devicelen, 0); 489 if (fd == -1 && errno == ENOENT) { 490 oerrno = errno; 491 strlcpy(device, name, devicelen); 492 errno = oerrno; 493 } 494 return (fd); 495} 496