$(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)
+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
======================================
#! /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>.
#
# 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'
# 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]...
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
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.
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 $@
# Define the identity of the package.
PACKAGE='exfatprogs'
- VERSION='1.2.2'
+ VERSION='1.2.3'
cat >>confdefs.h <<_ACEOF
# 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
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\\"
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);
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;
};
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)
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;
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,
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,
}
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;
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;
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;
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;
}
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;
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 */
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);
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,
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,
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,
}
}
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;
}
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;
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);
{
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;
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;
}
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;
}
/*
uint16_t checksum;
bool valid = true;
- ret = check_clus_chain(iter, node);
+ ret = check_clus_chain(iter, 1, node);
if (ret < 0)
return ret;
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);
struct exfat_dentry *stream_de;
size_t name_len;
__u16 hash;
+ int ret = 0;
exfat_de_iter_get(iter, 1, &stream_de);
"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;
}
"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, };
{
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);
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;
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 +
}
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) {
}
}
+ 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);
}
}
+ 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;
goto out;
}
- upcase = (__le16 *)malloc(size);
+ upcase = malloc(size);
if (!upcase) {
exfat_err("failed to allocate upcase table\n");
retval = -ENOMEM;
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;
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);
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) {
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;
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);
{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},
{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)
char *rename = NULL;
__u16 hash;
struct exfat_dentry *dentry;
- int ret, i;
+ int ret;
switch (num) {
case 1:
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;
#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;
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;
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 {
__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);
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
#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 */
#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
};
#define VOLUME_LABEL_MAX_LEN 11
-#define VOLUME_GUID_LEN 16
+#define EXFAT_GUID_LEN 16
#define ENTRY_NAME_MAX 15
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));
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;
#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;
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);
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
#ifndef _VERSION_H
-#define EXFAT_PROGS_VERSION "1.2.2"
+#define EXFAT_PROGS_VERSION "1.2.3"
#endif /* !_VERSION_H */
bool version_only = false;
int serial_mode = 0;
int flags = 0;
+ unsigned long volume_serial;
init_user_input(&ui);
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 {
exfat = exfat_alloc_exfat(&bd, bs);
if (!exfat) {
- free(bs);
ret = -ENOMEM;
goto close_fd_out;
}
##__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;
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,
device_offset + i * iter->write_size)
!= (ssize_t)iter->write_size)
return -EIO;
- desc->dirty[i] = 0;
+ BITMAP_CLEAR(desc->dirty, i);
}
}
return 0;
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;
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;
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
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);
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) {
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;
}
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) {
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;
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;
}
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;
}
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)
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;
}
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;
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;
}
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;
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);
}
}
{
struct exfat *exfat;
- exfat = (struct exfat *)calloc(1, sizeof(*exfat));
+ exfat = calloc(1, sizeof(*exfat));
if (!exfat)
return NULL;
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;
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);
}
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;
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;
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;
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;
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;
}
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 {
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;
}
}
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);
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)) {
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;
+}
.B \-b
.I boundary_alignment
] [
+.B \-s
+.I sector_size
+] [
.B \-c
.I cluster_size
] [
.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
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) {
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"
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 },
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",
}
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;
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;
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
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)
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]);
{
int c;
int ret = EXIT_FAILURE;
+ unsigned long volume_serial;
struct exfat_blk_dev bd;
struct exfat_user_input ui;
bool version_only = false;
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':
exfat = exfat_alloc_exfat(&bd, bs);
if (!exfat) {
- free(bs);
ret = -ENOMEM;
goto close_fd_out;
}