]> git.sven.stormbind.net Git - sven/fuse-exfat.git/blob - fuse/main.c
Imported Upstream version 1.2.4
[sven/fuse-exfat.git] / fuse / main.c
1 /*
2         main.c (01.09.09)
3         FUSE-based exFAT implementation. Requires FUSE 2.6 or later.
4
5         Free exFAT implementation.
6         Copyright (C) 2010-2016  Andrew Nayenko
7
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.
12
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.
17
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.
21 */
22
23 #include <exfat.h>
24 #define FUSE_USE_VERSION 26
25 #include <fuse.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <inttypes.h>
32 #include <limits.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <unistd.h>
36
37 #ifndef DEBUG
38         #define exfat_debug(format, ...)
39 #endif
40
41 #if !defined(FUSE_VERSION) || (FUSE_VERSION < 26)
42         #error FUSE 2.6 or later is required
43 #endif
44
45 const char* default_options = "ro_fallback,allow_other,blkdev,big_writes,"
46                 "default_permissions";
47
48 struct exfat ef;
49
50 static struct exfat_node* get_node(const struct fuse_file_info* fi)
51 {
52         return (struct exfat_node*) (size_t) fi->fh;
53 }
54
55 static void set_node(struct fuse_file_info* fi, struct exfat_node* node)
56 {
57         fi->fh = (uint64_t) (size_t) node;
58         fi->keep_cache = 1;
59 }
60
61 static int fuse_exfat_getattr(const char* path, struct stat* stbuf)
62 {
63         struct exfat_node* node;
64         int rc;
65
66         exfat_debug("[%s] %s", __func__, path);
67
68         rc = exfat_lookup(&ef, &node, path);
69         if (rc != 0)
70                 return rc;
71
72         exfat_stat(&ef, node, stbuf);
73         exfat_put_node(&ef, node);
74         return 0;
75 }
76
77 static int fuse_exfat_truncate(const char* path, off_t size)
78 {
79         struct exfat_node* node;
80         int rc;
81
82         exfat_debug("[%s] %s, %"PRId64, __func__, path, size);
83
84         rc = exfat_lookup(&ef, &node, path);
85         if (rc != 0)
86                 return rc;
87
88         rc = exfat_truncate(&ef, node, size, true);
89         if (rc != 0)
90         {
91                 exfat_flush_node(&ef, node);    /* ignore return code */
92                 exfat_put_node(&ef, node);
93                 return rc;
94         }
95         rc = exfat_flush_node(&ef, node);
96         exfat_put_node(&ef, node);
97         return rc;
98 }
99
100 static int fuse_exfat_readdir(const char* path, void* buffer,
101                 fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi)
102 {
103         struct exfat_node* parent;
104         struct exfat_node* node;
105         struct exfat_iterator it;
106         int rc;
107         char name[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
108
109         exfat_debug("[%s] %s", __func__, path);
110
111         rc = exfat_lookup(&ef, &parent, path);
112         if (rc != 0)
113                 return rc;
114         if (!(parent->flags & EXFAT_ATTRIB_DIR))
115         {
116                 exfat_put_node(&ef, parent);
117                 exfat_error("'%s' is not a directory (0x%x)", path, parent->flags);
118                 return -ENOTDIR;
119         }
120
121         filler(buffer, ".", NULL, 0);
122         filler(buffer, "..", NULL, 0);
123
124         rc = exfat_opendir(&ef, parent, &it);
125         if (rc != 0)
126         {
127                 exfat_put_node(&ef, parent);
128                 exfat_error("failed to open directory '%s'", path);
129                 return rc;
130         }
131         while ((node = exfat_readdir(&ef, &it)))
132         {
133                 exfat_get_name(node, name, sizeof(name) - 1);
134                 exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
135                                 name, IS_CONTIGUOUS(*node) ? "contiguous" : "fragmented",
136                                 node->size, node->start_cluster);
137                 filler(buffer, name, NULL, 0);
138                 exfat_put_node(&ef, node);
139         }
140         exfat_closedir(&ef, &it);
141         exfat_put_node(&ef, parent);
142         return 0;
143 }
144
145 static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
146 {
147         struct exfat_node* node;
148         int rc;
149
150         exfat_debug("[%s] %s", __func__, path);
151
152         rc = exfat_lookup(&ef, &node, path);
153         if (rc != 0)
154                 return rc;
155         set_node(fi, node);
156         return 0;
157 }
158
159 static int fuse_exfat_create(const char* path, mode_t mode,
160                 struct fuse_file_info* fi)
161 {
162         struct exfat_node* node;
163         int rc;
164
165         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
166
167         rc = exfat_mknod(&ef, path);
168         if (rc != 0)
169                 return rc;
170         rc = exfat_lookup(&ef, &node, path);
171         if (rc != 0)
172                 return rc;
173         set_node(fi, node);
174         return 0;
175 }
176
177 static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
178 {
179         /*
180            This handler is called by FUSE on close() syscall. If the FUSE
181            implementation does not call flush handler, we will flush node here.
182            But in this case we will not be able to return an error to the caller.
183            See fuse_exfat_flush() below.
184         */
185         exfat_debug("[%s] %s", __func__, path);
186         exfat_flush_node(&ef, get_node(fi));
187         exfat_put_node(&ef, get_node(fi));
188         return 0; /* FUSE ignores this return value */
189 }
190
191 static int fuse_exfat_flush(const char* path, struct fuse_file_info* fi)
192 {
193         /*
194            This handler may be called by FUSE on close() syscall. FUSE also deals
195            with removals of open files, so we don't free clusters on close but
196            only on rmdir and unlink. If the FUSE implementation does not call this
197            handler we will flush node on release. See fuse_exfat_relase() above.
198         */
199         exfat_debug("[%s] %s", __func__, path);
200         return exfat_flush_node(&ef, get_node(fi));
201 }
202
203 static int fuse_exfat_fsync(const char* path, int datasync,
204                 struct fuse_file_info *fi)
205 {
206         int rc;
207
208         exfat_debug("[%s] %s", __func__, path);
209         rc = exfat_flush_nodes(&ef);
210         if (rc != 0)
211                 return rc;
212         rc = exfat_flush(&ef);
213         if (rc != 0)
214                 return rc;
215         return exfat_fsync(ef.dev);
216 }
217
218 static int fuse_exfat_read(const char* path, char* buffer, size_t size,
219                 off_t offset, struct fuse_file_info* fi)
220 {
221         ssize_t ret;
222
223         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
224         ret = exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
225         if (ret < 0)
226                 return -EIO;
227         return ret;
228 }
229
230 static int fuse_exfat_write(const char* path, const char* buffer, size_t size,
231                 off_t offset, struct fuse_file_info* fi)
232 {
233         ssize_t ret;
234
235         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
236         ret = exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
237         if (ret < 0)
238                 return -EIO;
239         return ret;
240 }
241
242 static int fuse_exfat_unlink(const char* path)
243 {
244         struct exfat_node* node;
245         int rc;
246
247         exfat_debug("[%s] %s", __func__, path);
248
249         rc = exfat_lookup(&ef, &node, path);
250         if (rc != 0)
251                 return rc;
252
253         rc = exfat_unlink(&ef, node);
254         exfat_put_node(&ef, node);
255         if (rc != 0)
256                 return rc;
257         return exfat_cleanup_node(&ef, node);
258 }
259
260 static int fuse_exfat_rmdir(const char* path)
261 {
262         struct exfat_node* node;
263         int rc;
264
265         exfat_debug("[%s] %s", __func__, path);
266
267         rc = exfat_lookup(&ef, &node, path);
268         if (rc != 0)
269                 return rc;
270
271         rc = exfat_rmdir(&ef, node);
272         exfat_put_node(&ef, node);
273         if (rc != 0)
274                 return rc;
275         return exfat_cleanup_node(&ef, node);
276 }
277
278 static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev)
279 {
280         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
281         return exfat_mknod(&ef, path);
282 }
283
284 static int fuse_exfat_mkdir(const char* path, mode_t mode)
285 {
286         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
287         return exfat_mkdir(&ef, path);
288 }
289
290 static int fuse_exfat_rename(const char* old_path, const char* new_path)
291 {
292         exfat_debug("[%s] %s => %s", __func__, old_path, new_path);
293         return exfat_rename(&ef, old_path, new_path);
294 }
295
296 static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
297 {
298         struct exfat_node* node;
299         int rc;
300
301         exfat_debug("[%s] %s", __func__, path);
302
303         rc = exfat_lookup(&ef, &node, path);
304         if (rc != 0)
305                 return rc;
306
307         exfat_utimes(node, tv);
308         rc = exfat_flush_node(&ef, node);
309         exfat_put_node(&ef, node);
310         return rc;
311 }
312
313 static int fuse_exfat_chmod(const char* path, mode_t mode)
314 {
315         const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
316                         S_IRWXU | S_IRWXG | S_IRWXO;
317
318         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
319         if (mode & ~VALID_MODE_MASK)
320                 return -EPERM;
321         return 0;
322 }
323
324 static int fuse_exfat_chown(const char* path, uid_t uid, gid_t gid)
325 {
326         exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
327         if (uid != ef.uid || gid != ef.gid)
328                 return -EPERM;
329         return 0;
330 }
331
332 static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
333 {
334         exfat_debug("[%s]", __func__);
335
336         sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
337         sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
338         sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits;
339         sfs->f_bavail = exfat_count_free_clusters(&ef);
340         sfs->f_bfree = sfs->f_bavail;
341         sfs->f_namemax = EXFAT_NAME_MAX;
342
343         /*
344            Below are fake values because in exFAT there is
345            a) no simple way to count files;
346            b) no such thing as inode;
347            So here we assume that inode = cluster.
348         */
349         sfs->f_files = le32_to_cpu(ef.sb->cluster_count);
350         sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
351         sfs->f_ffree = sfs->f_bavail;
352
353         return 0;
354 }
355
356 static void* fuse_exfat_init(struct fuse_conn_info* fci)
357 {
358         exfat_debug("[%s]", __func__);
359 #ifdef FUSE_CAP_BIG_WRITES
360         fci->want |= FUSE_CAP_BIG_WRITES;
361 #endif
362         return NULL;
363 }
364
365 static void fuse_exfat_destroy(void* unused)
366 {
367         exfat_debug("[%s]", __func__);
368         exfat_unmount(&ef);
369 }
370
371 static void usage(const char* prog)
372 {
373         fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
374         exit(1);
375 }
376
377 static struct fuse_operations fuse_exfat_ops =
378 {
379         .getattr        = fuse_exfat_getattr,
380         .truncate       = fuse_exfat_truncate,
381         .readdir        = fuse_exfat_readdir,
382         .open           = fuse_exfat_open,
383         .create         = fuse_exfat_create,
384         .release        = fuse_exfat_release,
385         .flush          = fuse_exfat_flush,
386         .fsync          = fuse_exfat_fsync,
387         .fsyncdir       = fuse_exfat_fsync,
388         .read           = fuse_exfat_read,
389         .write          = fuse_exfat_write,
390         .unlink         = fuse_exfat_unlink,
391         .rmdir          = fuse_exfat_rmdir,
392         .mknod          = fuse_exfat_mknod,
393         .mkdir          = fuse_exfat_mkdir,
394         .rename         = fuse_exfat_rename,
395         .utimens        = fuse_exfat_utimens,
396         .chmod          = fuse_exfat_chmod,
397         .chown          = fuse_exfat_chown,
398         .statfs         = fuse_exfat_statfs,
399         .init           = fuse_exfat_init,
400         .destroy        = fuse_exfat_destroy,
401 };
402
403 static char* add_option(char* options, const char* name, const char* value)
404 {
405         size_t size;
406         char* optionsf = options;
407
408         if (value)
409                 size = strlen(options) + strlen(name) + strlen(value) + 3;
410         else
411                 size = strlen(options) + strlen(name) + 2;
412
413         options = realloc(options, size);
414         if (options == NULL)
415         {
416                 free(optionsf);
417                 exfat_error("failed to reallocate options string");
418                 return NULL;
419         }
420         strcat(options, ",");
421         strcat(options, name);
422         if (value)
423         {
424                 strcat(options, "=");
425                 strcat(options, value);
426         }
427         return options;
428 }
429
430 static void escape(char* escaped, const char* orig)
431 {
432         do
433         {
434                 if (*orig == ',' || *orig == '\\')
435                         *escaped++ = '\\';
436         }
437         while ((*escaped++ = *orig++));
438 }
439
440 static char* add_fsname_option(char* options, const char* spec)
441 {
442         /* escaped string cannot be more than twice as big as the original one */
443         char* escaped = malloc(strlen(spec) * 2 + 1);
444
445         if (escaped == NULL)
446         {
447                 free(options);
448                 exfat_error("failed to allocate escaped string for %s", spec);
449                 return NULL;
450         }
451
452         /* on some platforms (e.g. Android, Solaris) device names can contain
453            commas */
454         escape(escaped, spec);
455         options = add_option(options, "fsname", escaped);
456         free(escaped);
457         return options;
458 }
459
460 static char* add_user_option(char* options)
461 {
462         struct passwd* pw;
463
464         if (getuid() == 0)
465                 return options;
466
467         pw = getpwuid(getuid());
468         if (pw == NULL || pw->pw_name == NULL)
469         {
470                 free(options);
471                 exfat_error("failed to determine username");
472                 return NULL;
473         }
474         return add_option(options, "user", pw->pw_name);
475 }
476
477 static char* add_blksize_option(char* options, long cluster_size)
478 {
479         long page_size = sysconf(_SC_PAGESIZE);
480         char blksize[20];
481
482         if (page_size < 1)
483                 page_size = 0x1000;
484
485         snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
486         return add_option(options, "blksize", blksize);
487 }
488
489 static char* add_fuse_options(char* options, const char* spec)
490 {
491         options = add_fsname_option(options, spec);
492         if (options == NULL)
493                 return NULL;
494         options = add_user_option(options);
495         if (options == NULL)
496                 return NULL;
497         options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
498         if (options == NULL)
499                 return NULL;
500
501         return options;
502 }
503
504 int main(int argc, char* argv[])
505 {
506         struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL);
507         struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL);
508         const char* spec = NULL;
509         const char* mount_point = NULL;
510         char* mount_options;
511         int debug = 0;
512         struct fuse_chan* fc = NULL;
513         struct fuse* fh = NULL;
514         int opt;
515
516         printf("FUSE exfat %s\n", VERSION);
517
518         mount_options = strdup(default_options);
519         if (mount_options == NULL)
520         {
521                 exfat_error("failed to allocate options string");
522                 return 1;
523         }
524
525         while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
526         {
527                 switch (opt)
528                 {
529                 case 'd':
530                         debug = 1;
531                         break;
532                 case 'n':
533                         break;
534                 case 'o':
535                         mount_options = add_option(mount_options, optarg, NULL);
536                         if (mount_options == NULL)
537                                 return 1;
538                         break;
539                 case 'V':
540                         free(mount_options);
541                         puts("Copyright (C) 2010-2016  Andrew Nayenko");
542                         return 0;
543                 case 'v':
544                         break;
545                 default:
546                         free(mount_options);
547                         usage(argv[0]);
548                         break;
549                 }
550         }
551         if (argc - optind != 2)
552         {
553                 free(mount_options);
554                 usage(argv[0]);
555         }
556         spec = argv[optind];
557         mount_point = argv[optind + 1];
558
559         if (exfat_mount(&ef, spec, mount_options) != 0)
560         {
561                 free(mount_options);
562                 return 1;
563         }
564
565         if (ef.ro == -1) /* read-only fallback was used */
566         {
567                 mount_options = add_option(mount_options, "ro", NULL);
568                 if (mount_options == NULL)
569                 {
570                         exfat_unmount(&ef);
571                         return 1;
572                 }
573         }
574
575         mount_options = add_fuse_options(mount_options, spec);
576         if (mount_options == NULL)
577         {
578                 exfat_unmount(&ef);
579                 return 1;
580         }
581
582         /* create arguments for fuse_mount() */
583         if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
584                 fuse_opt_add_arg(&mount_args, "-o") != 0 ||
585                 fuse_opt_add_arg(&mount_args, mount_options) != 0)
586         {
587                 exfat_unmount(&ef);
588                 free(mount_options);
589                 return 1;
590         }
591
592         free(mount_options);
593
594         /* create FUSE mount point */
595         fc = fuse_mount(mount_point, &mount_args);
596         fuse_opt_free_args(&mount_args);
597         if (fc == NULL)
598         {
599                 exfat_unmount(&ef);
600                 return 1;
601         }
602
603         /* create arguments for fuse_new() */
604         if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
605                 (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
606         {
607                 fuse_unmount(mount_point, fc);
608                 exfat_unmount(&ef);
609                 return 1;
610         }
611
612         /* create new FUSE file system */
613         fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops,
614                         sizeof(struct fuse_operations), NULL);
615         fuse_opt_free_args(&newfs_args);
616         if (fh == NULL)
617         {
618                 fuse_unmount(mount_point, fc);
619                 exfat_unmount(&ef);
620                 return 1;
621         }
622
623         /* exit session on HUP, TERM and INT signals and ignore PIPE signal */
624         if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
625         {
626                 fuse_unmount(mount_point, fc);
627                 fuse_destroy(fh);
628                 exfat_unmount(&ef);
629                 exfat_error("failed to set signal handlers");
630                 return 1;
631         }
632
633         /* go to background (unless "-d" option is passed) and run FUSE
634            main loop */
635         if (fuse_daemonize(debug) == 0)
636         {
637                 if (fuse_loop(fh) != 0)
638                         exfat_error("FUSE loop failure");
639         }
640         else
641                 exfat_error("failed to daemonize");
642
643         fuse_remove_signal_handlers(fuse_get_session(fh));
644         /* note that fuse_unmount() must be called BEFORE fuse_destroy() */
645         fuse_unmount(mount_point, fc);
646         fuse_destroy(fh);
647         return 0;
648 }