summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Makefile.in2
-rw-r--r--NEWS24
-rwxr-xr-xconfigure20
-rw-r--r--dump/dump.c4
-rw-r--r--exfat2img/exfat2img.c93
-rw-r--r--fsck/fsck.c183
-rw-r--r--fsck/repair.c14
-rw-r--r--fsck/repair.h1
-rw-r--r--include/exfat_dir.h2
-rw-r--r--include/exfat_fs.h15
-rw-r--r--include/exfat_ondisk.h20
-rw-r--r--include/libexfat.h6
-rw-r--r--include/version.h2
-rw-r--r--label/label.c14
-rw-r--r--lib/exfat_dir.c57
-rw-r--r--lib/exfat_fs.c51
-rw-r--r--lib/libexfat.c53
-rw-r--r--manpages/mkfs.exfat.811
-rw-r--r--mkfs/mkfs.c88
-rw-r--r--tune/tune.c14
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)
diff --git a/NEWS b/NEWS
index 055343a..a72c9c6 100644
--- a/NEWS
+++ b/NEWS
@@ -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
======================================
diff --git a/configure b/configure
index 662b6b8..2bb25c3 100755
--- a/configure
+++ b/configure
@@ -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;
}