+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.
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()
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))
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)
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)
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);
}
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;
}
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)
}
ef->cmap.dirty = 1;
- /* FIXME update percentage of used space */
return 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,
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,
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;
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,
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);
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 */
#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)
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;
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;
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;
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;
#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)
{
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)
{
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)
{
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;
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);
}
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;
/* 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);
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;
}
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;
exfat_reset_cache(ef);
free(ef->root);
free(ef->zero_cluster);
- close(ef->fd);
+ exfat_close(ef->dev);
free(ef->sb);
return -EIO;
}
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);
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;
}
/* 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;
}
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)
{
}
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:
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:
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);
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;
}
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));
}
}
{
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))
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;
}
*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);
&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;
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);
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;
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++)
{
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));
{
struct iterator it;
int rc;
- const struct exfat_entry* entry;
rc = opendir(ef, ef->root, &it);
if (rc != 0)
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;
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;
}
--- /dev/null
+/*
+ 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;
+}
#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)
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)
return sum;
}
-
le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name)
{
size_t i;
#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 */