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