OSDN Git Service

b99a341aaab9ae42ecf43d9e53f90a48fe00f16c
[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 static char* add_user_option(char* options)
453 {
454         struct passwd* pw;
455
456         if (getuid() == 0)
457                 return options;
458
459         pw = getpwuid(getuid());
460         if (pw == NULL || pw->pw_name == NULL)
461         {
462                 free(options);
463                 exfat_error("failed to determine username");
464                 return NULL;
465         }
466         return add_option(options, "user", pw->pw_name);
467 }
468
469 static char* add_blksize_option(char* options, long cluster_size)
470 {
471         long page_size = sysconf(_SC_PAGESIZE);
472         char blksize[20];
473
474         if (page_size < 1)
475                 page_size = 0x1000;
476
477         snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
478         return add_option(options, "blksize", blksize);
479 }
480
481 static char* add_fuse_options(char* options, const char* spec, bool ro)
482 {
483         options = add_fsname_option(options, spec);
484         if (options == NULL)
485                 return NULL;
486         options = add_ro_option(options, ro);
487         if (options == NULL)
488                 return NULL;
489         options = add_user_option(options);
490         if (options == NULL)
491                 return NULL;
492         options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
493         if (options == NULL)
494                 return NULL;
495
496         return options;
497 }
498
499 static int fuse_exfat_main(char* mount_options, char* mount_point)
500 {
501         char* argv[] = {"exfat", "-s", "-o", mount_options, mount_point, NULL};
502         return fuse_main(sizeof(argv) / sizeof(argv[0]) - 1, argv,
503                         &fuse_exfat_ops, NULL);
504 }
505
506 int main(int argc, char* argv[])
507 {
508         const char* spec = NULL;
509         char* mount_point = NULL;
510         char* fuse_options;
511         char* exfat_options;
512         int opt;
513         int rc;
514
515         printf("FUSE exfat %s\n", VERSION);
516
517         fuse_options = strdup("allow_other,"
518                         "big_writes,"
519                         "blkdev,"
520                         "default_permissions");
521         exfat_options = strdup("ro_fallback");
522         if (fuse_options == NULL || exfat_options == NULL)
523         {
524                 exfat_error("failed to allocate options string");
525                 return 1;
526         }
527
528         while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
529         {
530                 switch (opt)
531                 {
532                 case 'd':
533                         fuse_options = add_option(fuse_options, "debug", NULL);
534                         if (fuse_options == NULL)
535                         {
536                                 free(exfat_options);
537                                 return 1;
538                         }
539                         break;
540                 case 'n':
541                         break;
542                 case 'o':
543                         exfat_options = add_option(exfat_options, optarg, NULL);
544                         if (exfat_options == NULL)
545                         {
546                                 free(fuse_options);
547                                 return 1;
548                         }
549                         break;
550                 case 'V':
551                         free(exfat_options);
552                         free(fuse_options);
553                         puts("Copyright (C) 2010-2018  Andrew Nayenko");
554                         return 0;
555                 case 'v':
556                         break;
557                 default:
558                         free(exfat_options);
559                         free(fuse_options);
560                         usage(argv[0]);
561                         break;
562                 }
563         }
564         if (argc - optind != 2)
565         {
566                 free(exfat_options);
567                 free(fuse_options);
568                 usage(argv[0]);
569         }
570         spec = argv[optind];
571         mount_point = argv[optind + 1];
572
573         if (exfat_mount(&ef, spec, exfat_options) != 0)
574         {
575                 free(exfat_options);
576                 free(fuse_options);
577                 return 1;
578         }
579
580         free(exfat_options);
581
582         fuse_options = add_fuse_options(fuse_options, spec, (ef.ro == -1));
583         if (fuse_options == NULL)
584         {
585                 exfat_unmount(&ef);
586                 return 1;
587         }
588
589         /* let FUSE do all its wizardry */
590         rc = fuse_exfat_main(fuse_options, mount_point);
591
592         free(fuse_options);
593         return rc;
594 }