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, ...) do {} while (0)
41 #if !defined(FUSE_VERSION) || (FUSE_VERSION < 26)
42 #error FUSE 2.6 or later is required
47 static struct exfat_node* get_node(const struct fuse_file_info* fi)
49 return (struct exfat_node*) (size_t) fi->fh;
52 static void set_node(struct fuse_file_info* fi, struct exfat_node* node)
54 fi->fh = (uint64_t) (size_t) node;
58 static int fuse_exfat_getattr(const char* path, struct stat* stbuf)
60 struct exfat_node* node;
63 exfat_debug("[%s] %s", __func__, path);
65 rc = exfat_lookup(&ef, &node, path);
69 exfat_stat(&ef, node, stbuf);
70 exfat_put_node(&ef, node);
74 static int fuse_exfat_truncate(const char* path, off_t size)
76 struct exfat_node* node;
79 exfat_debug("[%s] %s, %"PRId64, __func__, path, size);
81 rc = exfat_lookup(&ef, &node, path);
85 rc = exfat_truncate(&ef, node, size, true);
88 exfat_flush_node(&ef, node); /* ignore return code */
89 exfat_put_node(&ef, node);
92 rc = exfat_flush_node(&ef, node);
93 exfat_put_node(&ef, node);
97 static int fuse_exfat_readdir(const char* path, void* buffer,
98 fuse_fill_dir_t filler, UNUSED off_t offset,
99 UNUSED struct fuse_file_info* fi)
101 struct exfat_node* parent;
102 struct exfat_node* node;
103 struct exfat_iterator it;
105 char name[EXFAT_UTF8_NAME_BUFFER_MAX];
108 exfat_debug("[%s] %s", __func__, path);
110 rc = exfat_lookup(&ef, &parent, path);
113 if (!(parent->attrib & EXFAT_ATTRIB_DIR))
115 exfat_put_node(&ef, parent);
116 exfat_error("'%s' is not a directory (%#hx)", path, parent->attrib);
120 filler(buffer, ".", NULL, 0);
121 filler(buffer, "..", NULL, 0);
123 rc = exfat_opendir(&ef, parent, &it);
126 exfat_put_node(&ef, parent);
127 exfat_error("failed to open directory '%s'", path);
130 while ((node = exfat_readdir(&it)))
132 exfat_get_name(node, name);
133 exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
134 name, node->is_contiguous ? "contiguous" : "fragmented",
135 node->size, node->start_cluster);
136 exfat_stat(&ef, node, &stbuf);
137 filler(buffer, name, &stbuf, 0);
138 exfat_put_node(&ef, node);
140 exfat_closedir(&ef, &it);
141 exfat_put_node(&ef, parent);
145 static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
147 struct exfat_node* node;
150 exfat_debug("[%s] %s", __func__, path);
152 rc = exfat_lookup(&ef, &node, path);
159 static int fuse_exfat_create(const char* path, UNUSED mode_t mode,
160 struct fuse_file_info* fi)
162 struct exfat_node* node;
165 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
167 rc = exfat_mknod(&ef, path);
170 rc = exfat_lookup(&ef, &node, path);
177 static int fuse_exfat_release(UNUSED const char* path,
178 struct fuse_file_info* fi)
181 This handler is called by FUSE on close() syscall. If the FUSE
182 implementation does not call flush handler, we will flush node here.
183 But in this case we will not be able to return an error to the caller.
184 See fuse_exfat_flush() below.
186 exfat_debug("[%s] %s", __func__, path);
187 exfat_flush_node(&ef, get_node(fi));
188 exfat_put_node(&ef, get_node(fi));
189 return 0; /* FUSE ignores this return value */
192 static int fuse_exfat_flush(UNUSED const char* path, struct fuse_file_info* fi)
195 This handler may be called by FUSE on close() syscall. FUSE also deals
196 with removals of open files, so we don't free clusters on close but
197 only on rmdir and unlink. If the FUSE implementation does not call this
198 handler we will flush node on release. See fuse_exfat_relase() above.
200 exfat_debug("[%s] %s", __func__, path);
201 return exfat_flush_node(&ef, get_node(fi));
204 static int fuse_exfat_fsync(UNUSED const char* path, UNUSED int datasync,
205 UNUSED struct fuse_file_info *fi)
209 exfat_debug("[%s] %s", __func__, path);
210 rc = exfat_flush_nodes(&ef);
213 rc = exfat_flush(&ef);
216 return exfat_fsync(ef.dev);
219 static int fuse_exfat_read(UNUSED const char* path, char* buffer,
220 size_t size, off_t offset, struct fuse_file_info* fi)
222 exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
223 return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
226 static int fuse_exfat_write(UNUSED const char* path, const char* buffer,
227 size_t size, off_t offset, struct fuse_file_info* fi)
229 exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
230 return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
233 static int fuse_exfat_unlink(const char* path)
235 struct exfat_node* node;
238 exfat_debug("[%s] %s", __func__, path);
240 rc = exfat_lookup(&ef, &node, path);
244 rc = exfat_unlink(&ef, node);
245 exfat_put_node(&ef, node);
248 return exfat_cleanup_node(&ef, node);
251 static int fuse_exfat_rmdir(const char* path)
253 struct exfat_node* node;
256 exfat_debug("[%s] %s", __func__, path);
258 rc = exfat_lookup(&ef, &node, path);
262 rc = exfat_rmdir(&ef, node);
263 exfat_put_node(&ef, node);
266 return exfat_cleanup_node(&ef, node);
269 static int fuse_exfat_mknod(const char* path, UNUSED mode_t mode,
272 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
273 return exfat_mknod(&ef, path);
276 static int fuse_exfat_mkdir(const char* path, UNUSED mode_t mode)
278 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
279 return exfat_mkdir(&ef, path);
282 static int fuse_exfat_rename(const char* old_path, const char* new_path)
284 exfat_debug("[%s] %s => %s", __func__, old_path, new_path);
285 return exfat_rename(&ef, old_path, new_path);
288 static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
290 struct exfat_node* node;
293 exfat_debug("[%s] %s", __func__, path);
295 rc = exfat_lookup(&ef, &node, path);
299 exfat_utimes(node, tv);
300 rc = exfat_flush_node(&ef, node);
301 exfat_put_node(&ef, node);
305 static int fuse_exfat_chmod(UNUSED const char* path, mode_t mode)
307 const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
308 S_IRWXU | S_IRWXG | S_IRWXO;
310 exfat_debug("[%s] %s 0%ho", __func__, path, mode);
311 if (mode & ~VALID_MODE_MASK)
316 static int fuse_exfat_chown(UNUSED const char* path, uid_t uid, gid_t gid)
318 exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
319 if (uid != ef.uid || gid != ef.gid)
324 static int fuse_exfat_statfs(UNUSED const char* path, struct statvfs* sfs)
326 exfat_debug("[%s]", __func__);
328 sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
329 sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
330 sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits;
331 sfs->f_bavail = exfat_count_free_clusters(&ef);
332 sfs->f_bfree = sfs->f_bavail;
333 sfs->f_namemax = EXFAT_NAME_MAX;
336 Below are fake values because in exFAT there is
337 a) no simple way to count files;
338 b) no such thing as inode;
339 So here we assume that inode = cluster.
341 sfs->f_files = le32_to_cpu(ef.sb->cluster_count);
342 sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
343 sfs->f_ffree = sfs->f_bavail;
348 static void* fuse_exfat_init(struct fuse_conn_info* fci)
350 exfat_debug("[%s]", __func__);
351 #ifdef FUSE_CAP_BIG_WRITES
352 fci->want |= FUSE_CAP_BIG_WRITES;
355 /* mark super block as dirty; failure isn't a big deal */
356 exfat_soil_super_block(&ef);
361 static void fuse_exfat_destroy(UNUSED void* unused)
363 exfat_debug("[%s]", __func__);
367 static void usage(const char* prog)
369 fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
373 static struct fuse_operations fuse_exfat_ops =
375 .getattr = fuse_exfat_getattr,
376 .truncate = fuse_exfat_truncate,
377 .readdir = fuse_exfat_readdir,
378 .open = fuse_exfat_open,
379 .create = fuse_exfat_create,
380 .release = fuse_exfat_release,
381 .flush = fuse_exfat_flush,
382 .fsync = fuse_exfat_fsync,
383 .fsyncdir = fuse_exfat_fsync,
384 .read = fuse_exfat_read,
385 .write = fuse_exfat_write,
386 .unlink = fuse_exfat_unlink,
387 .rmdir = fuse_exfat_rmdir,
388 .mknod = fuse_exfat_mknod,
389 .mkdir = fuse_exfat_mkdir,
390 .rename = fuse_exfat_rename,
391 .utimens = fuse_exfat_utimens,
392 .chmod = fuse_exfat_chmod,
393 .chown = fuse_exfat_chown,
394 .statfs = fuse_exfat_statfs,
395 .init = fuse_exfat_init,
396 .destroy = fuse_exfat_destroy,
399 static char* add_option(char* options, const char* name, const char* value)
402 char* optionsf = options;
405 size = strlen(options) + strlen(name) + strlen(value) + 3;
407 size = strlen(options) + strlen(name) + 2;
409 options = realloc(options, size);
413 exfat_error("failed to reallocate options string");
416 strcat(options, ",");
417 strcat(options, name);
420 strcat(options, "=");
421 strcat(options, value);
426 static void escape(char* escaped, const char* orig)
430 if (*orig == ',' || *orig == '\\')
433 while ((*escaped++ = *orig++));
436 static char* add_fsname_option(char* options, const char* spec)
438 /* escaped string cannot be more than twice as big as the original one */
439 char* escaped = malloc(strlen(spec) * 2 + 1);
444 exfat_error("failed to allocate escaped string for %s", spec);
448 /* on some platforms (e.g. Android, Solaris) device names can contain
450 escape(escaped, spec);
451 options = add_option(options, "fsname", escaped);
456 static char* add_ro_option(char* options, bool ro)
458 return ro ? add_option(options, "ro", NULL) : options;
461 #if defined(__linux__)
462 static char* add_user_option(char* options)
469 pw = getpwuid(getuid());
470 if (pw == NULL || pw->pw_name == NULL)
473 exfat_error("failed to determine username");
476 return add_option(options, "user", pw->pw_name);
480 #if defined(__linux__)
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);
494 static char* add_fuse_options(char* options, const char* spec, bool ro)
496 options = add_fsname_option(options, spec);
499 options = add_ro_option(options, ro);
502 #if defined(__linux__)
503 options = add_user_option(options);
506 options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
513 static char* add_passthrough_fuse_options(char* fuse_options,
516 const char* passthrough_list[] =
518 #if defined(__FreeBSD__)
526 for (i = 0; passthrough_list[i] != NULL; i++)
527 if (exfat_match_option(options, passthrough_list[i]))
529 fuse_options = add_option(fuse_options, passthrough_list[i], NULL);
530 if (fuse_options == NULL)
537 static int fuse_exfat_main(char* mount_options, char* mount_point)
539 char* argv[] = {"exfat", "-s", "-o", mount_options, mount_point, NULL};
540 return fuse_main(sizeof(argv) / sizeof(argv[0]) - 1, argv,
541 &fuse_exfat_ops, NULL);
544 int main(int argc, char* argv[])
546 const char* spec = NULL;
547 char* mount_point = NULL;
553 printf("FUSE exfat %s\n", VERSION);
555 fuse_options = strdup("allow_other,"
556 #if defined(__linux__) || defined(__FreeBSD__)
559 #if defined(__linux__)
562 "default_permissions");
563 exfat_options = strdup("ro_fallback");
564 if (fuse_options == NULL || exfat_options == NULL)
568 exfat_error("failed to allocate options string");
572 while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
577 fuse_options = add_option(fuse_options, "debug", NULL);
578 if (fuse_options == NULL)
587 exfat_options = add_option(exfat_options, optarg, NULL);
588 if (exfat_options == NULL)
593 fuse_options = add_passthrough_fuse_options(fuse_options, optarg);
594 if (fuse_options == NULL)
603 puts("Copyright (C) 2010-2018 Andrew Nayenko");
614 if (argc - optind != 2)
621 mount_point = argv[optind + 1];
623 if (exfat_mount(&ef, spec, exfat_options) != 0)
632 fuse_options = add_fuse_options(fuse_options, spec, ef.ro != 0);
633 if (fuse_options == NULL)
639 /* let FUSE do all its wizardry */
640 rc = fuse_exfat_main(fuse_options, mount_point);