OSDN Git Service

b9b412a6acd45de3f92881ebb58b4970a28fac6e
[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-2013  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 #define exfat_debug(format, ...)
38
39 #if !defined(FUSE_VERSION) || (FUSE_VERSION < 26)
40         #error FUSE 2.6 or later is required
41 #endif
42
43 const char* default_options = "ro_fallback,allow_other,blkdev,big_writes,"
44                 "defer_permissions";
45
46 struct exfat ef;
47
48 static struct exfat_node* get_node(const struct fuse_file_info* fi)
49 {
50         return (struct exfat_node*) (size_t) fi->fh;
51 }
52
53 static void set_node(struct fuse_file_info* fi, struct exfat_node* node)
54 {
55         fi->fh = (uint64_t) (size_t) node;
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[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
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->flags & EXFAT_ATTRIB_DIR))
112         {
113                 exfat_put_node(&ef, parent);
114                 exfat_error("`%s' is not a directory (0x%x)", path, parent->flags);
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(&ef, &it)))
129         {
130                 exfat_get_name(node, name, sizeof(name) - 1);
131                 exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
132                                 name, IS_CONTIGUOUS(*node) ? "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         fi->keep_cache = 1;
154         return 0;
155 }
156
157 static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
158 {
159         /*
160            This handler is called by FUSE on close() syscall. If the FUSE
161            implementation does not call flush handler, we will flush node here.
162            But in this case we will not be able to return an error to the caller.
163            See fuse_exfat_flush() below.
164         */
165         exfat_debug("[%s] %s", __func__, path);
166         exfat_flush_node(&ef, get_node(fi));
167         exfat_put_node(&ef, get_node(fi));
168         return 0; /* FUSE ignores this return value */
169 }
170
171 static int fuse_exfat_flush(const char* path, struct fuse_file_info* fi)
172 {
173         /*
174            This handler may be called by FUSE on close() syscall. FUSE also deals
175            with removals of open files, so we don't free clusters on close but
176            only on rmdir and unlink. If the FUSE implementation does not call this
177            handler we will flush node on release. See fuse_exfat_relase() above.
178         */
179         exfat_debug("[%s] %s", __func__, path);
180         return exfat_flush_node(&ef, get_node(fi));
181 }
182
183 static int fuse_exfat_fsync(const char* path, int datasync,
184                 struct fuse_file_info *fi)
185 {
186         int rc;
187
188         exfat_debug("[%s] %s", __func__, path);
189         rc = exfat_flush(&ef);
190         if (rc != 0)
191                 return rc;
192         return exfat_fsync(ef.dev);
193 }
194
195 static int fuse_exfat_read(const char* path, char* buffer, size_t size,
196                 off_t offset, struct fuse_file_info* fi)
197 {
198         ssize_t ret;
199
200         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
201         ret = exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
202         if (ret < 0)
203                 return -EIO;
204         return ret;
205 }
206
207 static int fuse_exfat_write(const char* path, const char* buffer, size_t size,
208                 off_t offset, struct fuse_file_info* fi)
209 {
210         ssize_t ret;
211
212         exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
213         ret = exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
214         if (ret < 0)
215                 return -EIO;
216         return ret;
217 }
218
219 static int fuse_exfat_unlink(const char* path)
220 {
221         struct exfat_node* node;
222         int rc;
223
224         exfat_debug("[%s] %s", __func__, path);
225
226         rc = exfat_lookup(&ef, &node, path);
227         if (rc != 0)
228                 return rc;
229
230         rc = exfat_unlink(&ef, node);
231         exfat_put_node(&ef, node);
232         if (rc != 0)
233                 return rc;
234         return exfat_cleanup_node(&ef, node);
235 }
236
237 static int fuse_exfat_rmdir(const char* path)
238 {
239         struct exfat_node* node;
240         int rc;
241
242         exfat_debug("[%s] %s", __func__, path);
243
244         rc = exfat_lookup(&ef, &node, path);
245         if (rc != 0)
246                 return rc;
247
248         rc = exfat_rmdir(&ef, node);
249         exfat_put_node(&ef, node);
250         if (rc != 0)
251                 return rc;
252         return exfat_cleanup_node(&ef, node);
253 }
254
255 static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev)
256 {
257         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
258         return exfat_mknod(&ef, path);
259 }
260
261 static int fuse_exfat_mkdir(const char* path, mode_t mode)
262 {
263         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
264         return exfat_mkdir(&ef, path);
265 }
266
267 static int fuse_exfat_rename(const char* old_path, const char* new_path)
268 {
269         exfat_debug("[%s] %s => %s", __func__, old_path, new_path);
270         return exfat_rename(&ef, old_path, new_path);
271 }
272
273 static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
274 {
275         struct exfat_node* node;
276         int rc;
277
278         exfat_debug("[%s] %s", __func__, path);
279
280         rc = exfat_lookup(&ef, &node, path);
281         if (rc != 0)
282                 return rc;
283
284         exfat_utimes(node, tv);
285         rc = exfat_flush_node(&ef, node);
286         exfat_put_node(&ef, node);
287         return rc;
288 }
289
290 static int fuse_exfat_chmod(const char* path, mode_t mode)
291 {
292         const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
293                         S_IRWXU | S_IRWXG | S_IRWXO;
294
295         exfat_debug("[%s] %s 0%ho", __func__, path, mode);
296         if (mode & ~VALID_MODE_MASK)
297                 return -EPERM;
298         return 0;
299 }
300
301 static int fuse_exfat_chown(const char* path, uid_t uid, gid_t gid)
302 {
303         exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
304         if (uid != ef.uid || gid != ef.gid)
305                 return -EPERM;
306         return 0;
307 }
308
309 static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
310 {
311         exfat_debug("[%s]", __func__);
312
313         sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
314         sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
315         sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits;
316         sfs->f_bavail = exfat_count_free_clusters(&ef);
317         sfs->f_bfree = sfs->f_bavail;
318         sfs->f_namemax = EXFAT_NAME_MAX;
319
320         /*
321            Below are fake values because in exFAT there is
322            a) no simple way to count files;
323            b) no such thing as inode;
324            So here we assume that inode = cluster.
325         */
326         sfs->f_files = le32_to_cpu(ef.sb->cluster_count);
327         sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
328         sfs->f_ffree = sfs->f_bavail;
329
330         return 0;
331 }
332
333 static void* fuse_exfat_init(struct fuse_conn_info* fci)
334 {
335         exfat_debug("[%s]", __func__);
336 #ifdef FUSE_CAP_BIG_WRITES
337         fci->want |= FUSE_CAP_BIG_WRITES;
338 #endif
339         return NULL;
340 }
341
342 static void fuse_exfat_destroy(void* unused)
343 {
344         exfat_debug("[%s]", __func__);
345         exfat_unmount(&ef);
346 }
347
348 static void usage(const char* prog)
349 {
350         fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
351         exit(1);
352 }
353
354 static struct fuse_operations fuse_exfat_ops =
355 {
356         .getattr        = fuse_exfat_getattr,
357         .truncate       = fuse_exfat_truncate,
358         .readdir        = fuse_exfat_readdir,
359         .open           = fuse_exfat_open,
360         .release        = fuse_exfat_release,
361         .flush          = fuse_exfat_flush,
362         .fsync          = fuse_exfat_fsync,
363         .fsyncdir       = fuse_exfat_fsync,
364         .read           = fuse_exfat_read,
365         .write          = fuse_exfat_write,
366         .unlink         = fuse_exfat_unlink,
367         .rmdir          = fuse_exfat_rmdir,
368         .mknod          = fuse_exfat_mknod,
369         .mkdir          = fuse_exfat_mkdir,
370         .rename         = fuse_exfat_rename,
371         .utimens        = fuse_exfat_utimens,
372         .chmod          = fuse_exfat_chmod,
373         .chown          = fuse_exfat_chown,
374         .statfs         = fuse_exfat_statfs,
375         .init           = fuse_exfat_init,
376         .destroy        = fuse_exfat_destroy,
377 };
378
379 static char* add_option(char* options, const char* name, const char* value)
380 {
381         size_t size;
382
383         if (value)
384                 size = strlen(options) + strlen(name) + strlen(value) + 3;
385         else
386                 size = strlen(options) + strlen(name) + 2;
387
388         options = realloc(options, size);
389         if (options == NULL)
390         {
391                 exfat_error("failed to reallocate options string");
392                 return NULL;
393         }
394         strcat(options, ",");
395         strcat(options, name);
396         if (value)
397         {
398                 strcat(options, "=");
399                 strcat(options, value);
400         }
401         return options;
402 }
403
404 static char* add_user_option(char* options)
405 {
406         struct passwd* pw;
407
408         if (getuid() == 0)
409                 return options;
410
411         pw = getpwuid(getuid());
412         if (pw == NULL || pw->pw_name == NULL)
413         {
414                 free(options);
415                 exfat_error("failed to determine username");
416                 return NULL;
417         }
418         return add_option(options, "user", pw->pw_name);
419 }
420
421 static char* add_blksize_option(char* options, long cluster_size)
422 {
423         long page_size = sysconf(_SC_PAGESIZE);
424         char blksize[20];
425
426         if (page_size < 1)
427                 page_size = 0x1000;
428
429         snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
430         return add_option(options, "blksize", blksize);
431 }
432
433 static char* add_fuse_options(char* options, const char* spec)
434 {
435         options = add_option(options, "fsname", spec);
436         if (options == NULL)
437                 return NULL;
438         options = add_user_option(options);
439         if (options == NULL)
440                 return NULL;
441         options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
442         if (options == NULL)
443                 return NULL;
444
445         return options;
446 }
447
448 int main(int argc, char* argv[])
449 {
450         struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL);
451         struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL);
452         const char* spec = NULL;
453         const char* mount_point = NULL;
454         char* mount_options;
455         int debug = 0;
456         struct fuse_chan* fc = NULL;
457         struct fuse* fh = NULL;
458         int opt;
459
460         printf("FUSE exfat %u.%u.%u\n",
461                         EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
462
463         mount_options = strdup(default_options);
464         if (mount_options == NULL)
465         {
466                 exfat_error("failed to allocate options string");
467                 return 1;
468         }
469
470         while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
471         {
472                 switch (opt)
473                 {
474                 case 'd':
475                         debug = 1;
476                         break;
477                 case 'n':
478                         break;
479                 case 'o':
480                         mount_options = add_option(mount_options, optarg, NULL);
481                         if (mount_options == NULL)
482                                 return 1;
483                         break;
484                 case 'V':
485                         free(mount_options);
486                         puts("Copyright (C) 2010-2013  Andrew Nayenko");
487                         return 0;
488                 case 'v':
489                         break;
490                 default:
491                         free(mount_options);
492                         usage(argv[0]);
493                         break;
494                 }
495         }
496         if (argc - optind != 2)
497         {
498                 free(mount_options);
499                 usage(argv[0]);
500         }
501         spec = argv[optind];
502         mount_point = argv[optind + 1];
503
504         if (exfat_mount(&ef, spec, mount_options) != 0)
505         {
506                 free(mount_options);
507                 return 1;
508         }
509
510         if (ef.ro == -1) /* read-only fallback was used */
511         {
512                 mount_options = add_option(mount_options, "ro", NULL);
513                 if (mount_options == NULL)
514                 {
515                         exfat_unmount(&ef);
516                         return 1;
517                 }
518         }
519
520         mount_options = add_fuse_options(mount_options, spec);
521         if (mount_options == NULL)
522         {
523                 exfat_unmount(&ef);
524                 return 1;
525         }
526
527         /* create arguments for fuse_mount() */
528         if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
529                 fuse_opt_add_arg(&mount_args, "-o") != 0 ||
530                 fuse_opt_add_arg(&mount_args, mount_options) != 0)
531         {
532                 exfat_unmount(&ef);
533                 free(mount_options);
534                 return 1;
535         }
536
537         free(mount_options);
538
539         /* create FUSE mount point */
540         fc = fuse_mount(mount_point, &mount_args);
541         fuse_opt_free_args(&mount_args);
542         if (fc == NULL)
543         {
544                 exfat_unmount(&ef);
545                 return 1;
546         }
547
548         /* create arguments for fuse_new() */
549         if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
550                 (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
551         {
552                 fuse_unmount(mount_point, fc);
553                 exfat_unmount(&ef);
554                 return 1;
555         }
556
557         /* create new FUSE file system */
558         fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops,
559                         sizeof(struct fuse_operations), NULL);
560         fuse_opt_free_args(&newfs_args);
561         if (fh == NULL)
562         {
563                 fuse_unmount(mount_point, fc);
564                 exfat_unmount(&ef);
565                 return 1;
566         }
567
568         /* exit session on HUP, TERM and INT signals and ignore PIPE signal */
569         if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
570         {
571                 fuse_unmount(mount_point, fc);
572                 fuse_destroy(fh);
573                 exfat_unmount(&ef);
574                 exfat_error("failed to set signal handlers");
575                 return 1;
576         }
577
578         /* go to background (unless "-d" option is passed) and run FUSE
579            main loop */
580         if (fuse_daemonize(debug) == 0)
581         {
582                 if (fuse_loop(fh) != 0)
583                         exfat_error("FUSE loop failure");
584         }
585         else
586                 exfat_error("failed to daemonize");
587
588         fuse_remove_signal_handlers(fuse_get_session(fh));
589         /* note that fuse_unmount() must be called BEFORE fuse_destroy() */
590         fuse_unmount(mount_point, fc);
591         fuse_destroy(fh);
592         return 0;
593 }