OSDN Git Service

c645390d3f3b5594122ece7ae05192151967670e
[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 struct exfat ef;
46
47 static struct exfat_node* get_node(const struct fuse_file_info* fi)
48 {
49         return (struct exfat_node*) (size_t) fi->fh;
50 }
51
52 static void set_node(struct fuse_file_info* fi, struct exfat_node* node)
53 {
54         fi->fh = (uint64_t) (size_t) node;
55         fi->keep_cache = 1;
56 }
57
58 static int fuse_exfat_getattr(const char* path, struct stat* stbuf)
59 {
60         struct exfat_node* node;
61         int rc;
62
63         exfat_debug("[%s] %s", __func__, path);
64
65         rc = exfat_lookup(&ef, &node, path);
66         if (rc != 0)
67                 return rc;
68
69         exfat_stat(&ef, node, stbuf);
70         exfat_put_node(&ef, node);
71         return 0;
72 }
73
74 static int fuse_exfat_truncate(const char* path, off_t size)
75 {
76         struct exfat_node* node;
77         int rc;
78
79         exfat_debug("[%s] %s, %"PRId64, __func__, path, size);
80
81         rc = exfat_lookup(&ef, &node, path);
82         if (rc != 0)
83                 return rc;
84
85         rc = exfat_truncate(&ef, node, size, true);
86         if (rc != 0)
87         {
88                 exfat_flush_node(&ef, node);    /* ignore return code */
89                 exfat_put_node(&ef, node);
90                 return rc;
91         }
92         rc = exfat_flush_node(&ef, node);
93         exfat_put_node(&ef, node);
94         return rc;
95 }
96
97 static int fuse_exfat_readdir(const char* path, void* buffer,
98                 fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi)
99 {
100         struct exfat_node* parent;
101         struct exfat_node* node;
102         struct exfat_iterator it;
103         int rc;
104         char name[EXFAT_UTF8_NAME_BUFFER_MAX];
105         struct stat stbuf;
106
107         exfat_debug("[%s] %s", __func__, path);
108
109         rc = exfat_lookup(&ef, &parent, path);
110         if (rc != 0)
111                 return rc;
112         if (!(parent->attrib & EXFAT_ATTRIB_DIR))
113         {
114                 exfat_put_node(&ef, parent);
115                 exfat_error("'%s' is not a directory (%#hx)", path, parent->attrib);
116                 return -ENOTDIR;
117         }
118
119         filler(buffer, ".", NULL, 0);
120         filler(buffer, "..", NULL, 0);
121
122         rc = exfat_opendir(&ef, parent, &it);
123         if (rc != 0)
124         {
125                 exfat_put_node(&ef, parent);
126                 exfat_error("failed to open directory '%s'", path);
127                 return rc;
128         }
129         while ((node = exfat_readdir(&it)))
130         {
131                 exfat_get_name(node, name);
132                 exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
133                                 name, node->is_contiguous ? "contiguous" : "fragmented",
134                                 node->size, node->start_cluster);
135                 exfat_stat(&ef, node, &stbuf);
136                 filler(buffer, name, &stbuf, 0);
137                 exfat_put_node(&ef, node);
138         }
139         exfat_closedir(&ef, &it);
140         exfat_put_node(&ef, parent);
141         return 0;
142 }
143
144 static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
145 {
146         struct exfat_node* node;
147         int rc;
148
149         exfat_debug("[%s] %s", __func__, path);
150
151         rc = exfat_lookup(&ef, &node, path);
152         if (rc != 0)
153                 return rc;
154         set_node(fi, node);
155         return 0;
156 }
157
158 static int fuse_exfat_create(const char* path, mode_t mode,
159                 struct fuse_file_info* fi)
160 {
161         struct exfat_node* node;
162         int rc;
163
164         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
165
166         rc = exfat_mknod(&ef, path);
167         if (rc != 0)
168                 return rc;
169         rc = exfat_lookup(&ef, &node, path);
170         if (rc != 0)
171                 return rc;
172         set_node(fi, node);
173         return 0;
174 }
175
176 static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
177 {
178         /*
179            This handler is called by FUSE on close() syscall. If the FUSE
180            implementation does not call flush handler, we will flush node here.
181            But in this case we will not be able to return an error to the caller.
182            See fuse_exfat_flush() below.
183         */
184         exfat_debug("[%s] %s", __func__, path);
185         exfat_flush_node(&ef, get_node(fi));
186         exfat_put_node(&ef, get_node(fi));
187         return 0; /* FUSE ignores this return value */
188 }
189
190 static int fuse_exfat_flush(const char* path, struct fuse_file_info* fi)
191 {
192         /*
193            This handler may be called by FUSE on close() syscall. FUSE also deals
194            with removals of open files, so we don't free clusters on close but
195            only on rmdir and unlink. If the FUSE implementation does not call this
196            handler we will flush node on release. See fuse_exfat_relase() above.
197         */
198         exfat_debug("[%s] %s", __func__, path);
199         return exfat_flush_node(&ef, get_node(fi));
200 }
201
202 static int fuse_exfat_fsync(const char* path, int datasync,
203                 struct fuse_file_info *fi)
204 {
205         int rc;
206
207         exfat_debug("[%s] %s", __func__, path);
208         rc = exfat_flush_nodes(&ef);
209         if (rc != 0)
210                 return rc;
211         rc = exfat_flush(&ef);
212         if (rc != 0)
213                 return rc;
214         return exfat_fsync(ef.dev);
215 }
216
217 static int fuse_exfat_read(const char* path, char* buffer, size_t size,
218                 off_t offset, struct fuse_file_info* fi)
219 {
220         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
221         return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
222 }
223
224 static int fuse_exfat_write(const char* path, const char* buffer, size_t size,
225                 off_t offset, struct fuse_file_info* fi)
226 {
227         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
228         return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
229 }
230
231 static int fuse_exfat_unlink(const char* path)
232 {
233         struct exfat_node* node;
234         int rc;
235
236         exfat_debug("[%s] %s", __func__, path);
237
238         rc = exfat_lookup(&ef, &node, path);
239         if (rc != 0)
240                 return rc;
241
242         rc = exfat_unlink(&ef, node);
243         exfat_put_node(&ef, node);
244         if (rc != 0)
245                 return rc;
246         return exfat_cleanup_node(&ef, node);
247 }
248
249 static int fuse_exfat_rmdir(const char* path)
250 {
251         struct exfat_node* node;
252         int rc;
253
254         exfat_debug("[%s] %s", __func__, path);
255
256         rc = exfat_lookup(&ef, &node, path);
257         if (rc != 0)
258                 return rc;
259
260         rc = exfat_rmdir(&ef, node);
261         exfat_put_node(&ef, node);
262         if (rc != 0)
263                 return rc;
264         return exfat_cleanup_node(&ef, node);
265 }
266
267 static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev)
268 {
269         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
270         return exfat_mknod(&ef, path);
271 }
272
273 static int fuse_exfat_mkdir(const char* path, mode_t mode)
274 {
275         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
276         return exfat_mkdir(&ef, path);
277 }
278
279 static int fuse_exfat_rename(const char* old_path, const char* new_path)
280 {
281         exfat_debug("[%s] %s => %s", __func__, old_path, new_path);
282         return exfat_rename(&ef, old_path, new_path);
283 }
284
285 static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
286 {
287         struct exfat_node* node;
288         int rc;
289
290         exfat_debug("[%s] %s", __func__, path);
291
292         rc = exfat_lookup(&ef, &node, path);
293         if (rc != 0)
294                 return rc;
295
296         exfat_utimes(node, tv);
297         rc = exfat_flush_node(&ef, node);
298         exfat_put_node(&ef, node);
299         return rc;
300 }
301
302 static int fuse_exfat_chmod(const char* path, mode_t mode)
303 {
304         const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
305                         S_IRWXU | S_IRWXG | S_IRWXO;
306
307         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
308         if (mode & ~VALID_MODE_MASK)
309                 return -EPERM;
310         return 0;
311 }
312
313 static int fuse_exfat_chown(const char* path, uid_t uid, gid_t gid)
314 {
315         exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
316         if (uid != ef.uid || gid != ef.gid)
317                 return -EPERM;
318         return 0;
319 }
320
321 static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
322 {
323         exfat_debug("[%s]", __func__);
324
325         sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
326         sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
327         sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits;
328         sfs->f_bavail = exfat_count_free_clusters(&ef);
329         sfs->f_bfree = sfs->f_bavail;
330         sfs->f_namemax = EXFAT_NAME_MAX;
331
332         /*
333            Below are fake values because in exFAT there is
334            a) no simple way to count files;
335            b) no such thing as inode;
336            So here we assume that inode = cluster.
337         */
338         sfs->f_files = le32_to_cpu(ef.sb->cluster_count);
339         sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
340         sfs->f_ffree = sfs->f_bavail;
341
342         return 0;
343 }
344
345 static void* fuse_exfat_init(struct fuse_conn_info* fci)
346 {
347         exfat_debug("[%s]", __func__);
348 #ifdef FUSE_CAP_BIG_WRITES
349         fci->want |= FUSE_CAP_BIG_WRITES;
350 #endif
351         return NULL;
352 }
353
354 static void fuse_exfat_destroy(void* unused)
355 {
356         exfat_debug("[%s]", __func__);
357         exfat_unmount(&ef);
358 }
359
360 static void usage(const char* prog)
361 {
362         fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
363         exit(1);
364 }
365
366 static struct fuse_operations fuse_exfat_ops =
367 {
368         .getattr        = fuse_exfat_getattr,
369         .truncate       = fuse_exfat_truncate,
370         .readdir        = fuse_exfat_readdir,
371         .open           = fuse_exfat_open,
372         .create         = fuse_exfat_create,
373         .release        = fuse_exfat_release,
374         .flush          = fuse_exfat_flush,
375         .fsync          = fuse_exfat_fsync,
376         .fsyncdir       = fuse_exfat_fsync,
377         .read           = fuse_exfat_read,
378         .write          = fuse_exfat_write,
379         .unlink         = fuse_exfat_unlink,
380         .rmdir          = fuse_exfat_rmdir,
381         .mknod          = fuse_exfat_mknod,
382         .mkdir          = fuse_exfat_mkdir,
383         .rename         = fuse_exfat_rename,
384         .utimens        = fuse_exfat_utimens,
385         .chmod          = fuse_exfat_chmod,
386         .chown          = fuse_exfat_chown,
387         .statfs         = fuse_exfat_statfs,
388         .init           = fuse_exfat_init,
389         .destroy        = fuse_exfat_destroy,
390 };
391
392 static char* add_option(char* options, const char* name, const char* value)
393 {
394         size_t size;
395         char* optionsf = options;
396
397         if (value)
398                 size = strlen(options) + strlen(name) + strlen(value) + 3;
399         else
400                 size = strlen(options) + strlen(name) + 2;
401
402         options = realloc(options, size);
403         if (options == NULL)
404         {
405                 free(optionsf);
406                 exfat_error("failed to reallocate options string");
407                 return NULL;
408         }
409         strcat(options, ",");
410         strcat(options, name);
411         if (value)
412         {
413                 strcat(options, "=");
414                 strcat(options, value);
415         }
416         return options;
417 }
418
419 static void escape(char* escaped, const char* orig)
420 {
421         do
422         {
423                 if (*orig == ',' || *orig == '\\')
424                         *escaped++ = '\\';
425         }
426         while ((*escaped++ = *orig++));
427 }
428
429 static char* add_fsname_option(char* options, const char* spec)
430 {
431         /* escaped string cannot be more than twice as big as the original one */
432         char* escaped = malloc(strlen(spec) * 2 + 1);
433
434         if (escaped == NULL)
435         {
436                 free(options);
437                 exfat_error("failed to allocate escaped string for %s", spec);
438                 return NULL;
439         }
440
441         /* on some platforms (e.g. Android, Solaris) device names can contain
442            commas */
443         escape(escaped, spec);
444         options = add_option(options, "fsname", escaped);
445         free(escaped);
446         return options;
447 }
448
449 static char* add_ro_option(char* options, bool ro)
450 {
451         return ro ? add_option(options, "ro", NULL) : options;
452 }
453
454 #if defined(__linux__) || defined(__FreeBSD__)
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 #endif
472
473 #if defined(__linux__)
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 #endif
486
487 static char* add_fuse_options(char* options, const char* spec, bool ro)
488 {
489         options = add_fsname_option(options, spec);
490         if (options == NULL)
491                 return NULL;
492         options = add_ro_option(options, ro);
493         if (options == NULL)
494                 return NULL;
495 #if defined(__linux__) || defined(__FreeBSD__)
496         options = add_user_option(options);
497         if (options == NULL)
498                 return NULL;
499 #endif
500 #if defined(__linux__)
501         options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
502         if (options == NULL)
503                 return NULL;
504 #endif
505         return options;
506 }
507
508 static int fuse_exfat_main(char* mount_options, char* mount_point)
509 {
510         char* argv[] = {"exfat", "-s", "-o", mount_options, mount_point, NULL};
511         return fuse_main(sizeof(argv) / sizeof(argv[0]) - 1, argv,
512                         &fuse_exfat_ops, NULL);
513 }
514
515 int main(int argc, char* argv[])
516 {
517         const char* spec = NULL;
518         char* mount_point = NULL;
519         char* fuse_options;
520         char* exfat_options;
521         int opt;
522         int rc;
523
524         printf("FUSE exfat %s\n", VERSION);
525
526         fuse_options = strdup("allow_other,"
527 #if defined(__linux__) || defined(__FreeBSD__)
528                         "big_writes,"
529 #endif
530 #if defined(__linux__)
531                         "blkdev,"
532 #endif
533                         "default_permissions");
534         exfat_options = strdup("ro_fallback");
535         if (fuse_options == NULL || exfat_options == NULL)
536         {
537                 exfat_error("failed to allocate options string");
538                 return 1;
539         }
540
541         while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
542         {
543                 switch (opt)
544                 {
545                 case 'd':
546                         fuse_options = add_option(fuse_options, "debug", NULL);
547                         if (fuse_options == NULL)
548                         {
549                                 free(exfat_options);
550                                 return 1;
551                         }
552                         break;
553                 case 'n':
554                         break;
555                 case 'o':
556                         exfat_options = add_option(exfat_options, optarg, NULL);
557                         if (exfat_options == NULL)
558                         {
559                                 free(fuse_options);
560                                 return 1;
561                         }
562                         break;
563                 case 'V':
564                         free(exfat_options);
565                         free(fuse_options);
566                         puts("Copyright (C) 2010-2018  Andrew Nayenko");
567                         return 0;
568                 case 'v':
569                         break;
570                 default:
571                         free(exfat_options);
572                         free(fuse_options);
573                         usage(argv[0]);
574                         break;
575                 }
576         }
577         if (argc - optind != 2)
578         {
579                 free(exfat_options);
580                 free(fuse_options);
581                 usage(argv[0]);
582         }
583         spec = argv[optind];
584         mount_point = argv[optind + 1];
585
586         if (exfat_mount(&ef, spec, exfat_options) != 0)
587         {
588                 free(exfat_options);
589                 free(fuse_options);
590                 return 1;
591         }
592
593         free(exfat_options);
594
595         fuse_options = add_fuse_options(fuse_options, spec, ef.ro != 0);
596         if (fuse_options == NULL)
597         {
598                 exfat_unmount(&ef);
599                 return 1;
600         }
601
602         /* let FUSE do all its wizardry */
603         rc = fuse_exfat_main(fuse_options, mount_point);
604
605         free(fuse_options);
606         return rc;
607 }