]> git.sven.stormbind.net Git - sven/fuse-exfat.git/blobdiff - fuse/main.c
Imported Upstream version 1.1.0
[sven/fuse-exfat.git] / fuse / main.c
index 15273473b7ce2ead25f5c972e55b5c2a713b263e..58b7b2f12c8cdb6ab8afd8738ef53d8d32187e68 100644 (file)
@@ -2,11 +2,12 @@
        main.c (01.09.09)
        FUSE-based exFAT implementation. Requires FUSE 2.6 or later.
 
        main.c (01.09.09)
        FUSE-based exFAT implementation. Requires FUSE 2.6 or later.
 
-       Copyright (C) 2010-2013  Andrew Nayenko
+       Free exFAT implementation.
+       Copyright (C) 2010-2014  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,
@@ -14,8 +15,9 @@
        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.
 */
 
 #define FUSE_USE_VERSION 26
 */
 
 #define FUSE_USE_VERSION 26
@@ -80,7 +82,14 @@ static int fuse_exfat_truncate(const char* path, off_t size)
        if (rc != 0)
                return rc;
 
        if (rc != 0)
                return rc;
 
-       rc = exfat_truncate(&ef, node, size);
+       rc = exfat_truncate(&ef, node, size, true);
+       if (rc != 0)
+       {
+               exfat_flush_node(&ef, node);    /* ignore return code */
+               exfat_put_node(&ef, node);
+               return rc;
+       }
+       rc = exfat_flush_node(&ef, node);
        exfat_put_node(&ef, node);
        return rc;
 }
        exfat_put_node(&ef, node);
        return rc;
 }
