xref: /dragonfly/usr.sbin/fstyp/fstyp.c (revision 7d3e9a5b)
1 /*-
2  * Copyright (c) 2016 The DragonFly Project
3  * Copyright (c) 2014 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * This software was developed by Edward Tomasz Napierala under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31 
32 #include <sys/param.h>
33 #include <sys/diskslice.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <iconv.h>
39 #include <locale.h>
40 #include <stdbool.h>
41 #include <stddef.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <vis.h>
47 
48 #include "fstyp.h"
49 
50 #define	LABEL_LEN	512
51 
52 bool show_label = false;
53 
54 typedef int (*fstyp_function)(FILE *, char *, size_t, const char *);
55 typedef int (*fsvtyp_function)(const char *, char *, size_t);
56 
57 static struct {
58 	const char	*name;
59 	fstyp_function	function;
60 	bool		unmountable;
61 	const char	*precache_encoding;
62 } fstypes[] = {
63 	{ "apfs", &fstyp_apfs, true, NULL },
64 	{ "befs", &fstyp_befs, false, NULL },
65 	{ "cd9660", &fstyp_cd9660, false, NULL },
66 	{ "exfat", &fstyp_exfat, false, EXFAT_ENC },
67 	{ "ext2fs", &fstyp_ext2fs, false, NULL },
68 	{ "hfs+", &fstyp_hfsp, false, NULL },
69 	{ "msdosfs", &fstyp_msdosfs, false, NULL },
70 	{ "ntfs", &fstyp_ntfs, false, NTFS_ENC },
71 	{ "ufs", &fstyp_ufs, false, NULL },
72 	{ "hammer", &fstyp_hammer, false, NULL },
73 	{ "hammer2", &fstyp_hammer2, false, NULL },
74 	{ NULL, NULL, NULL, NULL }
75 };
76 
77 static struct {
78 	const char	*name;
79 	fsvtyp_function	function;
80 	bool		unmountable;
81 	const char	*precache_encoding;
82 } fsvtypes[] = {
83 	{ "hammer", &fsvtyp_hammer, false, NULL }, /* Must be before partial */
84 	{ "hammer(partial)", &fsvtyp_hammer_partial, true, NULL },
85 	{ "hammer2", &fsvtyp_hammer2, false, NULL }, /* Must be before partial */
86 	{ "hammer2(partial)", &fsvtyp_hammer2_partial, true, NULL },
87 	{ NULL, NULL, NULL, NULL }
88 };
89 
90 void *
91 read_buf(FILE *fp, off_t off, size_t len)
92 {
93 	int error;
94 	size_t nread;
95 	void *buf;
96 
97 	error = fseek(fp, off, SEEK_SET);
98 	if (error != 0) {
99 		warn("cannot seek to %jd", (uintmax_t)off);
100 		return (NULL);
101 	}
102 
103 	buf = malloc(len);
104 	if (buf == NULL) {
105 		warn("cannot malloc %zd bytes of memory", len);
106 		return (NULL);
107 	}
108 
109 	nread = fread(buf, len, 1, fp);
110 	if (nread != 1) {
111 		free(buf);
112 		if (feof(fp) == 0)
113 			warn("fread");
114 		return (NULL);
115 	}
116 
117 	return (buf);
118 }
119 
120 char *
121 checked_strdup(const char *s)
122 {
123 	char *c;
124 
125 	c = strdup(s);
126 	if (c == NULL)
127 		err(1, "strdup");
128 	return (c);
129 }
130 
131 void
132 rtrim(char *label, size_t size)
133 {
134 	ptrdiff_t i;
135 
136 	for (i = size - 1; i >= 0; i--) {
137 		if (label[i] == '\0')
138 			continue;
139 		else if (label[i] == ' ')
140 			label[i] = '\0';
141 		else
142 			break;
143 	}
144 }
145 
146 static void
147 usage(void)
148 {
149 
150 	fprintf(stderr, "usage: fstyp [-l] [-s] [-u] special\n");
151 	exit(1);
152 }
153 
154 static void
155 type_check(const char *path, FILE *fp)
156 {
157 	int error, fd;
158 	struct stat sb;
159 	struct partinfo pinfo;
160 
161 	fd = fileno(fp);
162 
163 	error = fstat(fd, &sb);
164 	if (error != 0)
165 		err(1, "%s: fstat", path);
166 
167 	if (S_ISREG(sb.st_mode))
168 		return;
169 
170 	error = ioctl(fd, DIOCGPART, &pinfo);
171 	if (error != 0)
172 		errx(1, "%s: not a disk", path);
173 }
174 
175 int
176 main(int argc, char **argv)
177 {
178 	int ch, error, i, nbytes;
179 	bool ignore_type = false, show_unmountable = false;
180 	char label[LABEL_LEN + 1], strvised[LABEL_LEN * 4 + 1];
181 	char fdpath[MAXPATHLEN];
182 	char *p;
183 	const char *path;
184 	const char *name = NULL;
185 	FILE *fp;
186 	fstyp_function fstyp_f;
187 	fsvtyp_function fsvtyp_f;
188 
189 	while ((ch = getopt(argc, argv, "lsu")) != -1) {
190 		switch (ch) {
191 		case 'l':
192 			show_label = true;
193 			break;
194 		case 's':
195 			ignore_type = true;
196 			break;
197 		case 'u':
198 			show_unmountable = true;
199 			break;
200 		default:
201 			usage();
202 		}
203 	}
204 
205 	argc -= optind;
206 	argv += optind;
207 	if (argc != 1)
208 		usage();
209 
210 	path = argv[0];
211 
212 	if (setlocale(LC_CTYPE, "") == NULL)
213 		err(1, "setlocale");
214 
215 	/*
216 	 * DragonFly: Filesystems may have syntax to decorate path.
217 	 * Make a wild guess.
218 	 */
219 	strlcpy(fdpath, path, sizeof(fdpath));
220 	p = strchr(fdpath, '@');
221 	if (p)
222 		*p = '\0';
223 
224 	fp = fopen(fdpath, "r");
225 	if (fp == NULL) {
226 		if (strcmp(path, fdpath))
227 			fp = fopen(path, "r");
228 		if (fp == NULL)
229 			goto fsvtyp; /* DragonFly */
230 		else
231 			strlcpy(fdpath, path, sizeof(fdpath));
232 	}
233 
234 	if (ignore_type == false)
235 		type_check(fdpath, fp);
236 
237 	memset(label, '\0', sizeof(label));
238 
239 	for (i = 0;; i++) {
240 		if (show_unmountable == false && fstypes[i].unmountable == true)
241 			continue;
242 		fstyp_f = fstypes[i].function;
243 		if (fstyp_f == NULL)
244 			break;
245 
246 		error = fstyp_f(fp, label, sizeof(label), path);
247 		if (error == 0) {
248 			name = fstypes[i].name;
249 			goto done;
250 		}
251 	}
252 fsvtyp:
253 	for (i = 0;; i++) {
254 		if (show_unmountable == false && fsvtypes[i].unmountable == true)
255 			continue;
256 		fsvtyp_f = fsvtypes[i].function;
257 		if (fsvtyp_f == NULL)
258 			break;
259 
260 		error = fsvtyp_f(path, label, sizeof(label));
261 		if (error == 0) {
262 			name = fsvtypes[i].name;
263 			goto done;
264 		}
265 	}
266 
267 	warnx("%s: filesystem not recognized", path);
268 	return (1);
269 done:
270 	if (show_label && label[0] != '\0') {
271 		/*
272 		 * XXX: I'd prefer VIS_HTTPSTYLE, but it unconditionally
273 		 *      encodes spaces.
274 		 */
275 		nbytes = strsnvis(strvised, sizeof(strvised), label,
276 		    VIS_GLOB | VIS_NL, "\"'$");
277 		if (nbytes == -1)
278 			err(1, "strsnvis");
279 
280 		printf("%s %s\n", name, strvised);
281 	} else {
282 		printf("%s\n", name);
283 	}
284 
285 	return (0);
286 }
287