xref: /freebsd/sbin/tunefs/tunefs.c (revision a3e8fd0b7f663db7eafff527d5c3ca3bcfa8a537)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1983, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)tunefs.c	8.2 (Berkeley) 4/19/94";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD$";
46 #endif /* not lint */
47 
48 /*
49  * tunefs: change layout parameters to an existing file system.
50  */
51 #include <sys/param.h>
52 #include <sys/mount.h>
53 #include <sys/disklabel.h>
54 #include <sys/stat.h>
55 
56 #include <ufs/ufs/ufsmount.h>
57 #include <ufs/ufs/dinode.h>
58 #include <ufs/ffs/fs.h>
59 
60 #include <err.h>
61 #include <fcntl.h>
62 #include <fstab.h>
63 #include <paths.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <unistd.h>
68 
69 /* the optimization warning string template */
70 #define	OPTWARN	"should optimize for %s with minfree %s %d%%"
71 
72 union {
73 	struct	fs sb;
74 	char pad[MAXBSIZE];
75 } sbun;
76 #define	sblock sbun.sb
77 
78 int fi;
79 long dev_bsize = 1;
80 
81 void bwrite(ufs2_daddr_t, const char *, int);
82 int bread(ufs2_daddr_t, char *, int);
83 void getsb(struct fs *, const char *);
84 void putsb(struct fs *, const char *, int);
85 void usage(void);
86 void printfs(void);
87 
88 int
89 main(argc, argv)
90 	int argc;
91 	char *argv[];
92 {
93 	char *special;
94 	const char *name;
95 	struct stat st;
96 	int Aflag = 0, active = 0, aflag = 0;
97 	int eflag = 0, fflag = 0, lflag = 0, mflag = 0;
98 	int nflag = 0, oflag = 0, pflag = 0, sflag = 0;
99 	int evalue = 0, fvalue = 0;
100 	int mvalue = 0, ovalue = 0, svalue = 0;
101 	char *avalue = NULL, *lvalue = NULL, *nvalue = NULL;
102 	struct fstab *fs;
103 	const char *chg[2];
104 	char device[MAXPATHLEN];
105 	struct ufs_args args;
106 	struct statfs stfs;
107 	int found_arg, ch;
108 
109         if (argc < 3)
110                 usage();
111 	found_arg = 0; /* at least one arg is required */
112 	while ((ch = getopt(argc, argv, "Aa:e:f:l:m:n:o:ps:")) != -1)
113 	  switch (ch) {
114 	  case 'A':
115 		found_arg = 1;
116 		Aflag++;
117 		break;
118 	  case 'a':
119 		found_arg = 1;
120 		name = "ACLs";
121 		avalue = optarg;
122 		if (strcmp(avalue, "enable") && strcmp(avalue, "disable")) {
123 			errx(10, "bad %s (options are %s)", name,
124 			    "`enable' or `disable'");
125 		}
126 		aflag = 1;
127 		break;
128 	  case 'e':
129 		found_arg = 1;
130 		name = "maximum blocks per file in a cylinder group";
131 		evalue = atoi(optarg);
132 		if (evalue < 1)
133 			errx(10, "%s must be >= 1 (was %s)", name, optarg);
134 		eflag = 1;
135 		break;
136 	  case 'f':
137 		found_arg = 1;
138 		name = "average file size";
139 		fvalue = atoi(optarg);
140 		if (fvalue < 1)
141 			errx(10, "%s must be >= 1 (was %s)", name, optarg);
142 		fflag = 1;
143 		break;
144 	  case 'l':
145 		found_arg = 1;
146 		name = "multilabel MAC file system";
147 		lvalue = optarg;
148 		if (strcmp(lvalue, "enable") && strcmp(lvalue, "disable")) {
149 			errx(10, "bad %s (options are %s)", name,
150 			    "`enable' or `disable'");
151 		}
152 		lflag = 1;
153 		break;
154 	  case 'm':
155 		found_arg = 1;
156 		name = "minimum percentage of free space";
157 		mvalue = atoi(optarg);
158 		if (mvalue < 0 || mvalue > 99)
159 			errx(10, "bad %s (%s)", name, optarg);
160 		mflag = 1;
161 		break;
162 	  case 'n':
163 		found_arg = 1;
164  		name = "soft updates";
165  		nvalue = optarg;
166                 if (strcmp(nvalue, "enable") && strcmp(nvalue, "disable")) {
167  			errx(10, "bad %s (options are %s)",
168  			    name, "`enable' or `disable'");
169  		}
170 		nflag = 1;
171  		break;
172 	  case 'o':
173 		found_arg = 1;
174 		name = "optimization preference";
175 		chg[FS_OPTSPACE] = "space";
176 		chg[FS_OPTTIME] = "time";
177 		if (strcmp(optarg, chg[FS_OPTSPACE]) == 0)
178 			ovalue = FS_OPTSPACE;
179 		else if (strcmp(optarg, chg[FS_OPTTIME]) == 0)
180 			ovalue = FS_OPTTIME;
181 		else
182 			errx(10, "bad %s (options are `space' or `time')",
183 					    name);
184 		oflag = 1;
185 		break;
186 	  case 'p':
187 		found_arg = 1;
188 		pflag = 1;
189 		break;
190 	  case 's':
191 		found_arg = 1;
192 		name = "expected number of files per directory";
193 		svalue = atoi(optarg);
194 		if (svalue < 1)
195 			errx(10, "%s must be >= 1 (was %s)", name, optarg);
196 		sflag = 1;
197 		break;
198 	  default:
199 		usage();
200 	  }
201 	argc -= optind;
202 	argv += optind;
203 
204 	if (found_arg == 0 || argc != 1)
205 	  usage();
206 
207 	special = argv[0];
208 	fs = getfsfile(special);
209 	if (fs) {
210 		if (statfs(special, &stfs) == 0 &&
211 		    strcmp(special, stfs.f_mntonname) == 0) {
212 			active = 1;
213 		}
214 		special = fs->fs_spec;
215 	}
216 again:
217 	if (stat(special, &st) < 0) {
218 		if (*special != '/') {
219 			if (*special == 'r')
220 				special++;
221 			(void)snprintf(device, sizeof(device), "%s%s",
222 				       _PATH_DEV, special);
223 			special = device;
224 			goto again;
225 		}
226 		err(1, "%s", special);
227 	}
228 	if (fs == NULL && (st.st_mode & S_IFMT) == S_IFDIR)
229 		errx(10, "%s: unknown file system", special);
230 	getsb(&sblock, special);
231 
232 	if (pflag) {
233 		printfs();
234 		exit(0);
235 	}
236 	if (aflag) {
237 		name = "ACLs";
238 		if (strcmp(avalue, "enable") == 0) {
239 			if (sblock.fs_flags & FS_ACLS) {
240 				warnx("%s remains unchanged as enabled", name);
241 			} else {
242 				sblock.fs_flags |= FS_ACLS;
243 				warnx("%s set", name);
244 			}
245 		} else if (strcmp(avalue, "disable") == 0) {
246 			if ((~sblock.fs_flags & FS_ACLS) ==
247 			    FS_ACLS) {
248 				warnx("%s remains unchanged as disabled",
249 				    name);
250 			} else {
251 				sblock.fs_flags &= ~FS_ACLS;
252 				warnx("%s cleared", name);
253 			}
254 		}
255 	}
256 	if (eflag) {
257 		name = "maximum blocks per file in a cylinder group";
258 		if (sblock.fs_maxbpg == evalue) {
259 			warnx("%s remains unchanged as %d", name, evalue);
260 		}
261 		else {
262 			warnx("%s changes from %d to %d",
263 					name, sblock.fs_maxbpg, evalue);
264 			sblock.fs_maxbpg = evalue;
265 		}
266 	}
267 	if (fflag) {
268 		name = "average file size";
269 		if (sblock.fs_avgfilesize == fvalue) {
270 			warnx("%s remains unchanged as %d", name, fvalue);
271 		}
272 		else {
273 			warnx("%s changes from %d to %d",
274 					name, sblock.fs_avgfilesize, fvalue);
275 			sblock.fs_avgfilesize = fvalue;
276 		}
277 	}
278 	if (lflag) {
279 		name = "multilabel";
280 		if (strcmp(lvalue, "enable") == 0) {
281 			if (sblock.fs_flags & FS_MULTILABEL) {
282 				warnx("%s remains unchanged as enabled", name);
283 			} else {
284 				sblock.fs_flags |= FS_MULTILABEL;
285 				warnx("%s set", name);
286 			}
287 		} else if (strcmp(lvalue, "disable") == 0) {
288 			if ((~sblock.fs_flags & FS_MULTILABEL) ==
289 			    FS_MULTILABEL) {
290 				warnx("%s remains unchanged as disabled",
291 				    name);
292 			} else {
293 				sblock.fs_flags &= ~FS_MULTILABEL;
294 				warnx("%s cleared", name);
295 			}
296 		}
297 	}
298 	if (mflag) {
299 		name = "minimum percentage of free space";
300 		if (sblock.fs_minfree == mvalue) {
301 			warnx("%s remains unchanged as %d%%", name, mvalue);
302 		}
303 		else {
304 			warnx("%s changes from %d%% to %d%%",
305 				    name, sblock.fs_minfree, mvalue);
306 			sblock.fs_minfree = mvalue;
307 			if (mvalue >= MINFREE && sblock.fs_optim == FS_OPTSPACE)
308 				warnx(OPTWARN, "time", ">=", MINFREE);
309 			if (mvalue < MINFREE && sblock.fs_optim == FS_OPTTIME)
310 				warnx(OPTWARN, "space", "<", MINFREE);
311 		}
312 	}
313 	if (nflag) {
314  		name = "soft updates";
315  		if (strcmp(nvalue, "enable") == 0) {
316 			if (sblock.fs_flags & FS_DOSOFTDEP) {
317 				warnx("%s remains unchanged as enabled", name);
318 			} else if (sblock.fs_clean == 0) {
319 				warnx("%s cannot be enabled until fsck is run",
320 				    name);
321 			} else {
322  				sblock.fs_flags |= FS_DOSOFTDEP;
323  				warnx("%s set", name);
324 			}
325  		} else if (strcmp(nvalue, "disable") == 0) {
326 			if ((~sblock.fs_flags & FS_DOSOFTDEP) == FS_DOSOFTDEP) {
327 				warnx("%s remains unchanged as disabled", name);
328 			} else {
329  				sblock.fs_flags &= ~FS_DOSOFTDEP;
330  				warnx("%s cleared", name);
331 			}
332  		}
333 	}
334 	if (oflag) {
335 		name = "optimization preference";
336 		chg[FS_OPTSPACE] = "space";
337 		chg[FS_OPTTIME] = "time";
338 		if (sblock.fs_optim == ovalue) {
339 			warnx("%s remains unchanged as %s", name, chg[ovalue]);
340 		}
341 		else {
342 			warnx("%s changes from %s to %s",
343 				    name, chg[sblock.fs_optim], chg[ovalue]);
344 			sblock.fs_optim = ovalue;
345 			if (sblock.fs_minfree >= MINFREE &&
346 					ovalue == FS_OPTSPACE)
347 				warnx(OPTWARN, "time", ">=", MINFREE);
348 			if (sblock.fs_minfree < MINFREE &&
349 					ovalue == FS_OPTTIME)
350 				warnx(OPTWARN, "space", "<", MINFREE);
351 		}
352 	}
353 	if (sflag) {
354 		name = "expected number of files per directory";
355 		if (sblock.fs_avgfpdir == svalue) {
356 			warnx("%s remains unchanged as %d", name, svalue);
357 		}
358 		else {
359 			warnx("%s changes from %d to %d",
360 					name, sblock.fs_avgfpdir, svalue);
361 			sblock.fs_avgfpdir = svalue;
362 		}
363 	}
364 
365 	putsb(&sblock, special, Aflag);
366 	if (active) {
367 		bzero(&args, sizeof(args));
368 		if (mount("ufs", fs->fs_file,
369 		    stfs.f_flags | MNT_UPDATE | MNT_RELOAD, &args) < 0)
370 			err(9, "%s: reload", special);
371 		warnx("file system reloaded");
372 	}
373 	exit(0);
374 }
375 
376 void
377 usage()
378 {
379 	fprintf(stderr, "%s\n%s\n%s\n",
380 "usage: tunefs [-A] [-a enable | disable] [-e maxbpg] [-f avgfilesize]",
381 "              [-l enable | disable] [-m minfree] [-n enable | disable]",
382 "              [-o space | time] [-p] [-s avgfpdir] special | filesystem");
383 	exit(2);
384 }
385 
386 /*
387  * Possible superblock locations ordered from most to least likely.
388  */
389 static int sblock_try[] = SBLOCKSEARCH;
390 static ufs2_daddr_t sblockloc;
391 
392 void
393 getsb(fs, file)
394 	struct fs *fs;
395 	const char *file;
396 {
397 	int i;
398 
399 	fi = open(file, O_RDONLY);
400 	if (fi < 0)
401 		err(3, "cannot open %s", file);
402 	for (i = 0; sblock_try[i] != -1; i++) {
403 		if (bread(sblock_try[i], (char *)fs, SBLOCKSIZE))
404 			err(4, "%s: bad super block", file);
405 		if ((fs->fs_magic == FS_UFS1_MAGIC ||
406 		     (fs->fs_magic == FS_UFS2_MAGIC &&
407 		      fs->fs_sblockloc == numfrags(fs, sblock_try[i]))) &&
408 		    fs->fs_bsize <= MAXBSIZE &&
409 		    fs->fs_bsize >= sizeof(struct fs))
410 			break;
411 	}
412 	if (sblock_try[i] == -1)
413 		err(5, "Cannot find file system superblock");
414 	dev_bsize = fs->fs_fsize / fsbtodb(fs, 1);
415 	sblockloc = sblock_try[i] / dev_bsize;
416 }
417 
418 void
419 putsb(fs, file, all)
420 	struct fs *fs;
421 	const char *file;
422 	int all;
423 {
424 	int i;
425 
426 	/*
427 	 * Re-open the device read-write. Use the read-only file
428 	 * descriptor as an interlock to prevent the device from
429 	 * being mounted while we are switching mode.
430 	 */
431 	i = fi;
432 	fi = open(file, O_RDWR);
433 	close(i);
434 	if (fi < 0)
435 		err(3, "cannot open %s", file);
436 	bwrite(sblockloc, (const char *)fs, SBLOCKSIZE);
437 	if (all)
438 		for (i = 0; i < fs->fs_ncg; i++)
439 			bwrite(fsbtodb(fs, cgsblock(fs, i)),
440 			    (const char *)fs, SBLOCKSIZE);
441 	close(fi);
442 }
443 
444 void
445 printfs()
446 {
447 	warnx("ACLs: (-a)                                         %s",
448 		(sblock.fs_flags & FS_ACLS)? "enabled" : "disabled");
449 	warnx("MAC multilabel: (-l)                               %s",
450 		(sblock.fs_flags & FS_MULTILABEL)? "enabled" : "disabled");
451 	warnx("soft updates:  (-n)                                %s",
452 		(sblock.fs_flags & FS_DOSOFTDEP)? "enabled" : "disabled");
453 	warnx("maximum blocks per file in a cylinder group: (-e)  %d",
454 	      sblock.fs_maxbpg);
455 	warnx("average file size: (-f)                            %d",
456 	      sblock.fs_avgfilesize);
457 	warnx("average number of files in a directory: (-s)       %d",
458 	      sblock.fs_avgfpdir);
459 	warnx("minimum percentage of free space: (-m)             %d%%",
460 	      sblock.fs_minfree);
461 	warnx("optimization preference: (-o)                      %s",
462 	      sblock.fs_optim == FS_OPTSPACE ? "space" : "time");
463 	if (sblock.fs_minfree >= MINFREE &&
464 	    sblock.fs_optim == FS_OPTSPACE)
465 		warnx(OPTWARN, "time", ">=", MINFREE);
466 	if (sblock.fs_minfree < MINFREE &&
467 	    sblock.fs_optim == FS_OPTTIME)
468 		warnx(OPTWARN, "space", "<", MINFREE);
469 }
470 
471 void
472 bwrite(blk, buf, size)
473 	ufs2_daddr_t blk;
474 	const char *buf;
475 	int size;
476 {
477 
478 	if (lseek(fi, (off_t)blk * dev_bsize, SEEK_SET) < 0)
479 		err(6, "FS SEEK");
480 	if (write(fi, buf, size) != size)
481 		err(7, "FS WRITE");
482 }
483 
484 int
485 bread(bno, buf, cnt)
486 	ufs2_daddr_t bno;
487 	char *buf;
488 	int cnt;
489 {
490 	int i;
491 
492 	if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0)
493 		return(1);
494 	if ((i = read(fi, buf, cnt)) != cnt) {
495 		for(i=0; i<sblock.fs_bsize; i++)
496 			buf[i] = 0;
497 		return (1);
498 	}
499 	return (0);
500 }
501