]> git.sven.stormbind.net Git - sven/fuse-exfat.git/blob - fuse/main.c
Imported Upstream version 1.1.0
[sven/fuse-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-2014  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         char* optionsf = options;
383
384         if (value)
385                 size = strlen(options) + strlen(name) + strlen(value) + 3;
386         else
387                 size = strlen(options) + strlen(name) + 2;
388
389         options = realloc(options, size);
390         if (options == NULL)
391         {
392                 free(optionsf);
393                 exfat_error("failed to reallocate options string");
394                 return NULL;
395         }
396         strcat(options, ",");
397         strcat(options, name);
398         if (value)
399         {
400                 strcat(options, "=");
401                 strcat(options, value);
402         }
403         return options;
404 }
405
406 static char* add_user_option(char* options)
407 {
408         struct passwd* pw;
409
410         if (getuid() == 0)
411                 return options;
412
413         pw = getpwuid(getuid());
414         if (pw == NULL || pw->pw_name == NULL)
415         {
416                 free(options);
417                 exfat_error("failed to determine username");
418                 return NULL;
419         }
420         return add_option(options, "user", pw->pw_name);
421 }
422
423 static char* add_blksize_option(char* options, long cluster_size)
424 {
425         long page_size = sysconf(_SC_PAGESIZE);
426         char blksize[20];
427
428         if (page_size < 1)
429                 page_size = 0x1000;
430
431         snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
432         return add_option(options, "blksize", blksize);
433 }
434
435 static char* add_fuse_options(char* options, const char* spec)
436 {
437         options = add_option(options, "fsname", spec);
438         if (options == NULL)
439                 return NULL;
440         options = add_user_option(options);
441         if (options == NULL)
442                 return NULL;
443         options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
444         if (options == NULL)
445                 return NULL;
446
447         return options;
448 }
449
450 int main(int argc, char* argv[])
451 {
452         struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL);
453         struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL);
454         const char* spec = NULL;
455         const char* mount_point = NULL;
456         char* mount_options;
457         int debug = 0;
458         struct fuse_chan* fc = NULL;
459         struct fuse* fh = NULL;
460         int opt;
461
462         printf("FUSE exfat %u.%u.%u\n",
463                         EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
464
465         mount_options = strdup(default_options);
466         if (mount_options == NULL)
467         {
468                 exfat_error("failed to allocate options string");
469                 return 1;
470         }
471
472         while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
473         {
474                 switch (opt)
475                 {
476                 case 'd':
477                         debug = 1;
478                         break;
479                 case 'n':
480                         break;
481                 case 'o':
482                         mount_options = add_option(mount_options, optarg, NULL);
483                         if (mount_options == NULL)
484                                 return 1;
485                         break;
486                 case 'V':
487                         free(mount_options);
488                         puts("Copyright (C) 2010-2014  Andrew Nayenko");
489                         return 0;
490                 case 'v':
491                         break;
492                 default:
493                         free(mount_options);
494                         usage(argv[0]);
495                         break;
496                 }
497         }
498         if (argc - optind != 2)
499         {
500                 free(mount_options);
501                 usage(argv[0]);
502         }
503         spec = argv[optind];
504         mount_point = argv[optind + 1];
505
506         if (exfat_mount(&ef, spec, mount_options) != 0)
507         {
508                 free(mount_options);
509                 return 1;
510         }
511
512         if (ef.ro == -1) /* read-only fallback was used */
513         {
514                 mount_options = add_option(mount_options, "ro", NULL);
515                 if (mount_options == NULL)
516                 {
517                         exfat_unmount(&ef);
518                         return 1;
519                 }
520         }
521
522         mount_options = add_fuse_options(mount_options, spec);
523         if (mount_options == NULL)
524         {
525                 exfat_unmount(&ef);
526                 return 1;
527         }
528
529         /* create arguments for fuse_mount() */
530         if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
531                 fuse_opt_add_arg(&mount_args, "-o") != 0 ||
532                 fuse_opt_add_arg(&mount_args, mount_options) != 0)
533         {
534                 exfat_unmount(&ef);
535                 free(mount_options);
536                 return 1;
537         }
538
539         free(mount_options);
540
541         /* create FUSE mount point */
542         fc = fuse_mount(mount_point, &mount_args);
543         fuse_opt_free_args(&mount_args);
544         if (fc == NULL)
545         {
546                 exfat_unmount(&ef);
547                 return 1;
548         }
549
550         /* create arguments for fuse_new() */
551         if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
552                 (debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
553         {
554                 fuse_unmount(mount_point, fc);
555                 exfat_unmount(&ef);
556                 return 1;
557         }
558
559         /* create new FUSE file system */
560         fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops,
561                         sizeof(struct fuse_operations), NULL);
562         fuse_opt_free_args(&newfs_args);
563         if (fh == NULL)
564         {
565                 fuse_unmount(mount_point, fc);
566                 exfat_unmount(&ef);
567                 return 1;
568         }
569
570         /* exit session on HUP, TERM and INT signals and ignore PIPE signal */
571         if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
572         {
573                 fuse_unmount(mount_point, fc);
574                 fuse_destroy(fh);
575                 exfat_unmount(&ef);
576                 exfat_error("failed to set signal handlers");
577                 return 1;
578         }
579
580         /* go to background (unless "-d" option is passed) and run FUSE
581            main loop */
582         if (fuse_daemonize(debug) == 0)
583         {
584                 if (fuse_loop(fh) != 0)
585                         exfat_error("FUSE loop failure");
586         }
587         else
588                 exfat_error("failed to daemonize");
589
590         fuse_remove_signal_handlers(fuse_get_session(fh));
591         /* note that fuse_unmount() must be called BEFORE fuse_destroy() */
592         fuse_unmount(mount_point, fc);
593         fuse_destroy(fh);
594         return 0;
595 }