blob: 9b78cfd4bcb008bbdf7dcfaab966e2a395a20e55 [file] [log] [blame]
Matt Mower523a0592015-12-13 11:31:00 -06001/*
2 fuse iconv module: file name charset conversion
3 Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
7*/
8
9#define FUSE_USE_VERSION 26
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17#include <iconv.h>
18#include <pthread.h>
19#include <locale.h>
20#include <langinfo.h>
21
22struct iconv {
23 struct fuse_fs *next;
24 pthread_mutex_t lock;
25 char *from_code;
26 char *to_code;
27 iconv_t tofs;
28 iconv_t fromfs;
29};
30
31struct iconv_dh {
32 struct iconv *ic;
33 void *prev_buf;
34 fuse_fill_dir_t prev_filler;
35};
36
37static struct iconv *iconv_get(void)
38{
39 return fuse_get_context()->private_data;
40}
41
42static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
43 int fromfs)
44{
45 size_t pathlen;
46 size_t newpathlen;
47 char *newpath;
48 size_t plen;
49 char *p;
50 size_t res;
51 int err;
52
53 if (path == NULL) {
54 *newpathp = NULL;
55 return 0;
56 }
57
58 pathlen = strlen(path);
59 newpathlen = pathlen * 4;
60 newpath = malloc(newpathlen + 1);
61 if (!newpath)
62 return -ENOMEM;
63
64 plen = newpathlen;
65 p = newpath;
66 pthread_mutex_lock(&ic->lock);
67 do {
68 res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path,
69 &pathlen, &p, &plen);
70 if (res == (size_t) -1) {
71 char *tmp;
72 size_t inc;
73
74 err = -EILSEQ;
75 if (errno != E2BIG)
76 goto err;
77
78 inc = (pathlen + 1) * 4;
79 newpathlen += inc;
80 tmp = realloc(newpath, newpathlen + 1);
81 err = -ENOMEM;
82 if (!tmp)
83 goto err;
84
85 p = tmp + (p - newpath);
86 plen += inc;
87 newpath = tmp;
88 }
89 } while (res == (size_t) -1);
90 pthread_mutex_unlock(&ic->lock);
91 *p = '\0';
92 *newpathp = newpath;
93 return 0;
94
95err:
96 iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL);
97 pthread_mutex_unlock(&ic->lock);
98 free(newpath);
99 return err;
100}
101
102static int iconv_getattr(const char *path, struct stat *stbuf)
103{
104 struct iconv *ic = iconv_get();
105 char *newpath;
106 int err = iconv_convpath(ic, path, &newpath, 0);
107 if (!err) {
108 err = fuse_fs_getattr(ic->next, newpath, stbuf);
109 free(newpath);
110 }
111 return err;
112}
113
114static int iconv_fgetattr(const char *path, struct stat *stbuf,
115 struct fuse_file_info *fi)
116{
117 struct iconv *ic = iconv_get();
118 char *newpath;
119 int err = iconv_convpath(ic, path, &newpath, 0);
120 if (!err) {
121 err = fuse_fs_fgetattr(ic->next, newpath, stbuf, fi);
122 free(newpath);
123 }
124 return err;
125}
126
127static int iconv_access(const char *path, int mask)
128{
129 struct iconv *ic = iconv_get();
130 char *newpath;
131 int err = iconv_convpath(ic, path, &newpath, 0);
132 if (!err) {
133 err = fuse_fs_access(ic->next, newpath, mask);
134 free(newpath);
135 }
136 return err;
137}
138
139static int iconv_readlink(const char *path, char *buf, size_t size)
140{
141 struct iconv *ic = iconv_get();
142 char *newpath;
143 int err = iconv_convpath(ic, path, &newpath, 0);
144 if (!err) {
145 err = fuse_fs_readlink(ic->next, newpath, buf, size);
146 if (!err) {
147 char *newlink;
148 err = iconv_convpath(ic, buf, &newlink, 1);
149 if (!err) {
150 strncpy(buf, newlink, size - 1);
151 buf[size - 1] = '\0';
152 free(newlink);
153 }
154 }
155 free(newpath);
156 }
157 return err;
158}
159
160static int iconv_opendir(const char *path, struct fuse_file_info *fi)
161{
162 struct iconv *ic = iconv_get();
163 char *newpath;
164 int err = iconv_convpath(ic, path, &newpath, 0);
165 if (!err) {
166 err = fuse_fs_opendir(ic->next, newpath, fi);
167 free(newpath);
168 }
169 return err;
170}
171
172static int iconv_dir_fill(void *buf, const char *name,
173 const struct stat *stbuf, loff_t off)
174{
175 struct iconv_dh *dh = buf;
176 char *newname;
177 int res = 0;
178 if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
179 res = dh->prev_filler(dh->prev_buf, newname, stbuf, off);
180 free(newname);
181 }
182 return res;
183}
184
185static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
186 loff_t offset, struct fuse_file_info *fi)
187{
188 struct iconv *ic = iconv_get();
189 char *newpath;
190 int err = iconv_convpath(ic, path, &newpath, 0);
191 if (!err) {
192 struct iconv_dh dh;
193 dh.ic = ic;
194 dh.prev_buf = buf;
195 dh.prev_filler = filler;
196 err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill,
197 offset, fi);
198 free(newpath);
199 }
200 return err;
201}
202
203static int iconv_releasedir(const char *path, struct fuse_file_info *fi)
204{
205 struct iconv *ic = iconv_get();
206 char *newpath;
207 int err = iconv_convpath(ic, path, &newpath, 0);
208 if (!err) {
209 err = fuse_fs_releasedir(ic->next, newpath, fi);
210 free(newpath);
211 }
212 return err;
213}
214
215static int iconv_mknod(const char *path, mode_t mode, dev_t rdev)
216{
217 struct iconv *ic = iconv_get();
218 char *newpath;
219 int err = iconv_convpath(ic, path, &newpath, 0);
220 if (!err) {
221 err = fuse_fs_mknod(ic->next, newpath, mode, rdev);
222 free(newpath);
223 }
224 return err;
225}
226
227static int iconv_mkdir(const char *path, mode_t mode)
228{
229 struct iconv *ic = iconv_get();
230 char *newpath;
231 int err = iconv_convpath(ic, path, &newpath, 0);
232 if (!err) {
233 err = fuse_fs_mkdir(ic->next, newpath, mode);
234 free(newpath);
235 }
236 return err;
237}
238
239static int iconv_unlink(const char *path)
240{
241 struct iconv *ic = iconv_get();
242 char *newpath;
243 int err = iconv_convpath(ic, path, &newpath, 0);
244 if (!err) {
245 err = fuse_fs_unlink(ic->next, newpath);
246 free(newpath);
247 }
248 return err;
249}
250
251static int iconv_rmdir(const char *path)
252{
253 struct iconv *ic = iconv_get();
254 char *newpath;
255 int err = iconv_convpath(ic, path, &newpath, 0);
256 if (!err) {
257 err = fuse_fs_rmdir(ic->next, newpath);
258 free(newpath);
259 }
260 return err;
261}
262
263static int iconv_symlink(const char *from, const char *to)
264{
265 struct iconv *ic = iconv_get();
266 char *newfrom;
267 char *newto;
268 int err = iconv_convpath(ic, from, &newfrom, 0);
269 if (!err) {
270 err = iconv_convpath(ic, to, &newto, 0);
271 if (!err) {
272 err = fuse_fs_symlink(ic->next, newfrom, newto);
273 free(newto);
274 }
275 free(newfrom);
276 }
277 return err;
278}
279
280static int iconv_rename(const char *from, const char *to)
281{
282 struct iconv *ic = iconv_get();
283 char *newfrom;
284 char *newto;
285 int err = iconv_convpath(ic, from, &newfrom, 0);
286 if (!err) {
287 err = iconv_convpath(ic, to, &newto, 0);
288 if (!err) {
289 err = fuse_fs_rename(ic->next, newfrom, newto);
290 free(newto);
291 }
292 free(newfrom);
293 }
294 return err;
295}
296
297static int iconv_link(const char *from, const char *to)
298{
299 struct iconv *ic = iconv_get();
300 char *newfrom;
301 char *newto;
302 int err = iconv_convpath(ic, from, &newfrom, 0);
303 if (!err) {
304 err = iconv_convpath(ic, to, &newto, 0);
305 if (!err) {
306 err = fuse_fs_link(ic->next, newfrom, newto);
307 free(newto);
308 }
309 free(newfrom);
310 }
311 return err;
312}
313
314static int iconv_chmod(const char *path, mode_t mode)
315{
316 struct iconv *ic = iconv_get();
317 char *newpath;
318 int err = iconv_convpath(ic, path, &newpath, 0);
319 if (!err) {
320 err = fuse_fs_chmod(ic->next, newpath, mode);
321 free(newpath);
322 }
323 return err;
324}
325
326static int iconv_chown(const char *path, uid_t uid, gid_t gid)
327{
328 struct iconv *ic = iconv_get();
329 char *newpath;
330 int err = iconv_convpath(ic, path, &newpath, 0);
331 if (!err) {
332 err = fuse_fs_chown(ic->next, newpath, uid, gid);
333 free(newpath);
334 }
335 return err;
336}
337
338static int iconv_truncate(const char *path, loff_t size)
339{
340 struct iconv *ic = iconv_get();
341 char *newpath;
342 int err = iconv_convpath(ic, path, &newpath, 0);
343 if (!err) {
344 err = fuse_fs_truncate(ic->next, newpath, size);
345 free(newpath);
346 }
347 return err;
348}
349
350static int iconv_ftruncate(const char *path, loff_t size,
351 struct fuse_file_info *fi)
352{
353 struct iconv *ic = iconv_get();
354 char *newpath;
355 int err = iconv_convpath(ic, path, &newpath, 0);
356 if (!err) {
357 err = fuse_fs_ftruncate(ic->next, newpath, size, fi);
358 free(newpath);
359 }
360 return err;
361}
362
363static int iconv_utimens(const char *path, const struct timespec ts[2])
364{
365 struct iconv *ic = iconv_get();
366 char *newpath;
367 int err = iconv_convpath(ic, path, &newpath, 0);
368 if (!err) {
369 err = fuse_fs_utimens(ic->next, newpath, ts);
370 free(newpath);
371 }
372 return err;
373}
374
375static int iconv_create(const char *path, mode_t mode,
376 struct fuse_file_info *fi)
377{
378 struct iconv *ic = iconv_get();
379 char *newpath;
380 int err = iconv_convpath(ic, path, &newpath, 0);
381 if (!err) {
382 err = fuse_fs_create(ic->next, newpath, mode, fi);
383 free(newpath);
384 }
385 return err;
386}
387
388static int iconv_open_file(const char *path, struct fuse_file_info *fi)
389{
390 struct iconv *ic = iconv_get();
391 char *newpath;
392 int err = iconv_convpath(ic, path, &newpath, 0);
393 if (!err) {
394 err = fuse_fs_open(ic->next, newpath, fi);
395 free(newpath);
396 }
397 return err;
398}
399
400static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
401 size_t size, loff_t offset, struct fuse_file_info *fi)
402{
403 struct iconv *ic = iconv_get();
404 char *newpath;
405 int err = iconv_convpath(ic, path, &newpath, 0);
406 if (!err) {
407 err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi);
408 free(newpath);
409 }
410 return err;
411}
412
413static int iconv_write_buf(const char *path, struct fuse_bufvec *buf,
414 loff_t offset, struct fuse_file_info *fi)
415{
416 struct iconv *ic = iconv_get();
417 char *newpath;
418 int err = iconv_convpath(ic, path, &newpath, 0);
419 if (!err) {
420 err = fuse_fs_write_buf(ic->next, newpath, buf, offset, fi);
421 free(newpath);
422 }
423 return err;
424}
425
426static int iconv_statfs(const char *path, struct statvfs *stbuf)
427{
428 struct iconv *ic = iconv_get();
429 char *newpath;
430 int err = iconv_convpath(ic, path, &newpath, 0);
431 if (!err) {
432 err = fuse_fs_statfs(ic->next, newpath, stbuf);
433 free(newpath);
434 }
435 return err;
436}
437
438static int iconv_flush(const char *path, struct fuse_file_info *fi)
439{
440 struct iconv *ic = iconv_get();
441 char *newpath;
442 int err = iconv_convpath(ic, path, &newpath, 0);
443 if (!err) {
444 err = fuse_fs_flush(ic->next, newpath, fi);
445 free(newpath);
446 }
447 return err;
448}
449
450static int iconv_release(const char *path, struct fuse_file_info *fi)
451{
452 struct iconv *ic = iconv_get();
453 char *newpath;
454 int err = iconv_convpath(ic, path, &newpath, 0);
455 if (!err) {
456 err = fuse_fs_release(ic->next, newpath, fi);
457 free(newpath);
458 }
459 return err;
460}
461
462static int iconv_fsync(const char *path, int isdatasync,
463 struct fuse_file_info *fi)
464{
465 struct iconv *ic = iconv_get();
466 char *newpath;
467 int err = iconv_convpath(ic, path, &newpath, 0);
468 if (!err) {
469 err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi);
470 free(newpath);
471 }
472 return err;
473}
474
475static int iconv_fsyncdir(const char *path, int isdatasync,
476 struct fuse_file_info *fi)
477{
478 struct iconv *ic = iconv_get();
479 char *newpath;
480 int err = iconv_convpath(ic, path, &newpath, 0);
481 if (!err) {
482 err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi);
483 free(newpath);
484 }
485 return err;
486}
487
488static int iconv_setxattr(const char *path, const char *name,
489 const char *value, size_t size, int flags)
490{
491 struct iconv *ic = iconv_get();
492 char *newpath;
493 int err = iconv_convpath(ic, path, &newpath, 0);
494 if (!err) {
495 err = fuse_fs_setxattr(ic->next, newpath, name, value, size,
496 flags);
497 free(newpath);
498 }
499 return err;
500}
501
502static int iconv_getxattr(const char *path, const char *name, char *value,
503 size_t size)
504{
505 struct iconv *ic = iconv_get();
506 char *newpath;
507 int err = iconv_convpath(ic, path, &newpath, 0);
508 if (!err) {
509 err = fuse_fs_getxattr(ic->next, newpath, name, value, size);
510 free(newpath);
511 }
512 return err;
513}
514
515static int iconv_listxattr(const char *path, char *list, size_t size)
516{
517 struct iconv *ic = iconv_get();
518 char *newpath;
519 int err = iconv_convpath(ic, path, &newpath, 0);
520 if (!err) {
521 err = fuse_fs_listxattr(ic->next, newpath, list, size);
522 free(newpath);
523 }
524 return err;
525}
526
527static int iconv_removexattr(const char *path, const char *name)
528{
529 struct iconv *ic = iconv_get();
530 char *newpath;
531 int err = iconv_convpath(ic, path, &newpath, 0);
532 if (!err) {
533 err = fuse_fs_removexattr(ic->next, newpath, name);
534 free(newpath);
535 }
536 return err;
537}
538
539static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd,
540 struct flock *lock)
541{
542 struct iconv *ic = iconv_get();
543 char *newpath;
544 int err = iconv_convpath(ic, path, &newpath, 0);
545 if (!err) {
546 err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock);
547 free(newpath);
548 }
549 return err;
550}
551
552static int iconv_flock(const char *path, struct fuse_file_info *fi, int op)
553{
554 struct iconv *ic = iconv_get();
555 char *newpath;
556 int err = iconv_convpath(ic, path, &newpath, 0);
557 if (!err) {
558 err = fuse_fs_flock(ic->next, newpath, fi, op);
559 free(newpath);
560 }
561 return err;
562}
563
564static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
565{
566 struct iconv *ic = iconv_get();
567 char *newpath;
568 int err = iconv_convpath(ic, path, &newpath, 0);
569 if (!err) {
570 err = fuse_fs_bmap(ic->next, newpath, blocksize, idx);
571 free(newpath);
572 }
573 return err;
574}
575
576static void *iconv_init(struct fuse_conn_info *conn)
577{
578 struct iconv *ic = iconv_get();
579 fuse_fs_init(ic->next, conn);
580 return ic;
581}
582
583static void iconv_destroy(void *data)
584{
585 struct iconv *ic = data;
586 fuse_fs_destroy(ic->next);
587 iconv_close(ic->tofs);
588 iconv_close(ic->fromfs);
589 pthread_mutex_destroy(&ic->lock);
590 free(ic->from_code);
591 free(ic->to_code);
592 free(ic);
593}
594
595static const struct fuse_operations iconv_oper = {
596 .destroy = iconv_destroy,
597 .init = iconv_init,
598 .getattr = iconv_getattr,
599 .fgetattr = iconv_fgetattr,
600 .access = iconv_access,
601 .readlink = iconv_readlink,
602 .opendir = iconv_opendir,
603 .readdir = iconv_readdir,
604 .releasedir = iconv_releasedir,
605 .mknod = iconv_mknod,
606 .mkdir = iconv_mkdir,
607 .symlink = iconv_symlink,
608 .unlink = iconv_unlink,
609 .rmdir = iconv_rmdir,
610 .rename = iconv_rename,
611 .link = iconv_link,
612 .chmod = iconv_chmod,
613 .chown = iconv_chown,
614 .truncate = iconv_truncate,
615 .ftruncate = iconv_ftruncate,
616 .utimens = iconv_utimens,
617 .create = iconv_create,
618 .open = iconv_open_file,
619 .read_buf = iconv_read_buf,
620 .write_buf = iconv_write_buf,
621 .statfs = iconv_statfs,
622 .flush = iconv_flush,
623 .release = iconv_release,
624 .fsync = iconv_fsync,
625 .fsyncdir = iconv_fsyncdir,
626 .setxattr = iconv_setxattr,
627 .getxattr = iconv_getxattr,
628 .listxattr = iconv_listxattr,
629 .removexattr = iconv_removexattr,
630 .lock = iconv_lock,
631 .flock = iconv_flock,
632 .bmap = iconv_bmap,
633
634 .flag_nullpath_ok = 1,
635 .flag_nopath = 1,
636};
637
638static const struct fuse_opt iconv_opts[] = {
639 FUSE_OPT_KEY("-h", 0),
640 FUSE_OPT_KEY("--help", 0),
641 { "from_code=%s", offsetof(struct iconv, from_code), 0 },
642 { "to_code=%s", offsetof(struct iconv, to_code), 1 },
643 FUSE_OPT_END
644};
645
646static void iconv_help(void)
647{
648 char *old = strdup(setlocale(LC_CTYPE, ""));
649 char *charmap = strdup(nl_langinfo(CODESET));
650 setlocale(LC_CTYPE, old);
651 free(old);
652 fprintf(stderr,
653" -o from_code=CHARSET original encoding of file names (default: UTF-8)\n"
654" -o to_code=CHARSET new encoding of the file names (default: %s)\n",
655 charmap);
656 free(charmap);
657}
658
659static int iconv_opt_proc(void *data, const char *arg, int key,
660 struct fuse_args *outargs)
661{
662 (void) data; (void) arg; (void) outargs;
663
664 if (!key) {
665 iconv_help();
666 return -1;
667 }
668
669 return 1;
670}
671
672static struct fuse_fs *iconv_new(struct fuse_args *args,
673 struct fuse_fs *next[])
674{
675 struct fuse_fs *fs;
676 struct iconv *ic;
677 char *old = NULL;
678 const char *from;
679 const char *to;
680
681 ic = calloc(1, sizeof(struct iconv));
682 if (ic == NULL) {
683 fprintf(stderr, "fuse-iconv: memory allocation failed\n");
684 return NULL;
685 }
686
687 if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
688 goto out_free;
689
690 if (!next[0] || next[1]) {
691 fprintf(stderr, "fuse-iconv: exactly one next filesystem required\n");
692 goto out_free;
693 }
694
695 from = ic->from_code ? ic->from_code : "UTF-8";
696 to = ic->to_code ? ic->to_code : "";
697 /* FIXME: detect charset equivalence? */
698 if (!to[0])
699 old = strdup(setlocale(LC_CTYPE, ""));
700 ic->tofs = iconv_open(from, to);
701 if (ic->tofs == (iconv_t) -1) {
702 fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
703 to, from);
704 goto out_free;
705 }
706 ic->fromfs = iconv_open(to, from);
707 if (ic->tofs == (iconv_t) -1) {
708 fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
709 from, to);
710 goto out_iconv_close_to;
711 }
712 if (old) {
713 setlocale(LC_CTYPE, old);
714 free(old);
715 }
716
717 ic->next = next[0];
718 fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
719 if (!fs)
720 goto out_iconv_close_from;
721
722 return fs;
723
724out_iconv_close_from:
725 iconv_close(ic->fromfs);
726out_iconv_close_to:
727 iconv_close(ic->tofs);
728out_free:
729 free(ic->from_code);
730 free(ic->to_code);
731 free(ic);
732 if (old) {
733 setlocale(LC_CTYPE, old);
734 free(old);
735 }
736 return NULL;
737}
738
739FUSE_REGISTER_MODULE(iconv, iconv_new);