]> git.sven.stormbind.net Git - sven/exfat-utils.git/blobdiff - libexfat/io.c
Update the Homepage field in d/control.
[sven/exfat-utils.git] / libexfat / io.c
index 4413aaa52ce5b5256a5706a011e652368c0bfc6d..3d7aaad2e903db2f2dbacd56d8bac62816560f46 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-2015  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
+#include <sys/mount.h>
 #ifdef USE_UBLIO
 #include <sys/uio.h>
 #include <ublio.h>
 #ifdef USE_UBLIO
 #include <sys/uio.h>
 #include <ublio.h>
@@ -63,6 +71,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
@@ -91,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-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 +111,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 +128,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 +140,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 +149,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 +176,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,14 +210,14 @@ 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;
                }
        }
@@ -208,32 +245,41 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
 
 int exfat_close(struct exfat_dev* dev)
 {
 
 int exfat_close(struct exfat_dev* dev)
 {
+       int rc = 0;
+
 #ifdef USE_UBLIO
        if (ublio_close(dev->ufh) != 0)
 #ifdef USE_UBLIO
        if (ublio_close(dev->ufh) != 0)
+       {
                exfat_error("failed to close ublio");
                exfat_error("failed to close ublio");
+               rc = -EIO;
+       }
 #endif
        if (close(dev->fd) != 0)
        {
 #endif
        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)
 {
+       int rc = 0;
+
 #ifdef USE_UBLIO
        if (ublio_fsync(dev->ufh) != 0)
 #ifdef USE_UBLIO
        if (ublio_fsync(dev->ufh) != 0)
-#else
-       if (fsync(dev->fd) != 0)
+       {
+               exfat_error("ublio fsync failed");
+               rc = -EIO;
+       }
 #endif
 #endif
+       if (fsync(dev->fd) != 0)
        {
        {
-               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)
@@ -280,28 +326,24 @@ ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
 #endif
 }
 
 #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)
 {
 #ifdef USE_UBLIO
                off_t offset)
 {
 #ifdef USE_UBLIO
-       if (ublio_pread(dev->ufh, buffer, size, offset) != size)
+       return ublio_pread(dev->ufh, buffer, size, offset);
 #else
 #else
-       if (pread(dev->fd, buffer, size, offset) != size)
+       return pread(dev->fd, buffer, size, offset);
 #endif
 #endif
-               exfat_bug("failed to read %zu bytes from file at %"PRIu64, size,
-                               (uint64_t) 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)
 {
 #ifdef USE_UBLIO
                off_t offset)
 {
 #ifdef USE_UBLIO
-       if (ublio_pwrite(dev->ufh, buffer, size, offset) != size)
+       return ublio_pwrite(dev->ufh, buffer, size, offset);
 #else
 #else
-       if (pwrite(dev->fd, buffer, size, offset) != size)
+       return pwrite(dev->fd, buffer, size, offset);
 #endif
 #endif
-               exfat_bug("failed to write %zu bytes to file at %"PRIu64, size,
-                               (uint64_t) 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,
@@ -333,7 +375,12 @@ ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
                        return -1;
                }
                lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
                        return -1;
                }
                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 -1;
+               }
                bufp += lsize;
                loffset = 0;
                remainder -= lsize;
                bufp += lsize;
                loffset = 0;
                remainder -= lsize;
@@ -351,9 +398,12 @@ ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
        const char* bufp = buffer;
        off_t lsize, loffset, remainder;
 
        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)
+               if (exfat_truncate(ef, node, offset, true) != 0)
+                       return -1;
+       if (offset + size > node->size)
+               if (exfat_truncate(ef, node, offset + size, false) != 0)
+                       return -1;
        if (size == 0)
                return 0;
 
        if (size == 0)
                return 0;
 
@@ -374,7 +424,12 @@ ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
                        return -1;
                }
                lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
                        return -1;
                }
                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 -1;
+               }
                bufp += lsize;
                loffset = 0;
                remainder -= lsize;
                bufp += lsize;
                loffset = 0;
                remainder -= lsize;