1 /* $OpenBSD */ 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 #ifndef lint 34 static const char copyright[] = 35 "@(#) Copyright (c) 1983, 1993\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "@(#)tunefs.c 8.3 (Berkeley) 5/3/95"; 42 #else 43 static const char rcsid[] = "$OpenBSD"; 44 #endif 45 #endif /* not lint */ 46 47 /* 48 * tunefs: change layout parameters to an existing file system. 49 */ 50 #include <sys/param.h> 51 52 #include <ufs/ufs/dinode.h> 53 #include <ufs/ffs/fs.h> 54 #include <ufs/ffs/ffs_extern.h> 55 56 #include <err.h> 57 #include <errno.h> 58 #include <fcntl.h> 59 #include <fstab.h> 60 #include <paths.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 #include <util.h> 66 67 /* the optimization warning string template */ 68 #define OPTWARN "should optimize for %s with minfree %s %d%%" 69 70 union { 71 struct fs sb; 72 char pad[MAXBSIZE]; 73 } sbun; 74 #define sblock sbun.sb 75 char buf[MAXBSIZE]; 76 77 int fi; 78 long dev_bsize = 512; 79 int is_ufs2 = 0; 80 off_t sblockloc; 81 82 static off_t sblock_try[] = SBLOCKSEARCH; 83 84 static void bwrite(daddr64_t, char *, int, const char *); 85 static void bread(daddr64_t, char *, int, const char *); 86 static int getnum(const char *, const char *, int, int); 87 static void getsb(struct fs *, const char *); 88 static int openpartition(const char *, int, char *, size_t); 89 static void usage(void); 90 91 int 92 main(int argc, char *argv[]) 93 { 94 #define OPTSTRING "AFNe:g:h:m:o:" 95 int i, ch, Aflag, Fflag, Nflag, openflags; 96 const char *special, *chg[2]; 97 char device[MAXPATHLEN]; 98 int maxbpg, minfree, optim; 99 int avgfilesize, avgfpdir; 100 101 Aflag = Fflag = Nflag = 0; 102 maxbpg = minfree = optim = -1; 103 avgfilesize = avgfpdir = -1; 104 chg[FS_OPTSPACE] = "space"; 105 chg[FS_OPTTIME] = "time"; 106 107 while ((ch = getopt(argc, argv, OPTSTRING)) != -1) { 108 switch (ch) { 109 110 case 'A': 111 Aflag++; 112 break; 113 114 case 'F': 115 Fflag++; 116 break; 117 118 case 'N': 119 Nflag++; 120 break; 121 122 case 'e': 123 maxbpg = getnum(optarg, 124 "maximum blocks per file in a cylinder group", 125 1, INT_MAX); 126 break; 127 128 case 'g': 129 avgfilesize = getnum(optarg, 130 "average file size", 1, INT_MAX); 131 break; 132 133 case 'h': 134 avgfpdir = getnum(optarg, 135 "expected number of files per directory", 136 1, INT_MAX); 137 break; 138 139 case 'm': 140 minfree = getnum(optarg, 141 "minimum percentage of free space", 0, 99); 142 break; 143 144 case 'o': 145 if (strcmp(optarg, chg[FS_OPTSPACE]) == 0) 146 optim = FS_OPTSPACE; 147 else if (strcmp(optarg, chg[FS_OPTTIME]) == 0) 148 optim = FS_OPTTIME; 149 else 150 errx(10, 151 "bad %s (options are `space' or `time')", 152 "optimization preference"); 153 break; 154 155 default: 156 usage(); 157 } 158 } 159 argc -= optind; 160 argv += optind; 161 if (argc != 1) 162 usage(); 163 164 special = argv[0]; 165 openflags = Nflag ? O_RDONLY : O_RDWR; 166 if (Fflag) 167 fi = open(special, openflags); 168 else { 169 fi = openpartition(special, openflags, device, sizeof(device)); 170 special = device; 171 } 172 if (fi == -1) 173 err(1, "%s", special); 174 getsb(&sblock, special); 175 176 #define CHANGEVAL(old, new, type, suffix) do \ 177 if ((new) != -1) { \ 178 if ((new) == (old)) \ 179 warnx("%s remains unchanged at %d%s", \ 180 (type), (old), (suffix)); \ 181 else { \ 182 warnx("%s changes from %d%s to %d%s", \ 183 (type), (old), (suffix), (new), (suffix)); \ 184 (old) = (new); \ 185 } \ 186 } while (/* CONSTCOND */0) 187 188 warnx("tuning %s", special); 189 CHANGEVAL(sblock.fs_maxbpg, maxbpg, 190 "maximum blocks per file in a cylinder group", ""); 191 CHANGEVAL(sblock.fs_minfree, minfree, 192 "minimum percentage of free space", "%"); 193 if (minfree != -1) { 194 if (minfree >= MINFREE && 195 sblock.fs_optim == FS_OPTSPACE) 196 warnx(OPTWARN, "time", ">=", MINFREE); 197 if (minfree < MINFREE && 198 sblock.fs_optim == FS_OPTTIME) 199 warnx(OPTWARN, "space", "<", MINFREE); 200 } 201 if (optim != -1) { 202 if (sblock.fs_optim == optim) { 203 warnx("%s remains unchanged as %s", 204 "optimization preference", 205 chg[optim]); 206 } else { 207 warnx("%s changes from %s to %s", 208 "optimization preference", 209 chg[sblock.fs_optim], chg[optim]); 210 sblock.fs_optim = optim; 211 if (sblock.fs_minfree >= MINFREE && 212 optim == FS_OPTSPACE) 213 warnx(OPTWARN, "time", ">=", MINFREE); 214 if (sblock.fs_minfree < MINFREE && 215 optim == FS_OPTTIME) 216 warnx(OPTWARN, "space", "<", MINFREE); 217 } 218 } 219 CHANGEVAL(sblock.fs_avgfilesize, avgfilesize, 220 "average file size", ""); 221 CHANGEVAL(sblock.fs_avgfpdir, avgfpdir, 222 "expected number of files per directory", ""); 223 224 if (Nflag) { 225 fprintf(stdout, "tunefs: current settings of %s\n", special); 226 fprintf(stdout, "\tmaximum contiguous block count %d\n", 227 sblock.fs_maxcontig); 228 fprintf(stdout, 229 "\tmaximum blocks per file in a cylinder group %d\n", 230 sblock.fs_maxbpg); 231 fprintf(stdout, "\tminimum percentage of free space %d%%\n", 232 sblock.fs_minfree); 233 fprintf(stdout, "\toptimization preference: %s\n", 234 chg[sblock.fs_optim]); 235 fprintf(stdout, "\taverage file size: %d\n", 236 sblock.fs_avgfilesize); 237 fprintf(stdout, 238 "\texpected number of files per directory: %d\n", 239 sblock.fs_avgfpdir); 240 fprintf(stdout, "tunefs: no changes made\n"); 241 exit(0); 242 } 243 244 memcpy(buf, (char *)&sblock, SBLOCKSIZE); 245 bwrite(sblockloc, buf, SBLOCKSIZE, special); 246 if (Aflag) 247 for (i = 0; i < sblock.fs_ncg; i++) 248 bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)), 249 buf, SBLOCKSIZE, special); 250 close(fi); 251 exit(0); 252 } 253 254 static int 255 getnum(const char *num, const char *desc, int min, int max) 256 { 257 int n; 258 const char *errstr; 259 260 n = strtonum(num, min, max, &errstr); 261 if (errstr != NULL) 262 errx(1, "Invalid number `%s' for %s: %s", num, desc, errstr); 263 return (n); 264 } 265 266 static void 267 usage(void) 268 { 269 extern char *__progname; 270 271 fprintf(stderr, 272 "usage: %s [-AFN] [-e maxbpg] [-g avgfilesize] " 273 "[-h avgfpdir] [-m minfree]\n" 274 "\t[-o optimize_preference] special | filesys\n", 275 __progname); 276 277 exit(2); 278 } 279 280 static void 281 getsb(struct fs *fs, const char *file) 282 { 283 int i; 284 285 for (i = 0; ; i++) { 286 if (sblock_try[i] == -1) 287 errx(5, "cannot find filesystem superblock"); 288 bread(sblock_try[i] / dev_bsize, (char *)fs, SBLOCKSIZE, file); 289 switch(fs->fs_magic) { 290 case FS_UFS2_MAGIC: 291 is_ufs2 = 1; 292 /*FALLTHROUGH*/ 293 case FS_UFS1_MAGIC: 294 break; 295 default: 296 continue; 297 } 298 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2) 299 continue; 300 if ((is_ufs2 || fs->fs_flags & FS_FLAGS_UPDATED) 301 && fs->fs_sblockloc != sblock_try[i]) 302 continue; 303 break; 304 } 305 306 dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); 307 sblockloc = sblock_try[i] / dev_bsize; 308 } 309 310 static void 311 bwrite(daddr64_t blk, char *buffer, int size, const char *file) 312 { 313 off_t offset; 314 315 offset = (off_t)blk * dev_bsize; 316 if (lseek(fi, offset, SEEK_SET) == -1) 317 err(6, "%s: seeking to %lld", file, (long long)offset); 318 if (write(fi, buffer, size) != size) 319 err(7, "%s: writing %d bytes", file, size); 320 } 321 322 static void 323 bread(daddr64_t blk, char *buffer, int cnt, const char *file) 324 { 325 off_t offset; 326 int i; 327 328 offset = (off_t)blk * dev_bsize; 329 if (lseek(fi, offset, SEEK_SET) == -1) 330 err(4, "%s: seeking to %lld", file, (long long)offset); 331 if ((i = read(fi, buffer, cnt)) != cnt) 332 errx(5, "%s: short read", file); 333 } 334 335 static int 336 openpartition(const char *name, int flags, char *device, size_t devicelen) 337 { 338 char rawspec[MAXPATHLEN], *p; 339 struct fstab *fs; 340 int fd, oerrno; 341 342 fs = getfsfile(name); 343 if (fs) { 344 if ((p = strrchr(fs->fs_spec, '/')) != NULL) { 345 snprintf(rawspec, sizeof(rawspec), "%.*s/r%s", 346 (int)(p - fs->fs_spec), fs->fs_spec, p + 1); 347 name = rawspec; 348 } else 349 name = fs->fs_spec; 350 } 351 fd = opendisk(name, flags, device, devicelen, 0); 352 if (fd == -1 && errno == ENOENT) { 353 oerrno = errno; 354 strlcpy(device, name, devicelen); 355 errno = oerrno; 356 } 357 return (fd); 358 } 359