#include <string.h>
#include <exfat.h>
#include <inttypes.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
#define exfat_debug(format, ...)
#error FUSE 2.6 or later is required
#endif
+const char* default_options = "allow_other,blkdev";
+
struct exfat ef;
static struct exfat_node* get_node(const struct fuse_file_info* fi)
static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
{
- const uint64_t block_count = le64_to_cpu(ef.sb->block_count);
-
- sfs->f_bsize = BLOCK_SIZE(*ef.sb);
+ sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
- sfs->f_blocks = block_count;
- sfs->f_bavail = block_count - ef.sb->allocated_percent * block_count / 100;
+ sfs->f_blocks = le64_to_cpu(ef.sb->block_count) >> ef.sb->bpc_bits;
+ sfs->f_bavail = exfat_count_free_clusters(&ef);
sfs->f_bfree = sfs->f_bavail;
sfs->f_namemax = EXFAT_NAME_MAX;
static void usage(const char* prog)
{
- fprintf(stderr, "Usage: %s <spec> <mountpoint> [-o options]\n", prog);
+ fprintf(stderr, "Usage: %s [-d] [-o options] <device> <dir>\n", prog);
exit(1);
}
.destroy = fuse_exfat_destroy,
};
+static char* add_option(char* options, const char* name, const char* value)
+{
+ size_t size;
+
+ if (value)
+ size = strlen(options) + strlen(name) + strlen(value) + 3;
+ else
+ size = strlen(options) + strlen(name) + 2;
+
+ options = realloc(options, size);
+ if (options == NULL)
+ {
+ exfat_error("failed to reallocate options string");
+ return NULL;
+ }
+ strcat(options, ",");
+ strcat(options, name);
+ if (value)
+ {
+ strcat(options, "=");
+ strcat(options, value);
+ }
+ return options;
+}
+
+static char* add_fsname_option(char* options, const char* spec)
+{
+ char spec_abs[PATH_MAX];
+
+ if (realpath(spec, spec_abs) == NULL)
+ {
+ free(options);
+ exfat_error("failed to get absolute path for `%s'", spec);
+ return NULL;
+ }
+ 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);
struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL);
const char* spec = NULL;
const char* mount_point = NULL;
- const char* mount_options = "";
+ char* mount_options;
int debug = 0;
struct fuse_chan* fc = NULL;
struct fuse* fh = NULL;
printf("FUSE exfat %u.%u.%u\n",
EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
+ mount_options = strdup(default_options);
+ if (mount_options == NULL)
+ {
+ exfat_error("failed to allocate options string");
+ return 1;
+ }
+
for (pp = argv + 1; *pp; pp++)
{
if (strcmp(*pp, "-o") == 0)
pp++;
if (*pp == NULL)
usage(argv[0]);
- mount_options = *pp;
+ mount_options = add_option(mount_options, *pp, NULL);
+ if (mount_options == NULL)
+ return 1;
}
else if (strcmp(*pp, "-d") == 0)
debug = 1;
else if (mount_point == NULL)
mount_point = *pp;
else
+ {
+ free(mount_options);
usage(argv[0]);
+ }
}
if (spec == NULL || mount_point == NULL)
+ {
+ free(mount_options);
usage(argv[0]);
+ }
+
+ if (exfat_mount(&ef, spec, mount_options) != 0)
+ {
+ free(mount_options);
+ 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)
+ {
+ exfat_unmount(&ef);
return 1;
+ }
/* create arguments for fuse_new() */
if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
(debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
{
fuse_unmount(mount_point, fc);
+ exfat_unmount(&ef);
return 1;
}
if (fh == NULL)
{
fuse_unmount(mount_point, fc);
+ exfat_unmount(&ef);
return 1;
}
{
fuse_unmount(mount_point, fc);
fuse_destroy(fh);
- return 1;
- }
-
- if (exfat_mount(&ef, spec, mount_options) != 0)
- {
- fuse_unmount(mount_point, fc);
- fuse_destroy(fh);
+ exfat_unmount(&ef);
return 1;
}