1 /* $NetBSD: tunefs.c,v 1.41 2009/09/13 14:13:23 bouyer Exp $ */
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
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
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
35 The Regents of the University of California. All rights reserved.");
40 static char sccsid
[] = "@(#)tunefs.c 8.3 (Berkeley) 5/3/95";
42 __RCSID("$NetBSD: tunefs.c,v 1.41 2009/09/13 14:13:23 bouyer Exp $");
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>
68 /* the optimization warning string template */
69 #define OPTWARN "should optimize for %s with minfree %s %d%%"
75 #define sblock sbun.sb
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);
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;
108 chg
[FS_OPTSPACE
] = "space";
109 chg
[FS_OPTTIME
] = "time";
111 while ((ch
= getopt(argc
, argv
, "AFNe:g:h:l:m:o:")) != -1) {
128 "maximum blocks per file in a cylinder group",
133 avgfilesize
= strsuftoll("average file size", optarg
,
138 avgfpdir
= strsuftoll(
139 "expected number of files per directory",
144 logfilesize
= strsuftoll("journal log file size",
149 minfree
= strsuftoll("minimum percentage of free space",
154 if (strcmp(optarg
, chg
[FS_OPTSPACE
]) == 0)
156 else if (strcmp(optarg
, chg
[FS_OPTTIME
]) == 0)
160 "bad %s (options are `space' or `time')",
161 "optimization preference");
174 openflags
= Nflag
? O_RDONLY
: O_RDWR
;
176 fi
= open(special
, openflags
);
178 fi
= openpartition(special
, openflags
, device
, sizeof(device
));
182 err(1, "%s", special
);
183 getsb(&sblock
, special
);
185 #define CHANGEVAL(old, new, type, suffix) do \
187 if ((new) == (old)) \
188 warnx("%s remains unchanged at %d%s", \
189 (type), (old), (suffix)); \
191 warnx("%s changes from %d%s to %d%s", \
192 (type), (old), (suffix), (new), (suffix)); \
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", "%");
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
);
211 if (sblock
.fs_optim
== optim
) {
212 warnx("%s remains unchanged as %s",
213 "optimization preference",
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
&&
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
);
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",
242 printf("\tminimum percentage of free space %d%%\n",
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",
249 printf("tunefs: no changes made\n");
253 memcpy(buf
, (char *)&sblock
, SBLOCKSIZE
);
255 ffs_sb_swap((struct fs
*)buf
, (struct fs
*)buf
);
256 bwrite(sblockloc
, buf
, SBLOCKSIZE
, special
);
258 for (i
= 0; i
< sblock
.fs_ncg
; i
++)
259 bwrite(fsbtodb(&sblock
, cgsblock(&sblock
, i
)),
260 buf
, SBLOCKSIZE
, special
);
269 uint64_t size
, blksize
, logsize
;
272 switch (sblock
.fs_journal_location
) {
273 case UFS_WAPBL_JOURNALLOC_NONE
:
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
];
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
];
297 logsize
= size
* blksize
;
299 printf("\tjournal log file location: %s\n", loc
);
300 printf("\tjournal log file size: ");
305 humanize_number(sizebuf
, 6, size
* blksize
, "B",
306 HN_AUTOSCALE
, HN_B
| HN_NOSPACE
| HN_DECIMAL
);
307 printf("%s (%" PRId64
" bytes)", sizebuf
, logsize
);
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");
320 change_log_info(long long logfilesize
)
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
329 * - if current is non-zero and new is zero, set flag to clear log
336 switch (sblock
.fs_journal_location
) {
337 case UFS_WAPBL_JOURNALLOC_END_PARTITION
:
339 old_size
= sblock
.fs_journallocs
[UFS_WAPBL_EPART_COUNT
] *
340 sblock
.fs_journallocs
[UFS_WAPBL_EPART_BLKSZ
];
343 case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM
:
345 old_size
= sblock
.fs_journallocs
[UFS_WAPBL_INFS_COUNT
] *
346 sblock
.fs_journallocs
[UFS_WAPBL_INFS_BLKSZ
];
349 case UFS_WAPBL_JOURNALLOC_NONE
:
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
);
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) {
373 warnx("log file size remains unchanged at %lld", logfilesize
);
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
);
389 "Can't change existing log size from %" PRIu64
" to %lld",
390 old_size
, logfilesize
);
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");
410 getsb(struct fs
*fs
, const char *file
)
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
) {
424 case FS_UFS2_MAGIC_SWAPPED
:
427 case FS_UFS1_MAGIC_SWAPPED
:
428 warnx("%s: swapping byte order", file
);
435 if (!is_ufs2
&& sblock_try
[i
] == SBLOCK_UFS2
)
437 if ((is_ufs2
|| fs
->fs_old_flags
& FS_FLAGS_UPDATED
)
438 && fs
->fs_sblockloc
!= sblock_try
[i
])
443 dev_bsize
= fs
->fs_fsize
/ fsbtodb(fs
, 1);
444 sblockloc
= sblock_try
[i
] / dev_bsize
;
448 bwrite(daddr_t blk
, char *buffer
, int size
, const char *file
)
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
);
460 bread(daddr_t blk
, char *buffer
, int cnt
, const char *file
)
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
);
473 openpartition(const char *name
, int flags
, char *device
, size_t devicelen
)
475 char rawspec
[MAXPATHLEN
], *p
;
479 fs
= getfsfile(name
);
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);
488 fd
= opendisk(name
, flags
, device
, devicelen
, 0);
489 if (fd
== -1 && errno
== ENOENT
) {
491 strlcpy(device
, name
, devicelen
);