1 /* $OpenBSD: tunefs.c,v 1.31 2011/05/05 16:29:33 millert Exp $ */ 2 /* $NetBSD: tunefs.c,v 1.33 2005/01/19 20:46:16 xtraeme Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * tunefs: change layout parameters to an existing file system. 35 */ 36 #include <sys/param.h> 37 38 #include <ufs/ufs/dinode.h> 39 #include <ufs/ffs/fs.h> 40 #include <ufs/ffs/ffs_extern.h> 41 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <fstab.h> 46 #include <paths.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <unistd.h> 51 #include <util.h> 52 53 /* the optimization warning string template */ 54 #define OPTWARN "should optimize for %s with minfree %s %d%%" 55 56 union { 57 struct fs sb; 58 char pad[MAXBSIZE]; 59 } sbun; 60 #define sblock sbun.sb 61 char buf[MAXBSIZE]; 62 63 int fi; 64 long dev_bsize = 512; 65 int is_ufs2 = 0; 66 off_t sblockloc; 67 68 static off_t sblock_try[] = SBLOCKSEARCH; 69 70 static void bwrite(daddr64_t, char *, int, const char *); 71 static void bread(daddr64_t, char *, int, const char *); 72 static int getnum(const char *, const char *, int, int); 73 static void getsb(struct fs *, const char *); 74 static int openpartition(char *, int, char **); 75 static void usage(void); 76 77 int 78 main(int argc, char *argv[]) 79 { 80 #define OPTSTRING "AFNe:g:h:m:o:" 81 int i, ch, Aflag, Fflag, Nflag, openflags; 82 char *special; 83 const char *chg[2]; 84 int maxbpg, minfree, optim; 85 int avgfilesize, avgfpdir; 86 87 Aflag = Fflag = Nflag = 0; 88 maxbpg = minfree = optim = -1; 89 avgfilesize = avgfpdir = -1; 90 chg[FS_OPTSPACE] = "space"; 91 chg[FS_OPTTIME] = "time"; 92 93 while ((ch = getopt(argc, argv, OPTSTRING)) != -1) { 94 switch (ch) { 95 96 case 'A': 97 Aflag++; 98 break; 99 100 case 'F': 101 Fflag++; 102 break; 103 104 case 'N': 105 Nflag++; 106 break; 107 108 case 'e': 109 maxbpg = getnum(optarg, 110 "maximum blocks per file in a cylinder group", 111 1, INT_MAX); 112 break; 113 114 case 'g': 115 avgfilesize = getnum(optarg, 116 "average file size", 1, INT_MAX); 117 break; 118 119 case 'h': 120 avgfpdir = getnum(optarg, 121 "expected number of files per directory", 122 1, INT_MAX); 123 break; 124 125 case 'm': 126 minfree = getnum(optarg, 127 "minimum percentage of free space", 0, 99); 128 break; 129 130 case 'o': 131 if (strcmp(optarg, chg[FS_OPTSPACE]) == 0) 132 optim = FS_OPTSPACE; 133 else if (strcmp(optarg, chg[FS_OPTTIME]) == 0) 134 optim = FS_OPTTIME; 135 else 136 errx(10, 137 "bad %s (options are `space' or `time')", 138 "optimization preference"); 139 break; 140 141 default: 142 usage(); 143 } 144 } 145 argc -= optind; 146 argv += optind; 147 if (argc != 1) 148 usage(); 149 150 special = argv[0]; 151 openflags = Nflag ? O_RDONLY : O_RDWR; 152 if (Fflag) 153 fi = open(special, openflags); 154 else 155 fi = openpartition(special, openflags, &special); 156 if (fi == -1) 157 err(1, "%s", special); 158 getsb(&sblock, special); 159 160 #define CHANGEVAL(old, new, type, suffix) do \ 161 if ((new) != -1) { \ 162 if ((new) == (old)) \ 163 warnx("%s remains unchanged at %d%s", \ 164 (type), (old), (suffix)); \ 165 else { \ 166 warnx("%s changes from %d%s to %d%s", \ 167 (type), (old), (suffix), (new), (suffix)); \ 168 (old) = (new); \ 169 } \ 170 } while (/* CONSTCOND */0) 171 172 warnx("tuning %s", special); 173 CHANGEVAL(sblock.fs_maxbpg, maxbpg, 174 "maximum blocks per file in a cylinder group", ""); 175 CHANGEVAL(sblock.fs_minfree, minfree, 176 "minimum percentage of free space", "%"); 177 if (minfree != -1) { 178 if (minfree >= MINFREE && 179 sblock.fs_optim == FS_OPTSPACE) 180 warnx(OPTWARN, "time", ">=", MINFREE); 181 if (minfree < MINFREE && 182 sblock.fs_optim == FS_OPTTIME) 183 warnx(OPTWARN, "space", "<", MINFREE); 184 } 185 if (optim != -1) { 186 if (sblock.fs_optim == optim) { 187 warnx("%s remains unchanged as %s", 188 "optimization preference", 189 chg[optim]); 190 } else { 191 warnx("%s changes from %s to %s", 192 "optimization preference", 193 chg[sblock.fs_optim], chg[optim]); 194 sblock.fs_optim = optim; 195 if (sblock.fs_minfree >= MINFREE && 196 optim == FS_OPTSPACE) 197 warnx(OPTWARN, "time", ">=", MINFREE); 198 if (sblock.fs_minfree < MINFREE && 199 optim == FS_OPTTIME) 200 warnx(OPTWARN, "space", "<", MINFREE); 201 } 202 } 203 CHANGEVAL(sblock.fs_avgfilesize, avgfilesize, 204 "average file size", ""); 205 CHANGEVAL(sblock.fs_avgfpdir, avgfpdir, 206 "expected number of files per directory", ""); 207 208 if (Nflag) { 209 fprintf(stdout, "tunefs: current settings of %s\n", special); 210 fprintf(stdout, "\tmaximum contiguous block count %d\n", 211 sblock.fs_maxcontig); 212 fprintf(stdout, 213 "\tmaximum blocks per file in a cylinder group %d\n", 214 sblock.fs_maxbpg); 215 fprintf(stdout, "\tminimum percentage of free space %d%%\n", 216 sblock.fs_minfree); 217 fprintf(stdout, "\toptimization preference: %s\n", 218 chg[sblock.fs_optim]); 219 fprintf(stdout, "\taverage file size: %d\n", 220 sblock.fs_avgfilesize); 221 fprintf(stdout, 222 "\texpected number of files per directory: %d\n", 223 sblock.fs_avgfpdir); 224 fprintf(stdout, "tunefs: no changes made\n"); 225 exit(0); 226 } 227 228 memcpy(buf, (char *)&sblock, SBLOCKSIZE); 229 bwrite(sblockloc, buf, SBLOCKSIZE, special); 230 if (Aflag) 231 for (i = 0; i < sblock.fs_ncg; i++) 232 bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)), 233 buf, SBLOCKSIZE, special); 234 close(fi); 235 exit(0); 236 } 237 238 static int 239 getnum(const char *num, const char *desc, int min, int max) 240 { 241 int n; 242 const char *errstr; 243 244 n = strtonum(num, min, max, &errstr); 245 if (errstr != NULL) 246 errx(1, "Invalid number `%s' for %s: %s", num, desc, errstr); 247 return (n); 248 } 249 250 static void 251 usage(void) 252 { 253 extern char *__progname; 254 255 fprintf(stderr, 256 "usage: %s [-AFN] [-e maxbpg] [-g avgfilesize] " 257 "[-h avgfpdir] [-m minfree]\n" 258 "\t[-o optimize_preference] special | filesys\n", 259 __progname); 260 261 exit(2); 262 } 263 264 static void 265 getsb(struct fs *fs, const char *file) 266 { 267 int i; 268 269 for (i = 0; ; i++) { 270 if (sblock_try[i] == -1) 271 errx(5, "cannot find filesystem superblock"); 272 bread(sblock_try[i] / dev_bsize, (char *)fs, SBLOCKSIZE, file); 273 switch(fs->fs_magic) { 274 case FS_UFS2_MAGIC: 275 is_ufs2 = 1; 276 /*FALLTHROUGH*/ 277 case FS_UFS1_MAGIC: 278 break; 279 default: 280 continue; 281 } 282 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2) 283 continue; 284 if ((is_ufs2 || fs->fs_flags & FS_FLAGS_UPDATED) 285 && fs->fs_sblockloc != sblock_try[i]) 286 continue; 287 break; 288 } 289 290 dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); 291 sblockloc = sblock_try[i] / dev_bsize; 292 } 293 294 static void 295 bwrite(daddr64_t blk, char *buffer, int size, const char *file) 296 { 297 off_t offset; 298 299 offset = (off_t)blk * dev_bsize; 300 if (lseek(fi, offset, SEEK_SET) == -1) 301 err(6, "%s: seeking to %lld", file, (long long)offset); 302 if (write(fi, buffer, size) != size) 303 err(7, "%s: writing %d bytes", file, size); 304 } 305 306 static void 307 bread(daddr64_t blk, char *buffer, int cnt, const char *file) 308 { 309 off_t offset; 310 int i; 311 312 offset = (off_t)blk * dev_bsize; 313 if (lseek(fi, offset, SEEK_SET) == -1) 314 err(4, "%s: seeking to %lld", file, (long long)offset); 315 if ((i = read(fi, buffer, cnt)) != cnt) 316 errx(5, "%s: short read", file); 317 } 318 319 static int 320 openpartition(char *name, int flags, char **devicep) 321 { 322 char rawspec[MAXPATHLEN], *p; 323 struct fstab *fs; 324 int fd; 325 326 fs = getfsfile(name); 327 if (fs) { 328 if ((p = strrchr(fs->fs_spec, '/')) != NULL) { 329 snprintf(rawspec, sizeof(rawspec), "%.*s/r%s", 330 (int)(p - fs->fs_spec), fs->fs_spec, p + 1); 331 name = rawspec; 332 } else 333 name = fs->fs_spec; 334 } 335 fd = opendev(name, flags, 0, devicep); 336 if (fd == -1 && errno == ENOENT) 337 devicep = &name; 338 return (fd); 339 } 340