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