OSDN Git Service

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