New upstream version 1.2.5 upstream/1.2.5
authorSven Hoexter <sven@timegate.de>
Wed, 7 Dec 2016 16:55:33 +0000 (17:55 +0100)
committerSven Hoexter <sven@timegate.de>
Wed, 7 Dec 2016 16:55:33 +0000 (17:55 +0100)
ChangeLog
configure
configure.ac
dump/dumpexfat.8
dump/main.c
fsck/main.c
libexfat/cluster.c
libexfat/node.c
libexfat/time.c

index 7826825..639972e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+1.2.5 (2016-12-05)
+* Added an option for dumpexfat to show file fragments [Daniel Drake].
+* Fixed crash when directory starts with an invalid cluster.
+* Daylight saving time in now properly reflected in file timestamps.
+
 1.2.4 (2016-06-03)
 
 * Fixed wrong files names hashes when upper case table is compressed.
index 3c3b3ab..2ec2bb9 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 Free exFAT implementation 1.2.4.
+# Generated by GNU Autoconf 2.69 for Free exFAT implementation 1.2.5.
 #
 # Report bugs to <relan@users.noreply.github.com>.
 #
@@ -579,8 +579,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='Free exFAT implementation'
 PACKAGE_TARNAME='exfat-utils'
-PACKAGE_VERSION='1.2.4'
-PACKAGE_STRING='Free exFAT implementation 1.2.4'
+PACKAGE_VERSION='1.2.5'
+PACKAGE_STRING='Free exFAT implementation 1.2.5'
 PACKAGE_BUGREPORT='relan@users.noreply.github.com'
 PACKAGE_URL='https://github.com/relan/exfat'
 
