3 FUSE-based exFAT implementation. Requires FUSE 2.6 or later.
5 Free exFAT implementation.
6 Copyright (C) 2010-2023 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.
32 #include <sys/types.h>
37 #define exfat_debug(format, ...) do {} while (0)
40 #if !defined(FUSE_VERSION) || (FUSE_VERSION < 26)
41 #error FUSE 2.6 or later is required
46 static struct exfat_node* get_node(const struct fuse_file_info* fi)
48 return (struct exfat_node*) (size_t) fi->fh;
51 static void set_node(struct fuse_file_info* fi, struct exfat_node* node)
53 fi->fh = (uint64_t) (size_t) node;
57 static int fuse_exfat_getattr(const char* path, struct stat* stbuf
58 #if FUSE_USE_VERSION >= 30
59 , UNUSED struct fuse_file_info* fi
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
78 #if FUSE_USE_VERSION >= 30
79 , UNUSED struct fuse_file_info* fi
83 struct exfat_node* node;
86 exfat_debug("[%s] %s, %"PRId64, __func__, path, size);
88 rc = exfat_lookup(&ef, &node, path);
92 rc = exfat_truncate(&ef, node, size, true);
95 exfat_flush_node(&ef, node); /* ignore return code */
96 exfat_put_node(&ef, node);
99 rc = exfat_flush_node(&ef, node);
100 exfat_put_node(&ef, node);
104 static int fuse_exfat_readdir(const char* path, void* buffer,
105 fuse_fill_dir_t filler, UNUSED off_t offset,
106 UNUSED struct fuse_file_info* fi
107 #if FUSE_USE_VERSION >= 30
108 , UNUSED enum fuse_readdir_flags flags
112 struct exfat_node* parent;
113 struct exfat_node* node;
114 struct exfat_iterator it;
116 char name[EXFAT_UTF8_NAME_BUFFER_MAX];
119 exfat_debug("[%s] %s", __func__, path);
121 rc = exfat_lookup(&ef, &parent, path);
124 if (!(parent->attrib & EXFAT_ATTRIB_DIR))
126 exfat_put_node(&ef, parent);
127 exfat_error("'%s' is not a directory (%#hx)", path, parent->attrib);
131 #if FUSE_USE_VERSION < 30
132 filler(buffer, ".", NULL, 0);
133 filler(buffer, "..", NULL, 0);
135 filler(buffer, ".", NULL, 0, 0);
136 filler(buffer, "..", NULL, 0, 0);
139 rc = exfat_opendir(&ef, parent, &it);
142 exfat_put_node(&ef, parent);
143 exfat_error("failed to open directory '%s'", path);
146 while ((node = exfat_readdir(&it)))
148 exfat_get_name(node, name);
149 exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
150 name, node->is_contiguous ? "contiguous" : "fragmented",
151 node->size, node->start_cluster);
152 exfat_stat(&ef, node, &stbuf);
153 #if FUSE_USE_VERSION < 30
154 filler(buffer, name, &stbuf, 0);
156 filler(buffer, name, &stbuf, 0, 0);
158 exfat_put_node(&ef, node);
160 exfat_closedir(&ef, &it);
161 exfat_put_node(&ef, parent);
165 static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
167 struct exfat_node* node;
170 exfat_debug("[%s] %s flags %#x%s%s%s%s%s", __func__, path, fi->flags,
171 fi->flags & O_RDONLY ? " O_RDONLY" : "",
172 fi->flags & O_WRONLY ? " O_WRONLY" : "",
173 fi->flags & O_RDWR ? " O_RDWR" : "",
174 fi->flags & O_APPEND ? " O_APPEND" : "",
175 fi->flags & O_TRUNC ? " O_TRUNC" : "");
177 rc = exfat_lookup(&ef, &node, path);
180 /* FUSE 2.x will call fuse_exfat_truncate() explicitly */
181 #if FUSE_USE_VERSION >= 30
182 if (fi->flags & O_TRUNC)
184 rc = exfat_truncate(&ef, node, 0, true);
187 exfat_put_node(&ef, node);
196 static int fuse_exfat_create(const char* path, UNUSED mode_t mode,
197 struct fuse_file_info* fi)
199 struct exfat_node* node;
202 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
204 rc = exfat_mknod(&ef, path);
207 rc = exfat_lookup(&ef, &node, path);
214 static int fuse_exfat_release(UNUSED const char* path,
215 struct fuse_file_info* fi)
218 This handler is called by FUSE on close() syscall. If the FUSE
219 implementation does not call flush handler, we will flush node here.
220 But in this case we will not be able to return an error to the caller.
221 See fuse_exfat_flush() below.
223 exfat_debug("[%s] %s", __func__, path);
224 exfat_flush_node(&ef, get_node(fi));
225 exfat_put_node(&ef, get_node(fi));
226 return 0; /* FUSE ignores this return value */
229 static int fuse_exfat_flush(UNUSED const char* path, struct fuse_file_info* fi)
232 This handler may be called by FUSE on close() syscall. FUSE also deals
233 with removals of open files, so we don't free clusters on close but
234 only on rmdir and unlink. If the FUSE implementation does not call this
235 handler we will flush node on release. See fuse_exfat_release() above.
237 exfat_debug("[%s] %s", __func__, path);
238 return exfat_flush_node(&ef, get_node(fi));
241 static int fuse_exfat_fsync(UNUSED const char* path, UNUSED int datasync,
242 UNUSED struct fuse_file_info* fi)
246 exfat_debug("[%s] %s", __func__, path);
247 rc = exfat_flush_nodes(&ef);
250 rc = exfat_flush(&ef);
253 return exfat_fsync(ef.dev);
256 static int fuse_exfat_read(UNUSED const char* path, char* buffer,
257 size_t size, off_t offset, struct fuse_file_info* fi)
259 exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
260 return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
263 static int fuse_exfat_write(UNUSED const char* path, const char* buffer,
264 size_t size, off_t offset, struct fuse_file_info* fi)
266 exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
267 return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
270 static int fuse_exfat_unlink(const char* path)
272 struct exfat_node* node;
275 exfat_debug("[%s] %s", __func__, path);
277 rc = exfat_lookup(&ef, &node, path);
281 rc = exfat_unlink(&ef, node);
282 exfat_put_node(&ef, node);
285 return exfat_cleanup_node(&ef, node);
288 static int fuse_exfat_rmdir(const char* path)
290 struct exfat_node* node;
293 exfat_debug("[%s] %s", __func__, path);
295 rc = exfat_lookup(&ef, &node, path);
299 rc = exfat_rmdir(&ef, node);
300 exfat_put_node(&ef, node);
303 return exfat_cleanup_node(&ef, node);
306 static int fuse_exfat_mknod(const char* path, UNUSED mode_t mode,
309 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
310 return exfat_mknod(&ef, path);
313 static int fuse_exfat_mkdir(const char* path, UNUSED mode_t mode)
315 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
316 return exfat_mkdir(&ef, path);
319 static int fuse_exfat_rename(const char* old_path, const char* new_path
320 #if FUSE_USE_VERSION >= 30
321 , UNUSED unsigned int flags
325 exfat_debug("[%s] %s => %s", __func__, old_path, new_path);
326 return exfat_rename(&ef, old_path, new_path);
329 static int fuse_exfat_utimens(const char* path, const struct timespec tv[2]
330 #if FUSE_USE_VERSION >= 30
331 , UNUSED struct fuse_file_info* fi
335 struct exfat_node* node;
338 exfat_debug("[%s] %s", __func__, path);
340 rc = exfat_lookup(&ef, &node, path);
344 exfat_utimes(node, tv);
345 rc = exfat_flush_node(&ef, node);
346 exfat_put_node(&ef, node);
350 static int fuse_exfat_chmod(UNUSED const char* path, mode_t mode
351 #if FUSE_USE_VERSION >= 30
352 , UNUSED struct fuse_file_info* fi
356 const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
357 S_IRWXU | S_IRWXG | S_IRWXO;
359 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
360 if (mode & ~VALID_MODE_MASK)
365 static int fuse_exfat_chown(UNUSED const char* path, uid_t uid, gid_t gid
366 #if FUSE_USE_VERSION >= 30
367 , UNUSED struct fuse_file_info* fi
371 exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
372 if (uid != ef.uid || gid != ef.gid)
377 static int fuse_exfat_statfs(UNUSED const char* path, struct statvfs* sfs)
379 exfat_debug("[%s]", __func__);
381 sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
382 sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
383 sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits;
384 sfs->f_bavail = exfat_count_free_clusters(&ef);
385 sfs->f_bfree = sfs->f_bavail;
386 sfs->f_namemax = EXFAT_NAME_MAX;
389 Below are fake values because in exFAT there is
390 a) no simple way to count files;
391 b) no such thing as inode;
392 So here we assume that inode = cluster.
394 sfs->f_files = le32_to_cpu(ef.sb->cluster_count);
395 sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
396 sfs->f_ffree = sfs->f_bavail;
401 static void* fuse_exfat_init(
402 #ifdef FUSE_CAP_BIG_WRITES
403 struct fuse_conn_info* fci
405 UNUSED struct fuse_conn_info* fci
407 #if FUSE_USE_VERSION >= 30
408 , UNUSED struct fuse_config* cfg
412 exfat_debug("[%s]", __func__);
413 #ifdef FUSE_CAP_BIG_WRITES
414 fci->want |= FUSE_CAP_BIG_WRITES;
417 /* mark super block as dirty; failure isn't a big deal */
418 exfat_soil_super_block(&ef);
423 static void fuse_exfat_destroy(UNUSED void* unused)
425 exfat_debug("[%s]", __func__);
429 static void usage(const char* prog)
431 fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
435 static struct fuse_operations fuse_exfat_ops =
437 .getattr = fuse_exfat_getattr,
438 .truncate = fuse_exfat_truncate,
439 .readdir = fuse_exfat_readdir,
440 .open = fuse_exfat_open,
441 .create = fuse_exfat_create,
442 .release = fuse_exfat_release,
443 .flush = fuse_exfat_flush,
444 .fsync = fuse_exfat_fsync,
445 .fsyncdir = fuse_exfat_fsync,
446 .read = fuse_exfat_read,
447 .write = fuse_exfat_write,
448 .unlink = fuse_exfat_unlink,
449 .rmdir = fuse_exfat_rmdir,
450 .mknod = fuse_exfat_mknod,
451 .mkdir = fuse_exfat_mkdir,
452 .rename = fuse_exfat_rename,
453 .utimens = fuse_exfat_utimens,
454 .chmod = fuse_exfat_chmod,
455 .chown = fuse_exfat_chown,
456 .statfs = fuse_exfat_statfs,
457 .init = fuse_exfat_init,
458 .destroy = fuse_exfat_destroy,
461 static char* add_option(char* options, const char* name, const char* value)
464 char* optionsf = options;
467 size = strlen(options) + strlen(name) + strlen(value) + 3;
469 size = strlen(options) + strlen(name) + 2;
471 options = realloc(options, size);
475 exfat_error("failed to reallocate options string");
478 strcat(options, ",");
479 strcat(options, name);
482 strcat(options, "=");
483 strcat(options, value);
488 static void escape(char* escaped, const char* orig)
492 if (*orig == ',' || *orig == '\\')
495 while ((*escaped++ = *orig++));
498 static char* add_fsname_option(char* options, const char* spec)
500 /* escaped string cannot be more than twice as big as the original one */
501 char* escaped = malloc(strlen(spec) * 2 + 1);
506 exfat_error("failed to allocate escaped string for %s", spec);
510 /* on some platforms (e.g. Android, Solaris) device names can contain
512 escape(escaped, spec);
513 options = add_option(options, "fsname", escaped);
518 static char* add_ro_option(char* options, bool ro)
520 return ro ? add_option(options, "ro", NULL) : options;
523 #if defined(__linux__)
524 static char* add_user_option(char* options)
531 pw = getpwuid(getuid());
532 if (pw == NULL || pw->pw_name == NULL)
535 exfat_error("failed to determine username");
538 return add_option(options, "user", pw->pw_name);
542 #if defined(__linux__)
543 static char* add_blksize_option(char* options, long cluster_size)
545 long page_size = sysconf(_SC_PAGESIZE);
551 snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
552 return add_option(options, "blksize", blksize);
556 static char* add_fuse_options(char* options, const char* spec, bool ro)
558 options = add_fsname_option(options, spec);
561 options = add_ro_option(options, ro);
564 #if defined(__linux__)
565 options = add_user_option(options);
568 options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
575 static char* add_passthrough_fuse_options(char* fuse_options,
578 const char* passthrough_list[] =
580 #if defined(__FreeBSD__)
588 for (i = 0; passthrough_list[i] != NULL; i++)
589 if (exfat_match_option(options, passthrough_list[i]))
591 fuse_options = add_option(fuse_options, passthrough_list[i], NULL);
592 if (fuse_options == NULL)
599 static int fuse_exfat_main(char* mount_options, char* mount_point)
601 char* argv[] = {"exfat", "-s", "-o", mount_options, mount_point, NULL};
602 return fuse_main(sizeof(argv) / sizeof(argv[0]) - 1, argv,
603 &fuse_exfat_ops, NULL);
606 int main(int argc, char* argv[])
608 const char* spec = NULL;
609 char* mount_point = NULL;
615 printf("FUSE exfat %s (libfuse%d)\n", VERSION, FUSE_USE_VERSION / 10);
617 fuse_options = strdup("allow_other,"
618 #if FUSE_USE_VERSION < 30 && (defined(__linux__) || defined(__FreeBSD__))
621 #if defined(__linux__)
624 "default_permissions");
625 exfat_options = strdup("ro_fallback");
626 if (fuse_options == NULL || exfat_options == NULL)
630 exfat_error("failed to allocate options string");
634 while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
639 fuse_options = add_option(fuse_options, "debug", NULL);
640 if (fuse_options == NULL)
649 exfat_options = add_option(exfat_options, optarg, NULL);
650 if (exfat_options == NULL)
655 fuse_options = add_passthrough_fuse_options(fuse_options, optarg);
656 if (fuse_options == NULL)
665 puts("Copyright (C) 2010-2023 Andrew Nayenko");
676 if (argc - optind != 2)
683 mount_point = argv[optind + 1];
685 if (exfat_mount(&ef, spec, exfat_options) != 0)
694 fuse_options = add_fuse_options(fuse_options, spec, ef.ro != 0);
695 if (fuse_options == NULL)
701 /* let FUSE do all its wizardry */
702 rc = fuse_exfat_main(fuse_options, mount_point);