xref: /illumos-gate/usr/src/cmd/fs.d/switchout.c (revision 43d18f1c320355e93c47399bea0b2e022fe06364)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include	<stdio.h>
34 #include 	<limits.h>
35 #include	<locale.h>
36 #include	<libintl.h>
37 #include	<sys/fstyp.h>
38 #include	<errno.h>
39 #include	<sys/vfstab.h>
40 #include	<sys/types.h>
41 #include	<sys/stat.h>
42 #include	<fcntl.h>
43 #include	<string.h>
44 #include	<libdiskmgt.h>
45 #include	"fslib.h"
46 
47 
48 static int match(char **opts, char *s);
49 
50 #define	FSTYPE_MAX	8
51 #define	ARGV_MAX	1024
52 #define	VFS_PATH	"/usr/lib/fs"
53 #define	ALT_PATH	"/etc/fs"
54 
55 extern char	*default_fstype();
56 void	stat_snap(char *, char *, char *);
57 char	*special = NULL;  /*  device special name  */
58 char	*fstype = NULL;	  /*  fstype name is filled in here  */
59 char	*cbasename;	  /* name of command */
60 char	*newargv[ARGV_MAX]; 	/* args for the fstype specific command  */
61 char	vfstab[] = VFSTAB;
62 int	newargc = 2;
63 
64 /*
65  * TRANSLATION_NOTE - the usage strings in the c_usgstr[] of the
66  * following structures should be given a translation; the call to gettext
67  * is in the usage() function. The strings are the ones containing
68  * "[-F FSType]".
69  */
70 
71 struct commands {
72 	char *c_basename;
73 	char *c_optstr;
74 	char *c_usgstr[4]; /* make sure as large as largest array size */
75 } cmd_data[] = {
76 	"clri", "F:o:?V",
77 	{
78 		"[-F FSType] [-V] special inumber ...",
79 		NULL
80 	},
81 	"mkfs", "F:o:mb:?V",
82 	{
83 		"[-F FSType] [-V] [-m] [-o specific_options] special ",
84 		"[operands]", NULL
85 	},
86 	"dcopy", "F:o:?V",
87 	{
88 		"[-F FSType] [-V] special inumber ...",
89 		NULL
90 	},
91 	"fsdb", "F:o:z:?V",
92 	{
93 		"[-F FSType] [-V] [-o specific_options] special",
94 		NULL
95 	},
96 	"fssnap", "F:dio:?V",
97 	{
98 		"[-F FSType] [-V] -o special_options  /mount/point",
99 		"-d [-F FSType] [-V] /mount/point | dev",
100 		"-i [-F FSType] [-V] [-o special-options] [/mount/point | dev]",
101 		NULL
102 	},
103 	"labelit", "F:o:?nV",
104 	{
105 		"[-F FSType] [-V] [-o specific_options] special [operands]",
106 		NULL
107 	},
108 	NULL, "F:o:?V",
109 	{
110 		"[-F FSType] [-V] [-o specific_options] special [operands]",
111 		NULL
112 	}
113 };
114 struct 	commands *c_ptr;
115 
116 static void usage(char *cmd, char **usg);
117 static void lookup(void);
118 
119 int
120 main(int argc, char *argv[])
121 {
122 	char 	*ptr;
123 	char	full_path[PATH_MAX];
124 	char	*vfs_path = VFS_PATH;
125 	char	*alt_path = ALT_PATH;
126 	int	i;
127 	int	j;
128 	int	verbose = 0;		/* set if -V is specified */
129 	int	F_flg = 0;
130 	int	mflag = 0;
131 	int	Nflag = 0;
132 	char	*oopts = NULL;
133 	char	*tmpopts = NULL;	/* used for in use checking */
134 	int	iflag = 0;
135 	int	usgflag = 0;
136 	int	arg;			/* argument from getopt() */
137 	char	*msg;
138 	int	error;
139 	extern	char *optarg;		/* getopt specific */
140 	extern	int optind;
141 	extern	int opterr;
142 
143 	(void) setlocale(LC_ALL, "");
144 
145 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
146 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
147 #endif
148 
149 	(void) textdomain(TEXT_DOMAIN);
150 
151 	cbasename = ptr = argv[0];
152 	while (*ptr) {
153 		if (*ptr++ == '/')
154 			cbasename = ptr;
155 	}
156 
157 
158 	if (argc == 1) {
159 		for (c_ptr = cmd_data; ((c_ptr->c_basename != NULL) &&
160 		    (strcmp(c_ptr->c_basename, cbasename) != 0)); c_ptr++)
161 		;
162 		usage(cbasename, c_ptr->c_usgstr);
163 		exit(2);
164 	}
165 
166 	for (c_ptr = cmd_data; ((c_ptr->c_basename != NULL) &&
167 	    (strcmp(c_ptr->c_basename, cbasename) != 0));  c_ptr++)
168 		;
169 	while ((arg = getopt(argc, argv, c_ptr->c_optstr)) != -1) {
170 			switch (arg) {
171 			case 'V':	/* echo complete command line */
172 				verbose = 1;
173 				break;
174 			case 'F':	/* FSType specified */
175 				F_flg++;
176 				fstype = optarg;
177 				break;
178 			case 'o':	/* FSType specific arguments */
179 				newargv[newargc++] = "-o";
180 				newargv[newargc++] = optarg;
181 				oopts = optarg;
182 				if (!Nflag) {
183 					tmpopts = optarg;
184 					Nflag = has_Nflag(tmpopts);
185 				}
186 				break;
187 			case '?':	/* print usage message */
188 				newargv[newargc++] = "-?";
189 				usgflag = 1;
190 				break;
191 			case 'm':	/* FSType specific arguments */
192 				mflag = 1;
193 				newargv[newargc] = (char *)malloc(3);
194 				sprintf(newargv[newargc++], "-%c", arg);
195 				if (optarg)
196 					newargv[newargc++] = optarg;
197 				break;
198 			case 'i': /* fssnap only */
199 				iflag = 1;
200 				/*FALLTHROUGH*/
201 			default:
202 				newargv[newargc] = (char *)malloc(3);
203 				sprintf(newargv[newargc++], "-%c", arg);
204 				if (optarg)
205 					newargv[newargc++] = optarg;
206 				break;
207 			}
208 			optarg = NULL;
209 	}
210 	if (F_flg > 1) {
211 		(void) fprintf(stderr,
212 		    gettext("%s: more than one FSType specified\n"),
213 		    cbasename);
214 		usage(cbasename, c_ptr->c_usgstr);
215 		exit(2);
216 	}
217 	if (fstype != NULL) {
218 		if (strlen(fstype) > FSTYPE_MAX) {
219 			(void) fprintf(stderr,
220 			    gettext("%s: FSType %s exceeds %d characters\n"),
221 			    cbasename, fstype, FSTYPE_MAX);
222 			exit(2);
223 		}
224 	}
225 
226 	/*  perform a lookup if fstype is not specified  */
227 	special = argv[optind];
228 	optind++;
229 
230 	/* handle -i (fssnap command only) */
231 	if (iflag) {
232 		int diff = argc - optind;
233 		/*
234 		 * There is no reason to ever call a file system specific
235 		 * version since its all in kstats.
236 		 */
237 		if (diff > 0) /* gave more than one mountpoint or device */
238 			usage(cbasename, c_ptr->c_usgstr);
239 		stat_snap(cbasename, diff == 0 ? argv[argc-1] : NULL, oopts);
240 		exit(0);
241 	}
242 
243 	if ((special == NULL) && (!usgflag)) {
244 		(void) fprintf(stderr, gettext("%s: special not specified\n"),
245 		    cbasename);
246 		usage(cbasename, c_ptr->c_usgstr);
247 		exit(2);
248 	}
249 
250 	if ((fstype == NULL) && (usgflag))
251 		usage(cbasename, c_ptr->c_usgstr);
252 	if (fstype == NULL)
253 		lookup();
254 	if (fstype == NULL) {
255 		(void) fprintf(stderr,
256 		    gettext("%s: FSType cannot be identified\n"), cbasename);
257 		usage(cbasename, c_ptr->c_usgstr);
258 		exit(2);
259 	}
260 	newargv[newargc++] = special;
261 	for (; optind < argc; optind++)
262 		newargv[newargc++] = argv[optind];
263 
264 	/*  build the full pathname of the fstype dependent command  */
265 	sprintf(full_path, "%s/%s/%s", vfs_path, fstype, cbasename);
266 
267 	newargv[1] = cbasename;
268 
269 	if (verbose) {
270 		printf("%s -F %s ", cbasename, fstype);
271 		for (i = 2; newargv[i]; i++)
272 			printf("%s ", newargv[i]);
273 		printf("\n");
274 		exit(0);
275 	}
276 
277 	/*
278 	 * Prior to executing the command for mkfs check for device in use.
279 	 * If the mflag is set, user wants to see command that created
280 	 * an already existing filesystem. Do not check for in use in this
281 	 * case. If Nflag is set user wants to see what the parameters
282 	 * would be to create the filesystem. Do not check for in use in
283 	 * this case.
284 	 */
285 	if (strcmp(cbasename, "mkfs") == 0 && !mflag && !Nflag) {
286 		if (dm_inuse(special, &msg, DM_WHO_MKFS, &error) ||
287 		    error) {
288 			if (error != 0) {
289 				(void) fprintf(stderr, gettext("Error occurred"
290 				    " with device in use checking: %s\n"),
291 				    strerror(error));
292 			} else {
293 				(void) fprintf(stderr, "%s", msg);
294 				free(msg);
295 				exit(2);
296 			}
297 		}
298 	}
299 
300 	/*
301 	 *  Execute the FSType specific command.
302 	 */
303 	execv(full_path, &newargv[1]);
304 	if ((errno == ENOENT) || (errno == EACCES)) {
305 		/*  build the alternate pathname */
306 		sprintf(full_path, "%s/%s/%s", alt_path, fstype, cbasename);
307 		if (verbose) {
308 			printf("%s -F %s ", cbasename, fstype);
309 			for (i = 2; newargv[i]; i++)
310 				printf("%s ", newargv[i]);
311 			printf("\n");
312 			exit(0);
313 		}
314 		execv(full_path, &newargv[1]);
315 	}
316 	if (errno == ENOEXEC) {
317 		newargv[0] = "sh";
318 		newargv[1] = full_path;
319 		execv("/sbin/sh", &newargv[0]);
320 	}
321 	if (errno != ENOENT) {
322 		perror(cbasename);
323 		(void) fprintf(stderr, gettext("%s: cannot execute %s\n"),
324 		    cbasename, full_path);
325 		exit(2);
326 	}
327 
328 	if (sysfs(GETFSIND, fstype) == (-1)) {
329 		(void) fprintf(stderr,
330 		    gettext("%s: FSType %s not installed in the kernel\n"),
331 		    cbasename, fstype);
332 		exit(2);
333 	}
334 	(void) fprintf(stderr,
335 	    gettext("%s: Operation not applicable for FSType %s \n"),
336 	    cbasename, fstype);
337 	return (2);
338 }
339 
340 static void
341 usage(char *cmd, char **usg)
342 {
343 	int i;
344 	(void) fprintf(stderr, gettext("Usage:\n"));
345 	for (i = 0; usg[i] != NULL; i++)
346 		(void) fprintf(stderr, "%s %s\n", gettext(cmd),
347 		    gettext(usg[i]));
348 	exit(2);
349 }
350 
351 
352 /*
353  *  This looks up the /etc/vfstab entry given the device 'special'.
354  *  It is called when the fstype is not specified on the command line.
355  *
356  *  The following global variables are used:
357  *	special, fstype
358  */
359 
360 static void
361 lookup(void)
362 {
363 	FILE	*fd;
364 	int	ret;
365 	struct vfstab	vget, vref;
366 
367 	if ((fd = fopen(vfstab, "r")) == NULL) {
368 		(void) fprintf(stderr, gettext("%s: cannot open vfstab\n"),
369 		    cbasename);
370 		exit(1);
371 	}
372 	vfsnull(&vref);
373 	vref.vfs_special = special;
374 	ret = getvfsany(fd, &vget, &vref);
375 	if (ret == -1) {
376 		rewind(fd);
377 		vfsnull(&vref);
378 		vref.vfs_fsckdev = special;
379 		ret = getvfsany(fd, &vget, &vref);
380 	}
381 	fclose(fd);
382 
383 	switch (ret) {
384 	case -1:
385 		fstype = default_fstype(special);
386 		break;
387 	case 0:
388 		fstype = vget.vfs_fstype;
389 		break;
390 	case VFS_TOOLONG:
391 		(void) fprintf(stderr,
392 		    gettext("%s: line in vfstab exceeds %d characters\n"),
393 		    cbasename, VFS_LINE_MAX-2);
394 		exit(1);
395 		break;
396 	case VFS_TOOFEW:
397 		(void) fprintf(stderr,
398 		    gettext("%s: line in vfstab has too few entries\n"),
399 		    cbasename);
400 		exit(1);
401 		break;
402 	}
403 }
404 
405 void
406 stat_snap(char *cmd, char *mountpoint, char *opts)
407 {
408 	int fd; /* check mount point if given */
409 	int en;
410 	char *errstr;
411 
412 	if (mountpoint) {
413 		if ((fd = open(mountpoint, O_RDONLY)) < 0) {
414 			en = errno;
415 			errstr = strerror(errno);
416 			if (errstr == NULL)
417 				errstr = gettext("Unknown error");
418 
419 			(void) fprintf(stderr,
420 			    gettext("%s: %s: error %d: %s\n"),
421 			    cmd, mountpoint, en, errstr);
422 
423 			exit(2);
424 		}
425 		close(fd);
426 	}
427 	fssnap_show_status(mountpoint, opts, 1, (opts ? 0 : 1));
428 }
429 static int
430 has_Nflag(char *opts)
431 {
432 	while (opts != NULL && *opts != '\0') {
433 		if (match(&opts, "N")) {
434 			return (1);
435 		}
436 		if (!opts)
437 			break;
438 		if (*opts == ',')
439 			opts ++;
440 		if (*opts == ' ')
441 			opts ++;
442 	}
443 	return (0);
444 }
445 /*
446  * Parses the -o [fs specific options string] to search for the UFS -N flag.
447  * Return the opts string pointing to the next position in the string if
448  * match is not found. A delimiter of , or ' ' can be used depending on the
449  * caller, newfs or mkfs.
450  */
451 static int
452 match(char **opts, char *s)
453 {
454 	char *cs;
455 	char *tmp_str;
456 
457 	cs = *opts;
458 
459 	while (*cs++ == *s) {
460 		if (*s++ == '\0') {
461 			goto true;
462 		}
463 	}
464 	if (*s != '\0') {
465 		/*
466 		 * If we cannot find the delimiter it means we
467 		 * have hit the end of the string.
468 		 */
469 		tmp_str = strchr(*opts, ',');
470 		if (!tmp_str)
471 			tmp_str = strchr(*opts, ' ');
472 
473 		*opts = tmp_str;
474 		return (0);
475 	}
476 true:
477 	cs--;
478 	*opts = cs;
479 	return (1);
480 }
481