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