Index: head/usr.sbin/fstyp/exfat.c =================================================================== --- head/usr.sbin/fstyp/exfat.c (revision 355995) +++ head/usr.sbin/fstyp/exfat.c (revision 355996) @@ -1,77 +1,370 @@ /* * Copyright (c) 2017 Conrad Meyer * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); +#include +#include + +#include +#include +#include +#include +#include #include #include #include #include #include "fstyp.h" +/* + * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification + */ + struct exfat_vbr { char ev_jmp[3]; char ev_fsname[8]; char ev_zeros[53]; uint64_t ev_part_offset; uint64_t ev_vol_length; uint32_t ev_fat_offset; uint32_t ev_fat_length; uint32_t ev_cluster_offset; uint32_t ev_cluster_count; uint32_t ev_rootdir_cluster; uint32_t ev_vol_serial; uint16_t ev_fs_revision; uint16_t ev_vol_flags; uint8_t ev_log_bytes_per_sect; uint8_t ev_log_sect_per_clust; uint8_t ev_num_fats; uint8_t ev_drive_sel; uint8_t ev_percent_used; } __packed; +struct exfat_dirent { + uint8_t xde_type; +#define XDE_TYPE_INUSE_MASK 0x80 /* 1=in use */ +#define XDE_TYPE_INUSE_SHIFT 7 +#define XDE_TYPE_CATEGORY_MASK 0x40 /* 0=primary */ +#define XDE_TYPE_CATEGORY_SHIFT 6 +#define XDE_TYPE_IMPORTNC_MASK 0x20 /* 0=critical */ +#define XDE_TYPE_IMPORTNC_SHIFT 5 +#define XDE_TYPE_CODE_MASK 0x1f +/* InUse=0, ..., TypeCode=0: EOD. */ +#define XDE_TYPE_EOD 0x00 +#define XDE_TYPE_ALLOC_BITMAP (XDE_TYPE_INUSE_MASK | 0x01) +#define XDE_TYPE_UPCASE_TABLE (XDE_TYPE_INUSE_MASK | 0x02) +#define XDE_TYPE_VOL_LABEL (XDE_TYPE_INUSE_MASK | 0x03) +#define XDE_TYPE_FILE (XDE_TYPE_INUSE_MASK | 0x05) +#define XDE_TYPE_VOL_GUID (XDE_TYPE_INUSE_MASK | XDE_TYPE_IMPORTNC_MASK) +#define XDE_TYPE_STREAM_EXT (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK) +#define XDE_TYPE_FILE_NAME (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | 0x01) +#define XDE_TYPE_VENDOR (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK) +#define XDE_TYPE_VENDOR_ALLOC (XDE_TYPE_INUSE_MASK | XDE_TYPE_CATEGORY_MASK | XDE_TYPE_IMPORTNC_MASK | 0x01) + union { + uint8_t xde_generic_[19]; + struct exde_primary { + /* + * Count of "secondary" dirents following this one. + * + * A single logical entity may be composed of a + * sequence of several dirents, starting with a primary + * one; the rest are secondary dirents. + */ + uint8_t xde_secondary_count_; + uint16_t xde_set_chksum_; + uint16_t xde_prim_flags_; + uint8_t xde_prim_generic_[14]; + } __packed xde_primary_; + struct exde_secondary { + uint8_t xde_sec_flags_; + uint8_t xde_sec_generic_[18]; + } __packed xde_secondary_; + } u; + uint32_t xde_first_cluster; + uint64_t xde_data_len; +} __packed; +#define xde_generic u.xde_generic_ +#define xde_secondary_count u.xde_primary_.xde_secondary_count +#define xde_set_chksum u.xde_primary_.xde_set_chksum_ +#define xde_prim_flags u.xde_primary_.xde_prim_flags_ +#define xde_sec_flags u.xde_secondary_.xde_sec_flags_ +_Static_assert(sizeof(struct exfat_dirent) == 32, "spec"); + +struct exfat_de_label { + uint8_t xdel_type; /* XDE_TYPE_VOL_LABEL */ + uint8_t xdel_char_cnt; /* Length of UCS-2 label */ + uint16_t xdel_vol_lbl[11]; + uint8_t xdel_reserved[8]; +} __packed; +_Static_assert(sizeof(struct exfat_de_label) == 32, "spec"); + +#define MAIN_BOOT_REGION_SECT 0 +#define BACKUP_BOOT_REGION_SECT 12 + +#define SUBREGION_CHKSUM_SECT 11 + +#define FIRST_CLUSTER 2 +#define BAD_BLOCK_SENTINEL 0xfffffff7u +#define END_CLUSTER_SENTINEL 0xffffffffu + +static inline void * +read_sectn(FILE *fp, off_t sect, unsigned count, unsigned bytespersec) +{ + return (read_buf(fp, sect * bytespersec, bytespersec * count)); +} + +static inline void * +read_sect(FILE *fp, off_t sect, unsigned bytespersec) +{ + return (read_sectn(fp, sect, 1, bytespersec)); +} + +/* + * Compute the byte-by-byte multi-sector checksum of the given boot region + * (MAIN or BACKUP), for a given bytespersec (typically 512 or 4096). + * + * Endian-safe; result is host endian. + */ +static int +exfat_compute_boot_chksum(FILE *fp, unsigned region, unsigned bytespersec, + uint32_t *result) +{ + unsigned char *sector; + unsigned n, sect; + uint32_t checksum; + + checksum = 0; + for (sect = 0; sect < 11; sect++) { + sector = read_sect(fp, region + sect, bytespersec); + if (sector == NULL) + return (ENXIO); + for (n = 0; n < bytespersec; n++) { + if (sect == 0) { + switch (n) { + case 106: + case 107: + case 112: + continue; + } + } + checksum = ((checksum & 1) ? 0x80000000u : 0u) + + (checksum >> 1) + (uint32_t)sector[n]; + } + free(sector); + } + + *result = checksum; + return (0); +} + +static void +convert_label(const uint16_t *ucs2label /* LE */, unsigned ucs2len, char + *label_out, size_t label_sz) +{ + const char *label; + char *label_out_orig; + iconv_t cd; + size_t srcleft, rc; + + /* Currently hardcoded in fstyp.c as 256 or so. */ + assert(label_sz > 1); + + if (ucs2len == 0) { + /* + * Kind of seems bogus, but the spec allows an empty label + * entry with the same meaning as no label. + */ + return; + } + + if (ucs2len > 11) { + warnx("exfat: Bogus volume label length: %u", ucs2len); + return; + } + + /* dstname="" means convert to the current locale. */ + cd = iconv_open("", EXFAT_ENC); + if (cd == (iconv_t)-1) { + warn("exfat: Could not open iconv"); + return; + } + + label_out_orig = label_out; + + /* Dummy up the byte pointer and byte length iconv's API wants. */ + label = (const void *)ucs2label; + srcleft = ucs2len * sizeof(*ucs2label); + + rc = iconv(cd, __DECONST(char **, &label), &srcleft, &label_out, + &label_sz); + if (rc == (size_t)-1) { + warn("exfat: iconv()"); + *label_out_orig = '\0'; + } else { + /* NUL-terminate result (iconv advances label_out). */ + if (label_sz == 0) + label_out--; + *label_out = '\0'; + } + + iconv_close(cd); +} + +/* + * Using the FAT table, look up the next cluster in this chain. + */ +static uint32_t +exfat_fat_next(FILE *fp, const struct exfat_vbr *ev, unsigned BPS, + uint32_t cluster) +{ + uint32_t fat_offset_sect, clsect, clsectoff; + uint32_t *fatsect, nextclust; + + fat_offset_sect = le32toh(ev->ev_fat_offset); + clsect = fat_offset_sect + (cluster / (BPS / sizeof(cluster))); + clsectoff = (cluster % (BPS / sizeof(cluster))); + + /* XXX This is pretty wasteful without a block cache for the FAT. */ + fatsect = read_sect(fp, clsect, BPS); + nextclust = le32toh(fatsect[clsectoff]); + free(fatsect); + + return (nextclust); +} + +static void +exfat_find_label(FILE *fp, const struct exfat_vbr *ev, unsigned BPS, + char *label_out, size_t label_sz) +{ + uint32_t rootdir_cluster, sects_per_clust, cluster_offset_sect; + off_t rootdir_sect; + struct exfat_dirent *declust, *it; + + cluster_offset_sect = le32toh(ev->ev_cluster_offset); + rootdir_cluster = le32toh(ev->ev_rootdir_cluster); + sects_per_clust = (1u << ev->ev_log_sect_per_clust); + + if (rootdir_cluster < FIRST_CLUSTER) { + warnx("%s: invalid rootdir cluster %u < %d", __func__, + rootdir_cluster, FIRST_CLUSTER); + return; + } + + + for (; rootdir_cluster != END_CLUSTER_SENTINEL; + rootdir_cluster = exfat_fat_next(fp, ev, BPS, rootdir_cluster)) { + if (rootdir_cluster == BAD_BLOCK_SENTINEL) { + warnx("%s: Bogus bad block in root directory chain", + __func__); + return; + } + + rootdir_sect = (rootdir_cluster - FIRST_CLUSTER) * + sects_per_clust + cluster_offset_sect; + declust = read_sectn(fp, rootdir_sect, sects_per_clust, BPS); + for (it = declust; + it < declust + (sects_per_clust * BPS / sizeof(*it)); it++) { + bool eod = false; + + /* + * Simplistic directory traversal; doesn't do any + * validation of "MUST" requirements in spec. + */ + switch (it->xde_type) { + case XDE_TYPE_EOD: + eod = true; + break; + case XDE_TYPE_VOL_LABEL: { + struct exfat_de_label *lde = (void*)it; + convert_label(lde->xdel_vol_lbl, + lde->xdel_char_cnt, label_out, label_sz); + free(declust); + return; + } + } + + if (eod) + break; + } + free(declust); + } +} + int fstyp_exfat(FILE *fp, char *label, size_t size) { struct exfat_vbr *ev; + uint32_t *cksect; + unsigned bytespersec; + uint32_t chksum; + int error; + cksect = NULL; ev = (struct exfat_vbr *)read_buf(fp, 0, 512); if (ev == NULL || strncmp(ev->ev_fsname, "EXFAT ", 8) != 0) goto fail; + if (ev->ev_log_bytes_per_sect < 9 || ev->ev_log_bytes_per_sect > 12) { + warnx("exfat: Invalid BytesPerSectorShift"); + goto done; + } + + bytespersec = (1u << ev->ev_log_bytes_per_sect); + + error = exfat_compute_boot_chksum(fp, MAIN_BOOT_REGION_SECT, + bytespersec, &chksum); + if (error != 0) + goto done; + + cksect = read_sect(fp, MAIN_BOOT_REGION_SECT + SUBREGION_CHKSUM_SECT, + bytespersec); + /* - * Reading the volume label requires walking the root directory to look - * for a special label file. Left as an exercise for the reader. + * Technically the entire sector should be full of repeating 4-byte + * checksum pattern, but we only verify the first. */ + if (chksum != le32toh(cksect[0])) { + warnx("exfat: Found checksum 0x%08x != computed 0x%08x", + le32toh(cksect[0]), chksum); + goto done; + } + + if (show_label) + exfat_find_label(fp, ev, bytespersec, label, size); + +done: + free(cksect); free(ev); return (0); fail: free(ev); return (1); } Index: head/usr.sbin/fstyp/fstyp.c =================================================================== --- head/usr.sbin/fstyp/fstyp.c (revision 355995) +++ head/usr.sbin/fstyp/fstyp.c (revision 355996) @@ -1,236 +1,261 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include +#include #include #include #include #include #include #include #include #include "fstyp.h" #define LABEL_LEN 256 +bool show_label = false; + typedef int (*fstyp_function)(FILE *, char *, size_t); static struct { const char *name; fstyp_function function; bool unmountable; + char *precache_encoding; } fstypes[] = { - { "cd9660", &fstyp_cd9660, false }, - { "exfat", &fstyp_exfat, false }, - { "ext2fs", &fstyp_ext2fs, false }, - { "geli", &fstyp_geli, true }, - { "msdosfs", &fstyp_msdosfs, false }, - { "ntfs", &fstyp_ntfs, false }, - { "ufs", &fstyp_ufs, false }, + { "cd9660", &fstyp_cd9660, false, NULL }, + { "exfat", &fstyp_exfat, false, EXFAT_ENC }, + { "ext2fs", &fstyp_ext2fs, false, NULL }, + { "geli", &fstyp_geli, true, NULL }, + { "msdosfs", &fstyp_msdosfs, false, NULL }, + { "ntfs", &fstyp_ntfs, false, NULL }, + { "ufs", &fstyp_ufs, false, NULL }, #ifdef HAVE_ZFS - { "zfs", &fstyp_zfs, true }, + { "zfs", &fstyp_zfs, true, NULL }, #endif - { NULL, NULL, NULL } + { NULL, NULL, NULL, NULL } }; void * read_buf(FILE *fp, off_t off, size_t len) { int error; size_t nread; void *buf; error = fseek(fp, off, SEEK_SET); if (error != 0) { warn("cannot seek to %jd", (uintmax_t)off); return (NULL); } buf = malloc(len); if (buf == NULL) { warn("cannot malloc %zd bytes of memory", len); return (NULL); } nread = fread(buf, len, 1, fp); if (nread != 1) { free(buf); if (feof(fp) == 0) warn("fread"); return (NULL); } return (buf); } char * checked_strdup(const char *s) { char *c; c = strdup(s); if (c == NULL) err(1, "strdup"); return (c); } void rtrim(char *label, size_t size) { ptrdiff_t i; for (i = size - 1; i >= 0; i--) { if (label[i] == '\0') continue; else if (label[i] == ' ') label[i] = '\0'; else break; } } static void usage(void) { fprintf(stderr, "usage: fstyp [-l] [-s] [-u] special\n"); exit(1); } static void type_check(const char *path, FILE *fp) { int error, fd; off_t mediasize; struct stat sb; fd = fileno(fp); error = fstat(fd, &sb); if (error != 0) err(1, "%s: fstat", path); if (S_ISREG(sb.st_mode)) return; error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); if (error != 0) errx(1, "%s: not a disk", path); } int main(int argc, char **argv) { int ch, error, i, nbytes; - bool ignore_type = false, show_label = false, show_unmountable = false; + bool ignore_type = false, show_unmountable = false; char label[LABEL_LEN + 1], strvised[LABEL_LEN * 4 + 1]; char *path; FILE *fp; fstyp_function fstyp_f; while ((ch = getopt(argc, argv, "lsu")) != -1) { switch (ch) { case 'l': show_label = true; break; case 's': ignore_type = true; break; case 'u': show_unmountable = true; break; default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); path = argv[0]; + + if (setlocale(LC_CTYPE, "") == NULL) + err(1, "setlocale"); + caph_cache_catpages(); + + /* Cache iconv conversion data before entering capability mode. */ + if (show_label) { + for (i = 0; i < nitems(fstypes); i++) { + iconv_t cd; + + if (fstypes[i].precache_encoding == NULL) + continue; + cd = iconv_open("", fstypes[i].precache_encoding); + if (cd == (iconv_t)-1) + err(1, "%s: iconv_open %s", fstypes[i].name, + fstypes[i].precache_encoding); + /* Iconv keeps a small cache of unused encodings. */ + iconv_close(cd); + } + } fp = fopen(path, "r"); if (fp == NULL) err(1, "%s", path); if (caph_enter() < 0) err(1, "cap_enter"); if (ignore_type == false) type_check(path, fp); memset(label, '\0', sizeof(label)); for (i = 0;; i++) { if (show_unmountable == false && fstypes[i].unmountable == true) continue; fstyp_f = fstypes[i].function; if (fstyp_f == NULL) break; error = fstyp_f(fp, label, sizeof(label)); if (error == 0) break; } if (fstypes[i].name == NULL) { warnx("%s: filesystem not recognized", path); return (1); } if (show_label && label[0] != '\0') { /* * XXX: I'd prefer VIS_HTTPSTYLE, but it unconditionally * encodes spaces. */ nbytes = strsnvis(strvised, sizeof(strvised), label, VIS_GLOB | VIS_NL, "\"'$"); if (nbytes == -1) err(1, "strsnvis"); printf("%s %s\n", fstypes[i].name, strvised); } else { printf("%s\n", fstypes[i].name); } return (0); } Index: head/usr.sbin/fstyp/fstyp.h =================================================================== --- head/usr.sbin/fstyp/fstyp.h (revision 355995) +++ head/usr.sbin/fstyp/fstyp.h (revision 355996) @@ -1,52 +1,59 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef FSTYP_H #define FSTYP_H +#include + #define MIN(a,b) (((a)<(b))?(a):(b)) + +/* The spec doesn't seem to permit UTF-16 surrogates; definitely LE. */ +#define EXFAT_ENC "UCS-2LE" + +extern bool show_label; /* -l flag */ void *read_buf(FILE *fp, off_t off, size_t len); char *checked_strdup(const char *s); void rtrim(char *label, size_t size); int fstyp_cd9660(FILE *fp, char *label, size_t size); int fstyp_exfat(FILE *fp, char *label, size_t size); int fstyp_ext2fs(FILE *fp, char *label, size_t size); int fstyp_geli(FILE *fp, char *label, size_t size); int fstyp_msdosfs(FILE *fp, char *label, size_t size); int fstyp_ntfs(FILE *fp, char *label, size_t size); int fstyp_ufs(FILE *fp, char *label, size_t size); #ifdef HAVE_ZFS int fstyp_zfs(FILE *fp, char *label, size_t size); #endif #endif /* !FSTYP_H */ Index: head/usr.sbin/fstyp/tests/fstyp_test.sh =================================================================== --- head/usr.sbin/fstyp/tests/fstyp_test.sh (revision 355995) +++ head/usr.sbin/fstyp/tests/fstyp_test.sh (revision 355996) @@ -1,271 +1,281 @@ #!/bin/sh # # SPDX-License-Identifier: BSD-2-Clause-FreeBSD # # Copyright (c) 2015 Alan Somers # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # $FreeBSD$ atf_test_case cd9660 cd9660_head() { atf_set "descr" "fstyp(8) should detect cd9660 filesystems" } cd9660_body() { atf_check -s exit:0 mkdir -p dir/emptydir # makefs requires a nonempty directory atf_check -s exit:0 -o ignore makefs -t cd9660 -Z -s 64m cd9660.img dir atf_check -s exit:0 -o inline:"cd9660\n" fstyp cd9660.img atf_check -s exit:0 -o inline:"cd9660\n" fstyp -l cd9660.img } atf_test_case cd9660_label cd9660_label_head() { atf_set "descr" "fstyp(8) can read the label on a cd9660 filesystem" } cd9660_label_body() { atf_check -s exit:0 mkdir -p dir/emptydir # makefs requires a nonempty directory atf_check -s exit:0 -o ignore makefs -t cd9660 -o label=Foo -Z -s 64m cd9660.img dir atf_check -s exit:0 -o inline:"cd9660\n" fstyp cd9660.img # Note: cd9660 labels are always upper case atf_check -s exit:0 -o inline:"cd9660 FOO\n" fstyp -l cd9660.img } atf_test_case dir dir_head() { atf_set "descr" "fstyp(8) should fail on a directory" } dir_body() { atf_check -s exit:0 mkdir dir atf_check -s exit:1 -e match:"not a disk" fstyp dir } atf_test_case exfat exfat_head() { atf_set "descr" "fstyp(8) can detect exFAT filesystems" } exfat_body() { bzcat $(atf_get_srcdir)/dfr-01-xfat.img.bz2 > exfat.img atf_check -s exit:0 -o inline:"exfat\n" fstyp -u exfat.img } +atf_test_case exfat_label +exfat_label_head() { + atf_set "descr" "fstyp(8) can read exFAT labels" +} +exfat_label_body() { + bzcat $(atf_get_srcdir)/dfr-01-xfat.img.bz2 > exfat.img + atf_check -s exit:0 -o inline:"exfat exFat\n" fstyp -u -l exfat.img +} + atf_test_case empty empty_head() { atf_set "descr" "fstyp(8) should fail on an empty file" } empty_body() { atf_check -s exit:0 touch empty atf_check -s exit:1 -e match:"filesystem not recognized" fstyp empty } atf_test_case ext2 ext2_head() { atf_set "descr" "fstyp(8) can detect ext2 filesystems" } ext2_body() { bzcat $(atf_get_srcdir)/ext2.img.bz2 > ext2.img atf_check -s exit:0 -o inline:"ext2fs\n" fstyp ext2.img atf_check -s exit:0 -o inline:"ext2fs\n" fstyp -l ext2.img } atf_test_case ext3 ext3_head() { atf_set "descr" "fstyp(8) can detect ext3 filesystems" } ext3_body() { bzcat $(atf_get_srcdir)/ext3.img.bz2 > ext3.img atf_check -s exit:0 -o inline:"ext2fs\n" fstyp ext3.img atf_check -s exit:0 -o inline:"ext2fs\n" fstyp -l ext3.img } atf_test_case ext4 ext4_head() { atf_set "descr" "fstyp(8) can detect ext4 filesystems" } ext4_body() { bzcat $(atf_get_srcdir)/ext4.img.bz2 > ext4.img atf_check -s exit:0 -o inline:"ext2fs\n" fstyp ext4.img atf_check -s exit:0 -o inline:"ext2fs\n" fstyp -l ext4.img } atf_test_case ext4_label ext4_label_head() { atf_set "descr" "fstyp(8) can read the label on an ext4 filesystem" } ext4_label_body() { bzcat $(atf_get_srcdir)/ext4_with_label.img.bz2 > ext4_with_label.img atf_check -s exit:0 -o inline:"ext2fs foo\n" fstyp -l ext4_with_label.img } atf_test_case fat12 fat12_head() { atf_set "descr" "fstyp(8) can detect FAT12 filesystems" } fat12_body() { atf_check -s exit:0 truncate -s 64m msdos.img atf_check -s exit:0 -o ignore -e ignore newfs_msdos -F 12 ./msdos.img atf_check -s exit:0 -o inline:"msdosfs\n" fstyp msdos.img atf_check -s exit:0 -o inline:"msdosfs\n" fstyp -l msdos.img } atf_test_case fat16 fat16_head() { atf_set "descr" "fstyp(8) can detect FAT16 filesystems" } fat16_body() { atf_check -s exit:0 truncate -s 64m msdos.img atf_check -s exit:0 -o ignore -e ignore newfs_msdos -F 16 ./msdos.img atf_check -s exit:0 -o inline:"msdosfs\n" fstyp msdos.img atf_check -s exit:0 -o inline:"msdosfs\n" fstyp -l msdos.img } atf_test_case fat32 fat32_head() { atf_set "descr" "fstyp(8) can detect FAT32 filesystems" } fat32_body() { atf_check -s exit:0 truncate -s 64m msdos.img atf_check -s exit:0 -o ignore -e ignore newfs_msdos -F 32 -c 1 \ ./msdos.img atf_check -s exit:0 -o inline:"msdosfs\n" fstyp msdos.img atf_check -s exit:0 -o inline:"msdosfs\n" fstyp -l msdos.img } atf_test_case fat32_label fat32_label_head() { atf_set "descr" "fstyp(8) can read the label on an msdos filesystem" } fat32_label_body() { atf_check -s exit:0 truncate -s 64m msdos.img atf_check -s exit:0 -o ignore -e ignore newfs_msdos -F 32 -L Foo -c 1 \ ./msdos.img atf_check -s exit:0 -o inline:"msdosfs\n" fstyp msdos.img # Note: msdos labels are always upper case atf_check -s exit:0 -o inline:"msdosfs FOO\n" fstyp -l msdos.img } atf_test_case ntfs ntfs_head() { atf_set "descr" "fstyp(8) can detect ntfs filesystems" } ntfs_body() { bzcat $(atf_get_srcdir)/ntfs.img.bz2 > ntfs.img atf_check -s exit:0 -o inline:"ntfs\n" fstyp ntfs.img atf_check -s exit:0 -o inline:"ntfs\n" fstyp -l ntfs.img } atf_test_case ntfs_with_label ntfs_with_label_head() { atf_set "descr" "fstyp(8) can read labels on ntfs filesystems" } ntfs_with_label_body() { bzcat $(atf_get_srcdir)/ntfs_with_label.img.bz2 > ntfs_with_label.img atf_check -s exit:0 -o inline:"ntfs\n" fstyp ntfs_with_label.img atf_check -s exit:0 -o inline:"ntfs Foo\n" fstyp -l ntfs_with_label.img } atf_test_case ufs1 ufs1_head() { atf_set "descr" "fstyp(8) should detect UFS version 1 filesystems" } ufs1_body() { atf_check -s exit:0 mkdir dir atf_check -s exit:0 -o ignore makefs -Z -s 64m ufs.img dir atf_check -s exit:0 -o inline:"ufs\n" fstyp ufs.img atf_check -s exit:0 -o inline:"ufs\n" fstyp -l ufs.img } atf_test_case ufs2 ufs2_head() { atf_set "descr" "fstyp(8) should detect UFS version 2 filesystems" } ufs2_body() { atf_check -s exit:0 mkdir dir atf_check -s exit:0 -o ignore makefs -o version=2 -Z -s 64m ufs.img dir atf_check -s exit:0 -o inline:"ufs\n" fstyp ufs.img atf_check -s exit:0 -o inline:"ufs\n" fstyp -l ufs.img } atf_test_case ufs2_label ufs2_label_head() { atf_set "descr" "fstyp(8) can read the label on a UFS v2 filesystem" } ufs2_label_body() { atf_check -s exit:0 mkdir dir atf_check -s exit:0 -o ignore makefs -o version=2,label="foo" -Z -s 64m ufs.img dir atf_check -s exit:0 -o inline:"ufs foo\n" fstyp -l ufs.img } atf_test_case ufs_on_device cleanup ufs_on_device_head() { atf_set "descr" "fstyp(8) should work on device nodes" atf_set "require.user" "root" } ufs_on_device_body() { mdconfig -a -t swap -s 64m > mdname md=$(cat mdname) if [ -z "$md" ]; then atf_fail "Failed to create md(4) device" fi atf_check -s exit:0 -o ignore newfs -L foo /dev/$md atf_check -s exit:0 -o inline:"ufs\n" fstyp /dev/$md atf_check -s exit:0 -o inline:"ufs foo\n" fstyp -l /dev/$md } ufs_on_device_cleanup() { md=$(cat mdname) if [ -n "$md" ]; then mdconfig -d -u "$md" fi } atf_test_case zeros zeros_head() { atf_set "descr" "fstyp(8) should fail on a zero-filled file" } zeros_body() { atf_check -s exit:0 truncate -s 256m zeros atf_check -s exit:1 -e match:"filesystem not recognized" fstyp zeros } atf_init_test_cases() { atf_add_test_case cd9660 atf_add_test_case cd9660_label atf_add_test_case dir atf_add_test_case empty atf_add_test_case exfat + atf_add_test_case exfat_label atf_add_test_case ext2 atf_add_test_case ext3 atf_add_test_case ext4 atf_add_test_case ext4_label atf_add_test_case fat12 atf_add_test_case fat16 atf_add_test_case fat32 atf_add_test_case fat32_label atf_add_test_case ntfs atf_add_test_case ntfs_with_label atf_add_test_case ufs1 atf_add_test_case ufs2 atf_add_test_case ufs2_label atf_add_test_case ufs_on_device atf_add_test_case zeros }