OSDN Git Service

Unite code that forms options for FUSE.
[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
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->attrib & EXFAT_ATTRIB_DIR))
115         {
116                 exfat_put_node(&ef, parent);
117                 exfat_error("'%s' is not a directory (%#hx)", path, parent->attrib);
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(&it)))
132         {
133                 exfat_get_name(node, name);
134                 exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
135                                 name, node->is_contiguous ? "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_ro_option(char* options, bool ro)
451 {
452         return ro ? add_option(options, "ro", NULL) : options;
453 }
454
455 static char* add_user_option(char* options)
456 {
457         struct passwd* pw;
458
459         if (getuid() == 0)
460                 return options;
461
462         pw = getpwuid(getuid());
463         if (pw == NULL || pw->pw_name == NULL)
464         {
465                 free(options);
466                 exfat_error("failed to determine username");
467                 return NULL;
468         }
469         return add_option(options, "user", pw->pw_name);
470 }
471
472 static char* add_blksize_option(char* options, long cluster_size)
473 {
474         long page_size = sysconf(_SC_PAGESIZE);
475         char blksize[20];
476
477         if (page_size < 1)
478                 page_size = 0x1000;
479
480         snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
481         return add_option(options, "blksize", blksize);
482 }
483
484 static char* add_fuse_options(char* options, const char* spec, bool ro)
485 {
486         options = add_fsname_option(options, spec);
487         if (options == NULL)
488                 return NULL;
489         options = add_ro_option(options, ro);
490         if (options == NULL)
491                 return NULL;
492         options = add_user_option(options);
493         if (options == NULL)
494                 return NULL;
495         options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
496         if (options == NULL)
497                 return NULL;
498
499         return options;
500 }
501
502 int main(int argc, char* argv[])
503 {
504         struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL);
505         struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL);
506         const char* spec = NULL;
507         const char* mount_point = NULL;
508         char* mount_options;
509         int debug = 0;
510         struct fuse_chan* fc = NULL;
511         struct fuse* fh = NULL;
512         int opt;
513
514         printf("FUSE exfat %s\n", VERSION);
515
516         mount_options = strdup(default_options);
517         if (mount_options == NULL)
518         {
519                 exfat_error("failed to allocate options string");
520                 return 1;
521         }
522
523         while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
524         {
525                 switch (opt)
526                 {
527                 case 'd':
528                         debug = 1;
529                         break;
530                 case 'n':
531                         break;
532                 case 'o':
533                         mount_options = add_option(mount_options, optarg, NULL);
534                         if (mount_options == NULL)
535                                 return 1;
536                         break;
537                 case 'V':
538                         free(mount_options);
539                         puts("Copyright (C) 2010-2018  Andrew Nayenko");
540                         return 0;
541                 case 'v':
542                         break;
543                 default:
544                         free(mount_options);
545                         usage(argv[0]);
546                         break;
547                 }
548         }
549         if (argc - optind != 2)
550         {
551                 free(mount_options);
552                 usage(argv[0]);
553         }
554         spec = argv[optind];
555         mount_point = argv[optind + 1];
556
557         if (exfat_mount(&ef, spec, mount_options) != 0)
558         {
559                 free(mount_options);
560                 return 1;
561         }
562
563         mount_options = add_fuse_options(mount_options, spec, (ef.ro == -1));
564         if (mount_options == NULL)
565         {
566                 exfat_unmount(&ef);
567                 return 1;
568         }
569
570         /* create arguments for fuse_mount() */
571         if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
572                 fuse_opt_add_arg(&mount_args, "-o") != 0 ||
573                 fuse_opt_add_arg(&mount_args, mount_options) != 0)
574         {
575                 exfat_unmount(&ef);
576                 free(mount_options);
577                 return 1;
578         }
579
580         free(mount_options);
581
582         /* create FUSE mount point */
583         fc = fuse_mount(mount_point, &mount_args);
584         fuse_opt_free_args(&mount_args);
585         if (fc == NULL)
586         {
587                 exfat_unmount(&ef);
588                 return 1;
589         }
590
591         /* create arguments for fuse_new() */
592         if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
593                 (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
594         {
595                 fuse_unmount(mount_point, fc);
596                 exfat_unmount(&ef);
597                 return 1;
598         }
599
600         /* create new FUSE file system */
601         fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops,
602                         sizeof(struct fuse_operations), NULL);
603         fuse_opt_free_args(&newfs_args);
604         if (fh == NULL)
605         {
606                 fuse_unmount(mount_point, fc);
607                 exfat_unmount(&ef);
608                 return 1;
609         }
610
611         /* exit session on HUP, TERM and INT signals and ignore PIPE signal */
612         if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
613         {
614                 fuse_unmount(mount_point, fc);
615                 fuse_destroy(fh);
616                 exfat_unmount(&ef);
617                 exfat_error("failed to set signal handlers");
618                 return 1;
619         }
620
621         /* go to background (unless "-d" option is passed) and run FUSE
622            main loop */
623         if (fuse_daemonize(debug) == 0)
624         {
625                 if (fuse_loop(fh) != 0)
626                         exfat_error("FUSE loop failure");
627         }
628         else
629                 exfat_error("failed to daemonize");
630
631         fuse_remove_signal_handlers(fuse_get_session(fh));
632         /* note that fuse_unmount() must be called BEFORE fuse_destroy() */
633         fuse_unmount(mount_point, fc);
634         fuse_destroy(fh);
635         return 0;
636 }