]> git.sven.stormbind.net Git - sven/exfat-utils.git/blobdiff - libexfat/io.c
New upstream version 1.2.6
[sven/exfat-utils.git] / libexfat / io.c
index 4413aaa52ce5b5256a5706a011e652368c0bfc6d..514383ca4bedb64bf12ed4a28c6e4bbf3c87d6f6 100644 (file)
@@ -2,11 +2,12 @@
        io.c (02.09.09)
        exFAT file system implementation library.
 
        io.c (02.09.09)
        exFAT file system implementation library.
 
-       Copyright (C) 2010-2013  Andrew Nayenko
+       Free exFAT implementation.
+       Copyright (C) 2010-2017  Andrew Nayenko
 
 
-       This program is free software: you can redistribute it and/or modify
+       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
        it under the terms of the GNU General Public License as published by
-       the Free Software Foundation, either version 3 of the License, or
+       the Free Software Foundation, either version 2 of the License, or
        (at your option) any later version.
 
        This program is distributed in the hope that it will be useful,
        (at your option) any later version.
 
        This program is distributed in the hope that it will be useful,
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
 
        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/>.
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
 #include "exfat.h"
 #include <inttypes.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 */
 
 #include "exfat.h"
 #include <inttypes.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/mount.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <string.h>
-#ifdef __APPLE__
+#include <errno.h>
+#if defined(__APPLE__)
 #include <sys/disk.h>
 #include <sys/disk.h>
+#elif defined(__OpenBSD__)
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#include <sys/ioctl.h>
 #endif
 #endif
-#ifdef USE_UBLIO
-#include <sys/uio.h>
-#include <ublio.h>
-#endif
+#include <sys/mount.h>
 
 struct exfat_dev
 {
        int fd;
        enum exfat_mode mode;
        off_t size; /* in bytes */
 
 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 int open_ro(const char* spec)
 };
 
 static int open_ro(const char* spec)
@@ -63,6 +63,7 @@ static int open_rw(const char* spec)
        if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
        {
                close(fd);
        if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
        {
                close(fd);
+               errno = EROFS;
                return -1;
        }
 #endif
                return -1;
        }
 #endif
@@ -73,9 +74,6 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
 {
        struct exfat_dev* dev;
        struct stat stbuf;
 {
        struct exfat_dev* dev;
        struct stat stbuf;
-#ifdef USE_UBLIO
-       struct ublio_param up;
-#endif
 
        dev = malloc(sizeof(struct exfat_dev));
        if (dev == NULL)
 
        dev = malloc(sizeof(struct exfat_dev));
        if (dev == NULL)
@@ -91,7 +89,8 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
                if (dev->fd == -1)
                {
                        free(dev);
                if (dev->fd == -1)
                {
                        free(dev);
-                       exfat_error("failed to open `%s' in read-only mode", spec);
+                       exfat_error("failed to open '%s' in read-only mode: %s", spec,
+                                       strerror(errno));
                        return NULL;
                }
                dev->mode = EXFAT_MODE_RO;
                        return NULL;
                }
                dev->mode = EXFAT_MODE_RO;
@@ -101,7 +100,8 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
                if (dev->fd == -1)
                {
                        free(dev);
                if (dev->fd == -1)
                {
                        free(dev);
-                       exfat_error("failed to open `%s' in read-write mode", spec);
+                       exfat_error("failed to open '%s' in read-write mode: %s", spec,
+                                       strerror(errno));
                        return NULL;
                }
                dev->mode = EXFAT_MODE_RW;
                        return NULL;
                }
                dev->mode = EXFAT_MODE_RW;
@@ -117,11 +117,11 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
                if (dev->fd != -1)
                {
                        dev->mode = EXFAT_MODE_RO;
                if (dev->fd != -1)
                {
                        dev->mode = EXFAT_MODE_RO;
-                       exfat_warn("`%s' is write-protected, mounting read-only", spec);
+                       exfat_warn("'%s' is write-protected, mounting read-only", spec);
                        break;
                }
                free(dev);
                        break;
                }
                free(dev);
-               exfat_error("failed to open `%s'", spec);
+               exfat_error("failed to open '%s': %s", spec, strerror(errno));
                return NULL;
        }
 
                return NULL;
        }
 
