X-Git-Url: http://git.sven.stormbind.net/?a=blobdiff_plain;f=libexfat%2Fio.c;h=bc92c7cf9eab396eb465b93400cff7fc448a778b;hb=HEAD;hp=bae0cf15a1412dfdac2c67b4085d35aa56361bc6;hpb=30a13991ba3d86fa3f98db674dcafbb430eab372;p=sven%2Ffuse-exfat.git diff --git a/libexfat/io.c b/libexfat/io.c index bae0cf1..7af5316 100644 --- a/libexfat/io.c +++ b/libexfat/io.c @@ -3,7 +3,7 @@ exFAT file system implementation library. Free exFAT implementation. - Copyright (C) 2010-2018 Andrew Nayenko + Copyright (C) 2010-2023 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 @@ -35,15 +35,25 @@ #include #include #include +#elif defined(__NetBSD__) +#include #elif __linux__ #include #endif +#ifdef USE_UBLIO +#include +#include +#endif struct exfat_dev { int fd; enum exfat_mode mode; off_t size; /* in bytes */ +#ifdef USE_UBLIO + off_t pos; + ublio_filehandle_t ufh; +#endif }; static bool is_open(int fd) @@ -80,6 +90,9 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode) { struct exfat_dev* dev; struct stat stbuf; +#ifdef USE_UBLIO + struct ublio_param up; +#endif /* The system allocates file descriptors sequentially. If we have been started with stdin (0), stdout (1) or stderr (2) closed, the system @@ -215,6 +228,21 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode) "you can fix this with fdisk(8)"); } else +#elif defined(__NetBSD__) + if (!S_ISREG(stbuf.st_mode)) + { + off_t size; + + if (ioctl(dev->fd, DIOCGMEDIASIZE, &size) == -1) + { + close(dev->fd); + free(dev); + exfat_error("failed to get media size"); + return NULL; + } + dev->size = size; + } + else #endif { /* works for Linux, FreeBSD, Solaris */ @@ -235,6 +263,24 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode) } } +#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; } @@ -242,6 +288,13 @@ int exfat_close(struct exfat_dev* dev) { int rc = 0; +#ifdef USE_UBLIO + if (ublio_close(dev->ufh) != 0) + { + exfat_error("failed to close ublio"); + rc = -EIO; + } +#endif if (close(dev->fd) != 0) { exfat_error("failed to close device: %s", strerror(errno)); @@ -255,6 +308,13 @@ int exfat_fsync(struct exfat_dev* dev) { int rc = 0; +#ifdef USE_UBLIO + if (ublio_fsync(dev->ufh) != 0) + { + exfat_error("ublio fsync failed"); + rc = -EIO; + } +#endif if (fsync(dev->fd) != 0) { exfat_error("fsync failed: %s", strerror(errno)); @@ -275,52 +335,98 @@ off_t exfat_get_size(const struct exfat_dev* dev) 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 } ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size) { +#ifdef USE_UBLIO + ssize_t result = ublio_pwrite(dev->ufh, (void*) buffer, size, dev->pos); + if (result >= 0) + dev->pos += size; + return result; +#else return write(dev->fd, buffer, size); +#endif } ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size, off_t offset) { +#ifdef USE_UBLIO + return ublio_pread(dev->ufh, buffer, size, offset); +#else return pread(dev->fd, buffer, size, offset); +#endif } ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, off_t offset) { +#ifdef USE_UBLIO + return ublio_pwrite(dev->ufh, (void*) buffer, size, offset); +#else return pwrite(dev->fd, buffer, size, offset); +#endif } ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, void* buffer, size_t size, off_t offset) { + uint64_t uoffset = offset; cluster_t cluster; char* bufp = buffer; off_t lsize, loffset, remainder; - if (offset >= node->size) + if (offset < 0) + return -EINVAL; + if (uoffset >= node->size) return 0; if (size == 0) return 0; - cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb)); + if (uoffset + size > node->valid_size) + { + ssize_t bytes = 0; + + if (uoffset < node->valid_size) + { + bytes = exfat_generic_pread(ef, node, buffer, + node->valid_size - uoffset, offset); + if (bytes < 0 || (size_t) bytes < node->valid_size - uoffset) + return bytes; + } + memset(buffer + bytes, 0, + MIN(size - bytes, node->size - node->valid_size)); + return MIN(size, node->size - uoffset); + } + + cluster = exfat_advance_cluster(ef, node, uoffset / CLUSTER_SIZE(*ef->sb)); if (CLUSTER_INVALID(*ef->sb, cluster)) { exfat_error("invalid cluster 0x%x while reading", cluster); return -EIO; } - loffset = offset % CLUSTER_SIZE(*ef->sb); - remainder = MIN(size, node->size - offset); + loffset = uoffset % CLUSTER_SIZE(*ef->sb); + remainder = MIN(size, node->size - uoffset); while (remainder > 0) { if (CLUSTER_INVALID(*ef->sb, cluster)) @@ -342,40 +448,43 @@ ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, } if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime) exfat_update_atime(node); - return MIN(size, node->size - offset) - remainder; + return MIN(size, node->size - uoffset) - remainder; } ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, const void* buffer, size_t size, off_t offset) { + uint64_t uoffset = offset; int rc; cluster_t cluster; const char* bufp = buffer; off_t lsize, loffset, remainder; - if (offset > node->size) + if (offset < 0) + return -EINVAL; + if (uoffset > node->size) { - rc = exfat_truncate(ef, node, offset, true); + rc = exfat_truncate(ef, node, uoffset, true); if (rc != 0) return rc; } - if (offset + size > node->size) + if (uoffset + size > node->size) { - rc = exfat_truncate(ef, node, offset + size, false); + rc = exfat_truncate(ef, node, uoffset + size, false); if (rc != 0) return rc; } if (size == 0) return 0; - cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb)); + cluster = exfat_advance_cluster(ef, node, uoffset / CLUSTER_SIZE(*ef->sb)); if (CLUSTER_INVALID(*ef->sb, cluster)) { exfat_error("invalid cluster 0x%x while writing", cluster); return -EIO; } - loffset = offset % CLUSTER_SIZE(*ef->sb); + loffset = uoffset % CLUSTER_SIZE(*ef->sb); remainder = size; while (remainder > 0) { @@ -394,6 +503,7 @@ ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, bufp += lsize; loffset = 0; remainder -= lsize; + node->valid_size = MAX(node->valid_size, uoffset + size - remainder); cluster = exfat_next_cluster(ef, node, cluster); } if (!(node->attrib & EXFAT_ATTRIB_DIR))