xref: /openbsd-src/sbin/tunefs/tunefs.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
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