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