@@ -129,7 +129,7 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
        {
                close(dev->fd);
                free(dev);
        {
                close(dev->fd);
                free(dev);
-               exfat_error("failed to fstat `%s'", spec);
+               exfat_error("failed to fstat '%s'", spec);
                return NULL;
        }
        if (!S_ISBLK(stbuf.st_mode) &&
                return NULL;
        }
        if (!S_ISBLK(stbuf.st_mode) &&
@@ -138,11 +138,11 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
        {
                close(dev->fd);
                free(dev);
        {
                close(dev->fd);
                free(dev);
-               exfat_error("`%s' is neither a device, nor a regular file", spec);
+               exfat_error("'%s' is neither a device, nor a regular file", spec);
                return NULL;
        }
 
                return NULL;
        }
 
-#ifdef __APPLE__
+#if defined(__APPLE__)
        if (!S_ISREG(stbuf.st_mode))
        {
                uint32_t block_size = 0;
        if (!S_ISREG(stbuf.st_mode))
        {
                uint32_t block_size = 0;
@@ -165,6 +165,32 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
                dev->size = blocks * block_size;
        }
        else
                dev->size = blocks * block_size;
        }
        else
+#elif defined(__OpenBSD__)
+       if (!S_ISREG(stbuf.st_mode))
+       {
+               struct disklabel lab;
+               struct partition* pp;
+               char* partition;
+
+               if (ioctl(dev->fd, DIOCGDINFO, &lab) == -1)
+               {
+                       close(dev->fd);
+                       free(dev);
+                       exfat_error("failed to get disklabel");
+                       return NULL;
+               }
+
+               /* Don't need to check that partition letter is valid as we won't get
+                  this far otherwise. */
+               partition = strchr(spec, '\0') - 1;
+               pp = &(lab.d_partitions[*partition - 'a']);
+               dev->size = DL_GETPSIZE(pp) * lab.d_secsize;
+
+               if (pp->p_fstype != FS_NTFS)
+                       exfat_warn("partition type is not 0x07 (NTFS/exFAT); "
+                                       "you can fix this with fdisk(8)");
+       }
+       else
 #endif
        {
                /* works for Linux, FreeBSD, Solaris */
 #endif
        {
                /* works for Linux, FreeBSD, Solaris */
@@ -173,67 +199,44 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
                {
                        close(dev->fd);
                        free(dev);
                {
                        close(dev->fd);
                        free(dev);
-                       exfat_error("failed to get size of `%s'", spec);
+                       exfat_error("failed to get size of '%s'", spec);
                        return NULL;
                }
                if (exfat_seek(dev, 0, SEEK_SET) == -1)
                {
                        close(dev->fd);
                        free(dev);
                        return NULL;
                }
                if (exfat_seek(dev, 0, SEEK_SET) == -1)
                {
                        close(dev->fd);
                        free(dev);
-                       exfat_error("failed to seek to the beginning of `%s'", spec);
+                       exfat_error("failed to seek to the beginning of '%s'", spec);
                        return NULL;
                }
        }
 
                        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)
 {
        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
+       int rc = 0;
+
        if (close(dev->fd) != 0)
        {
        if (close(dev->fd) != 0)
        {
-               free(dev);
-               exfat_error("failed to close device");
-               return 1;
+               exfat_error("failed to close device: %s", strerror(errno));
+               rc = -EIO;
        }
        free(dev);
        }
        free(dev);
-       return 0;
+       return rc;
 }
 
 int exfat_fsync(struct exfat_dev* dev)
 {
 }
 
 int exfat_fsync(struct exfat_dev* dev)
 {
-#ifdef USE_UBLIO
-       if (ublio_fsync(dev->ufh) != 0)
-#else
+       int rc = 0;
+
        if (fsync(dev->fd) != 0)
        if (fsync(dev->fd) != 0)
-#endif
        {
        {
-               exfat_error("fsync failed");
-               return 1;
+               exfat_error("fsync failed: %s", strerror(errno));
+               rc = -EIO;
        }
        }
-       return 0;
+       return rc;
 }
 
 enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
 }
 
 enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
@@ -248,60 +251,29 @@ off_t exfat_get_size(const struct exfat_dev* dev)
 
 off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
 {
 
 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);
        return lseek(dev->fd, offset, whence);
-#endif
 }
 
 ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
 {
 }
 
 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);
        return read(dev->fd, buffer, size);
-#endif
 }
 
 ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
 {
 }
 
 ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t 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);
        return write(dev->fd, buffer, size);
-#endif
 }
 
 }
 
-void exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
+ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
                off_t offset)
 {
                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);
+       return pread(dev->fd, buffer, size, offset);
 }
 
 }
 
-void exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
+ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
                off_t offset)
 {
                off_t offset)
 {
-#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);
+       return pwrite(dev->fd, buffer, size, offset);
 }
 
 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
 }
 
 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
