xref: /netbsd-src/usr.sbin/fstyp/fstyp.c (revision 654fb5e86636cdcca4fc163cb7123e59ef36a09e)
1*654fb5e8Smlelstv /*	$NetBSD: fstyp.c,v 1.13 2020/01/03 07:50:58 mlelstv Exp $	*/
2b985414bSchristos 
3b985414bSchristos /*-
4b985414bSchristos  * Copyright (c) 2017 The NetBSD Foundation, Inc.
5b985414bSchristos  * Copyright (c) 2016 The DragonFly Project
6b985414bSchristos  * Copyright (c) 2014 The FreeBSD Foundation
7b985414bSchristos  * All rights reserved.
8b985414bSchristos  *
9b985414bSchristos  * This code is derived from software contributed to The NetBSD Foundation
10b985414bSchristos  * by Tomohiro Kusumi <kusumi.tomohiro@gmail.com>.
11b985414bSchristos  *
12b985414bSchristos  * This software was developed by Edward Tomasz Napierala under sponsorship
13b985414bSchristos  * from the FreeBSD Foundation.
14b985414bSchristos  *
15b985414bSchristos  * Redistribution and use in source and binary forms, with or without
16b985414bSchristos  * modification, are permitted provided that the following conditions
17b985414bSchristos  * are met:
18b985414bSchristos  * 1. Redistributions of source code must retain the above copyright
19b985414bSchristos  *    notice, this list of conditions and the following disclaimer.
20b985414bSchristos  * 2. Redistributions in binary form must reproduce the above copyright
21b985414bSchristos  *    notice, this list of conditions and the following disclaimer in the
22b985414bSchristos  *    documentation and/or other materials provided with the distribution.
23b985414bSchristos  *
24b985414bSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25b985414bSchristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26b985414bSchristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27b985414bSchristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28b985414bSchristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29b985414bSchristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30b985414bSchristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31b985414bSchristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32b985414bSchristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33b985414bSchristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34b985414bSchristos  * SUCH DAMAGE.
35b985414bSchristos  *
36b985414bSchristos  */
37b985414bSchristos #include <sys/cdefs.h>
38*654fb5e8Smlelstv __RCSID("$NetBSD: fstyp.c,v 1.13 2020/01/03 07:50:58 mlelstv Exp $");
39b985414bSchristos 
400f41a17bStkusumi #include <sys/param.h>
41b985414bSchristos #include <sys/disklabel.h>
42*654fb5e8Smlelstv #include <sys/disk.h>
43b985414bSchristos #include <sys/ioctl.h>
44b985414bSchristos #include <sys/stat.h>
45b985414bSchristos #include <err.h>
46b985414bSchristos #include <errno.h>
470cd59d67Stkusumi #include <iconv.h>
480cd59d67Stkusumi #include <locale.h>
49b985414bSchristos #include <stdbool.h>
50b985414bSchristos #include <stddef.h>
51b985414bSchristos #include <stdio.h>
52b985414bSchristos #include <stdlib.h>
53b985414bSchristos #include <string.h>
54b985414bSchristos #include <unistd.h>
55b985414bSchristos #include <vis.h>
56b985414bSchristos 
57b985414bSchristos #include "fstyp.h"
58b985414bSchristos 
59ff998df0Stkusumi #define	LABEL_LEN	512
60b985414bSchristos 
610cd59d67Stkusumi bool show_label = false;
620cd59d67Stkusumi 
63b985414bSchristos typedef int (*fstyp_function)(FILE *, char *, size_t);
64b985414bSchristos typedef int (*fsvtyp_function)(const char *, char *, size_t);
65b985414bSchristos 
66b985414bSchristos static struct {
67b985414bSchristos 	const char	*name;
68b985414bSchristos 	fstyp_function	function;
69b985414bSchristos 	bool		unmountable;
700cd59d67Stkusumi 	const char	*precache_encoding;
71b985414bSchristos } fstypes[] = {
720cd59d67Stkusumi 	{ "apfs", &fstyp_apfs, true, NULL },
730cd59d67Stkusumi 	{ "cd9660", &fstyp_cd9660, false, NULL },
740cd59d67Stkusumi 	{ "exfat", &fstyp_exfat, false, EXFAT_ENC },
750cd59d67Stkusumi 	{ "ext2fs", &fstyp_ext2fs, false, NULL },
760cd59d67Stkusumi 	{ "hfs+", &fstyp_hfsp, false, NULL },
770cd59d67Stkusumi 	{ "msdosfs", &fstyp_msdosfs, false, NULL },
78bdaaf639Stkusumi 	{ "ntfs", &fstyp_ntfs, false, NTFS_ENC },
790cd59d67Stkusumi 	{ "ufs", &fstyp_ufs, false, NULL },
80ff998df0Stkusumi 	{ "hammer", &fstyp_hammer, false, NULL },
81ff998df0Stkusumi 	{ "hammer2", &fstyp_hammer2, false, NULL },
82b985414bSchristos #ifdef HAVE_ZFS
830cd59d67Stkusumi 	{ "zfs", &fstyp_zfs, true, NULL },
84b985414bSchristos #endif
850cd59d67Stkusumi 	{ NULL, NULL, NULL, NULL }
86b985414bSchristos };
87b985414bSchristos 
88b985414bSchristos static struct {
89b985414bSchristos 	const char	*name;
90b985414bSchristos 	fsvtyp_function	function;
91b985414bSchristos 	bool		unmountable;
92dcc92ff9Stkusumi 	const char	*precache_encoding;
93b985414bSchristos } fsvtypes[] = {
94442ef904Stkusumi 	{ "hammer", &fsvtyp_hammer, false, NULL }, /* Must be before partial */
95442ef904Stkusumi 	{ "hammer(partial)", &fsvtyp_hammer_partial, true, NULL },
96dcc92ff9Stkusumi 	{ NULL, NULL, NULL, NULL }
97b985414bSchristos };
98b985414bSchristos 
99b985414bSchristos void *
read_buf(FILE * fp,off_t off,size_t len)100b985414bSchristos read_buf(FILE *fp, off_t off, size_t len)
101b985414bSchristos {
102b985414bSchristos 	int error;
103b985414bSchristos 	size_t nread;
104b985414bSchristos 	void *buf;
105b985414bSchristos 
1061e774d3bSmartin 	error = fseeko(fp, off, SEEK_SET);
107b985414bSchristos 	if (error != 0) {
108b985414bSchristos 		warn("cannot seek to %jd", (uintmax_t)off);
109b985414bSchristos 		return NULL;
110b985414bSchristos 	}
111b985414bSchristos 
112b985414bSchristos 	buf = malloc(len);
113b985414bSchristos 	if (buf == NULL) {
114b985414bSchristos 		warn("cannot malloc %zd bytes of memory", len);
115b985414bSchristos 		return NULL;
116b985414bSchristos 	}
117b985414bSchristos 
118b985414bSchristos 	nread = fread(buf, len, 1, fp);
119b985414bSchristos 	if (nread != 1) {
120b985414bSchristos 		free(buf);
121b985414bSchristos 		if (feof(fp) == 0)
122b985414bSchristos 			warn("fread");
123b985414bSchristos 		return NULL;
124b985414bSchristos 	}
125b985414bSchristos 
126b985414bSchristos 	return buf;
127b985414bSchristos }
128b985414bSchristos 
129b985414bSchristos char *
checked_strdup(const char * s)130b985414bSchristos checked_strdup(const char *s)
131b985414bSchristos {
132b985414bSchristos 	char *c;
133b985414bSchristos 
134b985414bSchristos 	c = strdup(s);
135b985414bSchristos 	if (c == NULL)
136b985414bSchristos 		err(EXIT_FAILURE, "strdup");
137b985414bSchristos 	return c;
138b985414bSchristos }
139b985414bSchristos 
140b985414bSchristos void
rtrim(char * label,size_t size)141b985414bSchristos rtrim(char *label, size_t size)
142b985414bSchristos {
143b985414bSchristos 	for (size_t i = size; i > 0; i--) {
144b985414bSchristos 		size_t j = i - 1;
145b985414bSchristos 		if (label[j] == '\0')
146b985414bSchristos 			continue;
147b985414bSchristos 		else if (label[j] == ' ')
148b985414bSchristos 			label[j] = '\0';
149b985414bSchristos 		else
150b985414bSchristos 			break;
151b985414bSchristos 	}
152b985414bSchristos }
153b985414bSchristos 
154b985414bSchristos __dead static void
usage(void)155b985414bSchristos usage(void)
156b985414bSchristos {
157b985414bSchristos 
158b985414bSchristos 	fprintf(stderr, "Usage: %s [-l] [-s] [-u] special\n", getprogname());
159b985414bSchristos 	exit(EXIT_FAILURE);
160b985414bSchristos }
161b985414bSchristos 
162b985414bSchristos static void
type_check(const char * path,FILE * fp)163b985414bSchristos type_check(const char *path, FILE *fp)
164b985414bSchristos {
165b985414bSchristos 	int error, fd;
166b985414bSchristos 	struct stat sb;
167b985414bSchristos 	struct disklabel dl;
168*654fb5e8Smlelstv 	struct dkwedge_info dkw;
169b985414bSchristos 
170b985414bSchristos 	fd = fileno(fp);
171b985414bSchristos 
172b985414bSchristos 	error = fstat(fd, &sb);
173b985414bSchristos 	if (error != 0)
174b985414bSchristos 		err(EXIT_FAILURE, "%s: fstat", path);
175b985414bSchristos 
176b985414bSchristos 	if (S_ISREG(sb.st_mode))
177b985414bSchristos 		return;
178b985414bSchristos 
179b985414bSchristos 	error = ioctl(fd, DIOCGDINFO, &dl);
180b985414bSchristos 	if (error != 0)
181*654fb5e8Smlelstv 		error = ioctl(fd, DIOCGWEDGEINFO, &dkw);
182*654fb5e8Smlelstv 	if (error != 0)
183b985414bSchristos 		errx(EXIT_FAILURE, "%s: not a disk", path);
184b985414bSchristos }
185b985414bSchristos 
186b985414bSchristos int
main(int argc,char ** argv)187b985414bSchristos main(int argc, char **argv)
188b985414bSchristos {
189b985414bSchristos 	int ch, error, i, nbytes;
1900cd59d67Stkusumi 	bool ignore_type = false, show_unmountable = false;
191b985414bSchristos 	char label[LABEL_LEN + 1], strvised[LABEL_LEN * 4 + 1];
1920f41a17bStkusumi 	char fdpath[MAXPATHLEN];
1930f41a17bStkusumi 	char *p;
1940f41a17bStkusumi 	const char *path;
195b985414bSchristos 	const char *name = NULL;
196b985414bSchristos 	FILE *fp;
197b985414bSchristos 	fstyp_function fstyp_f;
198b985414bSchristos 	fsvtyp_function fsvtyp_f;
199b985414bSchristos 
200b985414bSchristos 	while ((ch = getopt(argc, argv, "lsu")) != -1) {
201b985414bSchristos 		switch (ch) {
202b985414bSchristos 		case 'l':
203b985414bSchristos 			show_label = true;
204b985414bSchristos 			break;
205b985414bSchristos 		case 's':
206b985414bSchristos 			ignore_type = true;
207b985414bSchristos 			break;
208b985414bSchristos 		case 'u':
209b985414bSchristos 			show_unmountable = true;
210b985414bSchristos 			break;
211b985414bSchristos 		default:
212b985414bSchristos 			usage();
213b985414bSchristos 		}
214b985414bSchristos 	}
215b985414bSchristos 
216b985414bSchristos 	argc -= optind;
217b985414bSchristos 	argv += optind;
218b985414bSchristos 	if (argc != 1)
219b985414bSchristos 		usage();
220b985414bSchristos 
221b985414bSchristos 	path = argv[0];
222b985414bSchristos 
2230cd59d67Stkusumi 	if (setlocale(LC_CTYPE, "") == NULL)
2240cd59d67Stkusumi 		err(1, "setlocale");
2250cd59d67Stkusumi 
2260f41a17bStkusumi 	/*
2270f41a17bStkusumi 	 * DragonFly: Filesystems may have syntax to decorate path.
2280f41a17bStkusumi 	 * Make a wild guess.
2290f41a17bStkusumi 	 * XXX devpath is unsupported in NetBSD, but at least parse '@' for fp.
2300f41a17bStkusumi 	 */
2310f41a17bStkusumi 	strlcpy(fdpath, path, sizeof(fdpath));
2320f41a17bStkusumi 	p = strchr(fdpath, '@');
2330f41a17bStkusumi 	if (p)
2340f41a17bStkusumi 		*p = '\0';
2350f41a17bStkusumi 
2360f41a17bStkusumi 	fp = fopen(fdpath, "r");
2370f41a17bStkusumi 	if (fp == NULL) {
2380f41a17bStkusumi 		if (strcmp(path, fdpath))
239b985414bSchristos 			fp = fopen(path, "r");
240b985414bSchristos 		if (fp == NULL)
241b985414bSchristos 			goto fsvtyp; /* DragonFly */
2420f41a17bStkusumi 		else
2430f41a17bStkusumi 			strlcpy(fdpath, path, sizeof(fdpath));
2440f41a17bStkusumi 	}
245b985414bSchristos 
246b985414bSchristos 	if (ignore_type == false)
2470f41a17bStkusumi 		type_check(fdpath, fp);
248b985414bSchristos 
249b985414bSchristos 	memset(label, '\0', sizeof(label));
250b985414bSchristos 
251b985414bSchristos 	for (i = 0;; i++) {
252b985414bSchristos 		if (!show_unmountable && fstypes[i].unmountable)
253b985414bSchristos 			continue;
254b985414bSchristos 		fstyp_f = fstypes[i].function;
255b985414bSchristos 		if (fstyp_f == NULL)
256b985414bSchristos 			break;
257b985414bSchristos 
258b985414bSchristos 		error = fstyp_f(fp, label, sizeof(label));
259b985414bSchristos 		if (error == 0) {
260b985414bSchristos 			name = fstypes[i].name;
261b985414bSchristos 			goto done;
262b985414bSchristos 		}
263b985414bSchristos 	}
264b985414bSchristos fsvtyp:
265b985414bSchristos 	for (i = 0;; i++) {
266b985414bSchristos 		if (!show_unmountable && fsvtypes[i].unmountable)
267b985414bSchristos 			continue;
268b985414bSchristos 		fsvtyp_f = fsvtypes[i].function;
269b985414bSchristos 		if (fsvtyp_f == NULL)
270b985414bSchristos 			break;
271b985414bSchristos 
272b985414bSchristos 		error = fsvtyp_f(path, label, sizeof(label));
273b985414bSchristos 		if (error == 0) {
274b985414bSchristos 			name = fsvtypes[i].name;
275b985414bSchristos 			goto done;
276b985414bSchristos 		}
277b985414bSchristos 	}
278b985414bSchristos 
279b985414bSchristos 	err(EXIT_FAILURE, "%s: filesystem not recognized", path);
280b985414bSchristos 	/*NOTREACHED*/
281b985414bSchristos done:
282b985414bSchristos 	if (show_label && label[0] != '\0') {
283b985414bSchristos 		/*
284b985414bSchristos 		 * XXX: I'd prefer VIS_HTTPSTYLE, but it unconditionally
285b985414bSchristos 		 *      encodes spaces.
286b985414bSchristos 		 */
287b985414bSchristos 		nbytes = strsnvis(strvised, sizeof(strvised), label,
288b985414bSchristos 		    VIS_GLOB | VIS_NL, "\"'$");
289b985414bSchristos 		if (nbytes == -1)
290b985414bSchristos 			err(EXIT_FAILURE, "strsnvis");
291b985414bSchristos 
292b985414bSchristos 		printf("%s %s\n", name, strvised);
293b985414bSchristos 	} else {
294b985414bSchristos 		printf("%s\n", name);
295b985414bSchristos 	}
296b985414bSchristos 
297b985414bSchristos 	return EXIT_SUCCESS;
298b985414bSchristos }
299