You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

739 lines
16 KiB

  1. /*
  2. fuse iconv module: file name charset conversion
  3. Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
  4. This program can be distributed under the terms of the GNU LGPLv2.
  5. See the file COPYING.LIB
  6. */
  7. #define FUSE_USE_VERSION 26
  8. #include <fuse.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <stddef.h>
  12. #include <string.h>
  13. #include <errno.h>
  14. #include <iconv.h>
  15. #include <pthread.h>
  16. #include <locale.h>
  17. #include <langinfo.h>
  18. struct iconv {
  19. struct fuse_fs *next;
  20. pthread_mutex_t lock;
  21. char *from_code;
  22. char *to_code;
  23. iconv_t tofs;
  24. iconv_t fromfs;
  25. };
  26. struct iconv_dh {
  27. struct iconv *ic;
  28. void *prev_buf;
  29. fuse_fill_dir_t prev_filler;
  30. };
  31. static struct iconv *iconv_get(void)
  32. {
  33. return fuse_get_context()->private_data;
  34. }
  35. static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
  36. int fromfs)
  37. {
  38. size_t pathlen;
  39. size_t newpathlen;
  40. char *newpath;
  41. size_t plen;
  42. char *p;
  43. size_t res;
  44. int err;
  45. if (path == NULL) {
  46. *newpathp = NULL;
  47. return 0;
  48. }
  49. pathlen = strlen(path);
  50. newpathlen = pathlen * 4;
  51. newpath = malloc(newpathlen + 1);
  52. if (!newpath)
  53. return -ENOMEM;
  54. plen = newpathlen;
  55. p = newpath;
  56. pthread_mutex_lock(&ic->lock);
  57. do {
  58. res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path,
  59. &pathlen, &p, &plen);
  60. if (res == (size_t) -1) {
  61. char *tmp;
  62. size_t inc;
  63. err = -EILSEQ;
  64. if (errno != E2BIG)
  65. goto err;
  66. inc = (pathlen + 1) * 4;
  67. newpathlen += inc;
  68. tmp = realloc(newpath, newpathlen + 1);
  69. err = -ENOMEM;
  70. if (!tmp)
  71. goto err;
  72. p = tmp + (p - newpath);
  73. plen += inc;
  74. newpath = tmp;
  75. }
  76. } while (res == (size_t) -1);
  77. pthread_mutex_unlock(&ic->lock);
  78. *p = '\0';
  79. *newpathp = newpath;
  80. return 0;
  81. err:
  82. iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL);
  83. pthread_mutex_unlock(&ic->lock);
  84. free(newpath);
  85. return err;
  86. }
  87. static int iconv_getattr(const char *path, struct stat *stbuf)
  88. {
  89. struct iconv *ic = iconv_get();
  90. char *newpath;
  91. int err = iconv_convpath(ic, path, &newpath, 0);
  92. if (!err) {
  93. err = fuse_fs_getattr(ic->next, newpath, stbuf);
  94. free(newpath);
  95. }
  96. return err;
  97. }
  98. static int iconv_fgetattr(const char *path, struct stat *stbuf,
  99. struct fuse_file_info *fi)
  100. {
  101. struct iconv *ic = iconv_get();
  102. char *newpath;
  103. int err = iconv_convpath(ic, path, &newpath, 0);
  104. if (!err) {
  105. err = fuse_fs_fgetattr(ic->next, newpath, stbuf, fi);
  106. free(newpath);
  107. }
  108. return err;
  109. }
  110. static int iconv_access(const char *path, int mask)
  111. {
  112. struct iconv *ic = iconv_get();
  113. char *newpath;
  114. int err = iconv_convpath(ic, path, &newpath, 0);
  115. if (!err) {
  116. err = fuse_fs_access(ic->next, newpath, mask);
  117. free(newpath);
  118. }
  119. return err;
  120. }
  121. static int iconv_readlink(const char *path, char *buf, size_t size)
  122. {
  123. struct iconv *ic = iconv_get();
  124. char *newpath;
  125. int err = iconv_convpath(ic, path, &newpath, 0);
  126. if (!err) {
  127. err = fuse_fs_readlink(ic->next, newpath, buf, size);
  128. if (!err) {
  129. char *newlink;
  130. err = iconv_convpath(ic, buf, &newlink, 1);
  131. if (!err) {
  132. strncpy(buf, newlink, size - 1);
  133. buf[size - 1] = '\0';
  134. free(newlink);
  135. }
  136. }
  137. free(newpath);
  138. }
  139. return err;
  140. }
  141. static int iconv_opendir(const char *path, struct fuse_file_info *fi)
  142. {
  143. struct iconv *ic = iconv_get();
  144. char *newpath;
  145. int err = iconv_convpath(ic, path, &newpath, 0);
  146. if (!err) {
  147. err = fuse_fs_opendir(ic->next, newpath, fi);
  148. free(newpath);
  149. }
  150. return err;
  151. }
  152. static int iconv_dir_fill(void *buf, const char *name,
  153. const struct stat *stbuf, off_t off)
  154. {
  155. struct iconv_dh *dh = buf;
  156. char *newname;
  157. int res = 0;
  158. if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
  159. res = dh->prev_filler(dh->prev_buf, newname, stbuf, off);
  160. free(newname);
  161. }
  162. return res;
  163. }
  164. static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
  165. off_t offset, struct fuse_file_info *fi)
  166. {
  167. struct iconv *ic = iconv_get();
  168. char *newpath;
  169. int err = iconv_convpath(ic, path, &newpath, 0);
  170. if (!err) {
  171. struct iconv_dh dh;
  172. dh.ic = ic;
  173. dh.prev_buf = buf;
  174. dh.prev_filler = filler;
  175. err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill,
  176. offset, fi);
  177. free(newpath);
  178. }
  179. return err;
  180. }
  181. static int iconv_releasedir(const char *path, struct fuse_file_info *fi)
  182. {
  183. struct iconv *ic = iconv_get();
  184. char *newpath;
  185. int err = iconv_convpath(ic, path, &newpath, 0);
  186. if (!err) {
  187. err = fuse_fs_releasedir(ic->next, newpath, fi);
  188. free(newpath);
  189. }
  190. return err;
  191. }
  192. static int iconv_mknod(const char *path, mode_t mode, dev_t rdev)
  193. {
  194. struct iconv *ic = iconv_get();
  195. char *newpath;
  196. int err = iconv_convpath(ic, path, &newpath, 0);
  197. if (!err) {
  198. err = fuse_fs_mknod(ic->next, newpath, mode, rdev);
  199. free(newpath);
  200. }
  201. return err;
  202. }
  203. static int iconv_mkdir(const char *path, mode_t mode)
  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_mkdir(ic->next, newpath, mode);
  210. free(newpath);
  211. }
  212. return err;
  213. }
  214. static int iconv_unlink(const char *path)
  215. {
  216. struct iconv *ic = iconv_get();
  217. char *newpath;
  218. int err = iconv_convpath(ic, path, &newpath, 0);
  219. if (!err) {
  220. err = fuse_fs_unlink(ic->next, newpath);
  221. free(newpath);
  222. }
  223. return err;
  224. }
  225. static int iconv_rmdir(const char *path)
  226. {
  227. struct iconv *ic = iconv_get();
  228. char *newpath;
  229. int err = iconv_convpath(ic, path, &newpath, 0);
  230. if (!err) {
  231. err = fuse_fs_rmdir(ic->next, newpath);
  232. free(newpath);
  233. }
  234. return err;
  235. }
  236. static int iconv_symlink(const char *from, const char *to)
  237. {
  238. struct iconv *ic = iconv_get();
  239. char *newfrom;
  240. char *newto;
  241. int err = iconv_convpath(ic, from, &newfrom, 0);
  242. if (!err) {
  243. err = iconv_convpath(ic, to, &newto, 0);
  244. if (!err) {
  245. err = fuse_fs_symlink(ic->next, newfrom, newto);
  246. free(newto);
  247. }
  248. free(newfrom);
  249. }
  250. return err;
  251. }
  252. static int iconv_rename(const char *from, const char *to)
  253. {
  254. struct iconv *ic = iconv_get();
  255. char *newfrom;
  256. char *newto;
  257. int err = iconv_convpath(ic, from, &newfrom, 0);
  258. if (!err) {
  259. err = iconv_convpath(ic, to, &newto, 0);
  260. if (!err) {
  261. err = fuse_fs_rename(ic->next, newfrom, newto);
  262. free(newto);
  263. }
  264. free(newfrom);
  265. }
  266. return err;
  267. }
  268. static int iconv_link(const char *from, const char *to)
  269. {
  270. struct iconv *ic = iconv_get();
  271. char *newfrom;
  272. char *newto;
  273. int err = iconv_convpath(ic, from, &newfrom, 0);
  274. if (!err) {
  275. err = iconv_convpath(ic, to, &newto, 0);
  276. if (!err) {
  277. err = fuse_fs_link(ic->next, newfrom, newto);
  278. free(newto);
  279. }
  280. free(newfrom);
  281. }
  282. return err;
  283. }
  284. static int iconv_chmod(const char *path, mode_t mode)
  285. {
  286. struct iconv *ic = iconv_get();
  287. char *newpath;
  288. int err = iconv_convpath(ic, path, &newpath, 0);
  289. if (!err) {
  290. err = fuse_fs_chmod(ic->next, newpath, mode);
  291. free(newpath);
  292. }
  293. return err;
  294. }
  295. static int iconv_chown(const char *path, uid_t uid, gid_t gid)
  296. {
  297. struct iconv *ic = iconv_get();
  298. char *newpath;
  299. int err = iconv_convpath(ic, path, &newpath, 0);
  300. if (!err) {
  301. err = fuse_fs_chown(ic->next, newpath, uid, gid);
  302. free(newpath);
  303. }
  304. return err;
  305. }
  306. static int iconv_truncate(const char *path, off_t size)
  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_truncate(ic->next, newpath, size);
  313. free(newpath);
  314. }
  315. return err;
  316. }
  317. static int iconv_ftruncate(const char *path, off_t size,
  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_ftruncate(ic->next, newpath, size, fi);
  325. free(newpath);
  326. }
  327. return err;
  328. }
  329. static int iconv_utimens(const char *path, const struct timespec ts[2])
  330. {
  331. struct iconv *ic = iconv_get();
  332. char *newpath;
  333. int err = iconv_convpath(ic, path, &newpath, 0);
  334. if (!err) {
  335. err = fuse_fs_utimens(ic->next, newpath, ts);
  336. free(newpath);
  337. }
  338. return err;
  339. }
  340. static int iconv_create(const char *path, mode_t mode,
  341. struct fuse_file_info *fi)
  342. {
  343. struct iconv *ic = iconv_get();
  344. char *newpath;
  345. int err = iconv_convpath(ic, path, &newpath, 0);
  346. if (!err) {
  347. err = fuse_fs_create(ic->next, newpath, mode, fi);
  348. free(newpath);
  349. }
  350. return err;
  351. }
  352. static int iconv_open_file(const char *path, struct fuse_file_info *fi)
  353. {
  354. struct iconv *ic = iconv_get();
  355. char *newpath;
  356. int err = iconv_convpath(ic, path, &newpath, 0);
  357. if (!err) {
  358. err = fuse_fs_open(ic->next, newpath, fi);
  359. free(newpath);
  360. }
  361. return err;
  362. }
  363. static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
  364. size_t size, off_t offset, struct fuse_file_info *fi)
  365. {
  366. struct iconv *ic = iconv_get();
  367. char *newpath;
  368. int err = iconv_convpath(ic, path, &newpath, 0);
  369. if (!err) {
  370. err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi);
  371. free(newpath);
  372. }
  373. return err;
  374. }
  375. static int iconv_write_buf(const char *path, struct fuse_bufvec *buf,
  376. off_t offset, 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_write_buf(ic->next, newpath, buf, offset, fi);
  383. free(newpath);
  384. }
  385. return err;
  386. }
  387. static int iconv_statfs(const char *path, struct statvfs *stbuf)
  388. {
  389. struct iconv *ic = iconv_get();
  390. char *newpath;
  391. int err = iconv_convpath(ic, path, &newpath, 0);
  392. if (!err) {
  393. err = fuse_fs_statfs(ic->next, newpath, stbuf);
  394. free(newpath);
  395. }
  396. return err;
  397. }
  398. static int iconv_flush(const char *path, struct fuse_file_info *fi)
  399. {
  400. struct iconv *ic = iconv_get();
  401. char *newpath;
  402. int err = iconv_convpath(ic, path, &newpath, 0);
  403. if (!err) {
  404. err = fuse_fs_flush(ic->next, newpath, fi);
  405. free(newpath);
  406. }
  407. return err;
  408. }
  409. static int iconv_release(const char *path, struct fuse_file_info *fi)
  410. {
  411. struct iconv *ic = iconv_get();
  412. char *newpath;
  413. int err = iconv_convpath(ic, path, &newpath, 0);
  414. if (!err) {
  415. err = fuse_fs_release(ic->next, newpath, fi);
  416. free(newpath);
  417. }
  418. return err;
  419. }
  420. static int iconv_fsync(const char *path, int isdatasync,
  421. struct fuse_file_info *fi)
  422. {
  423. struct iconv *ic = iconv_get();
  424. char *newpath;
  425. int err = iconv_convpath(ic, path, &newpath, 0);
  426. if (!err) {
  427. err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi);
  428. free(newpath);
  429. }
  430. return err;
  431. }
  432. static int iconv_fsyncdir(const char *path, int isdatasync,
  433. struct fuse_file_info *fi)
  434. {
  435. struct iconv *ic = iconv_get();
  436. char *newpath;
  437. int err = iconv_convpath(ic, path, &newpath, 0);
  438. if (!err) {
  439. err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi);
  440. free(newpath);
  441. }
  442. return err;
  443. }
  444. static int iconv_setxattr(const char *path, const char *name,
  445. const char *value, size_t size, int flags)
  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_setxattr(ic->next, newpath, name, value, size,
  452. flags);
  453. free(newpath);
  454. }
  455. return err;
  456. }
  457. static int iconv_getxattr(const char *path, const char *name, char *value,
  458. size_t size)
  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_getxattr(ic->next, newpath, name, value, size);
  465. free(newpath);
  466. }
  467. return err;
  468. }
  469. static int iconv_listxattr(const char *path, char *list, size_t size)
  470. {
  471. struct iconv *ic = iconv_get();
  472. char *newpath;
  473. int err = iconv_convpath(ic, path, &newpath, 0);
  474. if (!err) {
  475. err = fuse_fs_listxattr(ic->next, newpath, list, size);
  476. free(newpath);
  477. }
  478. return err;
  479. }
  480. static int iconv_removexattr(const char *path, const char *name)
  481. {
  482. struct iconv *ic = iconv_get();
  483. char *newpath;
  484. int err = iconv_convpath(ic, path, &newpath, 0);
  485. if (!err) {
  486. err = fuse_fs_removexattr(ic->next, newpath, name);
  487. free(newpath);
  488. }
  489. return err;
  490. }
  491. static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd,
  492. struct flock *lock)
  493. {
  494. struct iconv *ic = iconv_get();
  495. char *newpath;
  496. int err = iconv_convpath(ic, path, &newpath, 0);
  497. if (!err) {
  498. err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock);
  499. free(newpath);
  500. }
  501. return err;
  502. }
  503. static int iconv_flock(const char *path, struct fuse_file_info *fi, int op)
  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_flock(ic->next, newpath, fi, op);
  510. free(newpath);
  511. }
  512. return err;
  513. }
  514. static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
  515. {
  516. struct iconv *ic = iconv_get();
  517. char *newpath;
  518. int err = iconv_convpath(ic, path, &newpath, 0);
  519. if (!err) {
  520. err = fuse_fs_bmap(ic->next, newpath, blocksize, idx);
  521. free(newpath);
  522. }
  523. return err;
  524. }
  525. static void *iconv_init(struct fuse_conn_info *conn)
  526. {
  527. struct iconv *ic = iconv_get();
  528. fuse_fs_init(ic->next, conn);
  529. return ic;
  530. }
  531. static void iconv_destroy(void *data)
  532. {
  533. struct iconv *ic = data;
  534. fuse_fs_destroy(ic->next);
  535. iconv_close(ic->tofs);
  536. iconv_close(ic->fromfs);
  537. pthread_mutex_destroy(&ic->lock);
  538. free(ic->from_code);
  539. free(ic->to_code);
  540. free(ic);
  541. }
  542. static const struct fuse_operations iconv_oper = {
  543. .destroy = iconv_destroy,
  544. .init = iconv_init,
  545. .getattr = iconv_getattr,
  546. .fgetattr = iconv_fgetattr,
  547. .access = iconv_access,
  548. .readlink = iconv_readlink,
  549. .opendir = iconv_opendir,
  550. .readdir = iconv_readdir,
  551. .releasedir = iconv_releasedir,
  552. .mknod = iconv_mknod,
  553. .mkdir = iconv_mkdir,
  554. .symlink = iconv_symlink,
  555. .unlink = iconv_unlink,
  556. .rmdir = iconv_rmdir,
  557. .rename = iconv_rename,
  558. .link = iconv_link,
  559. .chmod = iconv_chmod,
  560. .chown = iconv_chown,
  561. .truncate = iconv_truncate,
  562. .ftruncate = iconv_ftruncate,
  563. .utimens = iconv_utimens,
  564. .create = iconv_create,
  565. .open = iconv_open_file,
  566. .read_buf = iconv_read_buf,
  567. .write_buf = iconv_write_buf,
  568. .statfs = iconv_statfs,
  569. .flush = iconv_flush,
  570. .release = iconv_release,
  571. .fsync = iconv_fsync,
  572. .fsyncdir = iconv_fsyncdir,
  573. .setxattr = iconv_setxattr,
  574. .getxattr = iconv_getxattr,
  575. .listxattr = iconv_listxattr,
  576. .removexattr = iconv_removexattr,
  577. .lock = iconv_lock,
  578. .flock = iconv_flock,
  579. .bmap = iconv_bmap,
  580. .flag_nullpath_ok = 1,
  581. .flag_nopath = 1,
  582. };
  583. static const struct fuse_opt iconv_opts[] = {
  584. FUSE_OPT_KEY("-h", 0),
  585. FUSE_OPT_KEY("--help", 0),
  586. { "from_code=%s", offsetof(struct iconv, from_code), 0 },
  587. { "to_code=%s", offsetof(struct iconv, to_code), 1 },
  588. FUSE_OPT_END
  589. };
  590. static void iconv_help(void)
  591. {
  592. char *old = strdup(setlocale(LC_CTYPE, ""));
  593. char *charmap = strdup(nl_langinfo(CODESET));
  594. setlocale(LC_CTYPE, old);
  595. free(old);
  596. fprintf(stderr,
  597. " -o from_code=CHARSET original encoding of file names (default: UTF-8)\n"
  598. " -o to_code=CHARSET new encoding of the file names (default: %s)\n",
  599. charmap);
  600. free(charmap);
  601. }
  602. static int iconv_opt_proc(void *data, const char *arg, int key,
  603. struct fuse_args *outargs)
  604. {
  605. (void) data; (void) arg; (void) outargs;
  606. if (!key) {
  607. iconv_help();
  608. return -1;
  609. }
  610. return 1;
  611. }
  612. static struct fuse_fs *iconv_new(struct fuse_args *args,
  613. struct fuse_fs *next[])
  614. {
  615. struct fuse_fs *fs;
  616. struct iconv *ic;
  617. char *old = NULL;
  618. const char *from;
  619. const char *to;
  620. ic = calloc(1, sizeof(struct iconv));
  621. if (ic == NULL) {
  622. fprintf(stderr, "fuse-iconv: memory allocation failed\n");
  623. return NULL;
  624. }
  625. if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
  626. goto out_free;
  627. if (!next[0] || next[1]) {
  628. fprintf(stderr, "fuse-iconv: exactly one next filesystem required\n");
  629. goto out_free;
  630. }
  631. from = ic->from_code ? ic->from_code : "UTF-8";
  632. to = ic->to_code ? ic->to_code : "";
  633. /* FIXME: detect charset equivalence? */
  634. if (!to[0])
  635. old = strdup(setlocale(LC_CTYPE, ""));
  636. ic->tofs = iconv_open(from, to);
  637. if (ic->tofs == (iconv_t) -1) {
  638. fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
  639. to, from);
  640. goto out_free;
  641. }
  642. ic->fromfs = iconv_open(to, from);
  643. if (ic->tofs == (iconv_t) -1) {
  644. fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
  645. from, to);
  646. goto out_iconv_close_to;
  647. }
  648. if (old) {
  649. setlocale(LC_CTYPE, old);
  650. free(old);
  651. }
  652. ic->next = next[0];
  653. fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
  654. if (!fs)
  655. goto out_iconv_close_from;
  656. return fs;
  657. out_iconv_close_from:
  658. iconv_close(ic->fromfs);
  659. out_iconv_close_to:
  660. iconv_close(ic->tofs);
  661. out_free:
  662. free(ic->from_code);
  663. free(ic->to_code);
  664. free(ic);
  665. if (old) {
  666. setlocale(LC_CTYPE, old);
  667. free(old);
  668. }
  669. return NULL;
  670. }
  671. FUSE_REGISTER_MODULE(iconv, iconv_new);