Imported Upstream version 0.9.7 upstream/0.9.7
authorSven Hoexter <sven@timegate.de>
Fri, 9 Mar 2012 18:37:31 +0000 (19:37 +0100)
committerSven Hoexter <sven@timegate.de>
Fri, 9 Mar 2012 18:37:31 +0000 (19:37 +0100)
23 files changed:
ChangeLog
SConstruct
dump/main.c
libexfat/cluster.c
libexfat/exfat.h
libexfat/exfatfs.h
libexfat/io.c
libexfat/mount.c
libexfat/node.c
libexfat/time.c [new file with mode: 0644]
libexfat/utils.c
libexfat/version.h
mkfs/cbm.c
mkfs/cbm.h
mkfs/fat.c
mkfs/fat.h
mkfs/main.c
mkfs/rootdir.c
mkfs/rootdir.h
mkfs/uct.c
mkfs/uct.h
mkfs/vbr.c
mkfs/vbr.h

index 2f46f63..d492c39 100644 (file)
--- 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.
index e4b2b10..14bd10e 100644 (file)
@@ -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)
index 357557d..cd424d9 100644 (file)
@@ -73,23 +73,22 @@ static void print_other_info(const struct exfat_super_block* sb)
 
 static int dump_sb(const char* spec)
 {
-       int fd;
+       struct exfat_dev* dev;
        struct exfat_super_block sb;
 
-       fd = exfat_open(spec, 1);
-       if (fd < 0)
+       dev = exfat_open(spec, 1);
+       if (dev == NULL)
                return 1;
 
-       if (read(fd, &sb, sizeof(struct exfat_super_block))
-                       != sizeof(struct exfat_super_block))
+       if (exfat_read(dev, &sb, sizeof(struct exfat_super_block)) < 0)
        {
-               close(fd);
+               exfat_close(dev);
                exfat_error("failed to read from `%s'", spec);
                return 1;
        }
        if (memcmp(sb.oem_name, "EXFAT   ", sizeof(sb.oem_name)) != 0)
        {
-               close(fd);
+               exfat_close(dev);
                exfat_error("exFAT file system is not found on `%s'", spec);
                return 1;
        }
@@ -99,7 +98,7 @@ static int dump_sb(const char* spec)
        print_cluster_info(&sb);
        print_other_info(&sb);
 
-       close(fd);
+       exfat_close(dev);
        return 0;
 }
 
index 7251c31..1257c7a 100644 (file)
@@ -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,
index de2e1a2..bca0d24 100644 (file)
@@ -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 */
index be75395..61e39a1 100644 (file)
@@ -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)
index 25988f0..c9c1e2b 100644 (file)
        along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#define _XOPEN_SOURCE 500 /* for pread() and pwrite() in Linux */
 #include "exfat.h"
 #include <inttypes.h>
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#define __USE_UNIX98 /* for pread() in Linux */
 #include <unistd.h>
+#include <string.h>
+#ifdef USE_UBLIO
+#include <sys/uio.h>
+#include <ublio.h>
+#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;
index 295d455..521b539 100644 (file)
@@ -24,8 +24,6 @@
 #include <errno.h>
 #include <unistd.h>
 #include <sys/types.h>
-#define _XOPEN_SOURCE /* for tzset() in Linux */
-#include <time.h>
 
 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);
index 0920847..2ae724d 100644 (file)
@@ -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 (file)
index 0000000..2e22a99
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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;
+}
index 31b7e8c..c165cf9 100644 (file)
@@ -22,8 +22,6 @@
 #include <string.h>
 #include <stdio.h>
 #include <inttypes.h>
-#define _XOPEN_SOURCE /* for timezone in Linux */
-#include <time.h>
 
 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;
index 0236f88..aa636f8 100644 (file)
@@ -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 */
index 4b76a12..1a79a6b 100644 (file)
@@ -36,7 +36,7 @@ off_t cbm_size(void)
        return DIV_ROUND_UP(le32_to_cpu(sb.cluster_count), CHAR_BIT);
 }
 
