OSDN Git Service

6d566d9f628bdb9c63fd6a2b4040fac4533cc690
[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-2018  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         struct stat stbuf;
109
110         exfat_debug("[%s] %s", __func__, path);
111
112         rc = exfat_lookup(&ef, &parent, path);
113         if (rc != 0)
114                 return rc;
115         if (!(parent->attrib & EXFAT_ATTRIB_DIR))
116         {
117                 exfat_put_node(&ef, parent);
118                 exfat_error("'%s' is not a directory (%#hx)", path, parent->attrib);
119                 return -ENOTDIR;
120         }
121
122         filler(buffer, ".", NULL, 0);
123         filler(buffer, "..", NULL, 0);
124
125         rc = exfat_opendir(&ef, parent, &it);
126         if (rc != 0)
127         {
128                 exfat_put_node(&ef, parent);
129                 exfat_error("failed to open directory '%s'", path);
130                 return rc;
131         }
132         while ((node = exfat_readdir(&it)))
133         {
134                 exfat_get_name(node, name);
135                 exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
136                                 name, node->is_contiguous ? "contiguous" : "fragmented",
137                                 node->size, node->start_cluster);
138                 exfat_stat(&ef, node, &stbuf);
139                 filler(buffer, name, &stbuf, 0);
140                 exfat_put_node(&ef, node);
141         }
142         exfat_closedir(&ef, &it);
143         exfat_put_node(&ef, parent);
144         return 0;
145 }
146
147 static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
148 {
149         struct exfat_node* node;
150         int rc;
151
152         exfat_debug("[%s] %s", __func__, path);
153
154         rc = exfat_lookup(&ef, &node, path);
155         if (rc != 0)
156                 return rc;
157         set_node(fi, node);
158         return 0;
159 }
160
161 static int fuse_exfat_create(const char* path, mode_t mode,
162                 struct fuse_file_info* fi)
163 {
164         struct exfat_node* node;
165         int rc;
166
167         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
168
169         rc = exfat_mknod(&ef, path);
170         if (rc != 0)
171                 return rc;
172         rc = exfat_lookup(&ef, &node, path);
173         if (rc != 0)
174                 return rc;
175         set_node(fi, node);
176         return 0;
177 }
178
179 static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
180 {
181         /*
182            This handler is called by FUSE on close() syscall. If the FUSE
183            implementation does not call flush handler, we will flush node here.
184            But in this case we will not be able to return an error to the caller.
185            See fuse_exfat_flush() below.
186         */
187         exfat_debug("[%s] %s", __func__, path);
188         exfat_flush_node(&ef, get_node(fi));
189         exfat_put_node(&ef, get_node(fi));
190         return 0; /* FUSE ignores this return value */
191 }
192
193 static int fuse_exfat_flush(const char* path, struct fuse_file_info* fi)
194 {
195         /*
196            This handler may be called by FUSE on close() syscall. FUSE also deals
197            with removals of open files, so we don't free clusters on close but
198            only on rmdir and unlink. If the FUSE implementation does not call this
199            handler we will flush node on release. See fuse_exfat_relase() above.
200         */
201         exfat_debug("[%s] %s", __func__, path);
202         return exfat_flush_node(&ef, get_node(fi));
203 }
204
205 static int fuse_exfat_fsync(const char* path, int datasync,
206                 struct fuse_file_info *fi)
207 {
208         int rc;
209
210         exfat_debug("[%s] %s", __func__, path);
211         rc = exfat_flush_nodes(&ef);
212         if (rc != 0)
213                 return rc;
214         rc = exfat_flush(&ef);
215         if (rc != 0)
216                 return rc;
217         return exfat_fsync(ef.dev);
218 }
219
220 static int fuse_exfat_read(const char* path, char* buffer, size_t size,
221                 off_t offset, struct fuse_file_info* fi)
222 {
223         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
224         return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
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         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
231         return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
232 }
233
234 static int fuse_exfat_unlink(const char* path)
235 {
236         struct exfat_node* node;
237         int rc;
238
239         exfat_debug("[%s] %s", __func__, path);
240
241         rc = exfat_lookup(&ef, &node, path);
242         if (rc != 0)
243                 return rc;
244
245         rc = exfat_unlink(&ef, node);
246         exfat_put_node(&ef, node);
247         if (rc != 0)
248                 return rc;
249         return exfat_cleanup_node(&ef, node);
250 }
251
252 static int fuse_exfat_rmdir(const char* path)
253 {
254         struct exfat_node* node;
255         int rc;
256
257         exfat_debug("[%s] %s", __func__, path);
258
259         rc = exfat_lookup(&ef, &node, path);
260         if (rc != 0)
261                 return rc;
262
263         rc = exfat_rmdir(&ef, node);
264         exfat_put_node(&ef, node);
265         if (rc != 0)
266                 return rc;
267         return exfat_cleanup_node(&ef, node);
268 }
269
270 static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev)
271 {
272         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
273         return exfat_mknod(&ef, path);
274 }
275
276 static int fuse_exfat_mkdir(const char* path, mode_t mode)
277 {
278         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
279         return exfat_mkdir(&ef, path);
280 }
281
282 static int fuse_exfat_rename(const char* old_path, const char* new_path)
283 {
284         exfat_debug("[%s] %s => %s", __func__, old_path, new_path);
285         return exfat_rename(&ef, old_path, new_path);
286 }
287
288 static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
289 {
290         struct exfat_node* node;
291         int rc;
292
293         exfat_debug("[%s] %s", __func__, path);
294
295         rc = exfat_lookup(&ef, &node, path);
296         if (rc != 0)
297                 return rc;
298
299         exfat_utimes(node, tv);
300         rc = exfat_flush_node(&ef, node);
301         exfat_put_node(&ef, node);
302         return rc;
303 }
304
305 static int fuse_exfat_chmod(const char* path, mode_t mode)
306 {
307         const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
308                         S_IRWXU | S_IRWXG | S_IRWXO;
309
310         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
311         if (mode & ~VALID_MODE_MASK)
312                 return -EPERM;
313         return 0;
314 }
315
316 static int fuse_exfat_chown(const char* path, uid_t uid, gid_t gid)
317 {
318         exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
319         if (uid != ef.uid || gid != ef.gid)
320                 return -EPERM;
321         return 0;
322 }
323
324 static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
325 {
326         exfat_debug("[%s]", __func__);
327
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;
334
335         /*
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.
340         */
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;
344
345         return 0;
346 }
347
348 static void* fuse_exfat_init(struct fuse_conn_info* fci)
349 {
350         exfat_debug("[%s]", __func__);
351 #ifdef FUSE_CAP_BIG_WRITES
352         fci->want |= FUSE_CAP_BIG_WRITES;
353 #endif
354         return NULL;
355 }
356
357 static void fuse_exfat_destroy(void* unused)
358 {
359         exfat_debug("[%s]", __func__);
360         exfat_unmount(&ef);
361 }
362
363 static void usage(const char* prog)
364 {
365         fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
366         exit(1);
367 }
368
369 static struct fuse_operations fuse_exfat_ops =
370 {
371         .getattr        = fuse_exfat_getattr,
372         .truncate       = fuse_exfat_truncate,
373         .readdir        = fuse_exfat_readdir,
374         .open           = fuse_exfat_open,
375         .create         = fuse_exfat_create,
376         .release        = fuse_exfat_release,
377         .flush          = fuse_exfat_flush,
378         .fsync          = fuse_exfat_fsync,
379         .fsyncdir       = fuse_exfat_fsync,
380         .read           = fuse_exfat_read,
381         .write          = fuse_exfat_write,
382         .unlink         = fuse_exfat_unlink,
383         .rmdir          = fuse_exfat_rmdir,
384         .mknod          = fuse_exfat_mknod,
385         .mkdir          = fuse_exfat_mkdir,
386         .rename         = fuse_exfat_rename,
387         .utimens        = fuse_exfat_utimens,
388         .chmod          = fuse_exfat_chmod,
389         .chown          = fuse_exfat_chown,
390         .statfs         = fuse_exfat_statfs,
391         .init           = fuse_exfat_init,
392         .destroy        = fuse_exfat_destroy,
393 };
394
395 static char* add_option(char* options, const char* name, const char* value)
396 {
397         size_t size;
398         char* optionsf = options;
399
400         if (value)
401                 size = strlen(options) + strlen(name) + strlen(value) + 3;
402         else
403                 size = strlen(options) + strlen(name) + 2;
404
405         options = realloc(options, size);
406         if (options == NULL)
407         {
408                 free(optionsf);
409                 exfat_error("failed to reallocate options string");
410                 return NULL;
411         }
412         strcat(options, ",");
413         strcat(options, name);
414         if (value)
415         {
416                 strcat(options, "=");
417                 strcat(options, value);
418         }
419         return options;
420 }
421
422 static void escape(char* escaped, const char* orig)
423 {
424         do
425         {
426                 if (*orig == ',' || *orig == '\\')
427                         *escaped++ = '\\';
428         }
429         while ((*escaped++ = *orig++));
430 }
431
432 static char* add_fsname_option(char* options, const char* spec)
433 {
434         /* escaped string cannot be more than twice as big as the original one */
435         char* escaped = malloc(strlen(spec) * 2 + 1);
436
437         if (escaped == NULL)
438         {
439                 free(options);
440                 exfat_error("failed to allocate escaped string for %s", spec);
441                 return NULL;
442         }
443
444         /* on some platforms (e.g. Android, Solaris) device names can contain
445            commas */
446         escape(escaped, spec);
447         options = add_option(options, "fsname", escaped);
448         free(escaped);
449         return options;
450 }
451
452 static char* add_ro_option(char* options, bool ro)
453 {
454         return ro ? add_option(options, "ro", NULL) : options;
455 }
456
457 static char* add_user_option(char* options)
458 {
459         struct passwd* pw;
460
461         if (getuid() == 0)
462                 return options;
463
464         pw = getpwuid(getuid());
465         if (pw == NULL || pw->pw_name == NULL)
466         {
467                 free(options);
468                 exfat_error("failed to determine username");
469                 return NULL;
470         }
471         return add_option(options, "user", pw->pw_name);
472 }
473
474 static char* add_blksize_option(char* options, long cluster_size)
475 {
476         long page_size = sysconf(_SC_PAGESIZE);
477         char blksize[20];
478
479         if (page_size < 1)
480                 page_size = 0x1000;
481
482         snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
483         return add_option(options, "blksize", blksize);
484 }
485
486 static char* add_fuse_options(char* options, const char* spec, bool ro)
487 {
488         options = add_fsname_option(options, spec);
489         if (options == NULL)
490                 return NULL;
491         options = add_ro_option(options, ro);
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-2018  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         mount_options = add_fuse_options(mount_options, spec, (ef.ro != 0));
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 }