From aa6bcd9c79094ec382ae0aa3338aea1b69cb9234 Mon Sep 17 00:00:00 2001 From: Sven Hoexter Date: Fri, 9 Mar 2012 19:39:22 +0100 Subject: [PATCH] Imported Upstream version 0.9.7 --- ChangeLog | 11 +++ SConstruct | 58 ++++++++++------ fuse/main.c | 4 +- libexfat/cluster.c | 12 ++-- libexfat/exfat.h | 29 +++++--- libexfat/exfatfs.h | 1 - libexfat/io.c | 165 +++++++++++++++++++++++++++++++++++++++------ libexfat/mount.c | 59 +++++++--------- libexfat/node.c | 129 +++++++++++++++-------------------- libexfat/time.c | 156 ++++++++++++++++++++++++++++++++++++++++++ libexfat/utils.c | 125 ---------------------------------- libexfat/version.h | 2 +- 12 files changed, 458 insertions(+), 293 deletions(-) create mode 100644 libexfat/time.c diff --git a/ChangeLog b/ChangeLog index 2f46f63..d492c39 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +0.9.7 (2012-03-08) + +* Out-of-the-box FreeBSD support (via ublio library). +* Fixed "missing EOD entry" error (could happen while reading directory that +consists of several clusters). +* Fixed interpretation of minutes field in files timestamps (minutes could be +displayed incorrectly). +* Fixed mtime seconds field initialization for newly created file (mtime could +be 1 sec less than creation time). +* SConscript now respects CC, CCFLAGS and LDFLAGS environment variables. + 0.9.6 (2012-01-14) * Fixed write performance regression introduced in 0.9.4. diff --git a/SConstruct b/SConstruct index e4b2b10..14bd10e 100644 --- a/SConstruct +++ b/SConstruct @@ -23,23 +23,46 @@ import platform import SCons env = Environment(**ARGUMENTS) +conf = Configure(env) + destdir = env.get('DESTDIR', '/sbin'); targets = [] +libs = ['exfat'] + +if 'CC' in os.environ: + conf.env.Replace(CC = os.environ['CC']) +if 'CCFLAGS' in os.environ: + conf.env.Replace(CCFLAGS = os.environ['CCFLAGS']) +# Set default CCFLAGS for known compilers +if not conf.env['CCFLAGS']: + if conf.env['CC'] == 'gcc': + conf.env.Replace(CCFLAGS = '-Wall -O2 -ggdb') + elif conf.env['CC'] == 'clang': + conf.env.Replace(CCFLAGS = '-Wall -O2 -g') +conf.env.Append(CPPDEFINES = {'FUSE_USE_VERSION': 26}) +conf.env.Append(CPPDEFINES = {'_FILE_OFFSET_BITS' : 64}) +conf.env.Append(CPPPATH = ['libexfat']) +if 'LDFLAGS' in os.environ: + conf.env.Append(LINKFLAGS = os.environ['LDFLAGS']) +conf.env.Append(LIBPATH = ['libexfat']) -if not env['CCFLAGS']: - if env['CC'] == 'gcc': - env['CCFLAGS'] = '-Wall -O2 -ggdb' -env.Append(CPPDEFINES = {'FUSE_USE_VERSION': 26}) -env.Append(CPPDEFINES = {'_FILE_OFFSET_BITS' : 64}) # __DARWIN_64_BIT_INO_T=0 define is needed because since Snow Leopard inode # numbers are 64-bit by default, but libfuse operates 32-bit ones. This define # forces 32-bit inode declaration in system headers, but it's also possible to # link against libfuse_ino64 instead. if platform.system() == 'Darwin': - env.Append(CPPDEFINES = {'__DARWIN_64_BIT_INO_T' : 0}) - env.Append(CPPDEFINES = {'__DARWIN_UNIX03' : 1}) -env.Append(CPPPATH = ['libexfat']) -env.Append(LINKFLAGS = '') + conf.env.Append(CPPDEFINES = {'__DARWIN_64_BIT_INO_T' : 0}) + conf.env.Append(CPPDEFINES = {'__DARWIN_UNIX03' : 1}) + +# FreeBSD does not support block devices, only raw devices. Ublio is required +# for unaligned I/O and caching. +if platform.system() == 'FreeBSD': + conf.env.Append(CPPDEFINES = 'USE_UBLIO') + libs.append('ublio') + conf.env.Append(CPPPATH = ['/usr/local/include']) + conf.env.Append(LIBPATH = ['/usr/local/lib']) + +env = conf.Finish() def make_symlink(dir, target, link_name): workdir = os.getcwd() @@ -55,14 +78,11 @@ symlink = SCons.Action.ActionFactory(make_symlink, lambda dir, target, link_name: 'make_symlink("%s", "%s", "%s")' % (dir, target, link_name)) -def program(pattern, output, alias = None, lib = None): +def program(pattern, output, alias, libs): sources = Glob(pattern) if not sources: return - libs = ['exfat'] - if lib: - libs.append(lib) - target = env.Program(output, sources, LIBS = libs, LIBPATH = 'libexfat') + target = env.Program(output, sources, LIBS = libs) if alias: Alias('install', Install(destdir, target), symlink(destdir, os.path.basename(output), alias)) @@ -72,10 +92,10 @@ def program(pattern, output, alias = None, lib = None): env.Library('libexfat/exfat', Glob('libexfat/*.c')) -program('fuse/*.c', 'fuse/mount.exfat-fuse', 'mount.exfat', ['fuse']) -program('dump/*.c', 'dump/dumpexfat') -program('fsck/*.c', 'fsck/exfatfsck', 'fsck.exfat') -program('mkfs/*.c', 'mkfs/mkexfatfs', 'mkfs.exfat') -program('label/*.c', 'label/exfatlabel') +program('fuse/*.c', 'fuse/mount.exfat-fuse', 'mount.exfat', [libs + ['fuse']]) +program('dump/*.c', 'dump/dumpexfat', None, libs) +program('fsck/*.c', 'fsck/exfatfsck', 'fsck.exfat', libs) +program('mkfs/*.c', 'mkfs/mkexfatfs', 'mkfs.exfat', libs) +program('label/*.c', 'label/exfatlabel', None, libs) Default(targets) diff --git a/fuse/main.c b/fuse/main.c index a813427..9155912 100644 --- a/fuse/main.c +++ b/fuse/main.c @@ -153,14 +153,14 @@ static int fuse_exfat_read(const char* path, char* buffer, size_t size, off_t offset, struct fuse_file_info* fi) { exfat_debug("[fuse_exfat_read] %s (%zu bytes)", path, size); - return exfat_read(&ef, get_node(fi), buffer, size, offset); + return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset); } static int fuse_exfat_write(const char* path, const char* buffer, size_t size, off_t offset, struct fuse_file_info* fi) { exfat_debug("[fuse_exfat_write] %s (%zu bytes)", path, size); - return exfat_write(&ef, get_node(fi), buffer, size, offset); + return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset); } static int fuse_exfat_unlink(const char* path) diff --git a/libexfat/cluster.c b/libexfat/cluster.c index 7251c31..1257c7a 100644 --- a/libexfat/cluster.c +++ b/libexfat/cluster.c @@ -80,7 +80,7 @@ cluster_t exfat_next_cluster(const struct exfat* ef, return cluster + 1; fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start)) + cluster * sizeof(cluster_t); - exfat_read_raw(&next, sizeof(next), fat_offset, ef->fd); + exfat_pread(ef->dev, &next, sizeof(next), fat_offset); return le32_to_cpu(next); } @@ -145,8 +145,8 @@ static cluster_t find_bit_and_set(uint8_t* bitmap, cluster_t start, void exfat_flush_cmap(struct exfat* ef) { - exfat_write_raw(ef->cmap.chunk, (ef->cmap.chunk_size + 7) / 8, - exfat_c2o(ef, ef->cmap.start_cluster), ef->fd); + exfat_pwrite(ef->dev, ef->cmap.chunk, (ef->cmap.chunk_size + 7) / 8, + exfat_c2o(ef, ef->cmap.start_cluster)); ef->cmap.dirty = 0; } @@ -161,7 +161,7 @@ static void set_next_cluster(const struct exfat* ef, int contiguous, fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start)) + current * sizeof(cluster_t); next_le32 = cpu_to_le32(next); - exfat_write_raw(&next_le32, sizeof(next_le32), fat_offset, ef->fd); + exfat_pwrite(ef->dev, &next_le32, sizeof(next_le32), fat_offset); } static cluster_t allocate_cluster(struct exfat* ef, cluster_t hint) @@ -182,7 +182,6 @@ static cluster_t allocate_cluster(struct exfat* ef, cluster_t hint) } ef->cmap.dirty = 1; - /* FIXME update percentage of used space */ return cluster; } @@ -196,7 +195,6 @@ static void free_cluster(struct exfat* ef, cluster_t cluster) BMAP_CLR(ef->cmap.chunk, cluster - EXFAT_FIRST_DATA_CLUSTER); ef->cmap.dirty = 1; - /* FIXME update percentage of used space */ } static void make_noncontiguous(const struct exfat* ef, cluster_t first, @@ -325,7 +323,7 @@ static int shrink_file(struct exfat* ef, struct exfat_node* node, static void erase_raw(struct exfat* ef, size_t size, off_t offset) { - exfat_write_raw(ef->zero_cluster, size, offset, ef->fd); + exfat_pwrite(ef->dev, ef->zero_cluster, size, offset); } static int erase_range(struct exfat* ef, struct exfat_node* node, diff --git a/libexfat/exfat.h b/libexfat/exfat.h index de2e1a2..bca0d24 100644 --- a/libexfat/exfat.h +++ b/libexfat/exfat.h @@ -69,10 +69,12 @@ struct exfat_node le16_t name[EXFAT_NAME_MAX + 1]; }; +struct exfat_dev; + struct exfat { + struct exfat_dev* dev; struct exfat_super_block* sb; - int fd; le16_t* upcase; size_t upcase_chars; struct exfat_node* root; @@ -120,12 +122,19 @@ void exfat_warn(const char* format, ...) void exfat_debug(const char* format, ...) __attribute__((format(printf, 1, 2))); -int exfat_open(const char* spec, int ro); -void exfat_read_raw(void* buffer, size_t size, off_t offset, int fd); -void exfat_write_raw(const void* buffer, size_t size, off_t offset, int fd); -ssize_t exfat_read(const struct exfat* ef, struct exfat_node* node, +struct exfat_dev* exfat_open(const char* spec, int ro); +int exfat_close(struct exfat_dev* dev); +int exfat_fsync(struct exfat_dev* dev); +off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence); +ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size); +ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size); +void exfat_pread(struct exfat_dev* dev, void* buffer, size_t size, + off_t offset); +void exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, + off_t offset); +ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, void* buffer, size_t size, off_t offset); -ssize_t exfat_write(struct exfat* ef, struct exfat_node* node, +ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, const void* buffer, size_t size, off_t offset); int exfat_opendir(struct exfat* ef, struct exfat_node* dir, @@ -149,9 +158,6 @@ int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b); void exfat_stat(const struct exfat* ef, const struct exfat_node* node, struct stat* stbuf); -time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec); -void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, - uint8_t* centisec); void exfat_get_name(const struct exfat_node* node, char* buffer, size_t n); uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry); uint16_t exfat_add_checksum(const void* entry, uint16_t sum); @@ -189,4 +195,9 @@ int exfat_set_label(struct exfat* ef, const char* label); int exfat_mount(struct exfat* ef, const char* spec, const char* options); void exfat_unmount(struct exfat* ef); +time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec); +void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, + uint8_t* centisec); +void exfat_tzset(void); + #endif /* ifndef EXFAT_H_INCLUDED */ diff --git a/libexfat/exfatfs.h b/libexfat/exfatfs.h index be75395..61e39a1 100644 --- a/libexfat/exfatfs.h +++ b/libexfat/exfatfs.h @@ -66,7 +66,6 @@ __attribute__((__packed__)); #define EXFAT_ENTRY_VALID 0x80 #define EXFAT_ENTRY_CONTINUED 0x40 -#define EXFAT_ENTRY_EOD (0x00) #define EXFAT_ENTRY_BITMAP (0x01 | EXFAT_ENTRY_VALID) #define EXFAT_ENTRY_UPCASE (0x02 | EXFAT_ENTRY_VALID) #define EXFAT_ENTRY_LABEL (0x03 | EXFAT_ENTRY_VALID) diff --git a/libexfat/io.c b/libexfat/io.c index 25988f0..c9c1e2b 100644 --- a/libexfat/io.c +++ b/libexfat/io.c @@ -18,62 +18,183 @@ along with this program. If not, see . */ +#define _XOPEN_SOURCE 500 /* for pread() and pwrite() in Linux */ #include "exfat.h" #include #include #include #include #include -#define __USE_UNIX98 /* for pread() in Linux */ #include +#include +#ifdef USE_UBLIO +#include +#include +#endif #if _FILE_OFFSET_BITS != 64 #error You should define _FILE_OFFSET_BITS=64 #endif -int exfat_open(const char* spec, int ro) +struct exfat_dev { int fd; +#ifdef USE_UBLIO + off_t pos; + ublio_filehandle_t ufh; +#endif +}; + +struct exfat_dev* exfat_open(const char* spec, int ro) +{ + struct exfat_dev* dev; struct stat stbuf; +#ifdef USE_UBLIO + struct ublio_param up; +#endif - fd = open(spec, ro ? O_RDONLY : O_RDWR); - if (fd < 0) + dev = malloc(sizeof(struct exfat_dev)); + if (dev == NULL) { + exfat_error("failed to allocate memory for device structure"); + return NULL; + } + + dev->fd = open(spec, ro ? O_RDONLY : O_RDWR); + if (dev->fd < 0) + { + free(dev); exfat_error("failed to open `%s' in read-%s mode", spec, ro ? "only" : "write"); - return -1; + return NULL; } - if (fstat(fd, &stbuf) != 0) + if (fstat(dev->fd, &stbuf) != 0) { - close(fd); + close(dev->fd); + free(dev); exfat_error("failed to fstat `%s'", spec); - return -1; + return NULL; } - if (!S_ISBLK(stbuf.st_mode) && !S_ISREG(stbuf.st_mode)) + if (!S_ISBLK(stbuf.st_mode) && + !S_ISCHR(stbuf.st_mode) && + !S_ISREG(stbuf.st_mode)) { - close(fd); - exfat_error("`%s' is neither a block device, nor a regular file", - spec); - return -1; + close(dev->fd); + free(dev); + exfat_error("`%s' is neither a device, nor a regular file", spec); + return NULL; + } + +#ifdef USE_UBLIO + memset(&up, 0, sizeof(struct ublio_param)); + up.up_blocksize = 256 * 1024; + up.up_items = 64; + up.up_grace = 32; + up.up_priv = &dev->fd; + + dev->pos = 0; + dev->ufh = ublio_open(&up); + if (dev->ufh == NULL) + { + close(dev->fd); + free(dev); + exfat_error("failed to initialize ublio"); + return NULL; + } +#endif + + return dev; +} + +int exfat_close(struct exfat_dev* dev) +{ +#ifdef USE_UBLIO + if (ublio_close(dev->ufh) != 0) + exfat_error("failed to close ublio"); +#endif + if (close(dev->fd) != 0) + { + free(dev); + exfat_error("failed to close device"); + return 1; } - return fd; + free(dev); + return 0; +} + +int exfat_fsync(struct exfat_dev* dev) +{ +#ifdef USE_UBLIO + if (ublio_fsync(dev->ufh) != 0) +#else + if (fsync(dev->fd) != 0) +#endif + { + exfat_error("fsync failed"); + return 1; + } + return 0; +} + +off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence) +{ +#ifdef USE_UBLIO + /* XXX SEEK_CUR will be handled incorrectly */ + return dev->pos = lseek(dev->fd, offset, whence); +#else + return lseek(dev->fd, offset, whence); +#endif +} + +ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size) +{ +#ifdef USE_UBLIO + ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos); + if (result >= 0) + dev->pos += size; + return result; +#else + return read(dev->fd, buffer, size); +#endif } -void exfat_read_raw(void* buffer, size_t size, off_t offset, int fd) +ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size) { - if (pread(fd, buffer, size, offset) != size) +#ifdef USE_UBLIO + ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos); + if (result >= 0) + dev->pos += size; + return result; +#else + return write(dev->fd, buffer, size); +#endif +} + +void exfat_pread(struct exfat_dev* dev, void* buffer, size_t size, + off_t offset) +{ +#ifdef USE_UBLIO + if (ublio_pread(dev->ufh, buffer, size, offset) != size) +#else + if (pread(dev->fd, buffer, size, offset) != size) +#endif exfat_bug("failed to read %zu bytes from file at %"PRIu64, size, (uint64_t) offset); } -void exfat_write_raw(const void* buffer, size_t size, off_t offset, int fd) +void exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, + off_t offset) { - if (pwrite(fd, buffer, size, offset) != size) +#ifdef USE_UBLIO + if (ublio_pwrite(dev->ufh, buffer, size, offset) != size) +#else + if (pwrite(dev->fd, buffer, size, offset) != size) +#endif exfat_bug("failed to write %zu bytes to file at %"PRIu64, size, (uint64_t) offset); } -ssize_t exfat_read(const struct exfat* ef, struct exfat_node* node, +ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, void* buffer, size_t size, off_t offset) { cluster_t cluster; @@ -102,7 +223,7 @@ ssize_t exfat_read(const struct exfat* ef, struct exfat_node* node, return -1; } lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder); - exfat_read_raw(bufp, lsize, exfat_c2o(ef, cluster) + loffset, ef->fd); + exfat_pread(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset); bufp += lsize; loffset = 0; remainder -= lsize; @@ -113,7 +234,7 @@ ssize_t exfat_read(const struct exfat* ef, struct exfat_node* node, return size - remainder; } -ssize_t exfat_write(struct exfat* ef, struct exfat_node* node, +ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, const void* buffer, size_t size, off_t offset) { cluster_t cluster; @@ -146,7 +267,7 @@ ssize_t exfat_write(struct exfat* ef, struct exfat_node* node, return -1; } lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder); - exfat_write_raw(bufp, lsize, exfat_c2o(ef, cluster) + loffset, ef->fd); + exfat_pwrite(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset); bufp += lsize; loffset = 0; remainder -= lsize; diff --git a/libexfat/mount.c b/libexfat/mount.c index 295d455..521b539 100644 --- a/libexfat/mount.c +++ b/libexfat/mount.c @@ -24,8 +24,6 @@ #include #include #include -#define _XOPEN_SOURCE /* for tzset() in Linux */ -#include static uint64_t rootdir_size(const struct exfat* ef) { @@ -92,20 +90,21 @@ static void parse_options(struct exfat* ef, const char* options) ef->noatime = match_option(options, "noatime"); } -static int verify_vbr_checksum(void* sector, off_t sector_size, int fd) +static int verify_vbr_checksum(struct exfat_dev* dev, void* sector, + off_t sector_size) { uint32_t vbr_checksum; int i; - exfat_read_raw(sector, sector_size, 0, fd); + exfat_pread(dev, sector, sector_size, 0); vbr_checksum = exfat_vbr_start_checksum(sector, sector_size); for (i = 1; i < 11; i++) { - exfat_read_raw(sector, sector_size, i * sector_size, fd); + exfat_pread(dev, sector, sector_size, i * sector_size); vbr_checksum = exfat_vbr_add_checksum(sector, sector_size, vbr_checksum); } - exfat_read_raw(sector, sector_size, i * sector_size, fd); + exfat_pread(dev, sector, sector_size, i * sector_size); for (i = 0; i < sector_size / sizeof(vbr_checksum); i++) if (le32_to_cpu(((const le32_t*) sector)[i]) != vbr_checksum) { @@ -118,13 +117,8 @@ static int verify_vbr_checksum(void* sector, off_t sector_size, int fd) static int commit_super_block(const struct exfat* ef) { - exfat_write_raw(ef->sb, sizeof(struct exfat_super_block), 0, ef->fd); - if (fsync(ef->fd) < 0) - { - exfat_error("fsync failed"); - return 1; - } - return 0; + exfat_pwrite(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0); + return exfat_fsync(ef->dev); } static int prepare_super_block(const struct exfat* ef) @@ -144,18 +138,18 @@ int exfat_mount(struct exfat* ef, const char* spec, const char* options) { int rc; - tzset(); + exfat_tzset(); memset(ef, 0, sizeof(struct exfat)); parse_options(ef, options); - ef->fd = exfat_open(spec, ef->ro); - if (ef->fd < 0) + ef->dev = exfat_open(spec, ef->ro); + if (ef->dev == NULL) { if (ef->ro || !match_option(options, "ro_fallback")) return -EIO; - ef->fd = exfat_open(spec, 1); - if (ef->fd < 0) + ef->dev = exfat_open(spec, 1); + if (ef->dev == NULL) return -EIO; exfat_warn("device is write-protected, mounting read-only"); ef->ro_fallback = ef->ro = 1; @@ -164,23 +158,23 @@ int exfat_mount(struct exfat* ef, const char* spec, const char* options) ef->sb = malloc(sizeof(struct exfat_super_block)); if (ef->sb == NULL) { - close(ef->fd); + exfat_close(ef->dev); exfat_error("failed to allocate memory for the super block"); return -ENOMEM; } memset(ef->sb, 0, sizeof(struct exfat_super_block)); - exfat_read_raw(ef->sb, sizeof(struct exfat_super_block), 0, ef->fd); + exfat_pread(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0); if (memcmp(ef->sb->oem_name, "EXFAT ", 8) != 0) { - close(ef->fd); + exfat_close(ef->dev); free(ef->sb); exfat_error("exFAT file system is not found"); return -EIO; } if (ef->sb->version.major != 1 || ef->sb->version.minor != 0) { - close(ef->fd); + exfat_close(ef->dev); exfat_error("unsupported exFAT version: %hhu.%hhu", ef->sb->version.major, ef->sb->version.minor); free(ef->sb); @@ -188,7 +182,7 @@ int exfat_mount(struct exfat* ef, const char* spec, const char* options) } if (ef->sb->fat_count != 1) { - close(ef->fd); + exfat_close(ef->dev); free(ef->sb); exfat_error("unsupported FAT count: %hhu", ef->sb->fat_count); return -EIO; @@ -196,7 +190,7 @@ int exfat_mount(struct exfat* ef, const char* spec, const char* options) /* officially exFAT supports cluster size up to 32 MB */ if ((int) ef->sb->sector_bits + (int) ef->sb->spc_bits > 25) { - close(ef->fd); + exfat_close(ef->dev); free(ef->sb); exfat_error("too big cluster size: 2^%d", (int) ef->sb->sector_bits + (int) ef->sb->spc_bits); @@ -206,17 +200,17 @@ int exfat_mount(struct exfat* ef, const char* spec, const char* options) ef->zero_cluster = malloc(CLUSTER_SIZE(*ef->sb)); if (ef->zero_cluster == NULL) { - close(ef->fd); + exfat_close(ef->dev); free(ef->sb); exfat_error("failed to allocate zero sector"); return -ENOMEM; } /* use zero_cluster as a temporary buffer for VBR checksum verification */ - if (verify_vbr_checksum(ef->zero_cluster, SECTOR_SIZE(*ef->sb), - ef->fd) != 0) + if (verify_vbr_checksum(ef->dev, ef->zero_cluster, + SECTOR_SIZE(*ef->sb)) != 0) { free(ef->zero_cluster); - close(ef->fd); + exfat_close(ef->dev); free(ef->sb); return -EIO; } @@ -226,7 +220,7 @@ int exfat_mount(struct exfat* ef, const char* spec, const char* options) if (ef->root == NULL) { free(ef->zero_cluster); - close(ef->fd); + exfat_close(ef->dev); free(ef->sb); exfat_error("failed to allocate root node"); return -ENOMEM; @@ -267,7 +261,7 @@ error: exfat_reset_cache(ef); free(ef->root); free(ef->zero_cluster); - close(ef->fd); + exfat_close(ef->dev); free(ef->sb); return -EIO; } @@ -301,9 +295,8 @@ void exfat_unmount(struct exfat* ef) free(ef->root); ef->root = NULL; finalize_super_block(ef); - if (close(ef->fd) < 0) /* close descriptor immediately after fsync */ - exfat_error("close failed"); - ef->fd = 0; + exfat_close(ef->dev); /* close descriptor immediately after fsync */ + ef->dev = NULL; free(ef->zero_cluster); ef->zero_cluster = NULL; free(ef->cmap.chunk); diff --git a/libexfat/node.c b/libexfat/node.c index 0920847..2ae724d 100644 --- a/libexfat/node.c +++ b/libexfat/node.c @@ -86,8 +86,8 @@ static int opendir(struct exfat* ef, const struct exfat_node* dir, exfat_error("out of memory"); return -ENOMEM; } - exfat_read_raw(it->chunk, CLUSTER_SIZE(*ef->sb), - exfat_c2o(ef, it->cluster), ef->fd); + exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb), + exfat_c2o(ef, it->cluster)); return 0; } @@ -108,20 +108,18 @@ static int fetch_next_entry(struct exfat* ef, const struct exfat_node* parent, /* fetch the next cluster if needed */ if ((it->offset & (CLUSTER_SIZE(*ef->sb) - 1)) == 0) { + /* reached the end of directory; the caller should check this + condition too */ if (it->offset >= parent->size) - { - exfat_error("missing EOD entry (0x%"PRIx64", 0x%"PRIx64")", - it->offset, parent->size); - return 1; - } + return 0; it->cluster = exfat_next_cluster(ef, parent, it->cluster); if (CLUSTER_INVALID(it->cluster)) { exfat_error("invalid cluster while reading directory"); return 1; } - exfat_read_raw(it->chunk, CLUSTER_SIZE(*ef->sb), - exfat_c2o(ef, it->cluster), ef->fd); + exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb), + exfat_c2o(ef, it->cluster)); } return 0; } @@ -189,21 +187,19 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, for (;;) { - /* every directory (even empty one) occupies at least one cluster and - must contain EOD entry */ - entry = get_entry_ptr(ef, it); - - switch (entry->type) + if (it->offset >= parent->size) { - case EXFAT_ENTRY_EOD: if (continuations != 0) { - exfat_error("expected %hhu continuations before EOD", - continuations); + exfat_error("expected %hhu continuations", continuations); goto error; } return -ENOENT; /* that's OK, means end of directory */ + } + entry = get_entry_ptr(ef, it); + switch (entry->type) + { case EXFAT_ENTRY_FILE: if (continuations != 0) { @@ -335,8 +331,8 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, } ef->upcase_chars = le64_to_cpu(upcase->size) / sizeof(le16_t); - exfat_read_raw(ef->upcase, le64_to_cpu(upcase->size), - exfat_c2o(ef, le32_to_cpu(upcase->start_cluster)), ef->fd); + exfat_pread(ef->dev, ef->upcase, le64_to_cpu(upcase->size), + exfat_c2o(ef, le32_to_cpu(upcase->start_cluster))); break; case EXFAT_ENTRY_BITMAP: @@ -367,8 +363,8 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent, goto error; } - exfat_read_raw(ef->cmap.chunk, le64_to_cpu(bitmap->size), - exfat_c2o(ef, ef->cmap.start_cluster), ef->fd); + exfat_pread(ef->dev, ef->cmap.chunk, le64_to_cpu(bitmap->size), + exfat_c2o(ef, ef->cmap.start_cluster)); break; case EXFAT_ENTRY_LABEL: @@ -505,14 +501,14 @@ void exfat_flush_node(struct exfat* ef, struct exfat_node* node) next_entry(ef, node->parent, &cluster, &offset); meta2_offset = co2o(ef, cluster, offset); - exfat_read_raw(&meta1, sizeof(meta1), meta1_offset, ef->fd); + exfat_pread(ef->dev, &meta1, sizeof(meta1), meta1_offset); if (meta1.type != EXFAT_ENTRY_FILE) exfat_bug("invalid type of meta1: 0x%hhx", meta1.type); meta1.attrib = cpu_to_le16(node->flags); exfat_unix2exfat(node->mtime, &meta1.mdate, &meta1.mtime, &meta1.mtime_cs); exfat_unix2exfat(node->atime, &meta1.adate, &meta1.atime, NULL); - exfat_read_raw(&meta2, sizeof(meta2), meta2_offset, ef->fd); + exfat_pread(ef->dev, &meta2, sizeof(meta2), meta2_offset); if (meta2.type != EXFAT_ENTRY_FILE_INFO) exfat_bug("invalid type of meta2: 0x%hhx", meta2.type); meta2.size = meta2.real_size = cpu_to_le64(node->size); @@ -525,8 +521,8 @@ void exfat_flush_node(struct exfat* ef, struct exfat_node* node) meta1.checksum = exfat_calc_checksum(&meta1, &meta2, node->name); - exfat_write_raw(&meta1, sizeof(meta1), meta1_offset, ef->fd); - exfat_write_raw(&meta2, sizeof(meta2), meta2_offset, ef->fd); + exfat_pwrite(ef->dev, &meta1, sizeof(meta1), meta1_offset); + exfat_pwrite(ef->dev, &meta2, sizeof(meta2), meta2_offset); node->flags &= ~EXFAT_ATTRIB_DIRTY; } @@ -539,17 +535,17 @@ static void erase_entry(struct exfat* ef, struct exfat_node* node) uint8_t entry_type; entry_type = EXFAT_ENTRY_FILE & ~EXFAT_ENTRY_VALID; - exfat_write_raw(&entry_type, 1, co2o(ef, cluster, offset), ef->fd); + exfat_pwrite(ef->dev, &entry_type, 1, co2o(ef, cluster, offset)); next_entry(ef, node->parent, &cluster, &offset); entry_type = EXFAT_ENTRY_FILE_INFO & ~EXFAT_ENTRY_VALID; - exfat_write_raw(&entry_type, 1, co2o(ef, cluster, offset), ef->fd); + exfat_pwrite(ef->dev, &entry_type, 1, co2o(ef, cluster, offset)); while (name_entries--) { next_entry(ef, node->parent, &cluster, &offset); entry_type = EXFAT_ENTRY_FILE_NAME & ~EXFAT_ENTRY_VALID; - exfat_write_raw(&entry_type, 1, co2o(ef, cluster, offset), ef->fd); + exfat_pwrite(ef->dev, &entry_type, 1, co2o(ef, cluster, offset)); } } @@ -582,10 +578,8 @@ static int shrink_directory(struct exfat* ef, struct exfat_node* dir, { const struct exfat_node* node; const struct exfat_node* last_node; - uint64_t entries = 1; /* a directory always has at leat 1 entry (EOD) */ + uint64_t entries = 0; uint64_t new_size; - struct exfat_entry eod; - off_t eod_offset; int rc; if (!(dir->flags & EXFAT_ATTRIB_DIR)) @@ -618,21 +612,13 @@ static int shrink_directory(struct exfat* ef, struct exfat_node* dir, new_size = DIV_ROUND_UP(entries * sizeof(struct exfat_entry), CLUSTER_SIZE(*ef->sb)) * CLUSTER_SIZE(*ef->sb); + if (new_size == 0) /* directory always has at least 1 cluster */ + new_size = CLUSTER_SIZE(*ef->sb); if (new_size == dir->size) return 0; rc = exfat_truncate(ef, dir, new_size); if (rc != 0) return rc; - - /* put EOD entry at the end of the last cluster */ - memset(&eod, 0, sizeof(eod)); - eod_offset = new_size - sizeof(struct exfat_entry); - if (last_node) - exfat_write_raw(&eod, sizeof(eod), - co2o(ef, last_node->entry_cluster, eod_offset), ef->fd); - else - exfat_write_raw(&eod, sizeof(eod), - co2o(ef, dir->start_cluster, eod_offset), ef->fd); return 0; } @@ -698,24 +684,22 @@ static int find_slot(struct exfat* ef, struct exfat_node* dir, *offset = it.offset; } entry = get_entry_ptr(ef, &it); - if (entry->type == EXFAT_ENTRY_EOD) + if (entry->type & EXFAT_ENTRY_VALID) + contiguous = 0; + else + contiguous++; + if (contiguous == subentries) + break; /* suitable slot is found */ + if (it.offset + sizeof(struct exfat_entry) >= dir->size) { - rc = grow_directory(ef, dir, - it.offset + sizeof(struct exfat_entry), /* actual size */ + rc = grow_directory(ef, dir, dir->size, (subentries - contiguous) * sizeof(struct exfat_entry)); if (rc != 0) { closedir(&it); return rc; } - break; } - if (entry->type & EXFAT_ENTRY_VALID) - contiguous = 0; - else - contiguous++; - if (contiguous == subentries) - break; /* suitable slot is found */ if (fetch_next_entry(ef, dir, &it) != 0) { closedir(&it); @@ -751,8 +735,7 @@ static int write_entry(struct exfat* ef, struct exfat_node* dir, &meta1.crtime_cs); meta1.adate = meta1.mdate = meta1.crdate; meta1.atime = meta1.mtime = meta1.crtime; - /* crtime_cs and mtime_cs contain addition to the time in centiseconds; - just ignore those fields because we operate with 2 sec resolution */ + meta1.mtime_cs = meta1.crtime_cs; /* there is no atime_cs */ memset(&meta2, 0, sizeof(meta2)); meta2.type = EXFAT_ENTRY_FILE_INFO; @@ -763,17 +746,17 @@ static int write_entry(struct exfat* ef, struct exfat_node* dir, meta1.checksum = exfat_calc_checksum(&meta1, &meta2, node->name); - exfat_write_raw(&meta1, sizeof(meta1), co2o(ef, cluster, offset), ef->fd); + exfat_pwrite(ef->dev, &meta1, sizeof(meta1), co2o(ef, cluster, offset)); next_entry(ef, dir, &cluster, &offset); - exfat_write_raw(&meta2, sizeof(meta2), co2o(ef, cluster, offset), ef->fd); + exfat_pwrite(ef->dev, &meta2, sizeof(meta2), co2o(ef, cluster, offset)); for (i = 0; i < name_entries; i++) { struct exfat_entry_name name_entry = {EXFAT_ENTRY_FILE_NAME, 0}; memcpy(name_entry.name, node->name + i * EXFAT_ENAME_MAX, EXFAT_ENAME_MAX * sizeof(le16_t)); next_entry(ef, dir, &cluster, &offset); - exfat_write_raw(&name_entry, sizeof(name_entry), - co2o(ef, cluster, offset), ef->fd); + exfat_pwrite(ef->dev, &name_entry, sizeof(name_entry), + co2o(ef, cluster, offset)); } init_node_meta1(node, &meta1); @@ -855,11 +838,11 @@ static void rename_entry(struct exfat* ef, struct exfat_node* dir, const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX); int i; - exfat_read_raw(&meta1, sizeof(meta1), co2o(ef, old_cluster, old_offset), - ef->fd); + exfat_pread(ef->dev, &meta1, sizeof(meta1), + co2o(ef, old_cluster, old_offset)); next_entry(ef, node->parent, &old_cluster, &old_offset); - exfat_read_raw(&meta2, sizeof(meta2), co2o(ef, old_cluster, old_offset), - ef->fd); + exfat_pread(ef->dev, &meta2, sizeof(meta2), + co2o(ef, old_cluster, old_offset)); meta1.continuations = 1 + name_entries; meta2.name_hash = exfat_calc_name_hash(ef, name); meta2.name_length = name_length; @@ -870,11 +853,11 @@ static void rename_entry(struct exfat* ef, struct exfat_node* dir, node->entry_cluster = new_cluster; node->entry_offset = new_offset; - exfat_write_raw(&meta1, sizeof(meta1), co2o(ef, new_cluster, new_offset), - ef->fd); + exfat_pwrite(ef->dev, &meta1, sizeof(meta1), + co2o(ef, new_cluster, new_offset)); next_entry(ef, dir, &new_cluster, &new_offset); - exfat_write_raw(&meta2, sizeof(meta2), co2o(ef, new_cluster, new_offset), - ef->fd); + exfat_pwrite(ef->dev, &meta2, sizeof(meta2), + co2o(ef, new_cluster, new_offset)); for (i = 0; i < name_entries; i++) { @@ -882,8 +865,8 @@ static void rename_entry(struct exfat* ef, struct exfat_node* dir, memcpy(name_entry.name, name + i * EXFAT_ENAME_MAX, EXFAT_ENAME_MAX * sizeof(le16_t)); next_entry(ef, dir, &new_cluster, &new_offset); - exfat_write_raw(&name_entry, sizeof(name_entry), - co2o(ef, new_cluster, new_offset), ef->fd); + exfat_pwrite(ef->dev, &name_entry, sizeof(name_entry), + co2o(ef, new_cluster, new_offset)); } memcpy(node->name, name, (EXFAT_NAME_MAX + 1) * sizeof(le16_t)); @@ -978,7 +961,6 @@ static int find_label(struct exfat* ef, cluster_t* cluster, off_t* offset) { struct iterator it; int rc; - const struct exfat_entry* entry; rc = opendir(ef, ef->root, &it); if (rc != 0) @@ -986,14 +968,13 @@ static int find_label(struct exfat* ef, cluster_t* cluster, off_t* offset) for (;;) { - entry = get_entry_ptr(ef, &it); - - if (entry->type == EXFAT_ENTRY_EOD) + if (it.offset >= ef->root->size) { closedir(&it); return -ENOENT; } - if (entry->type == EXFAT_ENTRY_LABEL) + + if (get_entry_ptr(ef, &it)->type == EXFAT_ENTRY_LABEL) { *cluster = it.cluster; *offset = it.offset; @@ -1034,7 +1015,7 @@ int exfat_set_label(struct exfat* ef, const char* label) if (entry.length == 0) entry.type ^= EXFAT_ENTRY_VALID; - exfat_write_raw(&entry, sizeof(struct exfat_entry_label), - co2o(ef, cluster, offset), ef->fd); + exfat_pwrite(ef->dev, &entry, sizeof(struct exfat_entry_label), + co2o(ef, cluster, offset)); return 0; } diff --git a/libexfat/time.c b/libexfat/time.c new file mode 100644 index 0000000..2e22a99 --- /dev/null +++ b/libexfat/time.c @@ -0,0 +1,156 @@ +/* + time.c (03.02.12) + exFAT file system implementation library. + + Copyright (C) 2009, 2010 Andrew Nayenko + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "exfat.h" + +/* timezone offset from UTC in seconds; positive for western timezones, + negative for eastern ones */ +static long exfat_timezone; + +#define SEC_IN_MIN 60ll +#define SEC_IN_HOUR (60 * SEC_IN_MIN) +#define SEC_IN_DAY (24 * SEC_IN_HOUR) +#define SEC_IN_YEAR (365 * SEC_IN_DAY) /* not leap year */ +/* Unix epoch started at 0:00:00 UTC 1 January 1970 */ +#define UNIX_EPOCH_YEAR 1970 +/* exFAT epoch started at 0:00:00 UTC 1 January 1980 */ +#define EXFAT_EPOCH_YEAR 1980 +/* number of years from Unix epoch to exFAT epoch */ +#define EPOCH_DIFF_YEAR (EXFAT_EPOCH_YEAR - UNIX_EPOCH_YEAR) +/* number of days from Unix epoch to exFAT epoch (considering leap days) */ +#define EPOCH_DIFF_DAYS (EPOCH_DIFF_YEAR * 365 + EPOCH_DIFF_YEAR / 4) +/* number of seconds from Unix epoch to exFAT epoch (considering leap days) */ +#define EPOCH_DIFF_SEC (EPOCH_DIFF_DAYS * SEC_IN_DAY) +/* number of leap years passed from exFAT epoch to the specified year + (excluding the specified year itself) */ +#define LEAP_YEARS(year) ((EXFAT_EPOCH_YEAR + (year) - 1) / 4 \ + - (EXFAT_EPOCH_YEAR - 1) / 4) +/* checks whether the specified year is leap */ +#define IS_LEAP_YEAR(year) ((EXFAT_EPOCH_YEAR + (year)) % 4 == 0) + +static const time_t days_in_year[] = +{ + /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec) +{ + time_t unix_time = EPOCH_DIFF_SEC; + uint16_t ndate = le16_to_cpu(date); + uint16_t ntime = le16_to_cpu(time); + + uint16_t day = ndate & 0x1f; /* 5 bits, 1-31 */ + uint16_t month = ndate >> 5 & 0xf; /* 4 bits, 1-12 */ + uint16_t year = ndate >> 9; /* 7 bits, 1-127 (+1980) */ + + uint16_t twosec = ntime & 0x1f; /* 5 bits, 0-29 (2 sec granularity) */ + uint16_t min = ntime >> 5 & 0x3f; /* 6 bits, 0-59 */ + uint16_t hour = ntime >> 11; /* 5 bits, 0-23 */ + + if (day == 0 || month == 0 || month > 12) + { + exfat_error("bad date %u-%02hu-%02hu", + year + EXFAT_EPOCH_YEAR, month, day); + return 0; + } + if (hour > 23 || min > 59 || twosec > 29) + { + exfat_error("bad time %hu:%02hu:%02u", + hour, min, twosec * 2); + return 0; + } + if (centisec > 199) + { + exfat_error("bad centiseconds count %hhu", centisec); + return 0; + } + + /* every 4th year between 1904 and 2096 is leap */ + unix_time += year * SEC_IN_YEAR + LEAP_YEARS(year) * SEC_IN_DAY; + unix_time += days_in_year[month] * SEC_IN_DAY; + /* if it's leap year and February has passed we should add 1 day */ + if ((EXFAT_EPOCH_YEAR + year) % 4 == 0 && month > 2) + unix_time += SEC_IN_DAY; + unix_time += (day - 1) * SEC_IN_DAY; + + unix_time += hour * SEC_IN_HOUR; + unix_time += min * SEC_IN_MIN; + /* exFAT represents time with 2 sec granularity */ + unix_time += twosec * 2; + unix_time += centisec / 100; + + /* exFAT stores timestamps in local time, so we correct it to UTC */ + unix_time += exfat_timezone; + + return unix_time; +} + +void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, + uint8_t* centisec) +{ + time_t shift = EPOCH_DIFF_SEC + exfat_timezone; + uint16_t day, month, year; + uint16_t twosec, min, hour; + int days; + int i; + + /* time before exFAT epoch cannot be represented */ + if (unix_time < shift) + unix_time = shift; + + unix_time -= shift; + + days = unix_time / SEC_IN_DAY; + year = (4 * days) / (4 * 365 + 1); + days -= year * 365 + LEAP_YEARS(year); + month = 0; + for (i = 1; i <= 12; i++) + { + int leap_day = (IS_LEAP_YEAR(year) && i == 2); + int leap_sub = (IS_LEAP_YEAR(year) && i >= 3); + + if (i == 12 || days - leap_sub < days_in_year[i + 1] + leap_day) + { + month = i; + days -= days_in_year[i] + leap_sub; + break; + } + } + day = days + 1; + + hour = (unix_time % SEC_IN_DAY) / SEC_IN_HOUR; + min = (unix_time % SEC_IN_HOUR) / SEC_IN_MIN; + twosec = (unix_time % SEC_IN_MIN) / 2; + + *date = cpu_to_le16(day | (month << 5) | (year << 9)); + *time = cpu_to_le16(twosec | (min << 5) | (hour << 11)); + if (centisec) + *centisec = (unix_time % 2) * 100; +} + +void exfat_tzset(void) +{ + time_t now; + + tzset(); + now = time(NULL); + exfat_timezone = mktime(gmtime(&now)) - now; +} diff --git a/libexfat/utils.c b/libexfat/utils.c index 31b7e8c..c165cf9 100644 --- a/libexfat/utils.c +++ b/libexfat/utils.c @@ -22,8 +22,6 @@ #include #include #include -#define _XOPEN_SOURCE /* for timezone in Linux */ -#include void exfat_stat(const struct exfat* ef, const struct exfat_node* node, struct stat* stbuf) @@ -46,128 +44,6 @@ void exfat_stat(const struct exfat* ef, const struct exfat_node* node, stbuf->st_ctime = node->mtime; } -#define SEC_IN_MIN 60ll -#define SEC_IN_HOUR (60 * SEC_IN_MIN) -#define SEC_IN_DAY (24 * SEC_IN_HOUR) -#define SEC_IN_YEAR (365 * SEC_IN_DAY) /* not leap year */ -/* Unix epoch started at 0:00:00 UTC 1 January 1970 */ -#define UNIX_EPOCH_YEAR 1970 -/* exFAT epoch started at 0:00:00 UTC 1 January 1980 */ -#define EXFAT_EPOCH_YEAR 1980 -/* number of years from Unix epoch to exFAT epoch */ -#define EPOCH_DIFF_YEAR (EXFAT_EPOCH_YEAR - UNIX_EPOCH_YEAR) -/* number of days from Unix epoch to exFAT epoch (considering leap days) */ -#define EPOCH_DIFF_DAYS (EPOCH_DIFF_YEAR * 365 + EPOCH_DIFF_YEAR / 4) -/* number of seconds from Unix epoch to exFAT epoch (considering leap days) */ -#define EPOCH_DIFF_SEC (EPOCH_DIFF_DAYS * SEC_IN_DAY) -/* number of leap years passed from exFAT epoch to the specified year - (excluding the specified year itself) */ -#define LEAP_YEARS(year) ((EXFAT_EPOCH_YEAR + (year) - 1) / 4 \ - - (EXFAT_EPOCH_YEAR - 1) / 4) -/* checks whether the specified year is leap */ -#define IS_LEAP_YEAR(year) ((EXFAT_EPOCH_YEAR + (year)) % 4 == 0) - -static const time_t days_in_year[] = -{ - /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ - 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 -}; - -time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec) -{ - time_t unix_time = EPOCH_DIFF_SEC; - uint16_t ndate = le16_to_cpu(date); - uint16_t ntime = le16_to_cpu(time); - - uint16_t day = ndate & 0x1f; /* 5 bits, 1-31 */ - uint16_t month = ndate >> 5 & 0xf; /* 4 bits, 1-12 */ - uint16_t year = ndate >> 9; /* 7 bits, 1-127 (+1980) */ - - uint16_t twosec = ntime & 0x1f; /* 5 bits, 0-29 (2 sec granularity) */ - uint16_t min = ntime >> 5 & 0xf; /* 6 bits, 0-59 */ - uint16_t hour = ntime >> 11; /* 5 bits, 0-23 */ - - if (day == 0 || month == 0 || month > 12) - { - exfat_error("bad date %hu-%02hu-%02hu", - year + EXFAT_EPOCH_YEAR, month, day); - return 0; - } - if (hour > 23 || min > 59 || twosec > 29) - { - exfat_error("bad time %hu:%02hu:%02hu", - hour, min, twosec * 2); - return 0; - } - if (centisec > 199) - { - exfat_error("bad centiseconds count %hhu", centisec); - return 0; - } - - /* every 4th year between 1904 and 2096 is leap */ - unix_time += year * SEC_IN_YEAR + LEAP_YEARS(year) * SEC_IN_DAY; - unix_time += days_in_year[month] * SEC_IN_DAY; - /* if it's leap year and February has passed we should add 1 day */ - if ((EXFAT_EPOCH_YEAR + year) % 4 == 0 && month > 2) - unix_time += SEC_IN_DAY; - unix_time += (day - 1) * SEC_IN_DAY; - - unix_time += hour * SEC_IN_HOUR; - unix_time += min * SEC_IN_MIN; - /* exFAT represents time with 2 sec granularity */ - unix_time += twosec * 2; - unix_time += centisec / 100; - - /* exFAT stores timestamps in local time, so we correct it to UTC */ - unix_time += timezone; - - return unix_time; -} - -void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, - uint8_t* centisec) -{ - time_t shift = EPOCH_DIFF_SEC + timezone; - uint16_t day, month, year; - uint16_t twosec, min, hour; - int days; - int i; - - /* time before exFAT epoch cannot be represented */ - if (unix_time < shift) - unix_time = shift; - - unix_time -= shift; - - days = unix_time / SEC_IN_DAY; - year = (4 * days) / (4 * 365 + 1); - days -= year * 365 + LEAP_YEARS(year); - month = 0; - for (i = 1; i <= 12; i++) - { - int leap_day = (IS_LEAP_YEAR(year) && i == 2); - int leap_sub = (IS_LEAP_YEAR(year) && i >= 3); - - if (i == 12 || days - leap_sub < days_in_year[i + 1] + leap_day) - { - month = i; - days -= days_in_year[i] + leap_sub; - break; - } - } - day = days + 1; - - hour = (unix_time % SEC_IN_DAY) / SEC_IN_HOUR; - min = (unix_time % SEC_IN_HOUR) / SEC_IN_MIN; - twosec = (unix_time % SEC_IN_MIN) / 2; - - *date = cpu_to_le16(day | (month << 5) | (year << 9)); - *time = cpu_to_le16(twosec | (min << 5) | (hour << 11)); - if (centisec) - *centisec = (unix_time % 2) * 100; -} - void exfat_get_name(const struct exfat_node* node, char* buffer, size_t n) { if (utf16_to_utf8(buffer, node->name, n, EXFAT_NAME_MAX) != 0) @@ -234,7 +110,6 @@ uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum) return sum; } - le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name) { size_t i; diff --git a/libexfat/version.h b/libexfat/version.h index 0236f88..aa636f8 100644 --- a/libexfat/version.h +++ b/libexfat/version.h @@ -23,6 +23,6 @@ #define EXFAT_VERSION_MAJOR 0 #define EXFAT_VERSION_MINOR 9 -#define EXFAT_VERSION_PATCH 6 +#define EXFAT_VERSION_PATCH 7 #endif /* ifndef VERSION_H_INCLUDED */ -- 2.39.5