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.

697 lines
15 KiB

  1. /*
  2. fuse subdir module: offset paths with a base directory
  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. struct subdir {
  15. char *base;
  16. size_t baselen;
  17. int rellinks;
  18. struct fuse_fs *next;
  19. };
  20. static struct subdir *subdir_get(void)
  21. {
  22. return fuse_get_context()->private_data;
  23. }
  24. static int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
  25. {
  26. char *newpath = NULL;
  27. if (path != NULL) {
  28. unsigned newlen = d->baselen + strlen(path);
  29. newpath = malloc(newlen + 2);
  30. if (!newpath)
  31. return -ENOMEM;
  32. if (path[0] == '/')
  33. path++;
  34. strcpy(newpath, d->base);
  35. strcpy(newpath + d->baselen, path);
  36. if (!newpath[0])
  37. strcpy(newpath, ".");
  38. }
  39. *newpathp = newpath;
  40. return 0;
  41. }
  42. static int subdir_getattr(const char *path, struct stat *stbuf)
  43. {
  44. struct subdir *d = subdir_get();
  45. char *newpath;
  46. int err = subdir_addpath(d, path, &newpath);
  47. if (!err) {
  48. err = fuse_fs_getattr(d->next, newpath, stbuf);
  49. free(newpath);
  50. }
  51. return err;
  52. }
  53. static int subdir_fgetattr(const char *path, struct stat *stbuf,
  54. struct fuse_file_info *fi)
  55. {
  56. struct subdir *d = subdir_get();
  57. char *newpath;
  58. int err = subdir_addpath(d, path, &newpath);
  59. if (!err) {
  60. err = fuse_fs_fgetattr(d->next, newpath, stbuf, fi);
  61. free(newpath);
  62. }
  63. return err;
  64. }
  65. static int subdir_access(const char *path, int mask)
  66. {
  67. struct subdir *d = subdir_get();
  68. char *newpath;
  69. int err = subdir_addpath(d, path, &newpath);
  70. if (!err) {
  71. err = fuse_fs_access(d->next, newpath, mask);
  72. free(newpath);
  73. }
  74. return err;
  75. }
  76. static int count_components(const char *p)
  77. {
  78. int ctr;
  79. for (; *p == '/'; p++);
  80. for (ctr = 0; *p; ctr++) {
  81. for (; *p && *p != '/'; p++);
  82. for (; *p == '/'; p++);
  83. }
  84. return ctr;
  85. }
  86. static void strip_common(const char **sp, const char **tp)
  87. {
  88. const char *s = *sp;
  89. const char *t = *tp;
  90. do {
  91. for (; *s == '/'; s++);
  92. for (; *t == '/'; t++);
  93. *tp = t;
  94. *sp = s;
  95. for (; *s == *t && *s && *s != '/'; s++, t++);
  96. } while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
  97. }
  98. static void transform_symlink(struct subdir *d, const char *path,
  99. char *buf, size_t size)
  100. {
  101. const char *l = buf;
  102. size_t llen;
  103. char *s;
  104. int dotdots;
  105. int i;
  106. if (l[0] != '/' || d->base[0] != '/')
  107. return;
  108. strip_common(&l, &path);
  109. if (l - buf < (long) d->baselen)
  110. return;
  111. dotdots = count_components(path);
  112. if (!dotdots)
  113. return;
  114. dotdots--;
  115. llen = strlen(l);
  116. if (dotdots * 3 + llen + 2 > size)
  117. return;
  118. s = buf + dotdots * 3;
  119. if (llen)
  120. memmove(s, l, llen + 1);
  121. else if (!dotdots)
  122. strcpy(s, ".");
  123. else
  124. *s = '\0';
  125. for (s = buf, i = 0; i < dotdots; i++, s += 3)
  126. memcpy(s, "../", 3);
  127. }
  128. static int subdir_readlink(const char *path, char *buf, size_t size)
  129. {
  130. struct subdir *d = subdir_get();
  131. char *newpath;
  132. int err = subdir_addpath(d, path, &newpath);
  133. if (!err) {
  134. err = fuse_fs_readlink(d->next, newpath, buf, size);
  135. if (!err && d->rellinks)
  136. transform_symlink(d, newpath, buf, size);
  137. free(newpath);
  138. }
  139. return err;
  140. }
  141. static int subdir_opendir(const char *path, struct fuse_file_info *fi)
  142. {
  143. struct subdir *d = subdir_get();
  144. char *newpath;
  145. int err = subdir_addpath(d, path, &newpath);
  146. if (!err) {
  147. err = fuse_fs_opendir(d->next, newpath, fi);
  148. free(newpath);
  149. }
  150. return err;
  151. }
  152. static int subdir_readdir(const char *path, void *buf,
  153. fuse_fill_dir_t filler, off_t offset,
  154. struct fuse_file_info *fi)
  155. {
  156. struct subdir *d = subdir_get();
  157. char *newpath;
  158. int err = subdir_addpath(d, path, &newpath);
  159. if (!err) {
  160. err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
  161. fi);
  162. free(newpath);
  163. }
  164. return err;
  165. }
  166. static int subdir_releasedir(const char *path, struct fuse_file_info *fi)
  167. {
  168. struct subdir *d = subdir_get();
  169. char *newpath;
  170. int err = subdir_addpath(d, path, &newpath);
  171. if (!err) {
  172. err = fuse_fs_releasedir(d->next, newpath, fi);
  173. free(newpath);
  174. }
  175. return err;
  176. }
  177. static int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
  178. {
  179. struct subdir *d = subdir_get();
  180. char *newpath;
  181. int err = subdir_addpath(d, path, &newpath);
  182. if (!err) {
  183. err = fuse_fs_mknod(d->next, newpath, mode, rdev);
  184. free(newpath);
  185. }
  186. return err;
  187. }
  188. static int subdir_mkdir(const char *path, mode_t mode)
  189. {
  190. struct subdir *d = subdir_get();
  191. char *newpath;
  192. int err = subdir_addpath(d, path, &newpath);
  193. if (!err) {
  194. err = fuse_fs_mkdir(d->next, newpath, mode);
  195. free(newpath);
  196. }
  197. return err;
  198. }
  199. static int subdir_unlink(const char *path)
  200. {
  201. struct subdir *d = subdir_get();
  202. char *newpath;
  203. int err = subdir_addpath(d, path, &newpath);
  204. if (!err) {
  205. err = fuse_fs_unlink(d->next, newpath);
  206. free(newpath);
  207. }
  208. return err;
  209. }
  210. static int subdir_rmdir(const char *path)
  211. {
  212. struct subdir *d = subdir_get();
  213. char *newpath;
  214. int err = subdir_addpath(d, path, &newpath);
  215. if (!err) {
  216. err = fuse_fs_rmdir(d->next, newpath);
  217. free(newpath);
  218. }
  219. return err;
  220. }
  221. static int subdir_symlink(const char *from, const char *path)
  222. {
  223. struct subdir *d = subdir_get();
  224. char *newpath;
  225. int err = subdir_addpath(d, path, &newpath);
  226. if (!err) {
  227. err = fuse_fs_symlink(d->next, from, newpath);
  228. free(newpath);
  229. }
  230. return err;
  231. }
  232. static int subdir_rename(const char *from, const char *to)
  233. {
  234. struct subdir *d = subdir_get();
  235. char *newfrom;
  236. char *newto;
  237. int err = subdir_addpath(d, from, &newfrom);
  238. if (!err) {
  239. err = subdir_addpath(d, to, &newto);
  240. if (!err) {
  241. err = fuse_fs_rename(d->next, newfrom, newto);
  242. free(newto);
  243. }
  244. free(newfrom);
  245. }
  246. return err;
  247. }
  248. static int subdir_link(const char *from, const char *to)
  249. {
  250. struct subdir *d = subdir_get();
  251. char *newfrom;
  252. char *newto;
  253. int err = subdir_addpath(d, from, &newfrom);
  254. if (!err) {
  255. err = subdir_addpath(d, to, &newto);
  256. if (!err) {
  257. err = fuse_fs_link(d->next, newfrom, newto);
  258. free(newto);
  259. }
  260. free(newfrom);
  261. }
  262. return err;
  263. }
  264. static int subdir_chmod(const char *path, mode_t mode)
  265. {
  266. struct subdir *d = subdir_get();
  267. char *newpath;
  268. int err = subdir_addpath(d, path, &newpath);
  269. if (!err) {
  270. err = fuse_fs_chmod(d->next, newpath, mode);
  271. free(newpath);
  272. }
  273. return err;
  274. }
  275. static int subdir_chown(const char *path, uid_t uid, gid_t gid)
  276. {
  277. struct subdir *d = subdir_get();
  278. char *newpath;
  279. int err = subdir_addpath(d, path, &newpath);
  280. if (!err) {
  281. err = fuse_fs_chown(d->next, newpath, uid, gid);
  282. free(newpath);
  283. }
  284. return err;
  285. }
  286. static int subdir_truncate(const char *path, off_t size)
  287. {
  288. struct subdir *d = subdir_get();
  289. char *newpath;
  290. int err = subdir_addpath(d, path, &newpath);
  291. if (!err) {
  292. err = fuse_fs_truncate(d->next, newpath, size);
  293. free(newpath);
  294. }
  295. return err;
  296. }
  297. static int subdir_ftruncate(const char *path, off_t size,
  298. struct fuse_file_info *fi)
  299. {
  300. struct subdir *d = subdir_get();
  301. char *newpath;
  302. int err = subdir_addpath(d, path, &newpath);
  303. if (!err) {
  304. err = fuse_fs_ftruncate(d->next, newpath, size, fi);
  305. free(newpath);
  306. }
  307. return err;
  308. }
  309. static int subdir_utimens(const char *path, const struct timespec ts[2])
  310. {
  311. struct subdir *d = subdir_get();
  312. char *newpath;
  313. int err = subdir_addpath(d, path, &newpath);
  314. if (!err) {
  315. err = fuse_fs_utimens(d->next, newpath, ts);
  316. free(newpath);
  317. }
  318. return err;
  319. }
  320. static int subdir_create(const char *path, mode_t mode,
  321. struct fuse_file_info *fi)
  322. {
  323. struct subdir *d = subdir_get();
  324. char *newpath;
  325. int err = subdir_addpath(d, path, &newpath);
  326. if (!err) {
  327. err = fuse_fs_create(d->next, newpath, mode, fi);
  328. free(newpath);
  329. }
  330. return err;
  331. }
  332. static int subdir_open(const char *path, struct fuse_file_info *fi)
  333. {
  334. struct subdir *d = subdir_get();
  335. char *newpath;
  336. int err = subdir_addpath(d, path, &newpath);
  337. if (!err) {
  338. err = fuse_fs_open(d->next, newpath, fi);
  339. free(newpath);
  340. }
  341. return err;
  342. }
  343. static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
  344. size_t size, off_t offset, struct fuse_file_info *fi)
  345. {
  346. struct subdir *d = subdir_get();
  347. char *newpath;
  348. int err = subdir_addpath(d, path, &newpath);
  349. if (!err) {
  350. err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
  351. free(newpath);
  352. }
  353. return err;
  354. }
  355. static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
  356. off_t offset, struct fuse_file_info *fi)
  357. {
  358. struct subdir *d = subdir_get();
  359. char *newpath;
  360. int err = subdir_addpath(d, path, &newpath);
  361. if (!err) {
  362. err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
  363. free(newpath);
  364. }
  365. return err;
  366. }
  367. static int subdir_statfs(const char *path, struct statvfs *stbuf)
  368. {
  369. struct subdir *d = subdir_get();
  370. char *newpath;
  371. int err = subdir_addpath(d, path, &newpath);
  372. if (!err) {
  373. err = fuse_fs_statfs(d->next, newpath, stbuf);
  374. free(newpath);
  375. }
  376. return err;
  377. }
  378. static int subdir_flush(const char *path, struct fuse_file_info *fi)
  379. {
  380. struct subdir *d = subdir_get();
  381. char *newpath;
  382. int err = subdir_addpath(d, path, &newpath);
  383. if (!err) {
  384. err = fuse_fs_flush(d->next, newpath, fi);
  385. free(newpath);
  386. }
  387. return err;
  388. }
  389. static int subdir_release(const char *path, struct fuse_file_info *fi)
  390. {
  391. struct subdir *d = subdir_get();
  392. char *newpath;
  393. int err = subdir_addpath(d, path, &newpath);
  394. if (!err) {
  395. err = fuse_fs_release(d->next, newpath, fi);
  396. free(newpath);
  397. }
  398. return err;
  399. }
  400. static int subdir_fsync(const char *path, int isdatasync,
  401. struct fuse_file_info *fi)
  402. {
  403. struct subdir *d = subdir_get();
  404. char *newpath;
  405. int err = subdir_addpath(d, path, &newpath);
  406. if (!err) {
  407. err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
  408. free(newpath);
  409. }
  410. return err;
  411. }
  412. static int subdir_fsyncdir(const char *path, int isdatasync,
  413. struct fuse_file_info *fi)
  414. {
  415. struct subdir *d = subdir_get();
  416. char *newpath;
  417. int err = subdir_addpath(d, path, &newpath);
  418. if (!err) {
  419. err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
  420. free(newpath);
  421. }
  422. return err;
  423. }
  424. static int subdir_setxattr(const char *path, const char *name,
  425. const char *value, size_t size, int flags)
  426. {
  427. struct subdir *d = subdir_get();
  428. char *newpath;
  429. int err = subdir_addpath(d, path, &newpath);
  430. if (!err) {
  431. err = fuse_fs_setxattr(d->next, newpath, name, value, size,
  432. flags);
  433. free(newpath);
  434. }
  435. return err;
  436. }
  437. static int subdir_getxattr(const char *path, const char *name, char *value,
  438. size_t size)
  439. {
  440. struct subdir *d = subdir_get();
  441. char *newpath;
  442. int err = subdir_addpath(d, path, &newpath);
  443. if (!err) {
  444. err = fuse_fs_getxattr(d->next, newpath, name, value, size);
  445. free(newpath);
  446. }
  447. return err;
  448. }
  449. static int subdir_listxattr(const char *path, char *list, size_t size)
  450. {
  451. struct subdir *d = subdir_get();
  452. char *newpath;
  453. int err = subdir_addpath(d, path, &newpath);
  454. if (!err) {
  455. err = fuse_fs_listxattr(d->next, newpath, list, size);
  456. free(newpath);
  457. }
  458. return err;
  459. }
  460. static int subdir_removexattr(const char *path, const char *name)
  461. {
  462. struct subdir *d = subdir_get();
  463. char *newpath;
  464. int err = subdir_addpath(d, path, &newpath);
  465. if (!err) {
  466. err = fuse_fs_removexattr(d->next, newpath, name);
  467. free(newpath);
  468. }
  469. return err;
  470. }
  471. static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
  472. struct flock *lock)
  473. {
  474. struct subdir *d = subdir_get();
  475. char *newpath;
  476. int err = subdir_addpath(d, path, &newpath);
  477. if (!err) {
  478. err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
  479. free(newpath);
  480. }
  481. return err;
  482. }
  483. static int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
  484. {
  485. struct subdir *d = subdir_get();
  486. char *newpath;
  487. int err = subdir_addpath(d, path, &newpath);
  488. if (!err) {
  489. err = fuse_fs_flock(d->next, newpath, fi, op);
  490. free(newpath);
  491. }
  492. return err;
  493. }
  494. static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
  495. {
  496. struct subdir *d = subdir_get();
  497. char *newpath;
  498. int err = subdir_addpath(d, path, &newpath);
  499. if (!err) {
  500. err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
  501. free(newpath);
  502. }
  503. return err;
  504. }
  505. static void *subdir_init(struct fuse_conn_info *conn)
  506. {
  507. struct subdir *d = subdir_get();
  508. fuse_fs_init(d->next, conn);
  509. return d;
  510. }
  511. static void subdir_destroy(void *data)
  512. {
  513. struct subdir *d = data;
  514. fuse_fs_destroy(d->next);
  515. free(d->base);
  516. free(d);
  517. }
  518. static const struct fuse_operations subdir_oper = {
  519. .destroy = subdir_destroy,
  520. .init = subdir_init,
  521. .getattr = subdir_getattr,
  522. .fgetattr = subdir_fgetattr,
  523. .access = subdir_access,
  524. .readlink = subdir_readlink,
  525. .opendir = subdir_opendir,
  526. .readdir = subdir_readdir,
  527. .releasedir = subdir_releasedir,
  528. .mknod = subdir_mknod,
  529. .mkdir = subdir_mkdir,
  530. .symlink = subdir_symlink,
  531. .unlink = subdir_unlink,
  532. .rmdir = subdir_rmdir,
  533. .rename = subdir_rename,
  534. .link = subdir_link,
  535. .chmod = subdir_chmod,
  536. .chown = subdir_chown,
  537. .truncate = subdir_truncate,
  538. .ftruncate = subdir_ftruncate,
  539. .utimens = subdir_utimens,
  540. .create = subdir_create,
  541. .open = subdir_open,
  542. .read_buf = subdir_read_buf,
  543. .write_buf = subdir_write_buf,
  544. .statfs = subdir_statfs,
  545. .flush = subdir_flush,
  546. .release = subdir_release,
  547. .fsync = subdir_fsync,
  548. .fsyncdir = subdir_fsyncdir,
  549. .setxattr = subdir_setxattr,
  550. .getxattr = subdir_getxattr,
  551. .listxattr = subdir_listxattr,
  552. .removexattr = subdir_removexattr,
  553. .lock = subdir_lock,
  554. .flock = subdir_flock,
  555. .bmap = subdir_bmap,
  556. .flag_nullpath_ok = 1,
  557. .flag_nopath = 1,
  558. };
  559. static const struct fuse_opt subdir_opts[] = {
  560. FUSE_OPT_KEY("-h", 0),
  561. FUSE_OPT_KEY("--help", 0),
  562. { "subdir=%s", offsetof(struct subdir, base), 0 },
  563. { "rellinks", offsetof(struct subdir, rellinks), 1 },
  564. { "norellinks", offsetof(struct subdir, rellinks), 0 },
  565. FUSE_OPT_END
  566. };
  567. static void subdir_help(void)
  568. {
  569. fprintf(stderr,
  570. " -o subdir=DIR prepend this directory to all paths (mandatory)\n"
  571. " -o [no]rellinks transform absolute symlinks to relative\n");
  572. }
  573. static int subdir_opt_proc(void *data, const char *arg, int key,
  574. struct fuse_args *outargs)
  575. {
  576. (void) data; (void) arg; (void) outargs;
  577. if (!key) {
  578. subdir_help();
  579. return -1;
  580. }
  581. return 1;
  582. }
  583. static struct fuse_fs *subdir_new(struct fuse_args *args,
  584. struct fuse_fs *next[])
  585. {
  586. struct fuse_fs *fs;
  587. struct subdir *d;
  588. d = calloc(1, sizeof(struct subdir));
  589. if (d == NULL) {
  590. fprintf(stderr, "fuse-subdir: memory allocation failed\n");
  591. return NULL;
  592. }
  593. if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
  594. goto out_free;
  595. if (!next[0] || next[1]) {
  596. fprintf(stderr, "fuse-subdir: exactly one next filesystem required\n");
  597. goto out_free;
  598. }
  599. if (!d->base) {
  600. fprintf(stderr, "fuse-subdir: missing 'subdir' option\n");
  601. goto out_free;
  602. }
  603. if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
  604. char *tmp = realloc(d->base, strlen(d->base) + 2);
  605. if (!tmp) {
  606. fprintf(stderr, "fuse-subdir: memory allocation failed\n");
  607. goto out_free;
  608. }
  609. d->base = tmp;
  610. strcat(d->base, "/");
  611. }
  612. d->baselen = strlen(d->base);
  613. d->next = next[0];
  614. fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
  615. if (!fs)
  616. goto out_free;
  617. return fs;
  618. out_free:
  619. free(d->base);
  620. free(d);
  621. return NULL;
  622. }
  623. FUSE_REGISTER_MODULE(subdir, subdir_new);