]> git.sven.stormbind.net Git - sven/exfatprogs.git/commitdiff
New upstream version 1.2.3 upstream/1.2.3
authorSven Hoexter <sven@stormbind.net>
Mon, 27 May 2024 18:35:38 +0000 (20:35 +0200)
committerSven Hoexter <sven@stormbind.net>
Mon, 27 May 2024 18:35:38 +0000 (20:35 +0200)
20 files changed:
Makefile.in
NEWS
configure
dump/dump.c
exfat2img/exfat2img.c
fsck/fsck.c
fsck/repair.c
fsck/repair.h
include/exfat_dir.h
include/exfat_fs.h
include/exfat_ondisk.h
include/libexfat.h
include/version.h
label/label.c
lib/exfat_dir.c
lib/exfat_fs.c
lib/libexfat.c
manpages/mkfs.exfat.8
mkfs/mkfs.c
tune/tune.c

index 01a27d797c373c2043d21442799038c2f64d2cab..914c23f45edfda10f1b5070936f2bb61e7944fe4 100644 (file)
@@ -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 055343af05a1d8d4a027fa41b2af9c82753ebace..a72c9c6ef0ee559701c2eb98b43135d52465c609 100644 (file)
--- 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
 ======================================
 
index 662b6b8be37c70bd151da396963091afa5bf51b6..2bb25c366c26720da32458a9c7345cbac4c93694 100755 (executable)
--- 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\\"
 
index 8c9637616a6cbce1f7679ea02f675b7200be81bd..73a231ab2c9e367837ae49b87833f098819c44f9 100644 (file)
@@ -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);
index 3f835889ad6baacc02dc9cf9b29ac5fd2c2b797f..bd9db444ca82bdea9561d8e2e67bbf1a8d31798b 100644 (file)
@@ -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;
 }
 
index 77272aa0bd991c0e1f9ae1b537e22a89eb4d7fa3..fe8a1220649b65057ecf0de32eb65a33411de9c3 100644 (file)
@@ -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);
index f983ee1e449e85ef68e8de1eb76fb7496a3e9087..6179b65e32157c7c61f646dec058be499226e5e8 100644 (file)
@@ -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;
index 634cc490534b9f42c99395617c422781083ee48e..f1cde24dd5684d95ab793c1ebeba0542ec694705 100644 (file)
@@ -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;
index d450c61000cbc8c9e978c00a1dff7da57561228c..6a3acfdddacbe161e5c696a99a068ca8b759caf4 100644 (file)
@@ -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;
index d35b12c02761642aa0eb6e08319e8807c7b9f809..e120707385af92f83c6017fba4c9fe4de92e58d4 100644 (file)
@@ -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
index 42cdadfbf37ae6f225d6f5097f847b44c45a9780..21372266a3382f5da409dc7e09d076481a6a29a1 100644 (file)
@@ -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));
 
index 9e853a88b92ea4cd97731c8949027d15880d7f5c..2ed9aa719bbdb384586019cb8cbbfa1a2d9e9172 100644 (file)
@@ -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
index f3cf9e221b764251f27e032c731bde962fd4e1f8..bef66aaf42179ad61334546f9f78990a9438b5ae 100644 (file)
@@ -5,6 +5,6 @@
 
 #ifndef _VERSION_H
 
-#define EXFAT_PROGS_VERSION "1.2.2"
+#define EXFAT_PROGS_VERSION "1.2.3"
 
 #endif /* !_VERSION_H */
index 8cd57488a86046e6c1cfb78f242acd46b9e02d0c..278014334aa3ce1021e4580b3bc854ec04c871c7 100644 (file)
@@ -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;
                }
index 98e820f9404e1011a5f78fe9ff2ac153fcd1e3d4..7b99af114c48cc5b6cbba2618b29085b836491f5 100644 (file)
@@ -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;
        }
index d563c6118101a29e7ce29b717c439087c891796c..be76e5974e704a91868c41ad7a3a2392d0bfcd58 100644 (file)
@@ -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);
 }
index d7b8344f760e6dd7ca1815029d5f14ef9ae424f2..0bcb4a42aef34774930c86202afd247ef7fca218 100644 (file)
@@ -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;
+}
index 76a00658db47e0a2d7e4e663f0834ec57a6ab6ec..930ec8f4e2c174f538cb10765b44d4e5f5ff98c5 100644 (file)
@@ -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
index 773c64e1232c04316fe94637afaa89f2261bf8c2..f9286c1d159e92fe7f8bc3682cf1241abeaf4764 100644 (file)
@@ -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]);
 
index f883556c794ed2bcfd82b9837a6c2f7e62298443..4966e0a1783a1815e8d0aef61ab0613f584e2155 100644 (file)
@@ -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;
        }