@@ -1228,7 +1228,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 Free exFAT implementation 1.2.4 to adapt to many kinds of systems.
+\`configure' configures Free exFAT implementation 1.2.5 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1294,7 +1294,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Free exFAT implementation 1.2.4:";;
+     short | recursive ) echo "Configuration of Free exFAT implementation 1.2.5:";;
    esac
   cat <<\_ACEOF
 
@@ -1386,7 +1386,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Free exFAT implementation configure 1.2.4
+Free exFAT implementation configure 1.2.5
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1441,7 +1441,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 Free exFAT implementation $as_me 1.2.4, which was
+It was created by Free exFAT implementation $as_me 1.2.5, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2304,7 +2304,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='exfat-utils'
- VERSION='1.2.4'
+ VERSION='1.2.5'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -4611,7 +4611,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 Free exFAT implementation $as_me 1.2.4, which was
+This file was extended by Free exFAT implementation $as_me 1.2.5, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -4678,7 +4678,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="\\
-Free exFAT implementation config.status 1.2.4
+Free exFAT implementation config.status 1.2.5
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
index 20160ee..c2f2e5f 100644 (file)
@@ -21,7 +21,7 @@
 #
 
 AC_INIT([Free exFAT implementation],
-       [1.2.4],
+       [1.2.5],
        [relan@users.noreply.github.com],
        [exfat-utils],
        [https://github.com/relan/exfat])
index 31c7dca..9408a62 100644 (file)
 .B \-u
 ]
 [
+.B \-f
+.I file
+]
+[
 .B \-V
 ]
 .I device
@@ -33,6 +37,11 @@ systems.
 Dump ranges of used sectors starting from 0 and separated with spaces. May be
 useful for backup tools.
 .TP
+.B \-f file
+Print out a list of fragments that compose the given file. Each fragment is
+printed on its own line, as the start offset (in bytes) into the file system,
+and the length (in bytes).
+.TP
 .BI \-V
 Print version and copyright.
 
index 75f7ff1..fda5b9b 100644 (file)
@@ -140,9 +140,66 @@ static int dump_full(const char* spec, bool used_sectors)
        return 0;
 }
 
+static int dump_file_fragments(const char* spec, const char* path)
+{
+       struct exfat ef;
+       struct exfat_node* node;
+       cluster_t cluster;
+       cluster_t next_cluster;
+       cluster_t fragment_start_cluster;
+       off_t remainder;
+       off_t fragment_size = 0;
+       int rc = 0;
+
+       if (exfat_mount(&ef, spec, "ro") != 0)
+               return 1;
+
+       rc = exfat_lookup(&ef, &node, path);
+       if (rc != 0)
+       {
+               exfat_unmount(&ef);
+               exfat_error("'%s': %s", path, strerror(-rc));
+               return 1;
+       }
+
+       cluster = fragment_start_cluster = node->start_cluster;
+       remainder = node->size;
+       while (remainder > 0)
+       {
+               off_t lsize;
+
+               if (CLUSTER_INVALID(cluster))
+               {
+                       exfat_error("'%s' has invalid cluster %#x", path, cluster);
+                       rc = 1;
+                       break;
+               }
+
+               lsize = MIN(CLUSTER_SIZE(*ef.sb), remainder);
+               fragment_size += lsize;
+               remainder -= lsize;
+
+               next_cluster = exfat_next_cluster(&ef, node, cluster);
+               if (next_cluster != cluster + 1 || remainder == 0)
+               {
+                       /* next cluster is not contiguous or this is EOF */
+                       printf("%"PRIu64" %"PRIu64"\n",
+                                       exfat_c2o(&ef, fragment_start_cluster), fragment_size);
+                       /* start a new fragment */
+                       fragment_start_cluster = next_cluster;
+                       fragment_size = 0;
+               }
+               cluster = next_cluster;
+       }
+
+       exfat_put_node(&ef, node);
+       exfat_unmount(&ef);
+       return rc;
+}
+
 static void usage(const char* prog)
 {
-       fprintf(stderr, "Usage: %s [-s] [-u] [-V] <device>\n", prog);
+       fprintf(stderr, "Usage: %s [-s] [-u] [-f file] [-V] <device>\n", prog);
        exit(1);
 }
 
@@ -152,10 +209,9 @@ int main(int argc, char* argv[])
        const char* spec = NULL;
        bool sb_only = false;
        bool used_sectors = false;
+       const char* file_path = NULL;
 
-       printf("dumpexfat %s\n", VERSION);
-
-       while ((opt = getopt(argc, argv, "suV")) != -1)
+       while ((opt = getopt(argc, argv, "suf:V")) != -1)
        {
                switch (opt)
                {
@@ -165,7 +221,11 @@ int main(int argc, char* argv[])
                case 'u':
                        used_sectors = true;
                        break;
+               case 'f':
+                       file_path = optarg;
+                       break;
                case 'V':
+                       printf("dumpexfat %s\n", VERSION);
                        puts("Copyright (C) 2011-2016  Andrew Nayenko");
                        return 0;
                default:
@@ -176,6 +236,9 @@ int main(int argc, char* argv[])
                usage(argv[0]);
        spec = argv[optind];
 
+       if (file_path)
+               return dump_file_fragments(spec, file_path);
+
        if (sb_only)
                return dump_sb(spec);
 
index a72d74c..cda1965 100644 (file)
@@ -33,7 +33,7 @@ uint64_t files_count, directories_count;
 static int nodeck(struct exfat* ef, struct exfat_node* node)
 {
        const cluster_t cluster_size = CLUSTER_SIZE(*ef->sb);
-       cluster_t clusters = (node->size + cluster_size - 1) / cluster_size;
+       cluster_t clusters = DIV_ROUND_UP(node->size, cluster_size);
        cluster_t c = node->start_cluster;
        int rc = 0;
 
index 13bc6ce..34f027c 100644 (file)
@@ -67,7 +67,7 @@ static cluster_t s2c(const struct exfat* ef, off_t sector)
 static uint32_t bytes2clusters(const struct exfat* ef, uint64_t bytes)
 {
        uint64_t cluster_size = CLUSTER_SIZE(*ef->sb);
-       return (bytes + cluster_size - 1) / cluster_size;
+       return DIV_ROUND_UP(bytes, cluster_size);
 }
 
 cluster_t exfat_next_cluster(const struct exfat* ef,
index fa04e25..90002eb 100644 (file)
@@ -30,7 +30,6 @@ struct iterator
 {
        cluster_t cluster;
        off_t offset;
-       int contiguous;
        char* chunk;
 };
 
@@ -95,21 +94,35 @@ static off_t co2o(struct exfat* ef, cluster_t cluster, off_t offset)
 static int opendir(struct exfat* ef, const struct exfat_node* dir,
                struct iterator* it)
 {
+       char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+
        if (!(dir->flags & EXFAT_ATTRIB_DIR))
-               exfat_bug("not a directory");
+       {
+               exfat_get_name(dir, buffer, sizeof(buffer) - 1);
+               exfat_bug("'%s' is not a directory", buffer);
+       }
+       if (CLUSTER_INVALID(dir->start_cluster))
+       {
+               exfat_get_name(dir, buffer, sizeof(buffer) - 1);
+               exfat_error("'%s' directory starts with invalid cluster %#x", buffer,
+                               dir->start_cluster);
+               return -EIO;
+       }
        it->cluster = dir->start_cluster;
        it->offset = 0;
-       it->contiguous = IS_CONTIGUOUS(*dir);
        it->chunk = malloc(CLUSTER_SIZE(*ef->sb));
        if (it->chunk == NULL)
        {
-               exfat_error("out of memory");
+               exfat_error("failed to allocate memory for directory cluster");
                return -ENOMEM;
        }
        if (exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb),
                        exfat_c2o(ef, it->cluster)) < 0)
        {
-               exfat_error("failed to read directory cluster %#x", it->cluster);
+               free(it->chunk);
+               exfat_get_name(dir, buffer, sizeof(buffer) - 1);
+               exfat_error("failed to read '%s' directory cluster %#x", buffer,
+                               it->cluster);
                return -EIO;
        }
        return 0;
@@ -119,7 +132,6 @@ static void closedir(struct iterator* it)
 {
        it->cluster = 0;
        it->offset = 0;
-       it->contiguous = 0;
        free(it->chunk);
        it->chunk = NULL;
 }
@@ -194,9 +206,10 @@ static const struct exfat_entry* get_entry_ptr(const struct exfat* ef,
 }
 
 static bool check_node(const struct exfat_node* node, uint16_t actual_checksum,
-               uint16_t reference_checksum, uint64_t valid_size)
+               uint16_t reference_checksum, uint64_t valid_size, int cluster_size)
 {
        char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+       bool ret = true;
 
        /*
           Validate checksum first. If it's invalid all other fields probably
@@ -207,7 +220,7 @@ static bool check_node(const struct exfat_node* node, uint16_t actual_checksum,
                exfat_get_name(node, buffer, sizeof(buffer) - 1);
                exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer,
                                actual_checksum, reference_checksum);
-               return false;
+               ret = false;
        }
 
        /*
@@ -221,10 +234,49 @@ static bool check_node(const struct exfat_node* node, uint16_t actual_checksum,
                exfat_get_name(node, buffer, sizeof(buffer) - 1);
                exfat_error("'%s' has valid size (%"PRIu64") greater than size "
                                "(%"PRIu64")", buffer, valid_size, node->size);
-               return false;
+               ret = false;
        }
 
-       return true;
+       /*
+          Empty file must have zero start cluster. Non-empty file must start
+          with a valid cluster. Directories cannot be empty (i.e. must always
+          have a valid start cluster), but we will check this later in opendir()
+          to give user a chance to read current directory.
+       */
+       if (node->size == 0 && node->start_cluster != EXFAT_CLUSTER_FREE)
+       {
+               exfat_get_name(node, buffer, sizeof(buffer) - 1);
+               exfat_error("'%s' is empty but start cluster is %#x", buffer,
+                               node->start_cluster);
+               ret = false;
+       }
+       if (node->size > 0 && CLUSTER_INVALID(node->start_cluster))
+       {
+               exfat_get_name(node, buffer, sizeof(buffer) - 1);
+               exfat_error("'%s' points to invalid cluster %#x", buffer,
+                               node->start_cluster);
+               ret = false;
+       }
+
+       /* Empty file or directory must be marked as non-contiguous. */
+       if (node->size == 0 && (node->flags & EXFAT_ATTRIB_CONTIGUOUS))
+       {
+               exfat_get_name(node, buffer, sizeof(buffer) - 1);
+               exfat_error("'%s' is empty but marked as contiguous (%#x)", buffer,
+                               node->flags);
+               ret = false;
+       }
+
+       /* Directory size must be aligned on at cluster boundary. */
+       if ((node->flags & EXFAT_ATTRIB_DIR) && node->size % cluster_size != 0)
+       {
+               exfat_get_name(node, buffer, sizeof(buffer) - 1);
+               exfat_error("'%s' directory size %"PRIu64" is not divisible by %d", buffer,
+                               node->size, cluster_size);
+               ret = false;
+       }
+
+       return ret;
 }
 
 static void decompress_upcase(uint16_t* output, const le16_t* source,
@@ -340,21 +392,6 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                        init_node_meta2(*node, meta2);
                        actual_checksum = exfat_add_checksum(entry, actual_checksum);
                        valid_size = le64_to_cpu(meta2->valid_size);
-                       /* empty files must be marked as non-contiguous */
-                       if ((*node)->size == 0 && (meta2->flags & EXFAT_FLAG_CONTIGUOUS))
-                       {
-                               exfat_error("empty file marked as contiguous (0x%hhx)",
-                                               meta2->flags);
-                               goto error;
-                       }
-                       /* directories must be aligned on at cluster boundary */
-                       if (((*node)->flags & EXFAT_ATTRIB_DIR) &&
-                               (*node)->size % CLUSTER_SIZE(*ef->sb) != 0)
-                       {
-                               exfat_error("directory has invalid size %"PRIu64" bytes",
-                                               (*node)->size);
-                               goto error;
-                       }
                        --continuations;
                        break;
 
@@ -375,7 +412,7 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                        if (--continuations == 0)
                        {
                                if (!check_node(*node, actual_checksum, reference_checksum,
-                                               valid_size))
+                                               valid_size, CLUSTER_SIZE(*ef->sb)))
                                        goto error;
                                if (!fetch_next_entry(ef, parent, it))
                                        goto error;
index 7d2becd..93c1d67 100644 (file)
@@ -151,8 +151,14 @@ void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
 void exfat_tzset(void)
 {
        time_t now;
+       struct tm* utc;
 
        tzset();
        now = time(NULL);
-       exfat_timezone = mktime(gmtime(&now)) - now;
+       utc = gmtime(&now);
+       /* gmtime() always sets tm_isdst to 0 because daylight savings never
+          affect UTC. Setting tm_isdst to -1 makes mktime() to determine whether
+          summer time is in effect. */
+       utc->tm_isdst = -1;
+       exfat_timezone = mktime(utc) - now;
 }