Imported Upstream version 2.9.0
[platform/upstream/fuse.git] / lib / modules / 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 #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
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 {
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
114 static 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
127 static 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
139 static 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
160 static 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
172 static int iconv_dir_fill(void *buf, const char *name,
173                           const struct stat *stbuf, off_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
185 static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
186                          off_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
203 static 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
215 static 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
227 static 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
239 static 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
251 static 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
263 static 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
280 static 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
297 static 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
314 static 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
326 static 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
338 static int iconv_truncate(const char *path, off_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
350 static int iconv_ftruncate(const char *path, off_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
363 static 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
375 static 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
388 static 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
400 static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
401                           size_t size, off_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
413 static int iconv_write_buf(const char *path, struct fuse_bufvec *buf,
414                            off_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
426 static 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
438 static 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
450 static 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
462 static 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
475 static 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
488 static 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
502 static 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
515 static 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
527 static 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
539 static 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
552 static 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
564 static 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
576 static 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
583 static 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
595 static 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
638 static 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
646 static 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
659 static 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
672 static 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
724 out_iconv_close_from:
725         iconv_close(ic->fromfs);
726 out_iconv_close_to:
727         iconv_close(ic->tofs);
728 out_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
739 FUSE_REGISTER_MODULE(iconv, iconv_new);