snj doesn't like my accent, so use proper English month names.
[netbsd-mini2440.git] / sbin / tunefs / tunefs.c
blob18425152dd105601782397a8d296d4260ee480b2
1 /* $NetBSD: tunefs.c,v 1.41 2009/09/13 14:13:23 bouyer Exp $ */
3 /*
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
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.
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.
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 */
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)tunefs.c 8.3 (Berkeley) 5/3/95";
41 #else
42 __RCSID("$NetBSD: tunefs.c,v 1.41 2009/09/13 14:13:23 bouyer Exp $");
43 #endif
44 #endif /* not lint */
47 * tunefs: change layout parameters to an existing file system.
49 #include <sys/param.h>
51 #include <ufs/ffs/fs.h>
52 #include <ufs/ffs/ffs_extern.h>
53 #include <ufs/ufs/ufs_wapbl.h>
55 #include <machine/bswap.h>
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>
68 /* the optimization warning string template */
69 #define OPTWARN "should optimize for %s with minfree %s %d%%"
71 union {
72 struct fs sb;
73 char pad[MAXBSIZE];
74 } sbun;
75 #define sblock sbun.sb
76 char buf[MAXBSIZE];
78 int fi;
79 long dev_bsize = 512;
80 int needswap = 0;
81 int is_ufs2 = 0;
82 off_t sblockloc;
84 static off_t sblock_try[] = SBLOCKSEARCH;
86 static void bwrite(daddr_t, char *, int, const char *);
87 static void bread(daddr_t, char *, int, const char *);
88 static void change_log_info(long long);
89 static void getsb(struct fs *, const char *);
90 static int openpartition(const char *, int, char *, size_t);
91 static void show_log_info(void);
92 static void usage(void);
94 int
95 main(int argc, char *argv[])
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;
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";
111 while ((ch = getopt(argc, argv, "AFNe:g:h:l:m:o:")) != -1) {
112 switch (ch) {
114 case 'A':
115 Aflag++;
116 break;
118 case 'F':
119 Fflag++;
120 break;
122 case 'N':
123 Nflag++;
124 break;
126 case 'e':
127 maxbpg = strsuftoll(
128 "maximum blocks per file in a cylinder group",
129 optarg, 1, INT_MAX);
130 break;
132 case 'g':
133 avgfilesize = strsuftoll("average file size", optarg,
134 1, INT_MAX);
135 break;
137 case 'h':
138 avgfpdir = strsuftoll(
139 "expected number of files per directory",
140 optarg, 1, INT_MAX);
141 break;
143 case 'l':
144 logfilesize = strsuftoll("journal log file size",
145 optarg, 0, INT_MAX);
146 break;
148 case 'm':
149 minfree = strsuftoll("minimum percentage of free space",
150 optarg, 0, 99);
151 break;
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;
164 default:
165 usage();
168 argc -= optind;
169 argv += optind;
170 if (argc != 1)
171 usage();
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;
181 if (fi == -1)
182 err(1, "%s", special);
183 getsb(&sblock, special);
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); \
195 } while (/* CONSTCOND */0)
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);
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);
228 CHANGEVAL(sblock.fs_avgfilesize, avgfilesize,
229 "average file size", "");
230 CHANGEVAL(sblock.fs_avgfpdir, avgfpdir,
231 "expected number of files per directory", "");
233 if (logfilesize >= 0)
234 change_log_info(logfilesize);
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);
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);
265 static void
266 show_log_info(void)
268 const char *loc;
269 uint64_t size, blksize, logsize;
270 int print;
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;
296 if (print) {
297 logsize = size * blksize;
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);
309 printf("\n");
310 printf("\tjournal log flags:");
311 if (sblock.fs_journal_flags & UFS_WAPBL_FLAGS_CREATE_LOG)
312 printf(" create-log");
313 if (sblock.fs_journal_flags & UFS_WAPBL_FLAGS_CLEAR_LOG)
314 printf(" clear-log");
315 printf("\n");
319 static void
320 change_log_info(long long logfilesize)
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
332 int in_fs_log;
333 uint64_t old_size;
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;
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;
349 case UFS_WAPBL_JOURNALLOC_NONE:
350 default:
351 in_fs_log = 0;
352 old_size = 0;
353 break;
356 if (logfilesize == 0) {
358 * Don't clear out the locators - the kernel might need
359 * these to find the log! Just set the "clear the log"
360 * flag and let the kernel do the rest.
362 sblock.fs_journal_flags |= UFS_WAPBL_FLAGS_CLEAR_LOG;
363 sblock.fs_journal_flags &= ~UFS_WAPBL_FLAGS_CREATE_LOG;
364 warnx("log file size cleared from %" PRIu64 "", old_size);
365 return;
368 if (!in_fs_log && logfilesize > 0 && old_size > 0)
369 errx(1, "Can't change size of non-in-filesystem log");
371 if (old_size == (uint64_t)logfilesize && logfilesize > 0) {
372 /* no action */
373 warnx("log file size remains unchanged at %lld", logfilesize);
374 return;
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);
394 static void
395 usage(void)
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);
409 static void
410 getsb(struct fs *fs, const char *file)
412 int i;
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;
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;
443 dev_bsize = fs->fs_fsize / fsbtodb(fs, 1);
444 sblockloc = sblock_try[i] / dev_bsize;
447 static void
448 bwrite(daddr_t blk, char *buffer, int size, const char *file)
450 off_t offset;
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);
459 static void
460 bread(daddr_t blk, char *buffer, int cnt, const char *file)
462 off_t offset;
463 int i;
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);
472 static int
473 openpartition(const char *name, int flags, char *device, size_t devicelen)
475 char rawspec[MAXPATHLEN], *p;
476 struct fstab *fs;
477 int fd, oerrno;
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;
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;
494 return (fd);