@@ -92,7 +101,7 @@ static int fuse_exfat_readdir(const char* path, void* buffer,
        struct exfat_node* node;
        struct exfat_iterator it;
        int rc;
        struct exfat_node* node;
        struct exfat_iterator it;
        int rc;
-       char name[EXFAT_NAME_MAX + 1];
+       char name[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
 
        exfat_debug("[%s] %s", __func__, path);
 
 
        exfat_debug("[%s] %s", __func__, path);
 
@@ -102,7 +111,7 @@ static int fuse_exfat_readdir(const char* path, void* buffer,
        if (!(parent->flags & EXFAT_ATTRIB_DIR))
        {
                exfat_put_node(&ef, parent);
        if (!(parent->flags & EXFAT_ATTRIB_DIR))
        {
                exfat_put_node(&ef, parent);
-               exfat_error("`%s' is not a directory (0x%x)", path, parent->flags);
+               exfat_error("'%s' is not a directory (0x%x)", path, parent->flags);
                return -ENOTDIR;
        }
 
                return -ENOTDIR;
        }
 
@@ -113,12 +122,12 @@ static int fuse_exfat_readdir(const char* path, void* buffer,
        if (rc != 0)
        {
                exfat_put_node(&ef, parent);
        if (rc != 0)
        {
                exfat_put_node(&ef, parent);
-               exfat_error("failed to open directory `%s'", path);
+               exfat_error("failed to open directory '%s'", path);
                return rc;
        }
        while ((node = exfat_readdir(&ef, &it)))
        {
                return rc;
        }
        while ((node = exfat_readdir(&ef, &it)))
        {
-               exfat_get_name(node, name, EXFAT_NAME_MAX);
+               exfat_get_name(node, name, sizeof(name) - 1);
                exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
                                name, IS_CONTIGUOUS(*node) ? "contiguous" : "fragmented",
                                node->size, node->start_cluster);
                exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
                                name, IS_CONTIGUOUS(*node) ? "contiguous" : "fragmented",
                                node->size, node->start_cluster);
@@ -147,27 +156,64 @@ static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
 
 static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
 {
 
 static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
 {
+       /*
+          This handler is called by FUSE on close() syscall. If the FUSE
+          implementation does not call flush handler, we will flush node here.
+          But in this case we will not be able to return an error to the caller.
+          See fuse_exfat_flush() below.
+       */
        exfat_debug("[%s] %s", __func__, path);
        exfat_debug("[%s] %s", __func__, path);
+       exfat_flush_node(&ef, get_node(fi));
        exfat_put_node(&ef, get_node(fi));
        exfat_put_node(&ef, get_node(fi));
-       return 0;
+       return 0; /* FUSE ignores this return value */
+}
+
+static int fuse_exfat_flush(const char* path, struct fuse_file_info* fi)
+{
+       /*
+          This handler may be called by FUSE on close() syscall. FUSE also deals
+          with removals of open files, so we don't free clusters on close but
+          only on rmdir and unlink. If the FUSE implementation does not call this
+          handler we will flush node on release. See fuse_exfat_relase() above.
+       */
+       exfat_debug("[%s] %s", __func__, path);
+       return exfat_flush_node(&ef, get_node(fi));
+}
+
+static int fuse_exfat_fsync(const char* path, int datasync,
+               struct fuse_file_info *fi)
+{
+       int rc;
+
+       exfat_debug("[%s] %s", __func__, path);
+       rc = exfat_flush(&ef);
+       if (rc != 0)
+               return rc;
+       return exfat_fsync(ef.dev);
 }
 
 static int fuse_exfat_read(const char* path, char* buffer, size_t size,
                off_t offset, struct fuse_file_info* fi)
 {
 }
 
 static int fuse_exfat_read(const char* path, char* buffer, size_t size,
                off_t offset, struct fuse_file_info* fi)
 {
+       ssize_t ret;
+
        exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
        exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
-       if (exfat_generic_pread(&ef, get_node(fi), buffer, size, offset) != size)
-               return EOF;
-       return size;
+       ret = exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
+       if (ret < 0)
+               return -EIO;
+       return ret;
 }
 
 static int fuse_exfat_write(const char* path, const char* buffer, size_t size,
                off_t offset, struct fuse_file_info* fi)
 {
 }
 
 static int fuse_exfat_write(const char* path, const char* buffer, size_t size,
                off_t offset, struct fuse_file_info* fi)
 {
+       ssize_t ret;
+
        exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
        exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
-       if (exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset) != size)
-               return EOF;
-       return size;
+       ret = exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
+       if (ret < 0)
+               return -EIO;
+       return ret;
 }
 
 static int fuse_exfat_unlink(const char* path)
 }
 
 static int fuse_exfat_unlink(const char* path)
@@ -183,7 +229,9 @@ static int fuse_exfat_unlink(const char* path)
 
        rc = exfat_unlink(&ef, node);
        exfat_put_node(&ef, node);
 
        rc = exfat_unlink(&ef, node);
        exfat_put_node(&ef, node);
-       return rc;
+       if (rc != 0)
+               return rc;
+       return exfat_cleanup_node(&ef, node);
 }
 
 static int fuse_exfat_rmdir(const char* path)
 }
 
 static int fuse_exfat_rmdir(const char* path)
@@ -199,7 +247,9 @@ static int fuse_exfat_rmdir(const char* path)
 
        rc = exfat_rmdir(&ef, node);
        exfat_put_node(&ef, node);
 
        rc = exfat_rmdir(&ef, node);
        exfat_put_node(&ef, node);
-       return rc;
+       if (rc != 0)
+               return rc;
+       return exfat_cleanup_node(&ef, node);
 }
 
 static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev)
 }
 
 static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev)
@@ -232,18 +282,29 @@ static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
                return rc;
 
        exfat_utimes(node, tv);
                return rc;
 
        exfat_utimes(node, tv);
+       rc = exfat_flush_node(&ef, node);
        exfat_put_node(&ef, node);
        exfat_put_node(&ef, node);
-       return 0;
+       return rc;
 }
 
 }
 
