X-Git-Url: https://git.sven.stormbind.net/?a=blobdiff_plain;f=libexfat%2Fio.c;h=0fc35d28b7cca2c7687869115c4e5eaf12cf812a;hb=1e7534bbaa34e6c2a8c9809dfcf6588112cbdee4;hp=683461b6def445c01fba666ea67f278199a68912;hpb=8f3a347d4472c8e5a73dd555c37c60e5d5368bc0;p=sven%2Ffuse-exfat.git diff --git a/libexfat/io.c b/libexfat/io.c index 683461b..0fc35d2 100644 --- a/libexfat/io.c +++ b/libexfat/io.c @@ -3,7 +3,7 @@ exFAT file system implementation library. Free exFAT implementation. - Copyright (C) 2010-2014 Andrew Nayenko + Copyright (C) 2010-2018 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,8 +35,11 @@ #include #include #include -#endif +#elif defined(__NetBSD__) +#include +#elif __linux__ #include +#endif #ifdef USE_UBLIO #include #include @@ -53,6 +56,11 @@ struct exfat_dev #endif }; +static bool is_open(int fd) +{ + return fcntl(fd, F_GETFD) != -1; +} + static int open_ro(const char* spec) { return open(spec, O_RDONLY); @@ -86,6 +94,24 @@ struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode) 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 + will give us descriptor 0, 1 or 2 later when we open block device, + FUSE communication pipe, etc. As a result, functions using stdin, + stdout or stderr will actually work with a different thing and can + corrupt it. Protect descriptors 0, 1 and 2 from such misuse. */ + while (!is_open(STDIN_FILENO) + || !is_open(STDOUT_FILENO) + || !is_open(STDERR_FILENO)) + { + /* we don't need those descriptors, let them leak */ + if (open("/dev/null", O_RDWR) == -1) + { + exfat_error("failed to open /dev/null"); + return NULL; + } + } + dev = malloc(sizeof(struct exfat_dev)); if (dev == NULL) { @@ -202,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 */ @@ -317,7 +358,7 @@ 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) { #ifdef USE_UBLIO - ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos); + ssize_t result = ublio_pwrite(dev->ufh, (void*) buffer, size, dev->pos); if (result >= 0) dev->pos += size; return result; @@ -340,7 +381,7 @@ 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, buffer, size, offset); + return ublio_pwrite(dev->ufh, (void*) buffer, size, offset); #else return pwrite(dev->fd, buffer, size, offset); #endif @@ -349,92 +390,108 @@ ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, void* buffer, size_t size, off_t offset) { + uint64_t newsize = offset; cluster_t cluster; char* bufp = buffer; off_t lsize, loffset, remainder; - if (offset >= node->size) + if (offset < 0) + return -EINVAL; + if (newsize >= node->size) return 0; if (size == 0) return 0; - cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb)); - if (CLUSTER_INVALID(cluster)) + cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb)); + if (CLUSTER_INVALID(*ef->sb, cluster)) { exfat_error("invalid cluster 0x%x while reading", cluster); - return -1; + return -EIO; } - loffset = offset % CLUSTER_SIZE(*ef->sb); - remainder = MIN(size, node->size - offset); + loffset = newsize % CLUSTER_SIZE(*ef->sb); + remainder = MIN(size, node->size - newsize); while (remainder > 0) { - if (CLUSTER_INVALID(cluster)) + if (CLUSTER_INVALID(*ef->sb, cluster)) { exfat_error("invalid cluster 0x%x while reading", cluster); - return -1; + return -EIO; } lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder); if (exfat_pread(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset) < 0) { exfat_error("failed to read cluster %#x", cluster); - return -1; + return -EIO; } 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; + return MIN(size, node->size - newsize) - remainder; } ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, const void* buffer, size_t size, off_t offset) { + uint64_t newsize = offset; + int rc; cluster_t cluster; const char* bufp = buffer; off_t lsize, loffset, remainder; - 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 (offset < 0) + return -EINVAL; + if (newsize > node->size) + { + rc = exfat_truncate(ef, node, newsize, true); + if (rc != 0) + return rc; + } + if (newsize + size > node->size) + { + rc = exfat_truncate(ef, node, newsize + size, false); + if (rc != 0) + return rc; + } if (size == 0) return 0; - cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb)); - if (CLUSTER_INVALID(cluster)) + cluster = exfat_advance_cluster(ef, node, newsize / CLUSTER_SIZE(*ef->sb)); + if (CLUSTER_INVALID(*ef->sb, cluster)) { exfat_error("invalid cluster 0x%x while writing", cluster); - return -1; + return -EIO; } - loffset = offset % CLUSTER_SIZE(*ef->sb); + loffset = newsize % CLUSTER_SIZE(*ef->sb); remainder = size; while (remainder > 0) { - if (CLUSTER_INVALID(cluster)) + if (CLUSTER_INVALID(*ef->sb, cluster)) { exfat_error("invalid cluster 0x%x while writing", cluster); - return -1; + return -EIO; } lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder); if (exfat_pwrite(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset) < 0) { exfat_error("failed to write cluster %#x", cluster); - return -1; + return -EIO; } 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; }