OSDN Git Service

OS X utilities report error if fchmod() fails. Added empty chmod() handler as a worka...
[android-x86/external-exfat.git] / fuse / main.c
index f79209c..84db40d 100644 (file)
@@ -2,7 +2,7 @@
        main.c (01.09.09)
        FUSE-based exFAT implementation. Requires FUSE 2.6 or later.
 
-       Copyright (C) 2009, 2010  Andrew Nayenko
+       Copyright (C) 2010-2012  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
@@ -18,6 +18,7 @@
        along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#define FUSE_USE_VERSION 26
 #include <fuse.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -27,6 +28,9 @@
 #include <exfat.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
 
 #define exfat_debug(format, ...)
 
@@ -34,7 +38,7 @@
        #error FUSE 2.6 or later is required
 #endif
 
-const char* default_options = "allow_other,blkdev";
+const char* default_options = "ro_fallback,allow_other,blkdev";
 
 struct exfat ef;
 
@@ -136,6 +140,7 @@ static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
        if (rc != 0)
                return rc;
        set_node(fi, node);
+       fi->keep_cache = 1;
        return 0;
 }
 
@@ -149,14 +154,14 @@ static int fuse_exfat_read(const char* path, char* buffer, size_t size,
                off_t offset, struct fuse_file_info* fi)
 {
        exfat_debug("[fuse_exfat_read] %s (%zu bytes)", path, size);
-       return exfat_read(&ef, get_node(fi), buffer, size, offset);
+       return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
 }
 
 static int fuse_exfat_write(const char* path, const char* buffer, size_t size,
                off_t offset, struct fuse_file_info* fi)
 {
        exfat_debug("[fuse_exfat_write] %s (%zu bytes)", path, size);
-       return exfat_write(&ef, get_node(fi), buffer, size, offset);
+       return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
 }
 
 static int fuse_exfat_unlink(const char* path)
@@ -225,11 +230,20 @@ static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
        return 0;
 }
 
+#ifdef __APPLE__
+static int fuse_exfat_chmod(const char* path, mode_t mode)
+{
+       exfat_debug("[fuse_exfat_chmod] %s 0%ho", path, mode);
+       /* make OS X utilities happy */
+       return 0;
+}
+#endif
+
 static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
 {
        sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
        sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
-       sfs->f_blocks = le64_to_cpu(ef.sb->block_count) >> ef.sb->bpc_bits;
+       sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits;
        sfs->f_bavail = exfat_count_free_clusters(&ef);
        sfs->f_bfree = sfs->f_bavail;
        sfs->f_namemax = EXFAT_NAME_MAX;
@@ -240,8 +254,8 @@ static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
           b) no such thing as inode;
           So here we assume that inode = cluster.
        */
-       sfs->f_files = (sfs->f_blocks - sfs->f_bfree) >> ef.sb->bpc_bits;
-       sfs->f_favail = sfs->f_bfree >> ef.sb->bpc_bits;
+       sfs->f_files = (sfs->f_blocks - sfs->f_bfree) >> ef.sb->spc_bits;
+       sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
        sfs->f_ffree = sfs->f_bavail;
 
        return 0;
@@ -254,7 +268,7 @@ static void fuse_exfat_destroy(void* unused)
 
 static void usage(const char* prog)
 {
-       fprintf(stderr, "Usage: %s <spec> <mountpoint> [-o options]\n", prog);
+       fprintf(stderr, "Usage: %s [-d] [-o options] [-v] <device> <dir>\n", prog);
        exit(1);
 }
 
@@ -273,6 +287,9 @@ static struct fuse_operations fuse_exfat_ops =
        .mkdir          = fuse_exfat_mkdir,
        .rename         = fuse_exfat_rename,
        .utimens        = fuse_exfat_utimens,
+#ifdef __APPLE__
+       .chmod          = fuse_exfat_chmod,
+#endif
        .statfs         = fuse_exfat_statfs,
        .destroy        = fuse_exfat_destroy,
 };
@@ -315,6 +332,50 @@ static char* add_fsname_option(char* options, const char* spec)
        return add_option(options, "fsname", spec_abs);
 }
 
