OSDN Git Service

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