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