+static char* add_user_option(char* options)
+{
+       struct passwd* pw;
+
+       if (getuid() == 0)
+               return options;
+
+       pw = getpwuid(getuid());
+       if (pw == NULL || pw->pw_name == NULL)
+       {
+               free(options);
+               exfat_error("failed to determine username");
+               return NULL;
+       }
+       return add_option(options, "user", pw->pw_name);
+}
+
+static char* add_blksize_option(char* options, long cluster_size)
+{
+       long page_size = sysconf(_SC_PAGESIZE);
+       char blksize[20];
+
+       if (page_size < 1)
+               page_size = 0x1000;
+
+       snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
+       return add_option(options, "blksize", blksize);
+}
+
+static char* add_fuse_options(char* options, const char* spec)
+{
+       options = add_fsname_option(options, spec);
+       if (options == NULL)
+               return NULL;
+       options = add_user_option(options);
+       if (options == NULL)
+               return NULL;
+       options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
+       if (options == NULL)
+               return NULL;
+
+       return options;
+}
+
 int main(int argc, char* argv[])
 {
        struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL);
@@ -350,6 +411,12 @@ int main(int argc, char* argv[])
                }
                else if (strcmp(*pp, "-d") == 0)
                        debug = 1;
+               else if (strcmp(*pp, "-v") == 0)
+               {
+                       free(mount_options);
+                       puts("Copyright (C) 2010-2012  Andrew Nayenko");
+                       return 0;
+               }
                else if (spec == NULL)
                        spec = *pp;
                else if (mount_point == NULL)
@@ -365,25 +432,48 @@ int main(int argc, char* argv[])
                free(mount_options);
                usage(argv[0]);
        }
-       mount_options = add_fsname_option(mount_options, spec);
+
+       if (exfat_mount(&ef, spec, mount_options) != 0)
+       {
+               free(mount_options);
+               return 1;
+       }
+
+       if (ef.ro_fallback)
+       {
+               mount_options = add_option(mount_options, "ro", NULL);
+               if (mount_options == NULL)
+               {
+                       exfat_unmount(&ef);
+                       return 1;
+               }
+       }
+
+       mount_options = add_fuse_options(mount_options, spec);
        if (mount_options == NULL)
+       {
+               exfat_unmount(&ef);
                return 1;
+       }
 
        /* create arguments for fuse_mount() */
        if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
                fuse_opt_add_arg(&mount_args, "-o") != 0 ||
                fuse_opt_add_arg(&mount_args, mount_options) != 0)
        {
+               exfat_unmount(&ef);
                free(mount_options);
                return 1;
        }
 
+       free(mount_options);
+
        /* create FUSE mount point */
        fc = fuse_mount(mount_point, &mount_args);
        fuse_opt_free_args(&mount_args);
        if (fc == NULL)
        {
-               free(mount_options);
+               exfat_unmount(&ef);
                return 1;
        }
 
@@ -392,7 +482,7 @@ int main(int argc, char* argv[])
                (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
        {
                fuse_unmount(mount_point, fc);
-               free(mount_options);
+               exfat_unmount(&ef);
                return 1;
        }
 
@@ -403,36 +493,32 @@ int main(int argc, char* argv[])
        if (fh == NULL)
        {
                fuse_unmount(mount_point, fc);
-               free(mount_options);
+               exfat_unmount(&ef);
                return 1;
        }
 
        /* exit session on HUP, TERM and INT signals and ignore PIPE signal */
-       if (fuse_set_signal_handlers(fuse_get_session(fh)))
+       if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
        {
                fuse_unmount(mount_point, fc);
                fuse_destroy(fh);
-               free(mount_options);
+               exfat_unmount(&ef);
+               exfat_error("failed to set signal handlers");
                return 1;
        }
 
-       if (exfat_mount(&ef, spec, mount_options) != 0)
+       /* go to background (unless "-d" option is passed) and run FUSE
+          main loop */
+       if (fuse_daemonize(debug) == 0)
        {
-               fuse_unmount(mount_point, fc);
-               fuse_destroy(fh);
-               free(mount_options);
-               return 1;
+               if (fuse_loop(fh) != 0)
+                       exfat_error("FUSE loop failure");
        }
-       free(mount_options);
-
-       /* go to background unless "-d" option is passed */
-       fuse_daemonize(debug);
-
-       /* FUSE main loop */
-       fuse_loop(fh);
+       else
+               exfat_error("failed to daemonize");
 
-       /* it's quite illogical but fuse_unmount() must be called BEFORE
-          fuse_destroy() */
+       fuse_remove_signal_handlers(fuse_get_session(fh));
+       /* note that fuse_unmount() must be called BEFORE fuse_destroy() */
        fuse_unmount(mount_point, fc);
        fuse_destroy(fh);
        return 0;