xref: /illumos-gate/usr/src/cmd/fs.d/ufs/tunefs/tunefs.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * tunefs: change layout parameters to an existing file system.
44  */
45 
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <ustat.h>
50 #include <sys/param.h>
51 #include <sys/types.h>
52 #include <time.h>
53 #include <sys/mntent.h>
54 
55 #define	bcopy(f, t, n)    memcpy(t, f, n)
56 #define	bzero(s, n)	memset(s, 0, n)
57 #define	bcmp(s, d, n)	memcmp(s, d, n)
58 
59 #define	index(s, r)	strchr(s, r)
60 #define	rindex(s, r)	strrchr(s, r)
61 
62 #include <sys/sysmacros.h>
63 #include <sys/stat.h>
64 #include <sys/fs/ufs_fs.h>
65 #include <sys/vnode.h>
66 #include <sys/fs/ufs_inode.h>
67 #include <fcntl.h>
68 #include <stdio.h>
69 #include <sys/mnttab.h>
70 #include <sys/vfstab.h>
71 #include <sys/ustat.h>
72 #include <sys/filio.h>
73 #include <sys/fs/ufs_filio.h>
74 
75 extern offset_t llseek();
76 
77 union {
78 	struct	fs sb;
79 	char pad[SBSIZE];
80 } sbun;
81 #define	sblock sbun.sb
82 
83 int fi;
84 struct ustat ustatarea;
85 extern int	optind;
86 extern char	*optarg;
87 
88 static void usage();
89 static void getsb();
90 static void bwrite();
91 static void fatal();
92 static int bread();
93 static int isnumber();
94 
95 extern char *getfullrawname(), *getfullblkname();
96 
97 static void
98 searchvfstab(char **specialp)
99 {
100 	FILE *vfstab;
101 	struct vfstab vfsbuf;
102 	char *blockspecial;
103 
104 	blockspecial = getfullblkname(*specialp);
105 	if (blockspecial == NULL)
106 		blockspecial = *specialp;
107 
108 	if ((vfstab = fopen(VFSTAB, "r")) == NULL) {
109 		fprintf(stderr, "%s: ", VFSTAB);
110 		perror("open");
111 	}
112 	while (getvfsent(vfstab, &vfsbuf) == NULL)
113 		if (strcmp(vfsbuf.vfs_fstype, MNTTYPE_UFS) == 0)
114 			if ((strcmp(vfsbuf.vfs_mountp, *specialp) == 0) ||
115 			    (strcmp(vfsbuf.vfs_special, *specialp) == 0) ||
116 			    (strcmp(vfsbuf.vfs_special, blockspecial) == 0) ||
117 			    (strcmp(vfsbuf.vfs_fsckdev, *specialp) == 0)) {
118 				*specialp = strdup(vfsbuf.vfs_special);
119 				return;
120 			}
121 	fclose(vfstab);
122 }
123 
124 static void
125 searchmnttab(char **specialp, char **mountpointp)
126 {
127 	FILE *mnttab;
128 	struct mnttab mntbuf;
129 	char *blockspecial;
130 
131 	blockspecial = getfullblkname(*specialp);
132 	if (blockspecial == NULL)
133 		blockspecial = *specialp;
134 
135 	if ((mnttab = fopen(MNTTAB, "r")) == NULL)
136 		return;
137 	while (getmntent(mnttab, &mntbuf) == NULL)
138 		if (strcmp(mntbuf.mnt_fstype, MNTTYPE_UFS) == 0)
139 			if ((strcmp(mntbuf.mnt_mountp, *specialp) == 0) ||
140 			    (strcmp(mntbuf.mnt_special, blockspecial) == 0) ||
141 			    (strcmp(mntbuf.mnt_special, *specialp) == 0)) {
142 				*specialp = strdup(mntbuf.mnt_special);
143 				*mountpointp = strdup(mntbuf.mnt_mountp);
144 				return;
145 			}
146 	fclose(mnttab);
147 }
148 
149 void
150 main(argc, argv)
151 	int argc;
152 	char *argv[];
153 {
154 	char *special, *name, *mountpoint = NULL;
155 	struct stat64 st;
156 	int i, mountfd;
157 	int Aflag = 0;
158 	char *chg[2];
159 	int	opt;
160 	struct fiotune fiotune;
161 
162 
163 	if (argc < 3)
164 		usage();
165 	special = argv[argc - 1];
166 
167 	/*
168 	 * For performance, don't search mnttab unless necessary
169 	 */
170 
171 	if (stat64(special, &st) >= 0) {
172 		/*
173 		 * If mounted directory, search mnttab for special
174 		 */
175 		if ((st.st_mode & S_IFMT) == S_IFDIR) {
176 			if (st.st_ino == UFSROOTINO)
177 				searchmnttab(&special, &mountpoint);
178 		/*
179 		 * If mounted device, search mnttab for mountpoint
180 		 */
181 		} else if ((st.st_mode & S_IFMT) == S_IFBLK ||
182 			    (st.st_mode & S_IFMT) == S_IFCHR) {
183 				if (ustat(st.st_rdev, &ustatarea) >= 0)
184 					searchmnttab(&special, &mountpoint);
185 		}
186 	}
187 	/*
188 	 * Doesn't appear to be mounted; take ``unmounted'' path
189 	 */
190 	if (mountpoint == NULL)
191 		searchvfstab(&special);
192 
193 	if ((special = getfullrawname(special)) == NULL) {
194 		fprintf(stderr, "tunefs: malloc failed\n");
195 		exit(32);
196 	}
197 
198 	if (*special == '\0') {
199 		fprintf(stderr, "tunefs: Could not find raw device for %s\n",
200 		    argv[argc -1]);
201 		exit(32);
202 	}
203 
204 	if (stat64(special, &st) < 0) {
205 		fprintf(stderr, "tunefs: "); perror(special);
206 		exit(31+1);
207 	}
208 
209 	/*
210 	 * If a mountpoint has been found then we will ioctl() the file
211 	 * system instead of writing to the file system's device
212 	 */
213 	/* ustat() ok because max number of UFS inodes can fit in ino_t */
214 	if (ustat(st.st_rdev, &ustatarea) >= 0) {
215 		if (mountpoint == NULL) {
216 			printf("%s is mounted, can't tunefs\n", special);
217 			exit(32);
218 		}
219 	} else
220 		mountpoint = NULL;
221 
222 	if ((st.st_mode & S_IFMT) != S_IFBLK &&
223 	    (st.st_mode & S_IFMT) != S_IFCHR)
224 		fatal("%s: not a block or character device", special);
225 	getsb(&sblock, special);
226 	while ((opt = getopt(argc, argv, "o:m:e:d:a:AV")) != EOF) {
227 		switch (opt) {
228 
229 		case 'A':
230 			Aflag++;
231 			continue;
232 
233 		case 'a':
234 			name = "maximum contiguous block count";
235 			if (!isnumber(optarg))
236 				fatal("%s: %s must be >= 1", *argv, name);
237 			i = atoi(optarg);
238 			if (i < 1)
239 				fatal("%s: %s must be >= 1", *argv, name);
240 			fprintf(stdout, "%s changes from %d to %d\n",
241 				name, sblock.fs_maxcontig, i);
242 			sblock.fs_maxcontig = i;
243 			continue;
244 
245 		case 'd':
246 			sblock.fs_rotdelay = 0;
247 			continue;
248 
249 		case 'e':
250 			name =
251 			    "maximum blocks per file in a cylinder group";
252 			if (!isnumber(optarg))
253 				fatal("%s: %s must be >= 1", *argv, name);
254 			i = atoi(optarg);
255 			if (i < 1)
256 				fatal("%s: %s must be >= 1", *argv, name);
257 			fprintf(stdout, "%s changes from %d to %d\n",
258 				name, sblock.fs_maxbpg, i);
259 			sblock.fs_maxbpg = i;
260 			continue;
261 
262 		case 'm':
263 			name = "minimum percentage of free space";
264 			if (!isnumber(optarg))
265 				fatal("%s: bad %s", *argv, name);
266 			i = atoi(optarg);
267 			if (i < 0 || i > 99)
268 				fatal("%s: bad %s", *argv, name);
269 			fprintf(stdout,
270 				"%s changes from %d%% to %d%%\n",
271 				name, sblock.fs_minfree, i);
272 			sblock.fs_minfree = i;
273 			continue;
274 
275 		case 'o':
276 			name = "optimization preference";
277 			chg[FS_OPTSPACE] = "space";
278 			chg[FS_OPTTIME] = "time";
279 			if (strcmp(optarg, chg[FS_OPTSPACE]) == 0)
280 				i = FS_OPTSPACE;
281 			else if (strcmp(optarg, chg[FS_OPTTIME]) == 0)
282 				i = FS_OPTTIME;
283 			else
284 			fatal("%s: bad %s (options are `space' or `time')",
285 					optarg, name);
286 			if (sblock.fs_optim == i) {
287 				fprintf(stdout,
288 					"%s remains unchanged as %s\n",
289 					name, chg[i]);
290 				continue;
291 			}
292 			fprintf(stdout,
293 				"%s changes from %s to %s\n",
294 				name, chg[sblock.fs_optim], chg[i]);
295 			sblock.fs_optim = i;
296 			continue;
297 
298 		case 'V':
299 			{
300 				char	*opt_text;
301 				int	opt_count;
302 
303 				(void) fprintf(stdout, "tunefs -F ufs ");
304 				for (opt_count = 1; opt_count < argc;
305 				    opt_count++) {
306 					opt_text = argv[opt_count];
307 					if (opt_text)
308 						(void) fprintf(stdout, " %s ",
309 						    opt_text);
310 				}
311 				(void) fprintf(stdout, "\n");
312 			}
313 			break;
314 
315 		default:
316 			usage();
317 		}
318 	}
319 	if ((argc - optind) != 1)
320 		usage();
321 	if (mountpoint) {
322 		mountfd = open(mountpoint, O_RDONLY);
323 		if (mountfd == -1) {
324 			perror(mountpoint);
325 			fprintf(stderr,
326 				"tunefs: can't tune %s\n", mountpoint);
327 			exit(32);
328 		}
329 		fiotune.maxcontig = sblock.fs_maxcontig;
330 		fiotune.rotdelay = sblock.fs_rotdelay;
331 		fiotune.maxbpg = sblock.fs_maxbpg;
332 		fiotune.minfree = sblock.fs_minfree;
333 		fiotune.optim = sblock.fs_optim;
334 		if (ioctl(mountfd, _FIOTUNE, &fiotune) == -1) {
335 			perror(mountpoint);
336 			fprintf(stderr,
337 				"tunefs: can't tune %s\n", mountpoint);
338 			exit(32);
339 		}
340 		close(mountfd);
341 	} else {
342 		bwrite((diskaddr_t)SBLOCK, (char *)&sblock, SBSIZE);
343 
344 		if (Aflag)
345 			for (i = 0; i < sblock.fs_ncg; i++)
346 				bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)),
347 				    (char *)&sblock, SBSIZE);
348 	}
349 
350 	close(fi);
351 	exit(0);
352 }
353 
354 void
355 usage()
356 {
357 	fprintf(stderr, "ufs usage: tunefs tuneup-options special-device\n");
358 	fprintf(stderr, "where tuneup-options are:\n");
359 	fprintf(stderr, "\t-a maximum contiguous blocks\n");
360 	fprintf(stderr, "\t-d rotational delay between contiguous blocks\n");
361 	fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n");
362 	fprintf(stderr, "\t-m minimum percentage of free space\n");
363 	fprintf(stderr, "\t-o optimization preference (`space' or `time')\n");
364 	exit(31+2);
365 }
366 
367 void
368 getsb(fs, file)
369 	struct fs *fs;
370 	char *file;
371 {
372 
373 	fi = open64(file, O_RDWR);
374 	if (fi < 0) {
375 		fprintf(stderr, "Cannot open ");
376 		perror(file);
377 		exit(31+3);
378 	}
379 	if (bread((diskaddr_t)SBLOCK, (char *)fs, SBSIZE)) {
380 		fprintf(stderr, "Bad super block ");
381 		perror(file);
382 		exit(31+4);
383 	}
384 	if ((fs->fs_magic != FS_MAGIC) && (fs->fs_magic != MTB_UFS_MAGIC)) {
385 		fprintf(stderr, "%s: bad magic number\n", file);
386 		exit(31+5);
387 	}
388 	if (fs->fs_magic == FS_MAGIC &&
389 	    (fs->fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 &&
390 	    fs->fs_version != UFS_VERSION_MIN)) {
391 		fprintf(stderr, "%s: unrecognized ufs version: %d\n", file,
392 		    fs->fs_version);
393 		exit(31+5);
394 	}
395 	if (fs->fs_magic == MTB_UFS_MAGIC &&
396 	    (fs->fs_version > MTB_UFS_VERSION_1 ||
397 	    fs->fs_version < MTB_UFS_VERSION_MIN)) {
398 		fprintf(stderr, "%s: unrecognized ufs version: %d\n", file,
399 		    fs->fs_version);
400 		exit(31+5);
401 	}
402 }
403 
404 void
405 bwrite(blk, buf, size)
406 	char *buf;
407 	diskaddr_t blk;
408 	int size;
409 {
410 	if (llseek(fi, (offset_t)blk * DEV_BSIZE, 0) < 0) {
411 		perror("FS SEEK");
412 		exit(31+6);
413 	}
414 	if (write(fi, buf, size) != size) {
415 		perror("FS WRITE");
416 		exit(31+7);
417 	}
418 }
419 
420 int
421 bread(bno, buf, cnt)
422 	diskaddr_t bno;
423 	char *buf;
424 {
425 	int	i;
426 
427 	if (llseek(fi, (offset_t)bno * DEV_BSIZE, 0) < 0) {
428 		fprintf(stderr, "bread: ");
429 		perror("llseek");
430 		return (1);
431 	}
432 	if ((i = read(fi, buf, cnt)) != cnt) {
433 		perror("read");
434 		for (i = 0; i < sblock.fs_bsize; i++)
435 			buf[i] = 0;
436 		return (1);
437 	}
438 	return (0);
439 }
440 
441 /* VARARGS1 */
442 void
443 fatal(fmt, arg1, arg2)
444 	char *fmt, *arg1, *arg2;
445 {
446 	fprintf(stderr, "tunefs: ");
447 	fprintf(stderr, fmt, arg1, arg2);
448 	putc('\n', stderr);
449 	exit(31+10);
450 }
451 
452 
453 int
454 isnumber(s)
455 	char *s;
456 {
457 	register c;
458 
459 	while (c = *s++)
460 		if (c < '0' || c > '9')
461 			return (0);
462 	return (1);
463 }
464