-int cbm_write(off_t base, int fd)
+int cbm_write(struct exfat_dev* dev, off_t base)
 {
        uint32_t allocated_clusters =
                        DIV_ROUND_UP(cbm_size(), CLUSTER_SIZE(sb)) +
@@ -54,7 +54,7 @@ int cbm_write(off_t base, int fd)
                        BMAP_SET(bitmap, i);
                else
                        BMAP_CLR(bitmap, i);
-       if (write(fd, bitmap, bitmap_size) == -1)
+       if (exfat_write(dev, bitmap, bitmap_size) < 0)
                return errno;
        free(bitmap);
 
index 796673d..b6007e0 100644 (file)
@@ -23,6 +23,6 @@
 
 off_t cbm_alignment(void);
 off_t cbm_size(void);
-int cbm_write(off_t base, int fd);
+int cbm_write(struct exfat_dev* dev, off_t base);
 
 #endif /* ifndef MKFS_CBM_H_INCLUDED */
index 7baa573..21d74d4 100644 (file)
@@ -36,28 +36,30 @@ off_t fat_size(void)
        return (off_t) le32_to_cpu(sb.fat_sector_count) * SECTOR_SIZE(sb);
 }
 
-static cluster_t fat_write_entry(cluster_t cluster, cluster_t value, int fd)
+static cluster_t fat_write_entry(struct exfat_dev* dev, cluster_t cluster,
+               cluster_t value)
 {
        le32_t fat_entry = cpu_to_le32(value);
-       if (write(fd, &fat_entry, sizeof(fat_entry)) == -1)
+       if (exfat_write(dev, &fat_entry, sizeof(fat_entry)) < 0)
                return 0;
        return cluster + 1;
 }
 
-static cluster_t fat_write_entries(cluster_t cluster, uint64_t length, int fd)
+static cluster_t fat_write_entries(struct exfat_dev* dev, cluster_t cluster,
+               uint64_t length)
 {
        cluster_t end = cluster + DIV_ROUND_UP(length, CLUSTER_SIZE(sb));
 
        while (cluster < end - 1)
        {
-               cluster = fat_write_entry(cluster, cluster + 1, fd);
+               cluster = fat_write_entry(dev, cluster, cluster + 1);
                if (cluster == 0)
                        return 0;
        }
-       return fat_write_entry(cluster, EXFAT_CLUSTER_END, fd);
+       return fat_write_entry(dev, cluster, EXFAT_CLUSTER_END);
 }
 
