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