xref: /illumos-gate/usr/src/cmd/fstyp/fstyp.c (revision 1979231e)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <libintl.h>
35 #include <locale.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <errno.h>
39 #include <dirent.h>
40 #include <dlfcn.h>
41 #include <sys/wait.h>
42 #include <sys/fstyp.h>
43 #include <libfstyp.h>
44 #include <sys/dktp/fdisk.h>
45 #include <sys/fs/pc_label.h>
46 
47 #define	FSTYP_LIBFS_DIR	"/usr/lib/fs"
48 
49 static const char *getmodfsname();
50 static char *getexecpathname();
51 static void dump_nvlist(nvlist_t *list, int indent);
52 static boolean_t dos_to_dev(char *path, char **devpath, int *num);
53 static boolean_t find_dos_drive(int fd, int num, int *relsect, int *numsect,
54     int *systid);
55 static void run_legacy_cmds(int fd, char *device, int vflag);
56 static int run_cmd(char *path, char *arg0, char *arg1, char *arg2);
57 
58 
59 static void
60 usage(void)
61 {
62 	(void) fprintf(stderr, gettext("Usage: fstyp [-av] <device>\n"));
63 	exit(1);
64 }
65 
66 int
67 main(int argc, char **argv)
68 {
69 	int		fd = -1;
70 	int		c;
71 	int		aflag = 0;
72 	int		vflag = 0;
73 	int		indent = 0;
74 	char		*devpath;
75 	boolean_t	is_dos;
76 	int		dos_num, systid, relsect, numsect;
77 	off_t		offset = 0;
78 	nvlist_t	*attr = NULL;
79 	fstyp_handle_t	h = NULL;
80 	const char	*modfsname;
81 	const char	*fsname;
82 	int		error = FSTYP_ERR_NO_MATCH;
83 
84 	(void) setlocale(LC_ALL, "");
85 
86 #if !defined(TEXT_DOMAIN)
87 #define	TEXT_DOMAIN "SYS_TEST"
88 #endif
89 	(void) textdomain(TEXT_DOMAIN);
90 
91 	while ((c = getopt(argc, argv, "av")) != -1) {
92 		switch (c) {
93 		case 'a':
94 			aflag = 1;
95 			break;
96 		case 'v':
97 			vflag = 1;
98 			break;
99 		default:
100 			usage();
101 			break;
102 		}
103 	}
104 
105 	argv += optind;
106 	argc -= optind;
107 
108 	if (argc != 1) {
109 		usage();
110 	}
111 
112 	modfsname = getmodfsname();
113 
114 	/*
115 	 * Open device, find partition offset if requested
116 	 */
117 	if (!(is_dos = dos_to_dev(argv[0], &devpath, &dos_num))) {
118 		devpath = argv[0];
119 	}
120 	if ((fd = open(devpath, O_RDONLY)) < 0) {
121 		error = FSTYP_ERR_DEV_OPEN;
122 		goto out;
123 	}
124 	if (is_dos) {
125 		if (find_dos_drive(fd, dos_num, &relsect, &numsect, &systid)) {
126 			offset = (off_t)relsect * 512;
127 		} else {
128 			error = FSTYP_ERR_NO_PARTITION;
129 			goto out;
130 		}
131 	}
132 
133 	/*
134 	 * Use libfstyp to identify filesystem
135 	 */
136 	if ((error = fstyp_init(fd, offset, NULL, &h)) != 0) {
137 		goto out;
138 	}
139 	if ((error = fstyp_ident(h, modfsname, &fsname)) != 0) {
140 		fstyp_fini(h);
141 		h = NULL;
142 
143 		run_legacy_cmds(fd, argv[0], vflag);
144 
145 		goto out;
146 	}
147 
148 	(void) printf("%s\n", fsname);
149 
150 	/*
151 	 * Output additional info if requested
152 	 */
153 	if (vflag) {
154 		error = fstyp_dump(h, stdout, stderr);
155 	}
156 	if (aflag || (vflag && (error == FSTYP_ERR_NOP))) {
157 		if ((error = fstyp_get_attr(h, &attr)) != 0) {
158 			goto out;
159 		}
160 		dump_nvlist(attr, indent);
161 	}
162 
163 out:
164 	if (error != 0) {
165 		(void) fprintf(stderr, gettext("unknown_fstyp (%s)\n"),
166 		    fstyp_strerror(h, error));
167 	}
168 	if (h != NULL) {
169 		fstyp_fini(h);
170 	}
171 	if (fd >= 0) {
172 		(void) close(fd);
173 	}
174 	if (devpath != argv[0]) {
175 		free(devpath);
176 	}
177 	return (error);
178 
179 }
180 
181 #define	NVP(elem, type, vtype, ptype, format) { \
182 	vtype	value; \
183 \
184 	(void) nvpair_value_##type(elem, &value); \
185 	(void) printf("%*s%s: " format "\n", indent, "", \
186 	    nvpair_name(elem), (ptype)value); \
187 }
188 
189 #define	NVPA(elem, type, vtype, ptype, format) { \
190 	uint_t	i, count; \
191 	vtype	*value;  \
192 \
193 	(void) nvpair_value_##type(elem, &value, &count); \
194 	for (i = 0; i < count; i++) { \
195 		(void) printf("%*s%s[%d]: " format "\n", indent, "", \
196 		    nvpair_name(elem), i, (ptype)value[i]); \
197 	} \
198 }
199 
200 static void
201 dump_nvlist(nvlist_t *list, int indent)
202 {
203 	nvpair_t	*elem = NULL;
204 	boolean_t	bool_value;
205 	nvlist_t	*nvlist_value;
206 	nvlist_t	**nvlist_array_value;
207 	uint_t		i, count;
208 
209 	if (list == NULL) {
210 		return;
211 	}
212 
213 	while ((elem = nvlist_next_nvpair(list, elem)) != NULL) {
214 		switch (nvpair_type(elem)) {
215 		case DATA_TYPE_BOOLEAN_VALUE:
216 			(void) nvpair_value_boolean_value(elem, &bool_value);
217 			(void) printf("%*s%s: %s\n", indent, "",
218 			    nvpair_name(elem), bool_value ? "true" : "false");
219 			break;
220 
221 		case DATA_TYPE_BYTE:
222 			NVP(elem, byte, uchar_t, int, "%u");
223 			break;
224 
225 		case DATA_TYPE_INT8:
226 			NVP(elem, int8, int8_t, int, "%d");
227 			break;
228 
229 		case DATA_TYPE_UINT8:
230 			NVP(elem, uint8, uint8_t, int, "%u");
231 			break;
232 
233 		case DATA_TYPE_INT16:
234 			NVP(elem, int16, int16_t, int, "%d");
235 			break;
236 
237 		case DATA_TYPE_UINT16:
238 			NVP(elem, uint16, uint16_t, int, "%u");
239 			break;
240 
241 		case DATA_TYPE_INT32:
242 			NVP(elem, int32, int32_t, long, "%ld");
243 			break;
244 
245 		case DATA_TYPE_UINT32:
246 			NVP(elem, uint32, uint32_t, ulong_t, "%lu");
247 			break;
248 
249 		case DATA_TYPE_INT64:
250 			NVP(elem, int64, int64_t, longlong_t, "%lld");
251 			break;
252 
253 		case DATA_TYPE_UINT64:
254 			NVP(elem, uint64, uint64_t, u_longlong_t, "%llu");
255 			break;
256 
257 		case DATA_TYPE_STRING:
258 			NVP(elem, string, char *, char *, "'%s'");
259 			break;
260 
261 		case DATA_TYPE_BYTE_ARRAY:
262 			NVPA(elem, byte_array, uchar_t, int, "%u");
263 			break;
264 
265 		case DATA_TYPE_INT8_ARRAY:
266 			NVPA(elem, int8_array, int8_t, int, "%d");
267 			break;
268 
269 		case DATA_TYPE_UINT8_ARRAY:
270 			NVPA(elem, uint8_array, uint8_t, int, "%u");
271 			break;
272 
273 		case DATA_TYPE_INT16_ARRAY:
274 			NVPA(elem, int16_array, int16_t, int, "%d");
275 			break;
276 
277 		case DATA_TYPE_UINT16_ARRAY:
278 			NVPA(elem, uint16_array, uint16_t, int, "%u");
279 			break;
280 
281 		case DATA_TYPE_INT32_ARRAY:
282 			NVPA(elem, int32_array, int32_t, long, "%ld");
283 			break;
284 
285 		case DATA_TYPE_UINT32_ARRAY:
286 			NVPA(elem, uint32_array, uint32_t, ulong_t, "%lu");
287 			break;
288 
289 		case DATA_TYPE_INT64_ARRAY:
290 			NVPA(elem, int64_array, int64_t, longlong_t, "%lld");
291 			break;
292 
293 		case DATA_TYPE_UINT64_ARRAY:
294 			NVPA(elem, uint64_array, uint64_t, u_longlong_t,
295 			    "%llu");
296 			break;
297 
298 		case DATA_TYPE_STRING_ARRAY:
299 			NVPA(elem, string_array, char *, char *, "'%s'");
300 			break;
301 
302 		case DATA_TYPE_NVLIST:
303 			(void) nvpair_value_nvlist(elem, &nvlist_value);
304 			(void) printf("%*s%s:\n", indent, "",
305 			    nvpair_name(elem));
306 			dump_nvlist(nvlist_value, indent + 4);
307 			break;
308 
309 		case DATA_TYPE_NVLIST_ARRAY:
310 			(void) nvpair_value_nvlist_array(elem,
311 			    &nvlist_array_value, &count);
312 			for (i = 0; i < count; i++) {
313 				(void) printf("%*s%s[%u]:\n", indent, "",
314 				    nvpair_name(elem), i);
315 				dump_nvlist(nvlist_array_value[i], indent + 4);
316 			}
317 			break;
318 
319 		default:
320 			(void) printf(gettext("bad config type %d for %s\n"),
321 			    nvpair_type(elem), nvpair_name(elem));
322 		}
323 	}
324 }
325 
326 /*
327  * If the executable is a fs-specific hardlink, /usr/lib/fs/<fsname>/fstyp,
328  * return that fsname; otherwise return NULL.
329  */
330 static const char *
331 getmodfsname()
332 {
333 	static char fsname_buf[FSTYPSZ + 1];
334 	char	*fsname = NULL;
335 	char	*path;
336 	char	*p;
337 	int	len;
338 
339 	if ((path = getexecpathname()) == NULL) {
340 		return (NULL);
341 	}
342 	if ((p = strrchr(path, '/')) != NULL) {
343 		*p = '\0';
344 		if ((p = strrchr(path, '/')) != NULL) {
345 			*p++ = '\0';
346 			len = strlen(p);
347 			if ((strcmp(path, FSTYP_LIBFS_DIR) == 0) &&
348 			    (len > 0) && (len < sizeof (fsname_buf))) {
349 				(void) strlcpy(fsname_buf, p,
350 				    sizeof (fsname_buf));
351 				fsname = fsname_buf;
352 			}
353 		}
354 	}
355 	free(path);
356 	return (fsname);
357 }
358 
359 /*
360  * Return executable's absolute pathname
361  */
362 static char *
363 getexecpathname()
364 {
365 	size_t		size;
366 	const char	*execname;
367 	char		*cwd;
368 	char		*path;
369 	char		*rpath;
370 
371 	size = pathconf(".", _PC_PATH_MAX) + 1;
372 	path = malloc(size);
373 	rpath = malloc(size);
374 	cwd = getcwd(NULL, size);
375 	if ((path == NULL) || (rpath == NULL) || (cwd == NULL)) {
376 		goto out;
377 	}
378 	execname = getexecname();
379 
380 	if (execname[0] == '/') {
381 		(void) snprintf(path, size, "%s", execname);
382 	} else {
383 		(void) snprintf(path, size, "%s/%s", cwd, execname);
384 	}
385 	if (realpath(path, rpath) == NULL) {
386 		free(rpath);
387 		rpath = NULL;
388 	}
389 
390 out:
391 	if (path != NULL) {
392 		free(path);
393 	}
394 	if (cwd != NULL) {
395 		free(cwd);
396 	}
397 	return (rpath);
398 }
399 
400 /*
401  * Separates dos notation device spec into device and drive number
402  */
403 static boolean_t
404 dos_to_dev(char *path, char **devpath, int *num)
405 {
406 	char *p;
407 
408 	if ((p = strrchr(path, ':')) == NULL) {
409 		return (B_FALSE);
410 	}
411 	if ((*num = atoi(p + 1)) == 0) {
412 		return (B_FALSE);
413 	}
414 	p[0] = '\0';
415 	*devpath = strdup(path);
416 	p[0] = ':';
417 	return (*devpath != NULL);
418 }
419 
420 static boolean_t
421 is_dos_drive(uchar_t type)
422 {
423 	return ((type == 1) || (type == 4) || (type == 5) || (type == 6) ||
424 	    ((type >= 8) && (type <= 0xf)));
425 }
426 
427 static boolean_t
428 is_dos_extended(uchar_t id)
429 {
430 	return ((id == EXTDOS) || (id == FDISK_EXTLBA));
431 }
432 
433 struct part_find_s {
434 	int	num;
435 	int	count;
436 	int	systid;
437 	int	r_systid;
438 	int	r_relsect;
439 	int	r_numsect;
440 };
441 
442 enum { WALK_CONTINUE, WALK_TERMINATE };
443 
444 /*
445  * Walk partition tables and invoke a callback for each.
446  */
447 static void
448 walk_partitions(int fd, int startsec, int (*f)(void *, int, int, int),
449     void *arg)
450 {
451 	uint32_t buf[1024/4];
452 	int bufsize = 1024;
453 	struct mboot *mboot = (struct mboot *)&buf[0];
454 	struct ipart ipart[FD_NUMPART];
455 	int sec = startsec;
456 	int lastsec = sec + 1;
457 	int relsect;
458 	int ext = 0;
459 	int systid;
460 	boolean_t valid;
461 	int i;
462 
463 	while (sec != lastsec) {
464 		if (pread(fd, buf, bufsize, (off_t)sec * 512) != bufsize) {
465 			break;
466 		}
467 		lastsec = sec;
468 		if (ltohs(mboot->signature) != MBB_MAGIC) {
469 			break;
470 		}
471 		bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart));
472 
473 		for (i = 0; i < FD_NUMPART; i++) {
474 			systid = ipart[i].systid;
475 			relsect = sec + ltohi(ipart[i].relsect);
476 			if (systid == 0) {
477 				continue;
478 			}
479 			valid = B_TRUE;
480 			if (is_dos_extended(systid) && (sec == lastsec)) {
481 				sec = startsec + ltohi(ipart[i].relsect);
482 				if (ext++ == 0) {
483 					relsect = startsec = sec;
484 				} else {
485 					valid = B_FALSE;
486 				}
487 			}
488 			if (valid && f(arg, ipart[i].systid, relsect,
489 			    ltohi(ipart[i].numsect)) == WALK_TERMINATE) {
490 				return;
491 			}
492 		}
493 	}
494 }
495 
496 static int
497 find_dos_drive_cb(void *arg, int systid, int relsect, int numsect)
498 {
499 	struct part_find_s *p = arg;
500 
501 	if (is_dos_drive(systid)) {
502 		if (++p->count == p->num) {
503 			p->r_relsect = relsect;
504 			p->r_numsect = numsect;
505 			p->r_systid = systid;
506 			return (WALK_TERMINATE);
507 		}
508 	}
509 
510 	return (WALK_CONTINUE);
511 }
512 
513 /*
514  * Given a dos drive number, return its relative sector number,
515  * number of sectors in partition and the system id.
516  */
517 static boolean_t
518 find_dos_drive(int fd, int num, int *relsect, int *numsect, int *systid)
519 {
520 	struct part_find_s p = { 0, 0, 0, 0, 0, 0 };
521 
522 	p.num = num;
523 
524 	if (num > 0) {
525 		walk_partitions(fd, 0, find_dos_drive_cb, &p);
526 		if (p.count == num) {
527 			*relsect = p.r_relsect;
528 			*numsect = p.r_numsect;
529 			*systid = p.r_systid;
530 			return (B_TRUE);
531 		}
532 	}
533 
534 	return (B_FALSE);
535 }
536 
537 /*
538  * libfstyp identification failed: as a last resort, try to
539  * find and run legacy /usr/lib/fs/<fsname>/fstyp commands.
540  */
541 static void
542 run_legacy_cmds(int fd, char *device, int vflag)
543 {
544 	char		*lib_dir = FSTYP_LIBFS_DIR;
545 	char		*path;
546 	long		name_max;
547 	DIR		*dirp;
548 	struct dirent	*dp_mem, *dp;
549 	struct stat	st;
550 	fstyp_handle_t	h;
551 	int		error;
552 	char		*arg1, *arg2;
553 
554 	if (vflag) {
555 		arg1 = "-v";
556 		arg2 = device;
557 	} else {
558 		arg1 = device;
559 		arg2 = NULL;
560 	}
561 
562 	if ((dirp = opendir(lib_dir)) == NULL) {
563 		return;
564 	}
565 
566 	name_max = pathconf(lib_dir, _PC_NAME_MAX);
567 	path = calloc(1, name_max + 1);
568 	dp = dp_mem = calloc(1, sizeof (struct dirent) + name_max + 1);
569 	if ((path == NULL) || (dp_mem == NULL)) {
570 		goto out;
571 	}
572 
573 	while ((readdir_r(dirp, dp, &dp) == 0) && (dp != NULL)) {
574 		if (dp->d_name[0] == '.') {
575 			continue;
576 		}
577 		(void) snprintf(path, name_max, "%s/%s", lib_dir, dp->d_name);
578 
579 		/* it's legacy if there's no libfstyp module for it */
580 		error = fstyp_init(fd, 0, path, &h);
581 		if (error != FSTYP_ERR_MOD_NOT_FOUND) {
582 			if (error == 0) {
583 				fstyp_fini(h);
584 			}
585 			continue;
586 		}
587 
588 		/* file must exist and be executable */
589 		(void) snprintf(path, name_max,
590 		    "%s/%s/fstyp", lib_dir, dp->d_name);
591 		if ((stat(path, &st) < 0) ||
592 		    ((st.st_mode & S_IXUSR) == 0)) {
593 			continue;
594 		}
595 
596 		if ((error = run_cmd(path, "fstyp", arg1, arg2)) == 0) {
597 			exit(0);
598 		}
599 	}
600 
601 out:
602 	if (dp_mem != NULL) {
603 		free(dp_mem);
604 	}
605 	if (path != NULL) {
606 		free(path);
607 	}
608 	(void) closedir(dirp);
609 }
610 
611 static int
612 run_cmd(char *path, char *arg0, char *arg1, char *arg2)
613 {
614 	pid_t	pid;
615 	int	status = 1;
616 
617 	pid = fork();
618 	if (pid < 0) {
619 		return (1);
620 	} else if (pid == 0) {
621 		/* child */
622 		(void) execl(path, arg0, arg1, arg2, 0);
623 		exit(1);
624 	}
625 	/* parent */
626 	(void) wait(&status);
627 	return (status);
628 }
629