]> git.sven.stormbind.net Git - sven/fuse-exfat.git/blob - fuse/main.c
releasing package fuse-exfat version 1.2.1-1
[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(&ef);
210         if (rc != 0)
211                 return rc;
212         return exfat_fsync(ef.dev);
213 }
214
215 static int fuse_exfat_read(const char* path, char* buffer, size_t size,
216                 off_t offset, struct fuse_file_info* fi)
217 {
218         ssize_t ret;
219
220         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
221         ret = exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
222         if (ret < 0)
223                 return -EIO;
224         return ret;
225 }
226
227 static int fuse_exfat_write(const char* path, const char* buffer, size_t size,
228                 off_t offset, struct fuse_file_info* fi)
229 {
230         ssize_t ret;
231
232         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
233         ret = exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
234         if (ret < 0)
235                 return -EIO;
236         return ret;
237 }
238
239 static int fuse_exfat_unlink(const char* path)
240 {
241         struct exfat_node* node;
242         int rc;
243
244         exfat_debug("[%s] %s", __func__, path);
245
246         rc = exfat_lookup(&ef, &node, path);
247         if (rc != 0)
248                 return rc;
249
250         rc = exfat_unlink(&ef, node);
251         exfat_put_node(&ef, node);
252         if (rc != 0)
253                 return rc;
254         return exfat_cleanup_node(&ef, node);
255 }
256
257 static int fuse_exfat_rmdir(const char* path)
258 {
259         struct exfat_node* node;
260         int rc;
261
262         exfat_debug("[%s] %s", __func__, path);
263
264         rc = exfat_lookup(&ef, &node, path);
265         if (rc != 0)
266                 return rc;
267
268         rc = exfat_rmdir(&ef, node);
269         exfat_put_node(&ef, node);
270         if (rc != 0)
271                 return rc;
272         return exfat_cleanup_node(&ef, node);
273 }
274
275 static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev)
276 {
277         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
278         return exfat_mknod(&ef, path);
279 }
280
281 static int fuse_exfat_mkdir(const char* path, mode_t mode)
282 {
283         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
284         return exfat_mkdir(&ef, path);
285 }
286
287 static int fuse_exfat_rename(const char* old_path, const char* new_path)
288 {
289         exfat_debug("[%s] %s => %s", __func__, old_path, new_path);
290         return exfat_rename(&ef, old_path, new_path);
291 }
292
293 static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
294 {
295         struct exfat_node* node;
296         int rc;
297
298         exfat_debug("[%s] %s", __func__, path);
299
300         rc = exfat_lookup(&ef, &node, path);
301         if (rc != 0)
302                 return rc;
303
304         exfat_utimes(node, tv);
305         rc = exfat_flush_node(&ef, node);
306         exfat_put_node(&ef, node);
307         return rc;
308 }
309
310 static int fuse_exfat_chmod(const char* path, mode_t mode)
311 {
312         const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
313                         S_IRWXU | S_IRWXG | S_IRWXO;
314
315         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
316         if (mode & ~VALID_MODE_MASK)
317                 return -EPERM;
318         return 0;
319 }
320
321 static int fuse_exfat_chown(const char* path, uid_t uid, gid_t gid)
322 {
323         exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
324         if (uid != ef.uid || gid != ef.gid)
325                 return -EPERM;
326         return 0;
327 }
328
329 static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
330 {
331         exfat_debug("[%s]", __func__);
332
333         sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
334         sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
335         sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits;
336         sfs->f_bavail = exfat_count_free_clusters(&ef);
337         sfs->f_bfree = sfs->f_bavail;
338         sfs->f_namemax = EXFAT_NAME_MAX;
339
340         /*
341            Below are fake values because in exFAT there is
342            a) no simple way to count files;
343            b) no such thing as inode;
344            So here we assume that inode = cluster.
345         */
346         sfs->f_files = le32_to_cpu(ef.sb->cluster_count);
347         sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
348         sfs->f_ffree = sfs->f_bavail;
349
350         return 0;
351 }
352
353 static void* fuse_exfat_init(struct fuse_conn_info* fci)
354 {
355         exfat_debug("[%s]", __func__);
356 #ifdef FUSE_CAP_BIG_WRITES
357         fci->want |= FUSE_CAP_BIG_WRITES;
358 #endif
359         return NULL;
360 }
361
362 static void fuse_exfat_destroy(void* unused)
363 {
364         exfat_debug("[%s]", __func__);
365         exfat_unmount(&ef);
366 }
367
368 static void usage(const char* prog)
369 {
370         fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
371         exit(1);
372 }
373
374 static struct fuse_operations fuse_exfat_ops =
375 {
376         .getattr        = fuse_exfat_getattr,
377         .truncate       = fuse_exfat_truncate,
378         .readdir        = fuse_exfat_readdir,
379         .open           = fuse_exfat_open,
380         .create         = fuse_exfat_create,
381         .release        = fuse_exfat_release,
382         .flush          = fuse_exfat_flush,
383         .fsync          = fuse_exfat_fsync,
384         .fsyncdir       = fuse_exfat_fsync,
385         .read           = fuse_exfat_read,
386         .write          = fuse_exfat_write,
387         .unlink         = fuse_exfat_unlink,
388         .rmdir          = fuse_exfat_rmdir,
389         .mknod          = fuse_exfat_mknod,
390         .mkdir          = fuse_exfat_mkdir,
391         .rename         = fuse_exfat_rename,
392         .utimens        = fuse_exfat_utimens,
393         .chmod          = fuse_exfat_chmod,
394         .chown          = fuse_exfat_chown,
395         .statfs         = fuse_exfat_statfs,
396         .init           = fuse_exfat_init,
397         .destroy        = fuse_exfat_destroy,
398 };
399
400 static char* add_option(char* options, const char* name, const char* value)
401 {
402         size_t size;
403         char* optionsf = options;
404
405         if (value)
406                 size = strlen(options) + strlen(name) + strlen(value) + 3;
407         else
408                 size = strlen(options) + strlen(name) + 2;
409
410         options = realloc(options, size);
411         if (options == NULL)
412         {
413                 free(optionsf);
414                 exfat_error("failed to reallocate options string");
415                 return NULL;
416         }
417         strcat(options, ",");
418         strcat(options, name);
419         if (value)
420         {
421                 strcat(options, "=");
422                 strcat(options, value);
423         }
424         return options;
425 }
426
427 static char* add_user_option(char* options)
428 {
429         struct passwd* pw;
430
431         if (getuid() == 0)
432                 return options;
433
434         pw = getpwuid(getuid());
435         if (pw == NULL || pw->pw_name == NULL)
436         {
437                 free(options);
438                 exfat_error("failed to determine username");
439                 return NULL;
440         }
441         return add_option(options, "user", pw->pw_name);
442 }
443
444 static char* add_blksize_option(char* options, long cluster_size)
445 {
446         long page_size = sysconf(_SC_PAGESIZE);
447         char blksize[20];
448
449         if (page_size < 1)
450                 page_size = 0x1000;
451
452         snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
453         return add_option(options, "blksize", blksize);
454 }
455
456 static char* add_fuse_options(char* options, const char* spec)
457 {
458         options = add_option(options, "fsname", spec);
459         if (options == NULL)
460                 return NULL;
461         options = add_user_option(options);
462         if (options == NULL)
463                 return NULL;
464         options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
465         if (options == NULL)
466                 return NULL;
467
468         return options;
469 }
470
471 int main(int argc, char* argv[])
472 {
473         struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL);
474         struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL);
475         const char* spec = NULL;
476         const char* mount_point = NULL;
477         char* mount_options;
478         int debug = 0;
479         struct fuse_chan* fc = NULL;
480         struct fuse* fh = NULL;
481         int opt;
482
483         printf("FUSE exfat %s\n", VERSION);
484
485         mount_options = strdup(default_options);
486         if (mount_options == NULL)
487         {
488                 exfat_error("failed to allocate options string");
489                 return 1;
490         }
491
492         while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
493         {
494                 switch (opt)
495                 {
496                 case 'd':
497                         debug = 1;
498                         break;
499                 case 'n':
500                         break;
501                 case 'o':
502                         mount_options = add_option(mount_options, optarg, NULL);
503                         if (mount_options == NULL)
504                                 return 1;
505                         break;
506                 case 'V':
507                         free(mount_options);
508                         puts("Copyright (C) 2010-2015  Andrew Nayenko");
509                         return 0;
510                 case 'v':
511                         break;
512                 default:
513                         free(mount_options);
514                         usage(argv[0]);
515                         break;
516                 }
517         }
518         if (argc - optind != 2)
519         {
520                 free(mount_options);
521                 usage(argv[0]);
522         }
523         spec = argv[optind];
524         mount_point = argv[optind + 1];
525
526         if (exfat_mount(&ef, spec, mount_options) != 0)
527         {
528                 free(mount_options);
529                 return 1;
530         }
531
532         if (ef.ro == -1) /* read-only fallback was used */
533         {
534                 mount_options = add_option(mount_options, "ro", NULL);
535                 if (mount_options == NULL)
536                 {
537                         exfat_unmount(&ef);
538                         return 1;
539                 }
540         }
541
542         mount_options = add_fuse_options(mount_options, spec);
543         if (mount_options == NULL)
544         {
545                 exfat_unmount(&ef);
546                 return 1;
547         }
548
549         /* create arguments for fuse_mount() */
550         if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
551                 fuse_opt_add_arg(&mount_args, "-o") != 0 ||
552                 fuse_opt_add_arg(&mount_args, mount_options) != 0)
553         {
554                 exfat_unmount(&ef);
555                 free(mount_options);
556                 return 1;
557         }
558
559         free(mount_options);
560
561         /* create FUSE mount point */
562         fc = fuse_mount(mount_point, &mount_args);
563         fuse_opt_free_args(&mount_args);
564         if (fc == NULL)
565         {
566                 exfat_unmount(&ef);
567                 return 1;
568         }
569
570         /* create arguments for fuse_new() */
571         if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
572                 (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
573         {
574                 fuse_unmount(mount_point, fc);
575                 exfat_unmount(&ef);
576                 return 1;
577         }
578
579         /* create new FUSE file system */
580         fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops,
581                         sizeof(struct fuse_operations), NULL);
582         fuse_opt_free_args(&newfs_args);
583         if (fh == NULL)
584         {
585                 fuse_unmount(mount_point, fc);
586                 exfat_unmount(&ef);
587                 return 1;
588         }
589
590         /* exit session on HUP, TERM and INT signals and ignore PIPE signal */
591         if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
592         {
593                 fuse_unmount(mount_point, fc);
594                 fuse_destroy(fh);
595                 exfat_unmount(&ef);
596                 exfat_error("failed to set signal handlers");
597                 return 1;
598         }
599
600         /* go to background (unless "-d" option is passed) and run FUSE
601            main loop */
602         if (fuse_daemonize(debug) == 0)
603         {
604                 if (fuse_loop(fh) != 0)
605                         exfat_error("FUSE loop failure");
606         }
607         else
608                 exfat_error("failed to daemonize");
609
610         fuse_remove_signal_handlers(fuse_get_session(fh));
611         /* note that fuse_unmount() must be called BEFORE fuse_destroy() */
612         fuse_unmount(mount_point, fc);
613         fuse_destroy(fh);
614         return 0;
615 }