-#ifdef __APPLE__
 static int fuse_exfat_chmod(const char* path, mode_t mode)
 {
 static int fuse_exfat_chmod(const char* path, mode_t mode)
 {
+       const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
+                       S_IRWXU | S_IRWXG | S_IRWXO;
+
        exfat_debug("[%s] %s 0%ho", __func__, path, mode);
        exfat_debug("[%s] %s 0%ho", __func__, path, mode);
-       /* make OS X utilities happy */
+       if (mode & ~VALID_MODE_MASK)
+               return -EPERM;
+       return 0;
+}
+
+static int fuse_exfat_chown(const char* path, uid_t uid, gid_t gid)
+{
+       exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
+       if (uid != ef.uid || gid != ef.gid)
+               return -EPERM;
        return 0;
 }
        return 0;
 }
-#endif
 
 static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
 {
 
 static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
 {
@@ -262,7 +323,7 @@ static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
           b) no such thing as inode;
           So here we assume that inode = cluster.
        */
           b) no such thing as inode;
           So here we assume that inode = cluster.
        */
-       sfs->f_files = (sfs->f_blocks - sfs->f_bfree) >> ef.sb->spc_bits;
+       sfs->f_files = le32_to_cpu(ef.sb->cluster_count);
        sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
        sfs->f_ffree = sfs->f_bavail;
 
        sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
        sfs->f_ffree = sfs->f_bavail;
 
@@ -286,7 +347,7 @@ static void fuse_exfat_destroy(void* unused)
 
 static void usage(const char* prog)
 {
 
 static void usage(const char* prog)
 {
-       fprintf(stderr, "Usage: %s [-d] [-o options] [-v] <device> <dir>\n", prog);
+       fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
        exit(1);
 }
 
        exit(1);
 }
 
@@ -297,6 +358,9 @@ static struct fuse_operations fuse_exfat_ops =
        .readdir        = fuse_exfat_readdir,
        .open           = fuse_exfat_open,
        .release        = fuse_exfat_release,
        .readdir        = fuse_exfat_readdir,
        .open           = fuse_exfat_open,
        .release        = fuse_exfat_release,
+       .flush          = fuse_exfat_flush,
+       .fsync          = fuse_exfat_fsync,
+       .fsyncdir       = fuse_exfat_fsync,
        .read           = fuse_exfat_read,
        .write          = fuse_exfat_write,
        .unlink         = fuse_exfat_unlink,
        .read           = fuse_exfat_read,
        .write          = fuse_exfat_write,
        .unlink         = fuse_exfat_unlink,
@@ -305,9 +369,8 @@ static struct fuse_operations fuse_exfat_ops =
        .mkdir          = fuse_exfat_mkdir,
        .rename         = fuse_exfat_rename,
        .utimens        = fuse_exfat_utimens,
        .mkdir          = fuse_exfat_mkdir,
        .rename         = fuse_exfat_rename,
        .utimens        = fuse_exfat_utimens,
-#ifdef __APPLE__
        .chmod          = fuse_exfat_chmod,
        .chmod          = fuse_exfat_chmod,
-#endif
+       .chown          = fuse_exfat_chown,
        .statfs         = fuse_exfat_statfs,
        .init           = fuse_exfat_init,
        .destroy        = fuse_exfat_destroy,
        .statfs         = fuse_exfat_statfs,
        .init           = fuse_exfat_init,
        .destroy        = fuse_exfat_destroy,
@@ -316,6 +379,7 @@ static struct fuse_operations fuse_exfat_ops =
 static char* add_option(char* options, const char* name, const char* value)
 {
        size_t size;
 static char* add_option(char* options, const char* name, const char* value)
 {
        size_t size;
+       char* optionsf = options;
 
        if (value)
                size = strlen(options) + strlen(name) + strlen(value) + 3;
 
        if (value)
                size = strlen(options) + strlen(name) + strlen(value) + 3;
@@ -325,6 +389,7 @@ static char* add_option(char* options, const char* name, const char* value)
        options = realloc(options, size);
        if (options == NULL)
        {
        options = realloc(options, size);
        if (options == NULL)
        {
+               free(optionsf);
                exfat_error("failed to reallocate options string");
                return NULL;
        }
                exfat_error("failed to reallocate options string");
                return NULL;
        }
@@ -338,21 +403,6 @@ static char* add_option(char* options, const char* name, const char* value)
        return options;
 }
 
        return options;
 }
 
