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 
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  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 
96 err:
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 
103 static 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 
116 static 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 
128 static 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 
149 static 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 
161 static 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 
175 static 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 
194 static 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 
206 static 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 
218 static 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 
230 static 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 
242 static 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 
254 static 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 
271 static 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 
288 static 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 
305 static 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 
318 static 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 
331 static 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 
344 static 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 
357 static 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 
370 static 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 
382 static 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 
395 static 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 
408 static 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 
420 static 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 
432 static 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 
444 static 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 
457 static 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 
470 static 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 
484 static 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 
497 static 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 
509 static 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 
521 static 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 
534 static 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 
546 static 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 
558 static 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 
571 static 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 
581 static 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 
593 static 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 
632 static 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 
640 static 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 
658 static 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 
671 static 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 
723 out_iconv_close_from:
724  iconv_close(ic->fromfs);
725 out_iconv_close_to:
726  iconv_close(ic->tofs);
727 out_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 
737 FUSE_REGISTER_MODULE(iconv, iconv_new);
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition: fuse.c:4804
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_context * fuse_get_context(void)
Definition: fuse.c:4598
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