xref: /illumos-gate/usr/src/cmd/fstyp/fstyp.c (revision 2d700530)
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 2007 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 == DOSOS12) || (type == DOSOS16) ||
424 	    (type == DOSHUGE) || (type == FDISK_WINDOWS) ||
425 	    (type == FDISK_EXT_WIN) || (type == FDISK_FAT95) ||
426 	    (type == DIAGPART));
427 }
428 
429 static boolean_t
430 is_dos_extended(uchar_t id)
431 {
432 	return ((id == EXTDOS) || (id == FDISK_EXTLBA));
433 }
434 
435 struct part_find_s {
436 	int	num;
437 	int	count;
438 	int	systid;
439 	int	r_systid;
440 	int	r_relsect;
441 	int	r_numsect;
442 };
443 
444 enum { WALK_CONTINUE, WALK_TERMINATE };
445 
446 /*
447  * Walk partition tables and invoke a callback for each.
448  */
449 static void
450 walk_partitions(int fd, int startsec, int (*f)(void *, int, int, int),
451     void *arg)
452 {
453 	uint32_t buf[1024/4];
454 	int bufsize = 1024;
455 	struct mboot *mboot = (struct mboot *)&buf[0];
456 	struct ipart ipart[FD_NUMPART];
457 	int sec = startsec;
458 	int lastsec = sec + 1;
459 	int relsect;
460 	int ext = 0;
461 	int systid;
462 	boolean_t valid;
463 	int i;
464 
465 	while (sec != lastsec) {
466 		if (pread(fd, buf, bufsize, (off_t)sec * 512) != bufsize) {
467 			break;
468 		}
469 		lastsec = sec;
470 		if (ltohs(mboot->signature) != MBB_MAGIC) {
471 			break;
472 		}
473 		bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart));
474 
475 		for (i = 0; i < FD_NUMPART; i++) {
476 			systid = ipart[i].systid;
477 			relsect = sec + ltohi(ipart[i].relsect);
478 			if (systid == 0) {
479 				continue;
480 			}
481 			valid = B_TRUE;
482 			if (is_dos_extended(systid) && (sec == lastsec)) {
483 				sec = startsec + ltohi(ipart[i].relsect);
484 				if (ext++ == 0) {
485 					relsect = startsec = sec;
486 				} else {
487 					valid = B_FALSE;
488 				}
489 			}
490 			if (valid && f(arg, ipart[i].systid, relsect,
491 			    ltohi(ipart[i].numsect)) == WALK_TERMINATE) {
492 				return;
493 			}
494 		}
495 	}
496 }
497 
498 static int
499 find_dos_drive_cb(void *arg, int systid, int relsect, int numsect)
500 {
501 	struct part_find_s *p = arg;
502 
503 	if (is_dos_drive(systid)) {
504 		if (++p->count == p->num) {
505 			p->r_relsect = relsect;
506 			p->r_numsect = numsect;
507 			p->r_systid = systid;
508 			return (WALK_TERMINATE);
509 		}
510 	}
511 
512 	return (WALK_CONTINUE);
513 }
514 
515 /*
516  * Given a dos drive number, return its relative sector number,
517  * number of sectors in partition and the system id.
518  */
519 static boolean_t
520 find_dos_drive(int fd, int num, int *relsect, int *numsect, int *systid)
521 {
522 	struct part_find_s p = { 0, 0, 0, 0, 0, 0 };
523 
524 	p.num = num;
525 
526 	if (num > 0) {
527 		walk_partitions(fd, 0, find_dos_drive_cb, &p);
528 		if (p.count == num) {
529 			*relsect = p.r_relsect;
530 			*numsect = p.r_numsect;
531 			*systid = p.r_systid;
532 			return (B_TRUE);
533 		}
534 	}
535 
536 	return (B_FALSE);
537 }
538 
539 /*
540  * libfstyp identification failed: as a last resort, try to
541  * find and run legacy /usr/lib/fs/<fsname>/fstyp commands.
542  */
543 static void
544 run_legacy_cmds(int fd, char *device, int vflag)
545 {
546 	char		*lib_dir = FSTYP_LIBFS_DIR;
547 	char		*path;
548 	long		name_max;
549 	DIR		*dirp;
550 	struct dirent	*dp_mem, *dp;
551 	struct stat	st;
552 	fstyp_handle_t	h;
553 	int		error;
554 	char		*arg1, *arg2;
555 
556 	if (vflag) {
557 		arg1 = "-v";
558 		arg2 = device;
559 	} else {
560 		arg1 = device;
561 		arg2 = NULL;
562 	}
563 
564 	if ((dirp = opendir(lib_dir)) == NULL) {
565 		return;
566 	}
567 
568 	name_max = pathconf(lib_dir, _PC_NAME_MAX);
569 	path = calloc(1, name_max + 1);
570 	dp = dp_mem = calloc(1, sizeof (struct dirent) + name_max + 1);
571 	if ((path == NULL) || (dp_mem == NULL)) {
572 		goto out;
573 	}
574 
575 	while ((readdir_r(dirp, dp, &dp) == 0) && (dp != NULL)) {
576 		if (dp->d_name[0] == '.') {
577 			continue;
578 		}
579 		(void) snprintf(path, name_max, "%s/%s", lib_dir, dp->d_name);
580 
581 		/* it's legacy if there's no libfstyp module for it */
582 		error = fstyp_init(fd, 0, path, &h);
583 		if (error != FSTYP_ERR_MOD_NOT_FOUND) {
584 			if (error == 0) {
585 				fstyp_fini(h);
586 			}
587 			continue;
588 		}
589 
590 		/* file must exist and be executable */
591 		(void) snprintf(path, name_max,
592 		    "%s/%s/fstyp", lib_dir, dp->d_name);
593 		if ((stat(path, &st) < 0) ||
594 		    ((st.st_mode & S_IXUSR) == 0)) {
595 			continue;
596 		}
597 
598 		if ((error = run_cmd(path, "fstyp", arg1, arg2)) == 0) {
599 			exit(0);
600 		}
601 	}
602 
603 out:
604 	if (dp_mem != NULL) {
605 		free(dp_mem);
606 	}
607 	if (path != NULL) {
608 		free(path);
609 	}
610 	(void) closedir(dirp);
611 }
612 
613 static int
614 run_cmd(char *path, char *arg0, char *arg1, char *arg2)
615 {
616 	pid_t	pid;
617 	int	status = 1;
618 
619 	pid = fork();
620 	if (pid < 0) {
621 		return (1);
622 	} else if (pid == 0) {
623 		/* child */
624 		(void) execl(path, arg0, arg1, arg2, 0);
625 		exit(1);
626 	}
627 	/* parent */
628 	(void) wait(&status);
629 	return (status);
630 }
631