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.

602 lines
14 KiB

  1. /*
  2. FUSE: Filesystem in Userspace
  3. Copyright (C) 2001-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. #include "config.h"
  8. #include "fuse_i.h"
  9. #include "fuse_misc.h"
  10. #include "fuse_opt.h"
  11. #include "mount_util.h"
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <unistd.h>
  15. #include <stddef.h>
  16. #include <string.h>
  17. #include <fcntl.h>
  18. #include <errno.h>
  19. #include <sys/poll.h>
  20. #include <sys/socket.h>
  21. #include <sys/un.h>
  22. #include <sys/wait.h>
  23. #include <sys/mount.h>
  24. #ifdef __NetBSD__
  25. #include <perfuse.h>
  26. #define MS_RDONLY MNT_RDONLY
  27. #define MS_NOSUID MNT_NOSUID
  28. #define MS_NODEV MNT_NODEV
  29. #define MS_NOEXEC MNT_NOEXEC
  30. #define MS_SYNCHRONOUS MNT_SYNCHRONOUS
  31. #define MS_NOATIME MNT_NOATIME
  32. #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
  33. #endif
  34. #define FUSERMOUNT_PROG "fusermount"
  35. #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
  36. #ifndef HAVE_FORK
  37. #define fork() vfork()
  38. #endif
  39. #ifndef MS_DIRSYNC
  40. #define MS_DIRSYNC 128
  41. #endif
  42. enum {
  43. KEY_KERN_FLAG,
  44. KEY_KERN_OPT,
  45. KEY_FUSERMOUNT_OPT,
  46. KEY_SUBTYPE_OPT,
  47. KEY_MTAB_OPT,
  48. KEY_RO,
  49. KEY_HELP,
  50. KEY_VERSION,
  51. };
  52. struct mount_opts {
  53. int allow_other;
  54. int ishelp;
  55. int flags;
  56. int auto_unmount;
  57. int blkdev;
  58. char *fsname;
  59. char *subtype;
  60. char *subtype_opt;
  61. char *mtab_opts;
  62. char *fusermount_opts;
  63. char *kernel_opts;
  64. };
  65. #define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
  66. static const struct fuse_opt fuse_mount_opts[] = {
  67. FUSE_MOUNT_OPT("allow_other", allow_other),
  68. FUSE_MOUNT_OPT("blkdev", blkdev),
  69. FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
  70. FUSE_MOUNT_OPT("fsname=%s", fsname),
  71. FUSE_MOUNT_OPT("subtype=%s", subtype),
  72. FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
  73. FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
  74. FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
  75. FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
  76. FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
  77. FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
  78. FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
  79. FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
  80. FUSE_OPT_KEY("context=", KEY_KERN_OPT),
  81. FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
  82. FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
  83. FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
  84. FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
  85. FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
  86. FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
  87. FUSE_OPT_KEY("-r", KEY_RO),
  88. FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
  89. FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
  90. FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
  91. FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
  92. FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
  93. FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
  94. FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
  95. FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
  96. FUSE_OPT_KEY("async", KEY_KERN_FLAG),
  97. FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
  98. FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
  99. FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
  100. FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
  101. FUSE_OPT_KEY("-h", KEY_HELP),
  102. FUSE_OPT_KEY("--help", KEY_HELP),
  103. FUSE_OPT_KEY("-V", KEY_VERSION),
  104. FUSE_OPT_KEY("--version", KEY_VERSION),
  105. FUSE_OPT_END
  106. };
  107. static void mount_help(void)
  108. {
  109. fprintf(stderr,
  110. " -o allow_other allow access to other users\n"
  111. " -o auto_unmount auto unmount on process termination\n"
  112. " -o default_permissions enable permission checking by kernel\n"
  113. " -o fsname=NAME set filesystem name\n"
  114. " -o subtype=NAME set filesystem type\n"
  115. " -o large_read issue large read requests (2.4 only)\n"
  116. " -o max_read=N set maximum size of read requests\n"
  117. "\n");
  118. }
  119. static void exec_fusermount(const char *argv[])
  120. {
  121. execv(FUSERMOUNT_DIR "/mergerfs-" FUSERMOUNT_PROG, (char **) argv);
  122. execvp("mergerfs-" FUSERMOUNT_PROG, (char **) argv);
  123. execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
  124. execvp(FUSERMOUNT_PROG, (char **) argv);
  125. }
  126. static void mount_version(void)
  127. {
  128. int pid = fork();
  129. if (!pid) {
  130. const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
  131. exec_fusermount(argv);
  132. _exit(1);
  133. } else if (pid != -1)
  134. waitpid(pid, NULL, 0);
  135. }
  136. struct mount_flags {
  137. const char *opt;
  138. unsigned long flag;
  139. int on;
  140. };
  141. static const struct mount_flags mount_flags[] = {
  142. {"rw", MS_RDONLY, 0},
  143. {"ro", MS_RDONLY, 1},
  144. {"suid", MS_NOSUID, 0},
  145. {"nosuid", MS_NOSUID, 1},
  146. {"dev", MS_NODEV, 0},
  147. {"nodev", MS_NODEV, 1},
  148. {"exec", MS_NOEXEC, 0},
  149. {"noexec", MS_NOEXEC, 1},
  150. {"async", MS_SYNCHRONOUS, 0},
  151. {"sync", MS_SYNCHRONOUS, 1},
  152. {"atime", MS_NOATIME, 0},
  153. {"noatime", MS_NOATIME, 1},
  154. #ifndef __NetBSD__
  155. {"dirsync", MS_DIRSYNC, 1},
  156. #endif
  157. {NULL, 0, 0}
  158. };
  159. static void set_mount_flag(const char *s, int *flags)
  160. {
  161. int i;
  162. for (i = 0; mount_flags[i].opt != NULL; i++) {
  163. const char *opt = mount_flags[i].opt;
  164. if (strcmp(opt, s) == 0) {
  165. if (mount_flags[i].on)
  166. *flags |= mount_flags[i].flag;
  167. else
  168. *flags &= ~mount_flags[i].flag;
  169. return;
  170. }
  171. }
  172. fprintf(stderr, "fuse: internal error, can't find mount flag\n");
  173. abort();
  174. }
  175. static int fuse_mount_opt_proc(void *data, const char *arg, int key,
  176. struct fuse_args *outargs)
  177. {
  178. struct mount_opts *mo = data;
  179. switch (key) {
  180. case KEY_RO:
  181. arg = "ro";
  182. /* fall through */
  183. case KEY_KERN_FLAG:
  184. set_mount_flag(arg, &mo->flags);
  185. return 0;
  186. case KEY_KERN_OPT:
  187. return fuse_opt_add_opt(&mo->kernel_opts, arg);
  188. case KEY_FUSERMOUNT_OPT:
  189. return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
  190. case KEY_SUBTYPE_OPT:
  191. return fuse_opt_add_opt(&mo->subtype_opt, arg);
  192. case KEY_MTAB_OPT:
  193. return fuse_opt_add_opt(&mo->mtab_opts, arg);
  194. case KEY_HELP:
  195. mount_help();
  196. mo->ishelp = 1;
  197. break;
  198. case KEY_VERSION:
  199. mount_version();
  200. mo->ishelp = 1;
  201. break;
  202. }
  203. return 1;
  204. }
  205. /* return value:
  206. * >= 0 => fd
  207. * -1 => error
  208. */
  209. static int receive_fd(int fd)
  210. {
  211. struct msghdr msg;
  212. struct iovec iov;
  213. char buf[1];
  214. int rv;
  215. size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
  216. struct cmsghdr *cmsg;
  217. iov.iov_base = buf;
  218. iov.iov_len = 1;
  219. memset(&msg, 0, sizeof(msg));
  220. msg.msg_name = 0;
  221. msg.msg_namelen = 0;
  222. msg.msg_iov = &iov;
  223. msg.msg_iovlen = 1;
  224. /* old BSD implementations should use msg_accrights instead of
  225. * msg_control; the interface is different. */
  226. msg.msg_control = ccmsg;
  227. msg.msg_controllen = sizeof(ccmsg);
  228. while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
  229. if (rv == -1) {
  230. perror("recvmsg");
  231. return -1;
  232. }
  233. if(!rv) {
  234. /* EOF */
  235. return -1;
  236. }
  237. cmsg = CMSG_FIRSTHDR(&msg);
  238. if (cmsg->cmsg_type != SCM_RIGHTS) {
  239. fprintf(stderr, "got control message of unknown type %d\n",
  240. cmsg->cmsg_type);
  241. return -1;
  242. }
  243. return *(int*)CMSG_DATA(cmsg);
  244. }
  245. void fuse_kern_unmount(const char *mountpoint, int fd)
  246. {
  247. int res;
  248. int pid;
  249. if (!mountpoint)
  250. return;
  251. if (fd != -1) {
  252. struct pollfd pfd;
  253. pfd.fd = fd;
  254. pfd.events = 0;
  255. res = poll(&pfd, 1, 0);
  256. /* Need to close file descriptor, otherwise synchronous umount
  257. would recurse into filesystem, and deadlock.
  258. Caller expects fuse_kern_unmount to close the fd, so close it
  259. anyway. */
  260. close(fd);
  261. /* If file poll returns POLLERR on the device file descriptor,
  262. then the filesystem is already unmounted */
  263. if (res == 1 && (pfd.revents & POLLERR))
  264. return;
  265. }
  266. if (geteuid() == 0) {
  267. fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
  268. return;
  269. }
  270. res = umount2(mountpoint, 2);
  271. if (res == 0)
  272. return;
  273. pid = fork();
  274. if(pid == -1)
  275. return;
  276. if(pid == 0) {
  277. const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
  278. "--", mountpoint, NULL };
  279. exec_fusermount(argv);
  280. _exit(1);
  281. }
  282. waitpid(pid, NULL, 0);
  283. }
  284. static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
  285. const char *opts, int quiet)
  286. {
  287. int fds[2], pid;
  288. int res;
  289. int rv;
  290. if (!mountpoint) {
  291. fprintf(stderr, "fuse: missing mountpoint parameter\n");
  292. return -1;
  293. }
  294. res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
  295. if(res == -1) {
  296. perror("fuse: socketpair() failed");
  297. return -1;
  298. }
  299. pid = fork();
  300. if(pid == -1) {
  301. perror("fuse: fork() failed");
  302. close(fds[0]);
  303. close(fds[1]);
  304. return -1;
  305. }
  306. if(pid == 0) {
  307. char env[10];
  308. const char *argv[32];
  309. int a = 0;
  310. if (quiet) {
  311. int fd = open("/dev/null", O_RDONLY);
  312. if (fd != -1) {
  313. dup2(fd, 1);
  314. dup2(fd, 2);
  315. }
  316. }
  317. argv[a++] = FUSERMOUNT_PROG;
  318. if (opts) {
  319. argv[a++] = "-o";
  320. argv[a++] = opts;
  321. }
  322. argv[a++] = "--";
  323. argv[a++] = mountpoint;
  324. argv[a++] = NULL;
  325. close(fds[1]);
  326. fcntl(fds[0], F_SETFD, 0);
  327. snprintf(env, sizeof(env), "%i", fds[0]);
  328. setenv(FUSE_COMMFD_ENV, env, 1);
  329. exec_fusermount(argv);
  330. perror("fuse: failed to exec fusermount");
  331. _exit(1);
  332. }
  333. close(fds[0]);
  334. rv = receive_fd(fds[1]);
  335. if (!mo->auto_unmount) {
  336. /* with auto_unmount option fusermount will not exit until
  337. this socket is closed */
  338. close(fds[1]);
  339. waitpid(pid, NULL, 0); /* bury zombie */
  340. }
  341. return rv;
  342. }
  343. static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
  344. const char *mnt_opts)
  345. {
  346. char tmp[128];
  347. const char *devname = "/dev/fuse";
  348. char *source = NULL;
  349. char *type = NULL;
  350. struct stat stbuf;
  351. int fd;
  352. int res;
  353. if (!mnt) {
  354. fprintf(stderr, "fuse: missing mountpoint parameter\n");
  355. return -1;
  356. }
  357. res = stat(mnt, &stbuf);
  358. if (res == -1) {
  359. fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
  360. mnt, strerror(errno));
  361. return -1;
  362. }
  363. if (mo->auto_unmount) {
  364. /* Tell the caller to fallback to fusermount because
  365. auto-unmount does not work otherwise. */
  366. return -2;
  367. }
  368. fd = open(devname, O_RDWR);
  369. if (fd == -1) {
  370. if (errno == ENODEV || errno == ENOENT)
  371. fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
  372. else
  373. fprintf(stderr, "fuse: failed to open %s: %s\n",
  374. devname, strerror(errno));
  375. return -1;
  376. }
  377. snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
  378. fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
  379. res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
  380. if (res == -1)
  381. goto out_close;
  382. source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
  383. (mo->subtype ? strlen(mo->subtype) : 0) +
  384. strlen(devname) + 32);
  385. type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
  386. if (!type || !source) {
  387. fprintf(stderr, "fuse: failed to allocate memory\n");
  388. goto out_close;
  389. }
  390. strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
  391. if (mo->subtype) {
  392. strcat(type, ".");
  393. strcat(type, mo->subtype);
  394. }
  395. strcpy(source,
  396. mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
  397. res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
  398. if (res == -1 && errno == ENODEV && mo->subtype) {
  399. /* Probably missing subtype support */
  400. strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
  401. if (mo->fsname) {
  402. if (!mo->blkdev)
  403. sprintf(source, "%s#%s", mo->subtype,
  404. mo->fsname);
  405. } else {
  406. strcpy(source, type);
  407. }
  408. res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
  409. }
  410. if (res == -1) {
  411. /*
  412. * Maybe kernel doesn't support unprivileged mounts, in this
  413. * case try falling back to fusermount
  414. */
  415. if (errno == EPERM) {
  416. res = -2;
  417. } else {
  418. int errno_save = errno;
  419. if (mo->blkdev && errno == ENODEV &&
  420. !fuse_mnt_check_fuseblk())
  421. fprintf(stderr,
  422. "fuse: 'fuseblk' support missing\n");
  423. else
  424. fprintf(stderr, "fuse: mount failed: %s\n",
  425. strerror(errno_save));
  426. }
  427. goto out_close;
  428. }
  429. #ifndef __NetBSD__
  430. #ifndef IGNORE_MTAB
  431. if (geteuid() == 0) {
  432. char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
  433. res = -1;
  434. if (!newmnt)
  435. goto out_umount;
  436. res = fuse_mnt_add_mount("fuse", source, newmnt, type,
  437. mnt_opts);
  438. free(newmnt);
  439. if (res == -1)
  440. goto out_umount;
  441. }
  442. #endif /* IGNORE_MTAB */
  443. #endif /* __NetBSD__ */
  444. free(type);
  445. free(source);
  446. return fd;
  447. out_umount:
  448. umount2(mnt, 2); /* lazy umount */
  449. out_close:
  450. free(type);
  451. free(source);
  452. close(fd);
  453. return res;
  454. }
  455. static int get_mnt_flag_opts(char **mnt_optsp, int flags)
  456. {
  457. int i;
  458. if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
  459. return -1;
  460. for (i = 0; mount_flags[i].opt != NULL; i++) {
  461. if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
  462. fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
  463. return -1;
  464. }
  465. return 0;
  466. }
  467. int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
  468. {
  469. struct mount_opts mo;
  470. int res = -1;
  471. char *mnt_opts = NULL;
  472. memset(&mo, 0, sizeof(mo));
  473. mo.flags = MS_NOSUID | MS_NODEV;
  474. if (args &&
  475. fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
  476. return -1;
  477. res = 0;
  478. if (mo.ishelp)
  479. goto out;
  480. res = -1;
  481. if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
  482. goto out;
  483. if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
  484. goto out;
  485. if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
  486. goto out;
  487. res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
  488. if (res == -2) {
  489. if (mo.fusermount_opts &&
  490. fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
  491. goto out;
  492. if (mo.subtype) {
  493. char *tmp_opts = NULL;
  494. res = -1;
  495. if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
  496. fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
  497. free(tmp_opts);
  498. goto out;
  499. }
  500. res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
  501. free(tmp_opts);
  502. if (res == -1)
  503. res = fuse_mount_fusermount(mountpoint, &mo,
  504. mnt_opts, 0);
  505. } else {
  506. res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
  507. }
  508. }
  509. out:
  510. free(mnt_opts);
  511. free(mo.fsname);
  512. free(mo.subtype);
  513. free(mo.fusermount_opts);
  514. free(mo.subtype_opt);
  515. free(mo.kernel_opts);
  516. free(mo.mtab_opts);
  517. return res;
  518. }