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
61 bool show_label = false;
62
63 typedef int (*fstyp_function)(FILE *, char *, size_t);
64 typedef int (*fsvtyp_function)(const char *, char *, size_t);
65
66 static 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
88 static 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
99 void *
read_buf(FILE * fp,off_t off,size_t len)100 read_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
129 char *
checked_strdup(const char * s)130 checked_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
140 void
rtrim(char * label,size_t size)141 rtrim(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
usage(void)155 usage(void)
156 {
157
158 fprintf(stderr, "Usage: %s [-l] [-s] [-u] special\n", getprogname());
159 exit(EXIT_FAILURE);
160 }
161
162 static void
type_check(const char * path,FILE * fp)163 type_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
186 int
main(int argc,char ** argv)187 main(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 }
264 fsvtyp:
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*/
281 done:
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