Home | History | Annotate | Line # | Download | only in fstyp
      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 (at) 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 *
    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 *
    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
    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
    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
    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
    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