1 /* $OpenBSD: tunefs.c,v 1.41 2016/05/28 23:44:27 tb 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> /* DEV_BSIZE MAXBSIZE */ 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 <limits.h> 52 #include <util.h> 53 54 /* the optimization warning string template */ 55 #define OPTWARN "should optimize for %s with minfree %s %d%%" 56 57 union { 58 struct fs sb; 59 char pad[MAXBSIZE]; 60 } sbun; 61 #define sblock sbun.sb 62 char buf[MAXBSIZE]; 63 64 int fi; 65 int is_ufs2 = 0; 66 off_t sblockloc; 67 68 static off_t sblock_try[] = SBLOCKSEARCH; 69 70 static void bwrite(daddr_t, char *, int, const char *); 71 static void bread(daddr_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 = 1; 98 break; 99 100 case 'F': 101 Fflag = 1; 102 break; 103 104 case 'N': 105 Nflag = 1; 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 159 if (pledge("stdio", NULL) == -1) 160 err(1, "pledge"); 161 162 getsb(&sblock, special); 163 164 #define CHANGEVAL(old, new, type, suffix) do \ 165 if ((new) != -1) { \ 166 if ((new) == (old)) \ 167 warnx("%s remains unchanged at %d%s", \ 168 (type), (old), (suffix)); \ 169 else { \ 170 warnx("%s changes from %d%s to %d%s", \ 171 (type), (old), (suffix), (new), (suffix)); \ 172 (old) = (new); \ 173 } \ 174 } while (/* CONSTCOND */0) 175 176 warnx("tuning %s", special); 177 CHANGEVAL(sblock.fs_maxbpg, maxbpg, 178 "maximum blocks per file in a cylinder group", ""); 179 CHANGEVAL(sblock.fs_minfree, minfree, 180 "minimum percentage of free space", "%"); 181 if (minfree != -1) { 182 if (minfree >= MINFREE && 183 sblock.fs_optim == FS_OPTSPACE) 184 warnx(OPTWARN, "time", ">=", MINFREE); 185 if (minfree < MINFREE && 186 sblock.fs_optim == FS_OPTTIME) 187 warnx(OPTWARN, "space", "<", MINFREE); 188 } 189 if (optim != -1) { 190 if (sblock.fs_optim == optim) { 191 warnx("%s remains unchanged as %s", 192 "optimization preference", 193 chg[optim]); 194 } else { 195 warnx("%s changes from %s to %s", 196 "optimization preference", 197 chg[sblock.fs_optim], chg[optim]); 198 sblock.fs_optim = optim; 199 if (sblock.fs_minfree >= MINFREE && 200 optim == FS_OPTSPACE) 201 warnx(OPTWARN, "time", ">=", MINFREE); 202 if (sblock.fs_minfree < MINFREE && 203 optim == FS_OPTTIME) 204 warnx(OPTWARN, "space", "<", MINFREE); 205 } 206 } 207 CHANGEVAL(sblock.fs_avgfilesize, avgfilesize, 208 "average file size", ""); 209 CHANGEVAL(sblock.fs_avgfpdir, avgfpdir, 210 "expected number of files per directory", ""); 211 212 if (Nflag) { 213 fprintf(stdout, "tunefs: current settings of %s\n", special); 214 fprintf(stdout, "\tmaximum contiguous block count %d\n", 215 sblock.fs_maxcontig); 216 fprintf(stdout, 217 "\tmaximum blocks per file in a cylinder group %d\n", 218 sblock.fs_maxbpg); 219 fprintf(stdout, "\tminimum percentage of free space %d%%\n", 220 sblock.fs_minfree); 221 fprintf(stdout, "\toptimization preference: %s\n", 222 chg[sblock.fs_optim]); 223 fprintf(stdout, "\taverage file size: %d\n", 224 sblock.fs_avgfilesize); 225 fprintf(stdout, 226 "\texpected number of files per directory: %d\n", 227 sblock.fs_avgfpdir); 228 fprintf(stdout, "tunefs: no changes made\n"); 229 exit(0); 230 } 231 232 memcpy(buf, (char *)&sblock, SBLOCKSIZE); 233 bwrite(sblockloc, buf, SBLOCKSIZE, special); 234 if (Aflag) 235 for (i = 0; i < sblock.fs_ncg; i++) 236 bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)), 237 buf, SBLOCKSIZE, special); 238 close(fi); 239 exit(0); 240 } 241 242 static int 243 getnum(const char *num, const char *desc, int min, int max) 244 { 245 int n; 246 const char *errstr; 247 248 n = strtonum(num, min, max, &errstr); 249 if (errstr != NULL) 250 errx(1, "Invalid number `%s' for %s: %s", num, desc, errstr); 251 return (n); 252 } 253 254 static void 255 usage(void) 256 { 257 extern char *__progname; 258 259 fprintf(stderr, 260 "usage: %s [-AFN] [-e maxbpg] [-g avgfilesize] " 261 "[-h avgfpdir] [-m minfree]\n" 262 "\t[-o optimize_preference] special | filesys\n", 263 __progname); 264 265 exit(2); 266 } 267 268 static void 269 getsb(struct fs *fs, const char *file) 270 { 271 int i; 272 273 for (i = 0; ; i++) { 274 if (sblock_try[i] == -1) 275 errx(5, "cannot find filesystem superblock"); 276 bread(sblock_try[i] / DEV_BSIZE, (char *)fs, SBLOCKSIZE, file); 277 switch(fs->fs_magic) { 278 case FS_UFS2_MAGIC: 279 is_ufs2 = 1; 280 /*FALLTHROUGH*/ 281 case FS_UFS1_MAGIC: 282 break; 283 default: 284 continue; 285 } 286 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2) 287 continue; 288 if ((is_ufs2 || fs->fs_flags & FS_FLAGS_UPDATED) 289 && fs->fs_sblockloc != sblock_try[i]) 290 continue; 291 break; 292 } 293 294 sblockloc = sblock_try[i] / DEV_BSIZE; 295 } 296 297 static void 298 bwrite(daddr_t blk, char *buffer, int size, const char *file) 299 { 300 if (pwrite(fi, buffer, size, blk * DEV_BSIZE) != size) 301 err(7, "%s: writing %d bytes @ %lld", file, size, 302 (long long)(blk * DEV_BSIZE)); 303 } 304 305 static void 306 bread(daddr_t blk, char *buffer, int cnt, const char *file) 307 { 308 if ((pread(fi, buffer, cnt, (off_t)blk * DEV_BSIZE)) != cnt) 309 errx(5, "%s: reading %d bytes @ %lld", file, cnt, 310 (long long)(blk * DEV_BSIZE)); 311 } 312 313 static int 314 openpartition(char *name, int flags, char **devicep) 315 { 316 char rawspec[PATH_MAX], *p; 317 struct fstab *fs; 318 int fd; 319 320 fs = getfsfile(name); 321 if (fs) { 322 if ((p = strrchr(fs->fs_spec, '/')) != NULL) { 323 snprintf(rawspec, sizeof(rawspec), "%.*s/r%s", 324 (int)(p - fs->fs_spec), fs->fs_spec, p + 1); 325 name = rawspec; 326 } else 327 name = fs->fs_spec; 328 } 329 fd = opendev(name, flags, 0, devicep); 330 if (fd == -1 && errno == ENOENT) 331 devicep = &name; 332 return (fd); 333 } 334