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