]> git.sven.stormbind.net Git - sven/fuse-exfat.git/blob - fuse/main.c
aad082b68f21265f21686cdab57097ecafc60b4d
[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-2015  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 char* add_user_option(char* options)
431 {
432         struct passwd* pw;
433
434         if (getuid() == 0)
435                 return options;
436
437         pw = getpwuid(getuid());
438         if (pw == NULL || pw->pw_name == NULL)
439         {
440                 free(options);
441                 exfat_error("failed to determine username");
442                 return NULL;
443         }
444         return add_option(options, "user", pw->pw_name);
445 }
446
447 static char* add_blksize_option(char* options, long cluster_size)
448 {
449         long page_size = sysconf(_SC_PAGESIZE);
450         char blksize[20];
451
452         if (page_size < 1)
453                 page_size = 0x1000;
454
455         snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
456         return add_option(options, "blksize", blksize);
457 }
458
459 static char* add_fuse_options(char* options, const char* spec)
460 {
461         options = add_option(options, "fsname", spec);
462         if (options == NULL)
463                 return NULL;
464         options = add_user_option(options);
465         if (options == NULL)
466                 return NULL;
467         options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
468         if (options == NULL)
469                 return NULL;
470
471         return options;
472 }
473
474 int main(int argc, char* argv[])
475 {
476         struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL);
477         struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL);
478         const char* spec = NULL;
479         const char* mount_point = NULL;
480         char* mount_options;
481         int debug = 0;
482         struct fuse_chan* fc = NULL;
483         struct fuse* fh = NULL;
484         int opt;
485
486         printf("FUSE exfat %s\n", VERSION);
487
488         mount_options = strdup(default_options);
489         if (mount_options == NULL)
490         {
491                 exfat_error("failed to allocate options string");
492                 return 1;
493         }
494
495         while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
496         {
497                 switch (opt)
498                 {
499                 case 'd':
500                         debug = 1;
501                         break;
502                 case 'n':
503                         break;
504                 case 'o':
505                         mount_options = add_option(mount_options, optarg, NULL);
506                         if (mount_options == NULL)
507                                 return 1;
508                         break;
509                 case 'V':
510                         free(mount_options);
511                         puts("Copyright (C) 2010-2015  Andrew Nayenko");
512                         return 0;
513                 case 'v':
514                         break;
515                 default:
516                         free(mount_options);
517                         usage(argv[0]);
518                         break;
519                 }
520         }
521         if (argc - optind != 2)
522         {
523                 free(mount_options);
524                 usage(argv[0]);
525         }
526         spec = argv[optind];
527         mount_point = argv[optind + 1];
528
529         if (exfat_mount(&ef, spec, mount_options) != 0)
530         {
531                 free(mount_options);
532                 return 1;
533         }
534
535         if (ef.ro == -1) /* read-only fallback was used */
536         {
537                 mount_options = add_option(mount_options, "ro", NULL);
538                 if (mount_options == NULL)
539                 {
540                         exfat_unmount(&ef);
541                         return 1;
542                 }
543         }
544
545         mount_options = add_fuse_options(mount_options, spec);
546         if (mount_options == NULL)
547         {
548                 exfat_unmount(&ef);
549                 return 1;
550         }
551
552         /* create arguments for fuse_mount() */
553         if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
554                 fuse_opt_add_arg(&mount_args, "-o") != 0 ||
555                 fuse_opt_add_arg(&mount_args, mount_options) != 0)
556         {
557                 exfat_unmount(&ef);
558                 free(mount_options);
559                 return 1;
560         }
561
562         free(mount_options);
563
564         /* create FUSE mount point */
565         fc = fuse_mount(mount_point, &mount_args);
566         fuse_opt_free_args(&mount_args);
567         if (fc == NULL)
568         {
569                 exfat_unmount(&ef);
570                 return 1;
571         }
572
573         /* create arguments for fuse_new() */
574         if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
575                 (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
576         {
577                 fuse_unmount(mount_point, fc);
578                 exfat_unmount(&ef);
579                 return 1;
580         }
581
582         /* create new FUSE file system */
583         fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops,
584                         sizeof(struct fuse_operations), NULL);
585         fuse_opt_free_args(&newfs_args);
586         if (fh == NULL)
587         {
588                 fuse_unmount(mount_point, fc);
589                 exfat_unmount(&ef);
590                 return 1;
591         }
592
593         /* exit session on HUP, TERM and INT signals and ignore PIPE signal */
594         if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
595         {
596                 fuse_unmount(mount_point, fc);
597                 fuse_destroy(fh);
598                 exfat_unmount(&ef);
599                 exfat_error("failed to set signal handlers");
600                 return 1;
601         }
602
603         /* go to background (unless "-d" option is passed) and run FUSE
604            main loop */
605         if (fuse_daemonize(debug) == 0)
606         {
607                 if (fuse_loop(fh) != 0)
608                         exfat_error("FUSE loop failure");
609         }
610         else
611                 exfat_error("failed to daemonize");
612
613         fuse_remove_signal_handlers(fuse_get_session(fh));
614         /* note that fuse_unmount() must be called BEFORE fuse_destroy() */
615         fuse_unmount(mount_point, fc);
616         fuse_destroy(fh);
617         return 0;
618 }