OSDN Git Service

Use GNU build system (autotools).
[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-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 #define FUSE_USE_VERSION 26
24 #include <fuse.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <exfat.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 %u.%u.%u\n",
484                         EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
485
486         mount_options = strdup(default_options);
487         if (mount_options == NULL)
488         {
489                 exfat_error("failed to allocate options string");
490                 return 1;
491         }
492
493         while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
494         {
495                 switch (opt)
496                 {
497                 case 'd':
498                         debug = 1;
499                         break;
500                 case 'n':
501                         break;
502                 case 'o':
503                         mount_options = add_option(mount_options, optarg, NULL);
504                         if (mount_options == NULL)
505                                 return 1;
506                         break;
507                 case 'V':
508                         free(mount_options);
509                         puts("Copyright (C) 2010-2015  Andrew Nayenko");
510                         return 0;
511                 case 'v':
512                         break;
513                 default:
514                         free(mount_options);
515                         usage(argv[0]);
516                         break;
517                 }
518         }
519         if (argc - optind != 2)
520         {
521                 free(mount_options);
522                 usage(argv[0]);
523         }
524         spec = argv[optind];
525         mount_point = argv[optind + 1];
526
527         if (exfat_mount(&ef, spec, mount_options) != 0)
528         {
529                 free(mount_options);
530                 return 1;
531         }
532
533         if (ef.ro == -1) /* read-only fallback was used */
534         {
535                 mount_options = add_option(mount_options, "ro", NULL);
536                 if (mount_options == NULL)
537                 {
538                         exfat_unmount(&ef);
539                         return 1;
540                 }
541         }
542
543         mount_options = add_fuse_options(mount_options, spec);
544         if (mount_options == NULL)
545         {
546                 exfat_unmount(&ef);
547                 return 1;
548         }
549
550         /* create arguments for fuse_mount() */
551         if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
552                 fuse_opt_add_arg(&mount_args, "-o") != 0 ||
553                 fuse_opt_add_arg(&mount_args, mount_options) != 0)
554         {
555                 exfat_unmount(&ef);
556                 free(mount_options);
557                 return 1;
558         }
559
560         free(mount_options);
561
562         /* create FUSE mount point */
563         fc = fuse_mount(mount_point, &mount_args);
564         fuse_opt_free_args(&mount_args);
565         if (fc == NULL)
566         {
567                 exfat_unmount(&ef);
568                 return 1;
569         }
570
571         /* create arguments for fuse_new() */
572         if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
573                 (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
574         {
575                 fuse_unmount(mount_point, fc);
576                 exfat_unmount(&ef);
577                 return 1;
578         }
579
580         /* create new FUSE file system */
581         fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops,
582                         sizeof(struct fuse_operations), NULL);
583         fuse_opt_free_args(&newfs_args);
584         if (fh == NULL)
585         {
586                 fuse_unmount(mount_point, fc);
587                 exfat_unmount(&ef);
588                 return 1;
589         }
590
591         /* exit session on HUP, TERM and INT signals and ignore PIPE signal */
592         if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
593         {
594                 fuse_unmount(mount_point, fc);
595                 fuse_destroy(fh);
596                 exfat_unmount(&ef);
597                 exfat_error("failed to set signal handlers");
598                 return 1;
599         }
600
601         /* go to background (unless "-d" option is passed) and run FUSE
602            main loop */
603         if (fuse_daemonize(debug) == 0)
604         {
605                 if (fuse_loop(fh) != 0)
606                         exfat_error("FUSE loop failure");
607         }
608         else
609                 exfat_error("failed to daemonize");
610
611         fuse_remove_signal_handlers(fuse_get_session(fh));
612         /* note that fuse_unmount() must be called BEFORE fuse_destroy() */
613         fuse_unmount(mount_point, fc);
614         fuse_destroy(fh);
615         return 0;
616 }