diff options
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | NEWS | 24 | ||||
-rwxr-xr-x | configure | 20 | ||||
-rw-r--r-- | dump/dump.c | 4 | ||||
-rw-r--r-- | exfat2img/exfat2img.c | 93 | ||||
-rw-r--r-- | fsck/fsck.c | 183 | ||||
-rw-r--r-- | fsck/repair.c | 14 | ||||
-rw-r--r-- | fsck/repair.h | 1 | ||||
-rw-r--r-- | include/exfat_dir.h | 2 | ||||
-rw-r--r-- | include/exfat_fs.h | 15 | ||||
-rw-r--r-- | include/exfat_ondisk.h | 20 | ||||
-rw-r--r-- | include/libexfat.h | 6 | ||||
-rw-r--r-- | include/version.h | 2 | ||||
-rw-r--r-- | label/label.c | 14 | ||||
-rw-r--r-- | lib/exfat_dir.c | 57 | ||||
-rw-r--r-- | lib/exfat_fs.c | 51 | ||||
-rw-r--r-- | lib/libexfat.c | 53 | ||||
-rw-r--r-- | manpages/mkfs.exfat.8 | 11 | ||||
-rw-r--r-- | mkfs/mkfs.c | 88 | ||||
-rw-r--r-- | tune/tune.c | 14 |
20 files changed, 445 insertions, 229 deletions
diff --git a/Makefile.in b/Makefile.in index 01a27d7..914c23f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -198,7 +198,7 @@ am__DIST_COMMON = $(dist_man8_MANS) $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/install-sh \ $(top_srcdir)/build-aux/ltmain.sh \ $(top_srcdir)/build-aux/missing COPYING NEWS build-aux/compile \ - build-aux/config.guess build-aux/config.sub build-aux/depcomp \ + build-aux/config.guess build-aux/config.sub \ build-aux/install-sh build-aux/ltmain.sh build-aux/missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) @@ -1,3 +1,27 @@ +exfatprogs 1.2.3 - released 2024-05-23 +====================================== + +CHANGES : + * dump.exfat: Report sector size in bytes and cluster size in + terms of sectors. + * fsck.exfat: Show checksum value if the SetChecksum of File + directory entry is invalid. + * mkfs.exfat: Improve FAT length calculation to reduce + the FAT size. + +NEW FEATURES : + * mkfs.exfat: Add the option "--sector-size". + * fsck.exfat: Support checking and repairing VendorAllcation and + VendorExtension directory entries. + +BUG FIXES : + * exfatprogs: Remove unnecessary memory allocations. + * fsck.exfat: Fix corruption that can occur if the cluster size + is 512-byte. + * fsck.exfat: Fix the SecondaryCount of File directory entry + when the count of Name directory entries is 17 or higher. + * tune.exfat: Fix an error that accepts invalid serial numbers. + exfatprogs 1.2.2 - released 2023-10-26 ====================================== @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for exfatprogs 1.2.2. +# Generated by GNU Autoconf 2.69 for exfatprogs 1.2.3. # # Report bugs to <linkinjeon@kernel.org>. # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='exfatprogs' PACKAGE_TARNAME='exfatprogs' -PACKAGE_VERSION='1.2.2' -PACKAGE_STRING='exfatprogs 1.2.2' +PACKAGE_VERSION='1.2.3' +PACKAGE_STRING='exfatprogs 1.2.3' PACKAGE_BUGREPORT='linkinjeon@kernel.org' PACKAGE_URL='https://github.com/exfatprogs/exfatprogs' @@ -1325,7 +1325,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures exfatprogs 1.2.2 to adapt to many kinds of systems. +\`configure' configures exfatprogs 1.2.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1396,7 +1396,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of exfatprogs 1.2.2:";; + short | recursive ) echo "Configuration of exfatprogs 1.2.3:";; esac cat <<\_ACEOF @@ -1508,7 +1508,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -exfatprogs configure 1.2.2 +exfatprogs configure 1.2.3 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1786,7 +1786,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by exfatprogs $as_me 1.2.2, which was +It was created by exfatprogs $as_me 1.2.3, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2654,7 +2654,7 @@ fi # Define the identity of the package. PACKAGE='exfatprogs' - VERSION='1.2.2' + VERSION='1.2.3' cat >>confdefs.h <<_ACEOF @@ -13253,7 +13253,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by exfatprogs $as_me 1.2.2, which was +This file was extended by exfatprogs $as_me 1.2.3, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -13320,7 +13320,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -exfatprogs config.status 1.2.2 +exfatprogs config.status 1.2.3 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/dump/dump.c b/dump/dump.c index 8c96376..73a231a 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -131,8 +131,8 @@ static int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) exfat_info("Cluster Count: \t\t\t\t%u\n", total_clus); exfat_info("Root Cluster (cluster offset): \t\t%u\n", root_clu); exfat_info("Volume Serial: \t\t\t\t0x%x\n", le32_to_cpu(pbsx->vol_serial)); - exfat_info("Sector Size Bits: \t\t\t%u\n", pbsx->sect_size_bits); - exfat_info("Sector per Cluster bits: \t\t%u\n\n", pbsx->sect_per_clus_bits); + exfat_info("Bytes per Sector: \t\t\t%u\n", 1 << pbsx->sect_size_bits); + exfat_info("Sectors per Cluster: \t\t\t%u\n\n", 1 << pbsx->sect_per_clus_bits); bd->cluster_size = 1 << (pbsx->sect_per_clus_bits + pbsx->sect_size_bits); diff --git a/exfat2img/exfat2img.c b/exfat2img/exfat2img.c index 3f83588..bd9db44 100644 --- a/exfat2img/exfat2img.c +++ b/exfat2img/exfat2img.c @@ -52,7 +52,7 @@ struct exfat2img { bool save_cc; struct exfat_blk_dev bdev; struct exfat *exfat; - struct buffer_desc *dump_bdesc; + void *dump_cluster; struct buffer_desc *scan_bdesc; struct exfat_de_iter de_iter; }; @@ -96,12 +96,12 @@ static void usage(const char *name) static void free_exfat2img(struct exfat2img *ei) { + if (ei->scan_bdesc) + exfat_free_buffer(ei->exfat, ei->scan_bdesc); if (ei->exfat) exfat_free_exfat(ei->exfat); - if (ei->dump_bdesc) - exfat_free_buffer(ei->dump_bdesc, 2); - if (ei->scan_bdesc) - exfat_free_buffer(ei->scan_bdesc, 2); + if (ei->dump_cluster) + free(ei->dump_cluster); if (ei->out_fd) close(ei->out_fd); if (ei->bdev.dev_fd) @@ -118,17 +118,13 @@ static int create_exfat2img(struct exfat2img *ei, if (!ei->exfat) return -ENOMEM; - ei->dump_bdesc = exfat_alloc_buffer(2, - ei->exfat->clus_size, - ei->exfat->sect_size); - if (!ei->dump_bdesc) { + ei->dump_cluster = malloc(ei->exfat->clus_size); + if (!ei->dump_cluster) { err = -ENOMEM; goto err; } - ei->scan_bdesc = exfat_alloc_buffer(2, - ei->exfat->clus_size, - ei->exfat->sect_size); + ei->scan_bdesc = exfat_alloc_buffer(ei->exfat); if (!ei->scan_bdesc) { err = -ENOMEM; goto err; @@ -180,8 +176,7 @@ static ssize_t dump_range(struct exfat2img *ei, off_t start, off_t end) len = (size_t)MIN(end - start, exfat->clus_size); ret = exfat_read(exfat->blk_dev->dev_fd, - ei->dump_bdesc[0].buffer, - len, start); + ei->dump_cluster, len, start); if (ret != (ssize_t)len) { exfat_err("failed to read %llu bytes at %llu\n", (unsigned long long)len, @@ -189,8 +184,7 @@ static ssize_t dump_range(struct exfat2img *ei, off_t start, off_t end) return -EIO; } - ret = pwrite(ei->out_fd, ei->dump_bdesc[0].buffer, - len, start); + ret = pwrite(ei->out_fd, ei->dump_cluster, len, start); if (ret != (ssize_t)len) { exfat_err("failed to write %llu bytes at %llu\n", (unsigned long long)len, @@ -552,7 +546,7 @@ out: } static int dump_bytes_to_stdout(struct exfat2img *ei, - off_t start, off_t end_excl, bool fill_zero) + off_t start, off_t end_excl) { struct exfat *exfat = ei->exfat; size_t len; @@ -567,32 +561,21 @@ static int dump_bytes_to_stdout(struct exfat2img *ei, while (start < end_excl) { len = (size_t)MIN(end_excl - start, exfat->clus_size); - if (!fill_zero) { - ret = exfat_read(exfat->blk_dev->dev_fd, - ei->dump_bdesc[0].buffer, - len, start); - if (ret != (ssize_t)len) { - exfat_err("failed to read %llu bytes at %llu\n", - (unsigned long long)len, - (unsigned long long)start); - return -EIO; - } + ret = exfat_read(exfat->blk_dev->dev_fd, ei->dump_cluster, + len, start); + if (ret != (ssize_t)len) { + exfat_err("failed to read %llu bytes at %llu\n", + (unsigned long long)len, + (unsigned long long)start); + return -EIO; + } - ret = write(ei->out_fd, ei->dump_bdesc[0].buffer, len); - if (ret != (ssize_t)len) { - exfat_err("failed to write %llu bytes at %llu\n", - (unsigned long long)len, - (unsigned long long)start); - return -EIO; - } - } else { - ret = write(ei->out_fd, exfat->zero_cluster, len); - if (ret != (ssize_t)len) { - exfat_err("failed to write %llu bytes at %llu\n", - (unsigned long long)len, - (unsigned long long)start); - return -EIO; - } + ret = write(ei->out_fd, ei->dump_cluster, len); + if (ret != (ssize_t)len) { + exfat_err("failed to write %llu bytes at %llu\n", + (unsigned long long)len, + (unsigned long long)start); + return -EIO; } start += len; @@ -644,8 +627,7 @@ static int dump_clusters_to_stdout(struct exfat2img *ei, start_off = exfat_c2o(ei->exfat, clu); end_off_excl = exfat_c2o(ei->exfat, clu + cc_clu_count); - if (dump_bytes_to_stdout(ei, start_off, end_off_excl, - false) < 0) + if (dump_bytes_to_stdout(ei, start_off, end_off_excl) < 0) return -EIO; } else { ei->stdout_offset += (off_t)cc_clu_count * ei->exfat->clus_size; @@ -665,7 +647,7 @@ static int dump_to_stdout(struct exfat2img *ei) start_off = 0; end_off = exfat_s2o(exfat, le32_to_cpu(exfat->bs->bsx.clu_offset)); - if (dump_bytes_to_stdout(ei, start_off, end_off, false) < 0) { + if (dump_bytes_to_stdout(ei, start_off, end_off) < 0) { exfat_err("failed to dump boot sectors and FAT tables\n"); return -EIO; } @@ -762,7 +744,7 @@ static ssize_t read_stream(int fd, void *buf, size_t len) static int restore_from_stdin(struct exfat2img *ei) { - int in_fd, ret; + int in_fd, ret = 0; unsigned char cc; unsigned int clu, end_clu; unsigned int cc_clu_count; @@ -792,8 +774,8 @@ static int restore_from_stdin(struct exfat2img *ei) clus_size = le32_to_cpu(ei_hdr.cluster_size); ei->out_fd = ei->bdev.dev_fd; - ei->dump_bdesc = exfat_alloc_buffer(2, clus_size, 512); - if (!ei->dump_bdesc) + ei->dump_cluster = malloc(clus_size); + if (!ei->dump_cluster) return -ENOMEM; /* restore boot regions, and FAT tables */ @@ -802,7 +784,7 @@ static int restore_from_stdin(struct exfat2img *ei) out_end_off_excl = le32_to_cpu(ei_hdr.heap_clus_offset); while (out_start_off < out_end_off_excl) { len = MIN(out_end_off_excl - out_start_off, clus_size); - if (read_stream(in_fd, ei->dump_bdesc[0].buffer, len) != (ssize_t)len) { + if (read_stream(in_fd, ei->dump_cluster, len) != (ssize_t)len) { exfat_err("failed to read first meta region. %llu ~ %llu\n", (unsigned long long)in_start_off, (unsigned long long)in_start_off + len); @@ -810,7 +792,7 @@ static int restore_from_stdin(struct exfat2img *ei) goto out; } - if (pwrite(ei->out_fd, ei->dump_bdesc[0].buffer, len, out_start_off) + if (pwrite(ei->out_fd, ei->dump_cluster, len, out_start_off) != (ssize_t)len) { exfat_err("failed to write first meta region. %llu ~ %llu\n", (unsigned long long)out_start_off, @@ -856,7 +838,7 @@ static int restore_from_stdin(struct exfat2img *ei) if (cc == EI_CC_COPY_1 || cc == EI_CC_COPY_2) { end_clu = clu + cc_clu_count; while (clu < end_clu) { - if (read_stream(in_fd, ei->dump_bdesc[0].buffer, + if (read_stream(in_fd, ei->dump_cluster, clus_size) != (ssize_t)clus_size) { exfat_err("failed to read range %llu ~ %llu\n", (unsigned long long)in_start_off, @@ -864,7 +846,7 @@ static int restore_from_stdin(struct exfat2img *ei) ret = -EIO; goto out; } - if (pwrite(ei->out_fd, ei->dump_bdesc[0].buffer, + if (pwrite(ei->out_fd, ei->dump_cluster, clus_size, out_start_off) != (ssize_t)clus_size) { exfat_err("failed to write range %llu ~ %llu\n", (unsigned long long)out_start_off, @@ -890,8 +872,11 @@ static int restore_from_stdin(struct exfat2img *ei) } } out: - fsync(ei->out_fd); - exfat_free_buffer(ei->dump_bdesc, 2); + if (fsync(ei->out_fd)) { + exfat_err("failed to fsync: %d\n", errno); + ret = -EIO; + } + free(ei->dump_cluster); return ret; } diff --git a/fsck/fsck.c b/fsck/fsck.c index 77272aa..fe8a122 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -102,7 +102,7 @@ static void usage(char *name) exfat_de_iter_device_offset(iter)); \ }) -static int check_clus_chain(struct exfat_de_iter *de_iter, +static int check_clus_chain(struct exfat_de_iter *de_iter, int stream_idx, struct exfat_inode *node) { struct exfat *exfat = de_iter->exfat; @@ -220,8 +220,8 @@ truncate_file: if (!exfat_heap_clus(exfat, prev)) node->first_clus = EXFAT_FREE_CLUSTER; - exfat_de_iter_get_dirty(de_iter, 1, &stream_de); - if (count * exfat->clus_size < + exfat_de_iter_get_dirty(de_iter, stream_idx, &stream_de); + if (stream_idx == 1 && count * exfat->clus_size < le64_to_cpu(stream_de->stream_valid_size)) stream_de->stream_valid_size = cpu_to_le64( count * exfat->clus_size); @@ -371,9 +371,10 @@ static int read_boot_region(struct exfat_blk_dev *bd, struct pbr **pbr, { struct pbr *bs; int ret = -EINVAL; + unsigned long long clu_max_count; *pbr = NULL; - bs = (struct pbr *)malloc(sizeof(struct pbr)); + bs = malloc(sizeof(struct pbr)); if (!bs) { exfat_err("failed to allocate memory\n"); return -ENOMEM; @@ -434,12 +435,13 @@ static int read_boot_region(struct exfat_blk_dev *bd, struct pbr **pbr, goto err; } - if (le32_to_cpu(bs->bsx.clu_count) * EXFAT_CLUSTER_SIZE(bs) > - bd->size) { + clu_max_count = (le64_to_cpu(bs->bsx.vol_length) - le32_to_cpu(bs->bsx.clu_offset)) >> + bs->bsx.sect_per_clus_bits; + if (le32_to_cpu(bs->bsx.clu_count) > clu_max_count) { if (verbose) - exfat_err("too large cluster count: %u, expected: %u\n", + exfat_err("too large cluster count: %u, expected: %llu\n", le32_to_cpu(bs->bsx.clu_count), - bd->num_clusters); + MIN(clu_max_count, EXFAT_MAX_NUM_CLUSTER)); goto err; } @@ -565,21 +567,24 @@ restore: return ret; } -static uint16_t file_calc_checksum(struct exfat_de_iter *iter) +static int file_calc_checksum(struct exfat_de_iter *iter, uint16_t *checksum) { - uint16_t checksum; struct exfat_dentry *file_de, *de; - int i; + int i, ret; - checksum = 0; - exfat_de_iter_get(iter, 0, &file_de); + *checksum = 0; + ret = exfat_de_iter_get(iter, 0, &file_de); + if (ret) + return ret; - exfat_calc_dentry_checksum(file_de, &checksum, true); + exfat_calc_dentry_checksum(file_de, checksum, true); for (i = 1; i <= file_de->file_num_ext; i++) { - exfat_de_iter_get(iter, i, &de); - exfat_calc_dentry_checksum(de, &checksum, false); + ret = exfat_de_iter_get(iter, i, &de); + if (ret) + return ret; + exfat_calc_dentry_checksum(de, checksum, false); } - return checksum; + return 0; } /* @@ -594,7 +599,7 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node) uint16_t checksum; bool valid = true; - ret = check_clus_chain(iter, node); + ret = check_clus_chain(iter, 1, node); if (ret < 0) return ret; @@ -624,7 +629,9 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node) valid = false; } - checksum = file_calc_checksum(iter); + ret = file_calc_checksum(iter, &checksum); + if (ret) + return ret; exfat_de_iter_get(iter, 0, &dentry); if (checksum != le16_to_cpu(dentry->file_checksum)) { exfat_de_iter_get_dirty(iter, 0, &dentry); @@ -670,6 +677,7 @@ static int check_name_dentry_set(struct exfat_de_iter *iter, struct exfat_dentry *stream_de; size_t name_len; __u16 hash; + int ret = 0; exfat_de_iter_get(iter, 1, &stream_de); @@ -679,6 +687,7 @@ static int check_name_dentry_set(struct exfat_de_iter *iter, "the name length of a file is wrong")) { exfat_de_iter_get_dirty(iter, 1, &stream_de); stream_de->stream_name_len = (__u8)name_len; + ret = 1; } else { return -EINVAL; } @@ -690,20 +699,18 @@ static int check_name_dentry_set(struct exfat_de_iter *iter, "the name hash of a file is wrong")) { exfat_de_iter_get_dirty(iter, 1, &stream_de); stream_de->stream_name_hash = cpu_to_le16(hash); + ret = 1; } else { return -EINVAL; } } if (BITMAP_GET(iter->name_hash_bitmap, hash)) { - int ret = handle_duplicated_filename(iter, inode); - - if (ret) - return ret; + ret = handle_duplicated_filename(iter, inode); } else BITMAP_SET(iter->name_hash_bitmap, hash); - return 0; + return ret; } const __le16 MSDOS_DOT[ENTRY_NAME_MAX] = {cpu_to_le16(46), 0, }; @@ -732,8 +739,8 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, { struct exfat_dentry *file_de, *stream_de, *dentry; struct exfat_inode *node = NULL; - int i, ret; - bool need_delete = false; + int i, j, ret, name_de_count; + bool need_delete = false, need_copy_up = false; uint16_t checksum; ret = exfat_de_iter_get(iter, 0, &file_de); @@ -742,10 +749,11 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, return -EINVAL; } - checksum = file_calc_checksum(iter); - if (checksum != le16_to_cpu(file_de->file_checksum)) { + ret = file_calc_checksum(iter, &checksum); + if (ret || checksum != le16_to_cpu(file_de->file_checksum)) { if (repair_file_ask(iter, NULL, ER_DE_CHECKSUM, - "the checksum of a file is wrong")) + "the checksum %#x of a file is wrong, expected: %#x", + le16_to_cpu(file_de->file_checksum), checksum)) need_delete = true; *skip_dentries = 1; goto skip_dset; @@ -774,17 +782,22 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, if (!node) return -ENOMEM; - for (i = 2; i <= MIN(file_de->file_num_ext, 1 + MAX_NAME_DENTRIES); i++) { + name_de_count = DIV_ROUND_UP(stream_de->stream_name_len, ENTRY_NAME_MAX); + for (i = 2; i <= MIN(name_de_count + 1, file_de->file_num_ext); i++) { ret = exfat_de_iter_get(iter, i, &dentry); if (ret || dentry->type != EXFAT_NAME) { - if (i > 2 && repair_file_ask(iter, NULL, ER_DE_NAME, - "failed to get name dentry")) { - exfat_de_iter_get_dirty(iter, 0, &file_de); - file_de->file_num_ext = i - 1; + if (repair_file_ask(iter, NULL, ER_DE_NAME, + "failed to get name dentry")) { + if (i == 2) { + need_delete = 1; + *skip_dentries = i + 1; + goto skip_dset; + } break; + } else { + *skip_dentries = i + 1; + goto skip_dset; } - *skip_dentries = i + 1; - goto skip_dset; } memcpy(node->name + @@ -793,9 +806,14 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, } ret = check_name_dentry_set(iter, node); - if (ret) { + if (ret < 0) { *skip_dentries = file_de->file_num_ext + 1; goto skip_dset; + } else if (ret) { + exfat_de_iter_get(iter, 1, &stream_de); + if (DIV_ROUND_UP(stream_de->stream_name_len, ENTRY_NAME_MAX) != + name_de_count) + i = DIV_ROUND_UP(stream_de->stream_name_len, ENTRY_NAME_MAX) + 2; } if (file_de->file_num_ext == 2 && stream_de->stream_name_len <= 2) { @@ -807,6 +825,69 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, } } + for (j = i; i <= file_de->file_num_ext; i++) { + exfat_de_iter_get(iter, i, &dentry); + if (dentry->type == EXFAT_VENDOR_EXT || + dentry->type == EXFAT_VENDOR_ALLOC) { + char zeroes[EXFAT_GUID_LEN] = {0}; + /* + * Vendor GUID should not be zero, But Windows fsck + * also does not check and fix it. + */ + if (!memcmp(dentry->dentry.vendor_ext.guid, + zeroes, EXFAT_GUID_LEN)) + repair_file_ask(iter, NULL, ER_VENDOR_GUID, + "Vendor Extension has zero filled GUID"); + if (dentry->type == EXFAT_VENDOR_ALLOC) { + struct exfat_inode *vendor_node; + + /* verify cluster chain */ + vendor_node = exfat_alloc_inode(0); + if (!vendor_node) { + *skip_dentries = i + i; + goto skip_dset; + } + vendor_node->first_clus = + le32_to_cpu(dentry->dentry.vendor_alloc.start_clu); + vendor_node->is_contiguous = ((dentry->dentry.vendor_alloc.flags + & EXFAT_SF_CONTIGUOUS) != 0); + vendor_node->size = + le64_to_cpu(dentry->dentry.vendor_alloc.size); + if (check_clus_chain(iter, i, vendor_node) < 0) { + exfat_free_inode(vendor_node); + *skip_dentries = i + 1; + goto skip_dset; + } + if (vendor_node->size == 0 && + vendor_node->is_contiguous) { + exfat_de_iter_get_dirty(iter, i, &dentry); + dentry->stream_flags &= ~EXFAT_SF_CONTIGUOUS; + + } + exfat_free_inode(vendor_node); + } + + if (need_copy_up) { + struct exfat_dentry *src_de; + + exfat_de_iter_get_dirty(iter, j, &src_de); + memcpy(src_de, dentry, sizeof(struct exfat_dentry)); + } + j++; + } else { + if (need_copy_up) { + continue; + } else if (repair_file_ask(iter, NULL, ER_DE_UNKNOWN, + "unknown entry type %#x", dentry->type)) { + j = i; + need_copy_up = true; + } else { + *skip_dentries = i + 1; + goto skip_dset; + } + } + } + node->first_clus = le32_to_cpu(stream_de->stream_start_clu); node->is_contiguous = ((stream_de->stream_flags & EXFAT_SF_CONTIGUOUS) != 0); @@ -827,6 +908,18 @@ static int read_file_dentry_set(struct exfat_de_iter *iter, } } + if (file_de->file_num_ext != j - 1) { + if (repair_file_ask(iter, node, ER_DE_SECONDARY_COUNT, + "SecondaryCount %d is different with %d", + file_de->file_num_ext, j - 1)) { + exfat_de_iter_get_dirty(iter, 0, &file_de); + file_de->file_num_ext = j - 1; + } else { + *skip_dentries = file_de->file_num_ext + 1; + goto skip_dset; + } + } + *skip_dentries = (file_de->file_num_ext + 1); *new_node = node; return 0; @@ -987,7 +1080,7 @@ static int read_upcase_table(struct exfat *exfat) goto out; } - upcase = (__le16 *)malloc(size); + upcase = malloc(size); if (!upcase) { exfat_err("failed to allocate upcase table\n"); retval = -ENOMEM; @@ -1016,8 +1109,7 @@ static int read_upcase_table(struct exfat *exfat) DIV_ROUND_UP(le64_to_cpu(dentry->upcase_size), exfat->clus_size)); - exfat->upcase_table = calloc(1, - sizeof(uint16_t) * EXFAT_UPCASE_TABLE_CHARS); + exfat->upcase_table = calloc(EXFAT_UPCASE_TABLE_CHARS, sizeof(uint16_t)); if (!exfat->upcase_table) { retval = -EIO; goto out; @@ -1101,9 +1193,7 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir) if (IS_EXFAT_DELETED(dentry->type)) break; if (repair_file_ask(de_iter, NULL, ER_DE_UNKNOWN, - "unknown entry type %#x at %07" PRIx64, - dentry->type, - exfat_de_iter_file_offset(de_iter))) { + "unknown entry type %#x", dentry->type)) { struct exfat_dentry *dentry; exfat_de_iter_get_dirty(de_iter, 0, &dentry); @@ -1248,7 +1338,6 @@ static int exfat_root_dir_check(struct exfat *exfat) err = exfat_read_volume_label(exfat); if (err && err != EOF) exfat_err("failed to read volume label\n"); - err = 0; err = read_bitmap(exfat); if (err) { @@ -1546,9 +1635,7 @@ int main(int argc, char * const argv[]) goto err; } - exfat_fsck.buffer_desc = exfat_alloc_buffer(2, - exfat_fsck.exfat->clus_size, - exfat_fsck.exfat->sect_size); + exfat_fsck.buffer_desc = exfat_alloc_buffer(exfat_fsck.exfat); if (!exfat_fsck.buffer_desc) { ret = -ENOMEM; goto err; @@ -1608,7 +1695,7 @@ err: exit_code = FSCK_EXIT_NO_ERRORS; if (exfat_fsck.buffer_desc) - exfat_free_buffer(exfat_fsck.buffer_desc, 2); + exfat_free_buffer(exfat_fsck.exfat, exfat_fsck.buffer_desc); if (exfat_fsck.exfat) exfat_free_exfat(exfat_fsck.exfat); close(bd.dev_fd); diff --git a/fsck/repair.c b/fsck/repair.c index f983ee1..6179b65 100644 --- a/fsck/repair.c +++ b/fsck/repair.c @@ -48,7 +48,7 @@ static struct exfat_repair_problem problems[] = { {ER_DE_CHECKSUM, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0}, {ER_DE_UNKNOWN, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0}, {ER_DE_FILE, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0}, - {ER_DE_SECONDARY_COUNT, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0}, + {ER_DE_SECONDARY_COUNT, ERF_PREEN_YES, ERP_FIX, 0, 0, 0}, {ER_DE_STREAM, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0}, {ER_DE_NAME, ERF_PREEN_YES, ERP_DELETE, 0, 0, 0}, {ER_DE_NAME_HASH, ERF_PREEN_YES, ERP_FIX, 0, 0, 0}, @@ -62,6 +62,7 @@ static struct exfat_repair_problem problems[] = { {ER_FILE_LARGER_SIZE, ERF_PREEN_YES, ERP_TRUNCATE, 0, 0, 0}, {ER_FILE_DUPLICATED_CLUS, ERF_PREEN_YES, ERP_TRUNCATE, 0, 0, 0}, {ER_FILE_ZERO_NOFAT, ERF_PREEN_YES, ERP_FIX, 0, 0, 0}, + {ER_VENDOR_GUID, ERF_DEFAULT_NO, ERP_FIX, 0, 0, 0}, }; static struct exfat_repair_problem *find_problem(er_problem_code_t prcode) @@ -245,7 +246,7 @@ ask_again: char *rename = NULL; __u16 hash; struct exfat_dentry *dentry; - int ret, i; + int ret; switch (num) { case 1: @@ -280,15 +281,8 @@ ask_again: exfat_de_iter_get_dirty(iter, 1, &dentry); dentry->stream_name_len = (__u8)ret; dentry->stream_name_hash = cpu_to_le16(hash); + return 1; - exfat_de_iter_get_dirty(iter, 0, &dentry); - i = dentry->file_num_ext; - dentry->file_num_ext = 2; - - for (; i > 2; i--) { - exfat_de_iter_get_dirty(iter, i, &dentry); - dentry->type &= EXFAT_DELETE; - } } return 0; diff --git a/fsck/repair.h b/fsck/repair.h index 634cc49..f1cde24 100644 --- a/fsck/repair.h +++ b/fsck/repair.h @@ -26,6 +26,7 @@ #define ER_FILE_LARGER_SIZE 0x00002005 #define ER_FILE_DUPLICATED_CLUS 0x00002006 #define ER_FILE_ZERO_NOFAT 0x00002007 +#define ER_VENDOR_GUID 0x00003001 typedef unsigned int er_problem_code_t; struct exfat_fsck; diff --git a/include/exfat_dir.h b/include/exfat_dir.h index d450c61..6a3acfd 100644 --- a/include/exfat_dir.h +++ b/include/exfat_dir.h @@ -16,7 +16,7 @@ struct buffer_desc; struct exfat_de_iter { struct exfat *exfat; struct exfat_inode *parent; - struct buffer_desc *buffer_desc; /* cluster * 2 */ + struct buffer_desc *buffer_desc; __u32 ra_next_clus; unsigned int ra_begin_offset; unsigned int ra_partial_size; diff --git a/include/exfat_fs.h b/include/exfat_fs.h index d35b12c..e120707 100644 --- a/include/exfat_fs.h +++ b/include/exfat_fs.h @@ -45,7 +45,8 @@ struct exfat { unsigned int disk_bitmap_size; __u16 *upcase_table; clus_t start_clu; - char *zero_cluster; + unsigned int buffer_count; + struct buffer_desc *lookup_buffer; /* for dentry set lookup */ }; struct exfat_dentry_loc { @@ -64,7 +65,7 @@ struct buffer_desc { __u32 p_clus; unsigned int offset; char *buffer; - char *dirty; + char dirty[EXFAT_BITMAP_SIZE(4 * KB / 512)]; }; struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs); @@ -82,7 +83,11 @@ int exfat_resolve_path(struct path_resolve_ctx *ctx, struct exfat_inode *child); int exfat_resolve_path_parent(struct path_resolve_ctx *ctx, struct exfat_inode *parent, struct exfat_inode *child); -struct buffer_desc *exfat_alloc_buffer(int count, - unsigned int clu_size, unsigned int sect_size); -void exfat_free_buffer(struct buffer_desc *bd, int count); +struct buffer_desc *exfat_alloc_buffer(struct exfat *exfat); +void exfat_free_buffer(const struct exfat *exfat, struct buffer_desc *bd); + +static inline unsigned int exfat_get_read_size(const struct exfat *exfat) +{ + return MIN(exfat->clus_size, 4 * KB); +} #endif diff --git a/include/exfat_ondisk.h b/include/exfat_ondisk.h index 42cdadf..2137226 100644 --- a/include/exfat_ondisk.h +++ b/include/exfat_ondisk.h @@ -41,6 +41,7 @@ #define MAX_EXFAT_DENTRIES 8388608 #define MIN_FILE_DENTRIES 3 #define MAX_NAME_DENTRIES 17 +#define MAX_EXT_DENTRIES 0xFF /* dentry types */ #define MSDOS_DELETED 0xE5 /* deleted mark */ @@ -60,6 +61,8 @@ #define EXFAT_STREAM 0xC0 /* stream entry */ #define EXFAT_NAME 0xC1 /* file name entry */ #define EXFAT_ACL 0xC2 /* stream entry */ +#define EXFAT_VENDOR_EXT 0xE0 +#define EXFAT_VENDOR_ALLOC 0xE1 /* checksum types */ #define CS_DIR_ENTRY 0 @@ -134,7 +137,7 @@ struct pbr { }; #define VOLUME_LABEL_MAX_LEN 11 -#define VOLUME_GUID_LEN 16 +#define EXFAT_GUID_LEN 16 #define ENTRY_NAME_MAX 15 struct exfat_dentry { @@ -196,9 +199,22 @@ struct exfat_dentry { __u8 num_ext; __le16 checksum; __u16 flags; - __u8 guid[VOLUME_GUID_LEN]; + __u8 guid[EXFAT_GUID_LEN]; __u8 reserved[10]; } __attribute__((packed)) guid; /* volume GUID directory entry */ + struct { + __u8 flags; + __u8 guid[EXFAT_GUID_LEN]; + __u8 vendor_defined[14]; + } __attribute__((packed)) vendor_ext ; /* vendor extension entry */ + struct { + __u8 flags; + __u8 guid[EXFAT_GUID_LEN]; + __u8 vendor_defined[2]; + __le32 start_clu; + __le64 size; + } __attribute__((packed)) vendor_alloc; /* vendor allocation entry */ + } __attribute__((packed)) dentry; } __attribute__((packed)); diff --git a/include/libexfat.h b/include/libexfat.h index 9e853a8..2ed9aa7 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -80,6 +80,7 @@ struct exfat_blk_dev { struct exfat_user_input { char dev_name[255]; bool writeable; + unsigned int sector_size; unsigned int cluster_size; unsigned int sec_per_clu; unsigned int boundary_align; @@ -113,6 +114,9 @@ typedef __u32 bitmap_t; #define BITMAP_SET(bmap, bit) \ (((bitmap_t *)(bmap))[BIT_ENTRY(bit)] |= BIT_MASK(bit)) +#define BITMAP_CLEAR(bmap, bit) \ + (((bitmap_t *)(bmap))[BIT_ENTRY(bit)] &= ~BIT_MASK(bit)) + static inline bool exfat_bitmap_get(char *bmap, clus_t c) { clus_t cc = c - EXFAT_FIRST_CLUSTER; @@ -150,6 +154,7 @@ int exfat_get_blk_dev_info(struct exfat_user_input *ui, struct exfat_blk_dev *bd); ssize_t exfat_read(int fd, void *buf, size_t size, off_t offset); ssize_t exfat_write(int fd, void *buf, size_t size, off_t offset); +ssize_t exfat_write_zero(int fd, size_t size, off_t offset); size_t exfat_utf16_len(const __le16 *str, size_t max_size); ssize_t exfat_utf16_enc(const char *in_str, __u16 *out_str, size_t out_size); @@ -184,6 +189,7 @@ int exfat_o2c(struct exfat *exfat, off_t device_offset, bool exfat_heap_clus(struct exfat *exfat, clus_t clus); int exfat_root_clus_count(struct exfat *exfat); int read_boot_sect(struct exfat_blk_dev *bdev, struct pbr **bs); +int exfat_parse_ulong(const char *s, unsigned long *out); /* * Exfat Print diff --git a/include/version.h b/include/version.h index f3cf9e2..bef66aa 100644 --- a/include/version.h +++ b/include/version.h @@ -5,6 +5,6 @@ #ifndef _VERSION_H -#define EXFAT_PROGS_VERSION "1.2.2" +#define EXFAT_PROGS_VERSION "1.2.3" #endif /* !_VERSION_H */ diff --git a/label/label.c b/label/label.c index 8cd5748..2780143 100644 --- a/label/label.c +++ b/label/label.c @@ -42,6 +42,7 @@ int main(int argc, char *argv[]) bool version_only = false; int serial_mode = 0; int flags = 0; + unsigned long volume_serial; init_user_input(&ui); @@ -92,7 +93,17 @@ int main(int argc, char *argv[]) if (flags == EXFAT_GET_VOLUME_SERIAL) { ret = exfat_show_volume_serial(bd.dev_fd); } else if (flags == EXFAT_SET_VOLUME_SERIAL) { - ui.volume_serial = strtoul(argv[3], NULL, 0); + ret = exfat_parse_ulong(argv[3], &volume_serial); + if (volume_serial > UINT_MAX) + ret = -ERANGE; + + + if (ret < 0) { + exfat_err("invalid serial number(%s)\n", argv[3]); + goto close_fd_out; + } + + ui.volume_serial = volume_serial; ret = exfat_set_volume_serial(&bd, &ui); } } else { @@ -105,7 +116,6 @@ int main(int argc, char *argv[]) exfat = exfat_alloc_exfat(&bd, bs); if (!exfat) { - free(bs); ret = -ENOMEM; goto close_fd_out; } diff --git a/lib/exfat_dir.c b/lib/exfat_dir.c index 98e820f..7b99af1 100644 --- a/lib/exfat_dir.c +++ b/lib/exfat_dir.c @@ -27,6 +27,12 @@ static struct path_resolve_ctx path_resolve_ctx; ##__VA_ARGS__); \ }) +static inline struct buffer_desc *exfat_de_iter_get_buffer( + struct exfat_de_iter *iter, unsigned int block) +{ + return &iter->buffer_desc[block % iter->exfat->buffer_count]; +} + static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block) { off_t device_offset; @@ -34,10 +40,10 @@ static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block) struct buffer_desc *desc; unsigned int i; - desc = &iter->buffer_desc[block & 0x01]; + desc = exfat_de_iter_get_buffer(iter, block); for (i = 0; i < iter->read_size / iter->write_size; i++) { - if (desc->dirty[i]) { + if (BITMAP_GET(desc->dirty, i)) { device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset; if (exfat_write(exfat->blk_dev->dev_fd, @@ -46,7 +52,7 @@ static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block) device_offset + i * iter->write_size) != (ssize_t)iter->write_size) return -EIO; - desc->dirty[i] = 0; + BITMAP_CLEAR(desc->dirty, i); } } return 0; @@ -169,7 +175,7 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block) off_t device_offset; ssize_t ret; - desc = &iter->buffer_desc[block & 0x01]; + desc = exfat_de_iter_get_buffer(iter, block); if (block == 0) { desc->p_clus = iter->parent->first_clus; desc->offset = 0; @@ -183,7 +189,7 @@ static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block) if (block > iter->parent->size / iter->read_size) return EOF; - prev_desc = &iter->buffer_desc[(block-1) & 0x01]; + prev_desc = exfat_de_iter_get_buffer(iter, block - 1); if (prev_desc->offset + 2 * iter->read_size <= exfat->clus_size) { desc->p_clus = prev_desc->p_clus; @@ -225,7 +231,7 @@ int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat, iter->exfat = exfat; iter->parent = dir; iter->write_size = exfat->sect_size; - iter->read_size = exfat->clus_size <= 4*KB ? exfat->clus_size : 4*KB; + iter->read_size = exfat_get_read_size(exfat); if (exfat->clus_size <= 32 * KB) iter->ra_partial_size = MAX(4 * KB, exfat->clus_size / 2); else @@ -257,6 +263,7 @@ int exfat_de_iter_get(struct exfat_de_iter *iter, off_t next_de_file_offset; ssize_t ret; unsigned int block; + struct buffer_desc *bd; next_de_file_offset = iter->de_file_offset + ith * sizeof(struct exfat_dentry); @@ -265,9 +272,6 @@ int exfat_de_iter_get(struct exfat_de_iter *iter, if (next_de_file_offset + sizeof(struct exfat_dentry) > iter->parent->size) return EOF; - /* the dentry must be in current, or next block which will be read */ - if (block > iter->de_file_offset / iter->read_size + 1) - return -ERANGE; /* read next cluster if needed */ if (next_de_file_offset >= iter->next_read_offset) { @@ -280,8 +284,8 @@ int exfat_de_iter_get(struct exfat_de_iter *iter, if (ith + 1 > iter->max_skip_dentries) iter->max_skip_dentries = ith + 1; - *dentry = (struct exfat_dentry *) - (iter->buffer_desc[block & 0x01].buffer + + bd = exfat_de_iter_get_buffer(iter, block); + *dentry = (struct exfat_dentry *)(bd->buffer + next_de_file_offset % iter->read_size); return 0; } @@ -292,6 +296,7 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter, off_t next_file_offset; unsigned int block; int ret, sect_idx; + struct buffer_desc *bd; ret = exfat_de_iter_get(iter, ith, dentry); if (!ret) { @@ -300,7 +305,8 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter, block = (unsigned int)(next_file_offset / iter->read_size); sect_idx = (int)((next_file_offset % iter->read_size) / iter->write_size); - iter->buffer_desc[block & 0x01].dirty[sect_idx] = 1; + bd = exfat_de_iter_get_buffer(iter, block); + BITMAP_SET(bd->dirty, sect_idx); } return ret; @@ -308,8 +314,11 @@ int exfat_de_iter_get_dirty(struct exfat_de_iter *iter, int exfat_de_iter_flush(struct exfat_de_iter *iter) { - if (write_block(iter, 0) || write_block(iter, 1)) - return -EIO; + unsigned int i; + + for (i = 0; i < iter->exfat->buffer_count; i++) + if (write_block(iter, i)) + return -EIO; return 0; } @@ -333,7 +342,7 @@ off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter) return EOF; block = iter->de_file_offset / iter->read_size; - bd = &iter->buffer_desc[block & 0x01]; + bd = exfat_de_iter_get_buffer(iter, block); return exfat_c2o(iter->exfat, bd->p_clus) + bd->offset + iter->de_file_offset % iter->read_size; } @@ -359,9 +368,12 @@ int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent, int dentry_count, empty_dentry_count = 0; int retval; - bd = exfat_alloc_buffer(2, exfat->clus_size, exfat->sect_size); - if (!bd) - return -ENOMEM; + if (!exfat->lookup_buffer) { + exfat->lookup_buffer = exfat_alloc_buffer(exfat); + if (!exfat->lookup_buffer) + return -ENOMEM; + } + bd = exfat->lookup_buffer; retval = exfat_de_iter_init(&de_iter, exfat, parent, bd); if (retval == EOF || retval) @@ -441,8 +453,6 @@ out: filter->out.file_offset = exfat_de_iter_file_offset(&de_iter); filter->out.dev_offset = EOF; } - if (bd) - exfat_free_buffer(bd, 2); return retval; } @@ -613,7 +623,7 @@ int exfat_build_file_dentry_set(struct exfat *exfat, const char *name, name_len = retval / 2; dcount = 2 + DIV_ROUND_UP(name_len, ENTRY_NAME_MAX); - dset = calloc(1, dcount * DENTRY_SIZE); + dset = calloc(dcount, DENTRY_SIZE); if (!dset) return -ENOMEM; @@ -841,9 +851,8 @@ static int exfat_alloc_cluster(struct exfat *exfat, struct exfat_inode *inode, return -EIO; /* zero out the new cluster */ - if (exfat_write(exfat->blk_dev->dev_fd, exfat->zero_cluster, - exfat->clus_size, exfat_c2o(exfat, *new_clu)) != - (ssize_t)exfat->clus_size) { + if (exfat_write_zero(exfat->blk_dev->dev_fd, exfat->clus_size, + exfat_c2o(exfat, *new_clu))) { exfat_err("failed to fill new cluster with zeroes\n"); return -EIO; } diff --git a/lib/exfat_fs.c b/lib/exfat_fs.c index d563c61..be76e59 100644 --- a/lib/exfat_fs.c +++ b/lib/exfat_fs.c @@ -22,7 +22,7 @@ struct exfat_inode *exfat_alloc_inode(__u16 attr) int size; size = offsetof(struct exfat_inode, name) + NAME_BUFFER_SIZE; - node = (struct exfat_inode *)calloc(1, size); + node = calloc(1, size); if (!node) { exfat_err("failed to allocate exfat_node\n"); return NULL; @@ -117,8 +117,8 @@ void exfat_free_exfat(struct exfat *exfat) free(exfat->upcase_table); if (exfat->root) exfat_free_inode(exfat->root); - if (exfat->zero_cluster) - free(exfat->zero_cluster); + if (exfat->lookup_buffer) + free(exfat->lookup_buffer); free(exfat); } } @@ -127,7 +127,7 @@ struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs) { struct exfat *exfat; - exfat = (struct exfat *)calloc(1, sizeof(*exfat)); + exfat = calloc(1, sizeof(*exfat)); if (!exfat) return NULL; @@ -139,32 +139,26 @@ struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs) exfat->sect_size = EXFAT_SECTOR_SIZE(bs); /* TODO: bitmap could be very large. */ - exfat->alloc_bitmap = (char *)calloc(1, - EXFAT_BITMAP_SIZE(exfat->clus_count)); + exfat->alloc_bitmap = calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count)); if (!exfat->alloc_bitmap) { exfat_err("failed to allocate bitmap\n"); goto err; } - exfat->ohead_bitmap = - calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count)); + exfat->ohead_bitmap = calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count)); if (!exfat->ohead_bitmap) { exfat_err("failed to allocate bitmap\n"); goto err; } - exfat->disk_bitmap = - calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count)); + exfat->disk_bitmap = calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count)); if (!exfat->disk_bitmap) { exfat_err("failed to allocate bitmap\n"); goto err; } - exfat->zero_cluster = calloc(1, exfat->clus_size); - if (!exfat->zero_cluster) { - exfat_err("failed to allocate a zero-filled cluster buffer\n"); - goto err; - } + exfat->buffer_count = ((MAX_EXT_DENTRIES + 1) * DENTRY_SIZE) / + exfat_get_read_size(exfat) + 1; exfat->start_clu = EXFAT_FIRST_CLUSTER; return exfat; @@ -173,39 +167,36 @@ err: return NULL; } -struct buffer_desc *exfat_alloc_buffer(int count, - unsigned int clu_size, unsigned int sect_size) +struct buffer_desc *exfat_alloc_buffer(struct exfat *exfat) { struct buffer_desc *bd; - int i; + unsigned int i; + unsigned int read_size = exfat_get_read_size(exfat); - bd = (struct buffer_desc *)calloc(sizeof(*bd), count); + bd = calloc(exfat->buffer_count, sizeof(*bd)); if (!bd) return NULL; - for (i = 0; i < count; i++) { - bd[i].buffer = (char *)malloc(clu_size); + for (i = 0; i < exfat->buffer_count; i++) { + bd[i].buffer = malloc(read_size); if (!bd[i].buffer) goto err; - bd[i].dirty = (char *)calloc(clu_size / sect_size, 1); - if (!bd[i].dirty) - goto err; + + memset(&bd[i].dirty, 0, sizeof(bd[i].dirty)); } return bd; err: - exfat_free_buffer(bd, count); + exfat_free_buffer(exfat, bd); return NULL; } -void exfat_free_buffer(struct buffer_desc *bd, int count) +void exfat_free_buffer(const struct exfat *exfat, struct buffer_desc *bd) { - int i; + unsigned int i; - for (i = 0; i < count; i++) { + for (i = 0; i < exfat->buffer_count; i++) { if (bd[i].buffer) free(bd[i].buffer); - if (bd[i].dirty) - free(bd[i].dirty); } free(bd); } diff --git a/lib/libexfat.c b/lib/libexfat.c index d7b8344..0bcb4a4 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -187,7 +187,9 @@ int exfat_get_blk_dev_info(struct exfat_user_input *ui, if (!ui->boundary_align) ui->boundary_align = DEFAULT_BOUNDARY_ALIGNMENT; - if (ioctl(fd, BLKSSZGET, &bd->sector_size) < 0) + if (ui->sector_size) + bd->sector_size = ui->sector_size; + else if (ioctl(fd, BLKSSZGET, &bd->sector_size) < 0) bd->sector_size = DEFAULT_SECTOR_SIZE; bd->sector_size_bits = sector_size_bits(bd->sector_size); bd->num_sectors = blk_dev_size / bd->sector_size; @@ -218,6 +220,24 @@ ssize_t exfat_write(int fd, void *buf, size_t size, off_t offset) return pwrite(fd, buf, size, offset); } +ssize_t exfat_write_zero(int fd, size_t size, off_t offset) +{ + const char zero_buf[4 * KB] = {0}; + + lseek(fd, offset, SEEK_SET); + + while (size > 0) { + int iter_size = MIN(size, sizeof(zero_buf)); + + if (iter_size != write(fd, zero_buf, iter_size)) + return -EIO; + + size -= iter_size; + } + + return 0; +} + size_t exfat_utf16_len(const __le16 *str, size_t max_size) { size_t i = 0; @@ -355,7 +375,7 @@ off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd) unsigned int cluster_size, sector_size; off_t root_clu_off; - bs = (struct pbr *)malloc(EXFAT_MAX_SECTOR_SIZE); + bs = malloc(EXFAT_MAX_SECTOR_SIZE); if (!bs) { exfat_err("failed to allocate memory\n"); return -ENOMEM; @@ -464,7 +484,7 @@ int exfat_set_volume_label(struct exfat *exfat, char *label_input) dcount = filter.out.dentry_count; memset(pvol->vol_label, 0, sizeof(pvol->vol_label)); } else { - pvol = calloc(sizeof(struct exfat_dentry), 1); + pvol = calloc(1, sizeof(struct exfat_dentry)); if (!pvol) return -ENOMEM; @@ -509,7 +529,7 @@ static int set_guid(__u8 *guid, const char *input) int i, j, zero_len = 0; int len = strlen(input); - if (len != VOLUME_GUID_LEN * 2 && len != VOLUME_GUID_LEN * 2 + 4) { + if (len != EXFAT_GUID_LEN * 2 && len != EXFAT_GUID_LEN * 2 + 4) { exfat_err("invalid format for volume guid\n"); return -EINVAL; } @@ -523,7 +543,7 @@ static int set_guid(__u8 *guid, const char *input) ch -= 'a' - 0xA; else if (ch >= 'A' && ch <= 'F') ch -= 'A' - 0xA; - else if (ch == '-' && len == VOLUME_GUID_LEN * 2 + 4 && + else if (ch == '-' && len == EXFAT_GUID_LEN * 2 + 4 && (i == 8 || i == 13 || i == 18 || i == 23)) continue; else { @@ -542,7 +562,7 @@ static int set_guid(__u8 *guid, const char *input) zero_len++; } - if (zero_len == VOLUME_GUID_LEN * 2) { + if (zero_len == EXFAT_GUID_LEN * 2) { exfat_err("%s is invalid for volume GUID\n", input); return -EINVAL; } @@ -814,7 +834,7 @@ int exfat_set_volume_serial(struct exfat_blk_dev *bd, } bd->sector_size = 1 << ppbr->bsx.sect_size_bits; - ppbr->bsx.vol_serial = ui->volume_serial; + ppbr->bsx.vol_serial = cpu_to_le32(ui->volume_serial); /* update main boot sector */ ret = exfat_write_sector(bd, (char *)ppbr, BOOT_SEC_IDX); @@ -981,6 +1001,10 @@ int read_boot_sect(struct exfat_blk_dev *bdev, struct pbr **bs) unsigned int sect_size, clu_size; pbr = malloc(sizeof(struct pbr)); + if (!pbr) { + exfat_err("failed to allocate memory\n"); + return -ENOMEM; + } if (exfat_read(bdev->dev_fd, pbr, sizeof(*pbr), 0) != (ssize_t)sizeof(*pbr)) { @@ -1017,3 +1041,18 @@ err: free(pbr); return err; } + +int exfat_parse_ulong(const char *s, unsigned long *out) +{ + char *endptr; + + *out = strtoul(s, &endptr, 0); + + if (errno) + return -errno; + + if (s == endptr || *endptr != '\0') + return -EINVAL; + + return 0; +} diff --git a/manpages/mkfs.exfat.8 b/manpages/mkfs.exfat.8 index 76a0065..930ec8f 100644 --- a/manpages/mkfs.exfat.8 +++ b/manpages/mkfs.exfat.8 @@ -7,6 +7,9 @@ mkfs.exfat \- create an exFAT filesystem .B \-b .I boundary_alignment ] [ +.B \-s +.I sector_size +] [ .B \-c .I cluster_size ] [ @@ -72,6 +75,14 @@ _ .TE The default is always 1 MiB. .TP +.BR \-s ", " \-\-sector\-size =\fIsize\fR +Specifies the sector size of the exFAT file system. +The \fIsize\fR argument is specified in bytes or may be specified with +\fBk\fR/\fBK\fR suffix for kibibytes and must either 512, 1024, 2048 or 4096 +bytes. +The default value is the sector size reported by the device, or 512 bytes if the +device sector size cannot be determined. +.TP .BR \-c ", " \-\-cluster\-size =\fIsize\fR Specifies the cluster size of the exFAT file system. The \fIsize\fR argument is specified in bytes or may be specified with diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 773c64e..f9286c1 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -312,15 +312,27 @@ static int exfat_create_fat_table(struct exfat_blk_dev *bd, static int exfat_create_bitmap(struct exfat_blk_dev *bd) { char *bitmap; - unsigned int i, nbytes; + unsigned int full_bytes, rem_bits, zero_offset; + unsigned int nbytes; - bitmap = calloc(round_up(finfo.bitmap_byte_len, sizeof(bitmap_t)), - sizeof(*bitmap)); + bitmap = malloc(finfo.bitmap_byte_len); if (!bitmap) return -1; - for (i = EXFAT_FIRST_CLUSTER; i < finfo.used_clu_cnt; i++) - exfat_bitmap_set(bitmap, i); + full_bytes = finfo.used_clu_cnt / 8; + rem_bits = finfo.used_clu_cnt % 8; + zero_offset = full_bytes; + + memset(bitmap, 0xff, full_bytes); + + if (rem_bits != 0) { + bitmap[full_bytes] = (1 << rem_bits) - 1; + ++zero_offset; + } + + if (zero_offset < finfo.bitmap_byte_len) + memset(bitmap + zero_offset, 0, finfo.bitmap_byte_len - zero_offset); + nbytes = pwrite(bd->dev_fd, bitmap, finfo.bitmap_byte_len, finfo.bitmap_byte_off); if (nbytes != finfo.bitmap_byte_len) { @@ -386,6 +398,7 @@ static void usage(void) fputs("Usage: mkfs.exfat\n" "\t-L | --volume-label=label Set volume label\n" "\t-U | --volume-guid=guid Set volume GUID\n" + "\t-s | --sector-size=size(or suffixed by 'K') Specify sector size\n" "\t-c | --cluster-size=size(or suffixed by 'K' or 'M') Specify cluster size\n" "\t-b | --boundary-align=size(or suffixed by 'K' or 'M') Specify boundary alignment\n" "\t --pack-bitmap Move bitmap into FAT segment\n" @@ -404,6 +417,7 @@ static void usage(void) static const struct option opts[] = { {"volume-label", required_argument, NULL, 'L' }, {"volume-guid", required_argument, NULL, 'U' }, + {"sector-size", required_argument, NULL, 's' }, {"cluster-size", required_argument, NULL, 'c' }, {"boundary-align", required_argument, NULL, 'b' }, {"pack-bitmap", no_argument, NULL, PACK_BITMAP }, @@ -453,7 +467,9 @@ static int exfat_build_mkfs_info(struct exfat_blk_dev *bd, struct exfat_user_input *ui) { unsigned long long total_clu_cnt; + unsigned long long max_clusters; int clu_len; + int num_fats = 1; if (ui->cluster_size < bd->sector_size) { exfat_err("cluster size (%u bytes) is smaller than sector size (%u bytes)\n", @@ -467,14 +483,17 @@ static int exfat_build_mkfs_info(struct exfat_blk_dev *bd, } finfo.fat_byte_off = round_up(bd->offset + 24 * bd->sector_size, ui->boundary_align) - bd->offset; + + max_clusters = (bd->size - finfo.fat_byte_off - 8 * num_fats - 1) / + (ui->cluster_size + 4 * num_fats) + 1; /* Prevent integer overflow when computing the FAT length */ - if (bd->num_clusters > UINT32_MAX / 4) { + if (max_clusters > UINT_MAX / 4 - 2) { exfat_err("cluster size (%u bytes) is too small\n", ui->cluster_size); return -1; } - finfo.fat_byte_len = round_up((bd->num_clusters * 4), ui->cluster_size); + finfo.fat_byte_len = round_up((max_clusters + 2) * 4, bd->sector_size); finfo.clu_byte_off = round_up(bd->offset + finfo.fat_byte_off + - finfo.fat_byte_len, ui->boundary_align) - bd->offset; + finfo.fat_byte_len * num_fats, ui->boundary_align) - bd->offset; if (bd->size <= finfo.clu_byte_off) { exfat_err("boundary alignment is too big\n"); return -1; @@ -508,35 +527,21 @@ static int exfat_build_mkfs_info(struct exfat_blk_dev *bd, static int exfat_zero_out_disk(struct exfat_blk_dev *bd, struct exfat_user_input *ui) { - int nbytes; + int ret; unsigned long long total_written = 0; - char *buf; - unsigned int chunk_size = ui->cluster_size; unsigned long long size; if (ui->quick) - size = finfo.root_byte_off + chunk_size; + size = finfo.root_byte_off + ui->cluster_size; else size = bd->size; - buf = malloc(chunk_size); - if (!buf) - return -1; - - memset(buf, 0, chunk_size); - lseek(bd->dev_fd, 0, SEEK_SET); - do { - - nbytes = write(bd->dev_fd, buf, chunk_size); - if (nbytes <= 0) { - if (nbytes < 0) - exfat_err("write failed(errno : %d)\n", errno); - break; - } - total_written += nbytes; - } while (total_written < size); + ret = exfat_write_zero(bd->dev_fd, size, 0); + if (ret) { + exfat_err("write failed(errno : %d)\n", errno); + return ret; + } - free(buf); exfat_debug("zero out written size : %llu, disk size : %llu\n", total_written, bd->size); return 0; @@ -629,7 +634,7 @@ int main(int argc, char *argv[]) exfat_err("failed to init locale/codeset\n"); opterr = 0; - while ((c = getopt_long(argc, argv, "n:L:U:c:b:fVqvh", opts, NULL)) != EOF) + while ((c = getopt_long(argc, argv, "n:L:U:s:c:b:fVqvh", opts, NULL)) != EOF) switch (c) { /* * Make 'n' option fallthrough to 'L' option for for backward @@ -650,6 +655,22 @@ int main(int argc, char *argv[]) if (*optarg != '\0' && *optarg != '\r') ui.guid = optarg; break; + case 's': + ret = parse_size(optarg); + if (ret < 0) + goto out; + else if (ret & (ret - 1)) { + exfat_err("sector size(%d) is not a power of 2\n", + ret); + goto out; + } else if ((ret & 0x1e00) == 0) { + exfat_err("sector size(%d) must be 512, 1024, " + "2048 or 4096 bytes\n", + ret); + goto out; + } + ui.sector_size = ret; + break; case 'c': ret = parse_size(optarg); if (ret < 0) @@ -709,6 +730,13 @@ int main(int argc, char *argv[]) usage(); } + if (ui.sector_size && ui.cluster_size && ui.sector_size > ui.cluster_size) { + exfat_err("cluster size (%u bytes) is smaller than sector size (%u bytes)\n", + ui.cluster_size, ui.sector_size); + ret = -1; + goto out; + } + memset(ui.dev_name, 0, sizeof(ui.dev_name)); snprintf(ui.dev_name, sizeof(ui.dev_name), "%s", argv[optind]); diff --git a/tune/tune.c b/tune/tune.c index f883556..4966e0a 100644 --- a/tune/tune.c +++ b/tune/tune.c @@ -49,6 +49,7 @@ int main(int argc, char *argv[]) { int c; int ret = EXIT_FAILURE; + unsigned long volume_serial; struct exfat_blk_dev bd; struct exfat_user_input ui; bool version_only = false; @@ -85,7 +86,17 @@ int main(int argc, char *argv[]) flags = EXFAT_GET_VOLUME_SERIAL; break; case 'I': - ui.volume_serial = strtoul(optarg, NULL, 0); + ret = exfat_parse_ulong(optarg, &volume_serial); + if (volume_serial > UINT_MAX) + ret = -ERANGE; + + + if (ret < 0) { + exfat_err("invalid serial number(%s)\n", optarg); + goto out; + } + + ui.volume_serial = volume_serial; flags = EXFAT_SET_VOLUME_SERIAL; break; case 'V': @@ -129,7 +140,6 @@ int main(int argc, char *argv[]) exfat = exfat_alloc_exfat(&bd, bs); if (!exfat) { - free(bs); ret = -ENOMEM; goto close_fd_out; } |