xref: /dragonfly/usr.sbin/fstyp/fstyp.c (revision 6025be5878fc8286162a074592be39505968dbee)
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 *
read_buf(FILE * fp,off_t off,size_t len)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 *
checked_strdup(const char * s)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
rtrim(char * label,size_t size)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
usage(void)147 usage(void)
148 {
149 
150           fprintf(stderr, "usage: fstyp [-l] [-s] [-u] special\n");
151           exit(1);
152 }
153 
154 static void
type_check(const char * path,FILE * fp)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
main(int argc,char ** argv)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