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 {
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 void *iconv_init(struct fuse_conn_info *conn,
558  struct fuse_config *cfg)
559 {
560  struct iconv *ic = iconv_get();
561  fuse_fs_init(ic->next, conn, cfg);
562  /* Don't touch cfg->nullpath_ok, we can work with
563  either */
564  return ic;
565 }
566 
567 static void iconv_destroy(void *data)
568 {
569  struct iconv *ic = data;
570  fuse_fs_destroy(ic->next);
571  iconv_close(ic->tofs);
572  iconv_close(ic->fromfs);
573  pthread_mutex_destroy(&ic->lock);
574  free(ic->from_code);
575  free(ic->to_code);
576  free(ic);
577 }
578 
579 static const struct fuse_operations iconv_oper = {
580  .destroy = iconv_destroy,
581  .init = iconv_init,
582  .getattr = iconv_getattr,
583  .access = iconv_access,
584  .readlink = iconv_readlink,
585  .opendir = iconv_opendir,
586  .readdir = iconv_readdir,
587  .releasedir = iconv_releasedir,
588  .mknod = iconv_mknod,
589  .mkdir = iconv_mkdir,
590  .symlink = iconv_symlink,
591  .unlink = iconv_unlink,
592  .rmdir = iconv_rmdir,
593  .rename = iconv_rename,
594  .link = iconv_link,
595  .chmod = iconv_chmod,
596  .chown = iconv_chown,
597  .truncate = iconv_truncate,
598  .utimens = iconv_utimens,
599  .create = iconv_create,
600  .open = iconv_open_file,
601  .read_buf = iconv_read_buf,
602  .write_buf = iconv_write_buf,
603  .statfs = iconv_statfs,
604  .flush = iconv_flush,
605  .release = iconv_release,
606  .fsync = iconv_fsync,
607  .fsyncdir = iconv_fsyncdir,
608  .setxattr = iconv_setxattr,
609  .getxattr = iconv_getxattr,
610  .listxattr = iconv_listxattr,
611  .removexattr = iconv_removexattr,
612  .lock = iconv_lock,
613  .flock = iconv_flock,
614  .bmap = iconv_bmap,
615 };
616 
617 static const struct fuse_opt iconv_opts[] = {
618  FUSE_OPT_KEY("-h", 0),
619  FUSE_OPT_KEY("--help", 0),
620  { "from_code=%s", offsetof(struct iconv, from_code), 0 },
621  { "to_code=%s", offsetof(struct iconv, to_code), 1 },
623 };
624 
625 static void iconv_help(void)
626 {
627  char *old = strdup(setlocale(LC_CTYPE, ""));
628  char *charmap = strdup(nl_langinfo(CODESET));
629  setlocale(LC_CTYPE, old);
630  free(old);
631  printf(
632 " -o from_code=CHARSET original encoding of file names (default: UTF-8)\n"
633 " -o to_code=CHARSET new encoding of the file names (default: %s)\n",
634  charmap);
635  free(charmap);
636 }
637 
638 static int iconv_opt_proc(void *data, const char *arg, int key,
639  struct fuse_args *outargs)
640 {
641  (void) data; (void) arg; (void) outargs;
642 
643  if (!key) {
644  iconv_help();
645  return -1;
646  }
647 
648  return 1;
649 }
650 
651 static struct fuse_fs *iconv_new(struct fuse_args *args,
652  struct fuse_fs *next[])
653 {
654  struct fuse_fs *fs;
655  struct iconv *ic;
656  char *old = NULL;
657  const char *from;
658  const char *to;
659 
660  ic = calloc(1, sizeof(struct iconv));
661  if (ic == NULL) {
662  fprintf(stderr, "fuse-iconv: memory allocation failed\n");
663  return NULL;
664  }
665 
666  if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
667  goto out_free;
668 
669  if (!next[0] || next[1]) {
670  fprintf(stderr, "fuse-iconv: exactly one next filesystem required\n");
671  goto out_free;
672  }
673 
674  from = ic->from_code ? ic->from_code : "UTF-8";
675  to = ic->to_code ? ic->to_code : "";
676  /* FIXME: detect charset equivalence? */
677  if (!to[0])
678  old = strdup(setlocale(LC_CTYPE, ""));
679  ic->tofs = iconv_open(from, to);
680  if (ic->tofs == (iconv_t) -1) {
681  fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
682  to, from);
683  goto out_free;
684  }
685  ic->fromfs = iconv_open(to, from);
686  if (ic->tofs == (iconv_t) -1) {
687  fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
688  from, to);
689  goto out_iconv_close_to;
690  }
691  if (old) {
692  setlocale(LC_CTYPE, old);
693  free(old);
694  }
695 
696  ic->next = next[0];
697  fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
698  if (!fs)
699  goto out_iconv_close_from;
700 
701  return fs;
702 
703 out_iconv_close_from:
704  iconv_close(ic->fromfs);
705 out_iconv_close_to:
706  iconv_close(ic->tofs);
707 out_free:
708  free(ic->from_code);
709  free(ic->to_code);
710  free(ic);
711  if (old) {
712  setlocale(LC_CTYPE, old);
713  free(old);
714  }
715  return NULL;
716 }
717 
718 FUSE_REGISTER_MODULE(iconv, iconv_new);
void(* destroy)(void *private_data)
Definition: fuse.h:580
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_readdir_flags
Definition: fuse.h:42
#define FUSE_REGISTER_MODULE(name_, factory_)
Definition: fuse.h:1207
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition: fuse.c:4618
struct fuse_context * fuse_get_context(void)
Definition: fuse.c:4415
#define FUSE_OPT_KEY(templ, key)
Definition: fuse_opt.h:98
fuse_fill_dir_flags
Definition: fuse.h:54
#define FUSE_OPT_END
Definition: fuse_opt.h:104
void * private_data
Definition: fuse.h:774
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition: fuse_opt.c:397