3 FUSE-based exFAT implementation. Requires FUSE 2.6 or later.
5 Free exFAT implementation.
6 Copyright (C) 2010-2018 Andrew Nayenko
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #define FUSE_USE_VERSION 26
33 #include <sys/types.h>
38 #define exfat_debug(format, ...)
41 #if !defined(FUSE_VERSION) || (FUSE_VERSION < 26)
42 #error FUSE 2.6 or later is required
45 const char* default_options = "ro_fallback,allow_other,blkdev,big_writes,"
46 "default_permissions";
50 static struct exfat_node* get_node(const struct fuse_file_info* fi)
52 return (struct exfat_node*) (size_t) fi->fh;
55 static void set_node(struct fuse_file_info* fi, struct exfat_node* node)
57 fi->fh = (uint64_t) (size_t) node;
61 static int fuse_exfat_getattr(const char* path, struct stat* stbuf)
63 struct exfat_node* node;
66 exfat_debug("[%s] %s", __func__, path);
68 rc = exfat_lookup(&ef, &node, path);
72 exfat_stat(&ef, node, stbuf);
73 exfat_put_node(&ef, node);
77 static int fuse_exfat_truncate(const char* path, off_t size)
79 struct exfat_node* node;
82 exfat_debug("[%s] %s, %"PRId64, __func__, path, size);
84 rc = exfat_lookup(&ef, &node, path);
88 rc = exfat_truncate(&ef, node, size, true);
91 exfat_flush_node(&ef, node); /* ignore return code */
92 exfat_put_node(&ef, node);
95 rc = exfat_flush_node(&ef, node);
96 exfat_put_node(&ef, node);
100 static int fuse_exfat_readdir(const char* path, void* buffer,
101 fuse_fill_dir_t filler, UNUSED off_t offset,
102 UNUSED struct fuse_file_info* fi)
104 struct exfat_node* parent;
105 struct exfat_node* node;
106 struct exfat_iterator it;
108 char name[EXFAT_UTF8_NAME_BUFFER_MAX];
111 exfat_debug("[%s] %s", __func__, path);
113 rc = exfat_lookup(&ef, &parent, path);
116 if (!(parent->attrib & EXFAT_ATTRIB_DIR))
118 exfat_put_node(&ef, parent);
119 exfat_error("'%s' is not a directory (%#hx)", path, parent->attrib);
123 filler(buffer, ".", NULL, 0);
124 filler(buffer, "..", NULL, 0);
126 rc = exfat_opendir(&ef, parent, &it);
129 exfat_put_node(&ef, parent);
130 exfat_error("failed to open directory '%s'", path);
133 while ((node = exfat_readdir(&it)))
135 exfat_get_name(node, name);
136 exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
137 name, node->is_contiguous ? "contiguous" : "fragmented",
138 node->size, node->start_cluster);
139 exfat_stat(&ef, node, &stbuf);
140 filler(buffer, name, &stbuf, 0);
141 exfat_put_node(&ef, node);
143 exfat_closedir(&ef, &it);
144 exfat_put_node(&ef, parent);
148 static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
150 struct exfat_node* node;
153 exfat_debug("[%s] %s", __func__, path);
155 rc = exfat_lookup(&ef, &node, path);
162 static int fuse_exfat_create(const char* path, UNUSED mode_t mode,
163 struct fuse_file_info* fi)
165 struct exfat_node* node;
168 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
170 rc = exfat_mknod(&ef, path);
173 rc = exfat_lookup(&ef, &node, path);
180 static int fuse_exfat_release(UNUSED const char* path,
181 struct fuse_file_info* fi)
184 This handler is called by FUSE on close() syscall. If the FUSE
185 implementation does not call flush handler, we will flush node here.
186 But in this case we will not be able to return an error to the caller.
187 See fuse_exfat_flush() below.
189 exfat_debug("[%s] %s", __func__, path);
190 exfat_flush_node(&ef, get_node(fi));
191 exfat_put_node(&ef, get_node(fi));
192 return 0; /* FUSE ignores this return value */
195 static int fuse_exfat_flush(UNUSED const char* path, struct fuse_file_info* fi)
198 This handler may be called by FUSE on close() syscall. FUSE also deals
199 with removals of open files, so we don't free clusters on close but
200 only on rmdir and unlink. If the FUSE implementation does not call this
201 handler we will flush node on release. See fuse_exfat_relase() above.
203 exfat_debug("[%s] %s", __func__, path);
204 return exfat_flush_node(&ef, get_node(fi));
207 static int fuse_exfat_fsync(UNUSED const char* path, UNUSED int datasync,
208 UNUSED struct fuse_file_info *fi)
212 exfat_debug("[%s] %s", __func__, path);
213 rc = exfat_flush_nodes(&ef);
216 rc = exfat_flush(&ef);
219 return exfat_fsync(ef.dev);
222 static int fuse_exfat_read(UNUSED const char* path, char* buffer,
223 size_t size, off_t offset, struct fuse_file_info* fi)
225 exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
226 return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
229 static int fuse_exfat_write(UNUSED const char* path, const char* buffer,
230 size_t size, off_t offset, struct fuse_file_info* fi)
232 exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
233 return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
236 static int fuse_exfat_unlink(const char* path)
238 struct exfat_node* node;
241 exfat_debug("[%s] %s", __func__, path);
243 rc = exfat_lookup(&ef, &node, path);
247 rc = exfat_unlink(&ef, node);
248 exfat_put_node(&ef, node);
251 return exfat_cleanup_node(&ef, node);
254 static int fuse_exfat_rmdir(const char* path)
256 struct exfat_node* node;
259 exfat_debug("[%s] %s", __func__, path);
261 rc = exfat_lookup(&ef, &node, path);
265 rc = exfat_rmdir(&ef, node);
266 exfat_put_node(&ef, node);
269 return exfat_cleanup_node(&ef, node);
272 static int fuse_exfat_mknod(const char* path, UNUSED mode_t mode,
275 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
276 return exfat_mknod(&ef, path);
279 static int fuse_exfat_mkdir(const char* path, UNUSED mode_t mode)
281 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
282 return exfat_mkdir(&ef, path);
285 static int fuse_exfat_rename(const char* old_path, const char* new_path)
287 exfat_debug("[%s] %s => %s", __func__, old_path, new_path);
288 return exfat_rename(&ef, old_path, new_path);
291 static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
293 struct exfat_node* node;
296 exfat_debug("[%s] %s", __func__, path);
298 rc = exfat_lookup(&ef, &node, path);
302 exfat_utimes(node, tv);
303 rc = exfat_flush_node(&ef, node);
304 exfat_put_node(&ef, node);
308 static int fuse_exfat_chmod(UNUSED const char* path, mode_t mode)
310 const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
311 S_IRWXU | S_IRWXG | S_IRWXO;
313 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
314 if (mode & ~VALID_MODE_MASK)
319 static int fuse_exfat_chown(UNUSED const char* path, uid_t uid, gid_t gid)
321 exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
322 if (uid != ef.uid || gid != ef.gid)
327 static int fuse_exfat_statfs(UNUSED const char* path, struct statvfs* sfs)
329 exfat_debug("[%s]", __func__);
331 sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
332 sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
333 sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits;
334 sfs->f_bavail = exfat_count_free_clusters(&ef);
335 sfs->f_bfree = sfs->f_bavail;
336 sfs->f_namemax = EXFAT_NAME_MAX;
339 Below are fake values because in exFAT there is
340 a) no simple way to count files;
341 b) no such thing as inode;
342 So here we assume that inode = cluster.
344 sfs->f_files = le32_to_cpu(ef.sb->cluster_count);
345 sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
346 sfs->f_ffree = sfs->f_bavail;
351 static void* fuse_exfat_init(struct fuse_conn_info* fci)
353 exfat_debug("[%s]", __func__);
354 #ifdef FUSE_CAP_BIG_WRITES
355 fci->want |= FUSE_CAP_BIG_WRITES;
358 /* mark super block as dirty; failure isn't a big deal */
359 exfat_soil_super_block(&ef);
364 static void fuse_exfat_destroy(UNUSED void* unused)
366 exfat_debug("[%s]", __func__);
370 static void usage(const char* prog)
372 fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
376 static struct fuse_operations fuse_exfat_ops =
378 .getattr = fuse_exfat_getattr,
379 .truncate = fuse_exfat_truncate,
380 .readdir = fuse_exfat_readdir,
381 .open = fuse_exfat_open,
382 .create = fuse_exfat_create,
383 .release = fuse_exfat_release,
384 .flush = fuse_exfat_flush,
385 .fsync = fuse_exfat_fsync,
386 .fsyncdir = fuse_exfat_fsync,
387 .read = fuse_exfat_read,
388 .write = fuse_exfat_write,
389 .unlink = fuse_exfat_unlink,
390 .rmdir = fuse_exfat_rmdir,
391 .mknod = fuse_exfat_mknod,
392 .mkdir = fuse_exfat_mkdir,
393 .rename = fuse_exfat_rename,
394 .utimens = fuse_exfat_utimens,
395 .chmod = fuse_exfat_chmod,
396 .chown = fuse_exfat_chown,
397 .statfs = fuse_exfat_statfs,
398 .init = fuse_exfat_init,
399 .destroy = fuse_exfat_destroy,
402 static char* add_option(char* options, const char* name, const char* value)
405 char* optionsf = options;
408 size = strlen(options) + strlen(name) + strlen(value) + 3;
410 size = strlen(options) + strlen(name) + 2;
412 options = realloc(options, size);
416 exfat_error("failed to reallocate options string");
419 strcat(options, ",");
420 strcat(options, name);
423 strcat(options, "=");
424 strcat(options, value);
429 static void escape(char* escaped, const char* orig)
433 if (*orig == ',' || *orig == '\\')
436 while ((*escaped++ = *orig++));
439 static char* add_fsname_option(char* options, const char* spec)
441 /* escaped string cannot be more than twice as big as the original one */
442 char* escaped = malloc(strlen(spec) * 2 + 1);
447 exfat_error("failed to allocate escaped string for %s", spec);
451 /* on some platforms (e.g. Android, Solaris) device names can contain
453 escape(escaped, spec);
454 options = add_option(options, "fsname", escaped);
459 static char* add_ro_option(char* options, bool ro)
461 return ro ? add_option(options, "ro", NULL) : options;
464 static char* add_user_option(char* options)
471 pw = getpwuid(getuid());
472 if (pw == NULL || pw->pw_name == NULL)
475 exfat_error("failed to determine username");
478 return add_option(options, "user", pw->pw_name);
481 static char* add_blksize_option(char* options, long cluster_size)
483 long page_size = sysconf(_SC_PAGESIZE);
489 snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
490 return add_option(options, "blksize", blksize);
493 static char* add_fuse_options(char* options, const char* spec, bool ro)
495 options = add_fsname_option(options, spec);
498 options = add_ro_option(options, ro);
501 options = add_user_option(options);
504 options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
511 int main(int argc, char* argv[])
513 struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL);
514 struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL);
515 const char* spec = NULL;
516 const char* mount_point = NULL;
519 struct fuse_chan* fc = NULL;
520 struct fuse* fh = NULL;
523 printf("FUSE exfat %s\n", VERSION);
525 mount_options = strdup(default_options);
526 if (mount_options == NULL)
528 exfat_error("failed to allocate options string");
532 while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
542 mount_options = add_option(mount_options, optarg, NULL);
543 if (mount_options == NULL)
548 puts("Copyright (C) 2010-2018 Andrew Nayenko");
558 if (argc - optind != 2)
564 mount_point = argv[optind + 1];
566 if (exfat_mount(&ef, spec, mount_options) != 0)
572 mount_options = add_fuse_options(mount_options, spec, (ef.ro != 0));
573 if (mount_options == NULL)
579 /* create arguments for fuse_mount() */
580 if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
581 fuse_opt_add_arg(&mount_args, "-o") != 0 ||
582 fuse_opt_add_arg(&mount_args, mount_options) != 0)
591 /* create FUSE mount point */
592 fc = fuse_mount(mount_point, &mount_args);
593 fuse_opt_free_args(&mount_args);
600 /* create arguments for fuse_new() */
601 if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
602 (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
604 fuse_unmount(mount_point, fc);
609 /* create new FUSE file system */
610 fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops,
611 sizeof(struct fuse_operations), NULL);
612 fuse_opt_free_args(&newfs_args);
615 fuse_unmount(mount_point, fc);
620 /* exit session on HUP, TERM and INT signals and ignore PIPE signal */
621 if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
623 fuse_unmount(mount_point, fc);
626 exfat_error("failed to set signal handlers");
630 /* go to background (unless "-d" option is passed) and run FUSE
632 if (fuse_daemonize(debug) == 0)
634 if (fuse_loop(fh) != 0)
635 exfat_error("FUSE loop failure");
638 exfat_error("failed to daemonize");
640 fuse_remove_signal_handlers(fuse_get_session(fh));
641 /* note that fuse_unmount() must be called BEFORE fuse_destroy() */
642 fuse_unmount(mount_point, fc);