-int fat_write(off_t base, int fd)
+int fat_write(struct exfat_dev* dev, off_t base)
 {
        cluster_t c = 0;
 
@@ -65,15 +67,15 @@ int fat_write(off_t base, int fd)
                exfat_bug("unexpected FAT location: %"PRIu64" (expected %u)",
                                base, le32_to_cpu(sb.fat_sector_start) * SECTOR_SIZE(sb));
 
-       if (!(c = fat_write_entry(c, 0xfffffff8, fd))) /* media type */
+       if (!(c = fat_write_entry(dev, c, 0xfffffff8))) /* media type */
                return errno;
-       if (!(c = fat_write_entry(c, 0xffffffff, fd))) /* some weird constant */
+       if (!(c = fat_write_entry(dev, c, 0xffffffff))) /* some weird constant */
                return errno;
-       if (!(c = fat_write_entries(c, cbm_size(), fd)))
+       if (!(c = fat_write_entries(dev, c, cbm_size())))
                return errno;
-       if (!(c = fat_write_entries(c, uct_size(), fd)))
+       if (!(c = fat_write_entries(dev, c, uct_size())))
                return errno;
-       if (!(c = fat_write_entries(c, rootdir_size(), fd)))
+       if (!(c = fat_write_entries(dev, c, rootdir_size())))
                return errno;
 
        return 0;
index 0df4fea..01b5c20 100644 (file)
@@ -23,6 +23,6 @@
 
 off_t fat_alignment(void);
 off_t fat_size(void);
-int fat_write(off_t base, int fd);
+int fat_write(struct exfat_dev* dev, off_t base);
 
 #endif /* ifndef MKFS_FAT_H_INCLUDED */
index 8deca15..885dd71 100644 (file)
@@ -46,7 +46,7 @@ struct exfat_structure
        int order;
        off_t (*get_alignment)(void);
        off_t (*get_size)(void);
-       int (*write_data)(off_t, int);
+       int (*write_data)(struct exfat_dev*, off_t);
 };
 
 static int init_sb(off_t volume_size, int sector_bits, int spc_bits,
@@ -99,7 +99,7 @@ static int init_sb(off_t volume_size, int sector_bits, int spc_bits,
        return 0;
 }
 
-static int erase_device(int fd)
+static int erase_device(struct exfat_dev* dev)
 {
        off_t erase_size;
        off_t erase_blocks;
@@ -123,7 +123,7 @@ static int erase_device(int fd)
 
        erase_blocks = DIV_ROUND_UP(erase_size, block_size);
 
-       if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+       if (exfat_seek(dev, 0, SEEK_SET) == (off_t) -1)
        {
                exfat_error("seek failed");
                return 1;
@@ -139,7 +139,7 @@ static int erase_device(int fd)
 
        for (i = 0; i < erase_blocks; i++)
        {
-               if (write(fd, block, block_size) == -1)
+               if (exfat_write(dev, block, block_size) < 0)
                {
                        free(block);
                        exfat_error("failed to erase block %"PRIu64, i);
@@ -183,20 +183,20 @@ static struct exfat_structure structures[] =
 };
 #undef FS_OBJECT
 
-static off_t write_structure(int fd, struct exfat_structure* structure,
-               off_t current)
+static off_t write_structure(struct exfat_dev* dev,
+               struct exfat_structure* structure, off_t current)
 {
        off_t alignment = structure->get_alignment();
        off_t base = ROUND_UP(current, alignment);
 
-       if (lseek(fd, base, SEEK_SET) == (off_t) -1)
+       if (exfat_seek(dev, base, SEEK_SET) == (off_t) -1)
        {
                exfat_error("seek to %"PRIu64" failed", base);
                return -1;
        }
        if (structure->order > 0)
        {
-               int rc = structure->write_data(base, fd);
+               int rc = structure->write_data(dev, base);
                if (rc != 0)
                {
                        exfat_error("%s creation failed: %s", structure->name,
@@ -208,7 +208,7 @@ static off_t write_structure(int fd, struct exfat_structure* structure,
        return base + structure->get_size();
 }
 
-static int write_structures(int fd)
+static int write_structures(struct exfat_dev* dev)
 {
        off_t current;
        size_t i;
@@ -220,7 +220,7 @@ static int write_structures(int fd)
                remainder = 0;
                for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
                {
-                       current = write_structure(fd, &structures[i], current);
+                       current = write_structure(dev, &structures[i], current);
                        if (current == (off_t) -1)
                                return 1;
                        remainder += structures[i].order;
@@ -243,7 +243,7 @@ static int get_spc_bits(int user_defined, off_t volume_size)
                return 8;       /* 128 KB */
 }
 
-static int set_volume_label(int fd, const char* volume_label)
+static int set_volume_label(const char* volume_label)
 {
        le16_t tmp[EXFAT_ENAME_MAX + 1];
 
@@ -253,10 +253,7 @@ static int set_volume_label(int fd, const char* volume_label)
        memset(tmp, 0, sizeof(tmp));
        if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
                                strlen(volume_label)) != 0)
-       {
-               close(fd);
                return 1;
-       }
        memcpy(label_entry.name, tmp, EXFAT_ENAME_MAX * sizeof(le16_t));
        label_entry.length = utf16_length(tmp);
        label_entry.type |= EXFAT_ENTRY_VALID;
@@ -278,32 +275,32 @@ static uint32_t get_volume_serial(uint32_t user_defined)
 static int mkfs(const char* spec, int sector_bits, int spc_bits,
                const char* volume_label, uint32_t volume_serial, int first_sector)
 {
-       int fd;
+       struct exfat_dev* dev;
        off_t volume_size;
 
-       fd = exfat_open(spec, 0);
-       if (fd < 0)
+       dev = exfat_open(spec, 0);
+       if (dev == NULL)
                return 1;
 
-       volume_size = lseek(fd, 0, SEEK_END);
+       volume_size = exfat_seek(dev, 0, SEEK_END);
        if (volume_size == (off_t) -1)
        {
-               close(fd);
+               exfat_close(dev);
                exfat_error("seek failed");
                return 1;
        }
        spc_bits = get_spc_bits(spc_bits, volume_size);
 
-       if (set_volume_label(fd, volume_label) != 0)
+       if (set_volume_label(volume_label) != 0)
        {
-               close(fd);
+               exfat_close(dev);
                return 1;
        }
 
        volume_serial = get_volume_serial(volume_serial);
        if (volume_serial == 0)
        {
-               close(fd);
+               exfat_close(dev);
                exfat_error("failed to get current time to form volume id");
                return 1;
        }
@@ -311,38 +308,34 @@ static int mkfs(const char* spec, int sector_bits, int spc_bits,
        if (init_sb(volume_size, sector_bits, spc_bits, volume_serial,
                                first_sector) != 0)
        {
-               close(fd);
+               exfat_close(dev);
                return 1;
        }
 
        printf("Creating... %2u%%", 0);
        fflush(stdout);
-       if (erase_device(fd) != 0)
+       if (erase_device(dev) != 0)
        {
-               close(fd);
+               exfat_close(dev);
                return 1;
        }
-       if (write_structures(fd) != 0)
+       if (write_structures(dev) != 0)
        {
-               close(fd);
+               exfat_close(dev);
                return 1;
        }
        puts("\b\b\b\bdone.");
 
        printf("Flushing... ");
        fflush(stdout);
-       if (fsync(fd) < 0)
+       if (exfat_fsync(dev) != 0)
        {
-               close(fd);
-               exfat_error("fsync failed");
+               exfat_close(dev);
                return 1;
        }
        puts("done.");
-       if (close(fd) < 0)
-       {
-               exfat_error("close failed");
+       if (exfat_close(dev) != 0)
                return 1;
-       }
        printf("File system created successfully.\n");
        return 0;
 }
index 4a1a361..547ca3b 100644 (file)
@@ -32,16 +32,14 @@ off_t rootdir_size(void)
        return CLUSTER_SIZE(sb);
 }
 
-int rootdir_write(off_t base, int fd)
+int rootdir_write(struct exfat_dev* dev, off_t base)
 {
-       if (write(fd, &label_entry, sizeof(struct exfat_entry)) == -1)
+       if (exfat_write(dev, &label_entry, sizeof(struct exfat_entry)) < 0)
                return 1;
-       if (write(fd, &bitmap_entry, sizeof(struct exfat_entry)) == -1)
+       if (exfat_write(dev, &bitmap_entry, sizeof(struct exfat_entry)) < 0)
                return 1;
-       if (write(fd, &upcase_entry, sizeof(struct exfat_entry)) == -1)
+       if (exfat_write(dev, &upcase_entry, sizeof(struct exfat_entry)) < 0)
                return 1;
-       /* No need to write EOD entry because the whole rootdir cluster was
-          erased by erase_device(). */
        sb.rootdir_cluster = cpu_to_le32(OFFSET_TO_CLUSTER(base));
        return 0;
 }
index 07ec2ad..f3df708 100644 (file)
@@ -23,6 +23,6 @@
 
 off_t rootdir_alignment(void);
 off_t rootdir_size(void);
-int rootdir_write(off_t base, int fd);
+int rootdir_write(struct exfat_dev* dev, off_t base);
 
 #endif /* ifndef MKFS_ROOTDIR_H_INCLUDED */
index 9038ed9..886bd32 100644 (file)
@@ -43,9 +43,9 @@ static le32_t uct_checksum(void)
        return cpu_to_le32(sum);
 }
 
-int uct_write(off_t base, int fd)
+int uct_write(struct exfat_dev* dev, off_t base)
 {
-       if (write(fd, upcase_table, sizeof(upcase_table)) == -1)
+       if (exfat_write(dev, upcase_table, sizeof(upcase_table)) < 0)
                return errno;
        upcase_entry.checksum = uct_checksum();
        upcase_entry.start_cluster = cpu_to_le32(OFFSET_TO_CLUSTER(base));
index 154c87d..b35a612 100644 (file)
@@ -23,6 +23,6 @@
 
 off_t uct_alignment(void);
 off_t uct_size(void);
-int uct_write(off_t base, int fd);
+int uct_write(struct exfat_dev* dev, off_t base);
 
 #endif /* ifndef MKFS_UCT_H_INCLUDED */
index 99535cf..4580565 100644 (file)
@@ -33,7 +33,7 @@ off_t vbr_size(void)
        return 12 * SECTOR_SIZE(sb);
 }
 
-int vbr_write(off_t base, int fd)
+int vbr_write(struct exfat_dev* dev, off_t base)
 {
        uint32_t checksum;
        le32_t* sector = malloc(SECTOR_SIZE(sb));
@@ -42,7 +42,7 @@ int vbr_write(off_t base, int fd)
        if (sector == NULL)
                return errno;
 
-       if (write(fd, &sb, sizeof(struct exfat_super_block)) == -1)
+       if (exfat_write(dev, &sb, sizeof(struct exfat_super_block)) < 0)
        {
                free(sector);
                return errno;
@@ -53,7 +53,7 @@ int vbr_write(off_t base, int fd)
        sector[SECTOR_SIZE(sb) / sizeof(sector[0]) - 1] = cpu_to_le32(0xaa550000);
        for (i = 0; i < 8; i++)
        {
-               if (write(fd, sector, SECTOR_SIZE(sb)) == -1)
+               if (exfat_write(dev, sector, SECTOR_SIZE(sb)) < 0)
                {
                        free(sector);
                        return errno;
@@ -62,13 +62,13 @@ int vbr_write(off_t base, int fd)
        }
 
        memset(sector, 0, SECTOR_SIZE(sb));
-       if (write(fd, sector, SECTOR_SIZE(sb)) == -1)
+       if (exfat_write(dev, sector, SECTOR_SIZE(sb)) < 0)
        {
                free(sector);
                return errno;
        }
        checksum = exfat_vbr_add_checksum(sector, SECTOR_SIZE(sb), checksum);
-       if (write(fd, sector, SECTOR_SIZE(sb)) == -1)
+       if (exfat_write(dev, sector, SECTOR_SIZE(sb)) < 0)
        {
                free(sector);
                return errno;
@@ -77,7 +77,7 @@ int vbr_write(off_t base, int fd)
 
        for (i = 0; i < SECTOR_SIZE(sb) / sizeof(sector[0]); i++)
                sector[i] = cpu_to_le32(checksum);
-       if (write(fd, sector, SECTOR_SIZE(sb)) == -1)
+       if (exfat_write(dev, sector, SECTOR_SIZE(sb)) < 0)
        {
                free(sector);
                return errno;
index fb6b7cd..d4cd56d 100644 (file)
@@ -23,6 +23,6 @@
 
 off_t vbr_alignment(void);
 off_t vbr_size(void);
-int vbr_write(off_t base, int fd);
+int vbr_write(struct exfat_dev* dev, off_t base);
 
 #endif /* ifndef MKFS_VBR_H_INCLUDED */