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