@@ -320,7 +292,7 @@ ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
        if (CLUSTER_INVALID(cluster))
        {
                exfat_error("invalid cluster 0x%x while reading", cluster);
        if (CLUSTER_INVALID(cluster))
        {
                exfat_error("invalid cluster 0x%x while reading", cluster);
-               return -1;
+               return -EIO;
        }
 
        loffset = offset % CLUSTER_SIZE(*ef->sb);
        }
 
        loffset = offset % CLUSTER_SIZE(*ef->sb);
@@ -330,16 +302,21 @@ ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
                if (CLUSTER_INVALID(cluster))
                {
                        exfat_error("invalid cluster 0x%x while reading", cluster);
                if (CLUSTER_INVALID(cluster))
                {
                        exfat_error("invalid cluster 0x%x while reading", cluster);
-                       return -1;
+                       return -EIO;
                }
                lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
                }
                lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
-               exfat_pread(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
+               if (exfat_pread(ef->dev, bufp, lsize,
+                                       exfat_c2o(ef, cluster) + loffset) < 0)
+               {
+                       exfat_error("failed to read cluster %#x", cluster);
+                       return -EIO;
+               }
                bufp += lsize;
                loffset = 0;
                remainder -= lsize;
                cluster = exfat_next_cluster(ef, node, cluster);
        }
                bufp += lsize;
                loffset = 0;
                remainder -= lsize;
                cluster = exfat_next_cluster(ef, node, cluster);
        }
-       if (!ef->ro && !ef->noatime)
+       if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime)
                exfat_update_atime(node);
        return MIN(size, node->size - offset) - remainder;
 }
                exfat_update_atime(node);
        return MIN(size, node->size - offset) - remainder;
 }
@@ -347,13 +324,23 @@ ssize_t exfat_generic_pread(const 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)
 {
 ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
                const void* buffer, size_t size, off_t offset)
 {
+       int rc;
        cluster_t cluster;
        const char* bufp = buffer;
        off_t lsize, loffset, remainder;
 
        cluster_t cluster;
        const char* bufp = buffer;
        off_t lsize, loffset, remainder;
 
-       if (offset + size > node->size)
-               if (exfat_truncate(ef, node, offset + size) != 0)
-                       return -1;
+       if (offset > node->size)
+       {
+               rc = exfat_truncate(ef, node, offset, true);
+               if (rc != 0)
+                       return rc;
+       }
+       if (offset + size > node->size)
+       {
+               rc = exfat_truncate(ef, node, offset + size, false);
+               if (rc != 0)
+                       return rc;
+       }
        if (size == 0)
                return 0;
 
        if (size == 0)
                return 0;
 
@@ -361,7 +348,7 @@ ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
        if (CLUSTER_INVALID(cluster))
        {
                exfat_error("invalid cluster 0x%x while writing", cluster);
        if (CLUSTER_INVALID(cluster))
        {
                exfat_error("invalid cluster 0x%x while writing", cluster);
-               return -1;
+               return -EIO;
        }
 
        loffset = offset % CLUSTER_SIZE(*ef->sb);
        }
 
        loffset = offset % CLUSTER_SIZE(*ef->sb);
@@ -371,15 +358,23 @@ ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
                if (CLUSTER_INVALID(cluster))
                {
                        exfat_error("invalid cluster 0x%x while writing", cluster);
                if (CLUSTER_INVALID(cluster))
                {
                        exfat_error("invalid cluster 0x%x while writing", cluster);
-                       return -1;
+                       return -EIO;
                }
                lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
                }
                lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
-               exfat_pwrite(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
+               if (exfat_pwrite(ef->dev, bufp, lsize,
+                               exfat_c2o(ef, cluster) + loffset) < 0)
+               {
+                       exfat_error("failed to write cluster %#x", cluster);
+                       return -EIO;
+               }
                bufp += lsize;
                loffset = 0;
                remainder -= lsize;
                cluster = exfat_next_cluster(ef, node, cluster);
        }
                bufp += lsize;
                loffset = 0;
                remainder -= lsize;
                cluster = exfat_next_cluster(ef, node, cluster);
        }
-       exfat_update_mtime(node);
+       if (!(node->attrib & EXFAT_ATTRIB_DIR))
+               /* directory's mtime should be updated by the caller only when it
+                  creates or removes something in this directory */
+               exfat_update_mtime(node);
        return size - remainder;
 }
        return size - remainder;
 }