From: Sven Hoexter Date: Wed, 7 Dec 2016 16:55:33 +0000 (+0100) Subject: New upstream version 1.2.5 X-Git-Tag: upstream/1.2.5^0 X-Git-Url: https://git.sven.stormbind.net/?p=sven%2Fexfat-utils.git;a=commitdiff_plain;h=0e295d91bb7f18afee1a0b0d587e5a0cf58bf2fb New upstream version 1.2.5 --- diff --git a/ChangeLog b/ChangeLog index 7826825..639972e 100644 --- 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. diff --git a/configure b/configure index 3c3b3ab..2ec2bb9 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for Free exFAT implementation 1.2.4. +# Generated by GNU Autoconf 2.69 for Free exFAT implementation 1.2.5. # # Report bugs to . # @@ -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\\" diff --git a/configure.ac b/configure.ac index 20160ee..c2f2e5f 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/dump/dumpexfat.8 b/dump/dumpexfat.8 index 31c7dca..9408a62 100644 --- a/dump/dumpexfat.8 +++ b/dump/dumpexfat.8 @@ -13,6 +13,10 @@ .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. diff --git a/dump/main.c b/dump/main.c index 75f7ff1..fda5b9b 100644 --- a/dump/main.c +++ b/dump/main.c @@ -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] \n", prog); + fprintf(stderr, "Usage: %s [-s] [-u] [-f file] [-V] \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); diff --git a/fsck/main.c b/fsck/main.c index a72d74c..cda1965 100644 --- a/fsck/main.c +++ b/fsck/main.c @@ -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; diff --git a/libexfat/cluster.c b/libexfat/cluster.c index 13bc6ce..34f027c 100644 --- a/libexfat/cluster.c +++ b/libexfat/cluster.c @@ -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, diff --git a/libexfat/node.c b/libexfat/node.c index fa04e25..90002eb 100644 --- a/libexfat/node.c +++ b/libexfat/node.c @@ -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; diff --git a/libexfat/time.c b/libexfat/time.c index 7d2becd..93c1d67 100644 --- a/libexfat/time.c +++ b/libexfat/time.c @@ -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; }