-static char* add_fsname_option(char* options, const char* spec)
-{
-       char* spec_abs = realpath(spec, NULL);
-
-       if (spec_abs == NULL)
-       {
-               free(options);
-               exfat_error("failed to get absolute path for `%s'", spec);
-               return NULL;
-       }
-       options = add_option(options, "fsname", spec_abs);
-       free(spec_abs);
-       return options;
-}
-
 static char* add_user_option(char* options)
 {
        struct passwd* pw;
 static char* add_user_option(char* options)
 {
        struct passwd* pw;
@@ -384,7 +434,7 @@ static char* add_blksize_option(char* options, long cluster_size)
 
 static char* add_fuse_options(char* options, const char* spec)
 {
 
 static char* add_fuse_options(char* options, const char* spec)
 {
-       options = add_fsname_option(options, spec);
+       options = add_option(options, "fsname", spec);
        if (options == NULL)
                return NULL;
        options = add_user_option(options);
        if (options == NULL)
                return NULL;
        options = add_user_option(options);
@@ -407,7 +457,7 @@ int main(int argc, char* argv[])
        int debug = 0;
        struct fuse_chan* fc = NULL;
        struct fuse* fh = NULL;
        int debug = 0;
        struct fuse_chan* fc = NULL;
        struct fuse* fh = NULL;
-       char** pp;
+       int opt;
 
        printf("FUSE exfat %u.%u.%u\n",
                        EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
 
        printf("FUSE exfat %u.%u.%u\n",
                        EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
@@ -419,40 +469,39 @@ int main(int argc, char* argv[])
                return 1;
        }
 
                return 1;
        }
 
-       for (pp = argv + 1; *pp; pp++)
+       while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
        {
        {
-               if (strcmp(*pp, "-o") == 0)
+               switch (opt)
                {
                {
-                       pp++;
-                       if (*pp == NULL)
-                               usage(argv[0]);
-                       mount_options = add_option(mount_options, *pp, NULL);
+               case 'd':
+                       debug = 1;
+                       break;
+               case 'n':
+                       break;
+               case 'o':
+                       mount_options = add_option(mount_options, optarg, NULL);
                        if (mount_options == NULL)
                                return 1;
                        if (mount_options == NULL)
                                return 1;
-               }
-               else if (strcmp(*pp, "-d") == 0)
-                       debug = 1;
-               else if (strcmp(*pp, "-v") == 0)
-               {
+                       break;
+               case 'V':
                        free(mount_options);
                        free(mount_options);
-                       puts("Copyright (C) 2010-2013  Andrew Nayenko");
+                       puts("Copyright (C) 2010-2014  Andrew Nayenko");
                        return 0;
                        return 0;
-               }
-               else if (spec == NULL)
-                       spec = *pp;
-               else if (mount_point == NULL)
-                       mount_point = *pp;
-               else
-               {
+               case 'v':
+                       break;
+               default:
                        free(mount_options);
                        usage(argv[0]);
                        free(mount_options);
                        usage(argv[0]);
+                       break;
                }
        }
                }
        }
-       if (spec == NULL || mount_point == NULL)
+       if (argc - optind != 2)
        {
                free(mount_options);
                usage(argv[0]);
        }
        {
                free(mount_options);
                usage(argv[0]);
        }
+       spec = argv[optind];
+       mount_point = argv[optind + 1];
 
        if (exfat_mount(&ef, spec, mount_options) != 0)
        {
 
        if (exfat_mount(&ef, spec, mount_options) != 0)
        {