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.

391 lines
8.4 KiB

  1. /*
  2. FUSE: Filesystem in Userspace
  3. Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
  4. This program can be distributed under the terms of the GNU LGPLv2.
  5. See the file COPYING.LIB.
  6. */
  7. #include "fuse_i.h"
  8. #include "fuse_misc.h"
  9. #include "fuse_opt.h"
  10. #include <sys/stat.h>
  11. #include <sys/wait.h>
  12. #include <sys/sysctl.h>
  13. #include <sys/user.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <unistd.h>
  17. #include <stddef.h>
  18. #include <fcntl.h>
  19. #include <errno.h>
  20. #include <string.h>
  21. #include <paths.h>
  22. #include <limits.h>
  23. #define FUSERMOUNT_PROG "mount_fusefs"
  24. #define FUSE_DEV_TRUNK "/dev/fuse"
  25. enum {
  26. KEY_ALLOW_ROOT,
  27. KEY_RO,
  28. KEY_HELP,
  29. KEY_VERSION,
  30. KEY_KERN
  31. };
  32. struct mount_opts {
  33. int allow_other;
  34. int allow_root;
  35. int ishelp;
  36. char *kernel_opts;
  37. };
  38. #define FUSE_DUAL_OPT_KEY(templ, key) \
  39. FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
  40. static const struct fuse_opt fuse_mount_opts[] = {
  41. { "allow_other", offsetof(struct mount_opts, allow_other), 1 },
  42. { "allow_root", offsetof(struct mount_opts, allow_root), 1 },
  43. FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
  44. FUSE_OPT_KEY("-r", KEY_RO),
  45. FUSE_OPT_KEY("-h", KEY_HELP),
  46. FUSE_OPT_KEY("--help", KEY_HELP),
  47. FUSE_OPT_KEY("-V", KEY_VERSION),
  48. FUSE_OPT_KEY("--version", KEY_VERSION),
  49. /* standard FreeBSD mount options */
  50. FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
  51. FUSE_DUAL_OPT_KEY("async", KEY_KERN),
  52. FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
  53. FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
  54. FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
  55. FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
  56. FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
  57. FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
  58. FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
  59. FUSE_DUAL_OPT_KEY("union", KEY_KERN),
  60. FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
  61. FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
  62. FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
  63. FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
  64. FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
  65. FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
  66. FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
  67. FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
  68. FUSE_DUAL_OPT_KEY("force", KEY_KERN),
  69. FUSE_DUAL_OPT_KEY("update", KEY_KERN),
  70. FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
  71. FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
  72. FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
  73. /* options supported under both Linux and FBSD */
  74. FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
  75. FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
  76. FUSE_OPT_KEY("max_read=", KEY_KERN),
  77. FUSE_OPT_KEY("subtype=", KEY_KERN),
  78. /* FBSD FUSE specific mount options */
  79. FUSE_DUAL_OPT_KEY("private", KEY_KERN),
  80. FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
  81. FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
  82. FUSE_OPT_KEY("nosync_unmount", KEY_KERN),
  83. /* stock FBSD mountopt parsing routine lets anything be negated... */
  84. /*
  85. * Linux specific mount options, but let just the mount util
  86. * handle them
  87. */
  88. FUSE_OPT_KEY("fsname=", KEY_KERN),
  89. FUSE_OPT_KEY("nonempty", KEY_KERN),
  90. FUSE_OPT_KEY("large_read", KEY_KERN),
  91. FUSE_OPT_END
  92. };
  93. static void mount_help(void)
  94. {
  95. fprintf(stderr,
  96. " -o allow_root allow access to root\n"
  97. );
  98. system(FUSERMOUNT_PROG " --help");
  99. fputc('\n', stderr);
  100. }
  101. static void mount_version(void)
  102. {
  103. system(FUSERMOUNT_PROG " --version");
  104. }
  105. static int fuse_mount_opt_proc(void *data, const char *arg, int key,
  106. struct fuse_args *outargs)
  107. {
  108. struct mount_opts *mo = data;
  109. switch (key) {
  110. case KEY_ALLOW_ROOT:
  111. if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
  112. fuse_opt_add_arg(outargs, "-oallow_root") == -1)
  113. return -1;
  114. return 0;
  115. case KEY_RO:
  116. arg = "ro";
  117. /* fall through */
  118. case KEY_KERN:
  119. return fuse_opt_add_opt(&mo->kernel_opts, arg);
  120. case KEY_HELP:
  121. mount_help();
  122. mo->ishelp = 1;
  123. break;
  124. case KEY_VERSION:
  125. mount_version();
  126. mo->ishelp = 1;
  127. break;
  128. }
  129. return 1;
  130. }
  131. void fuse_unmount_compat22(const char *mountpoint)
  132. {
  133. char dev[128];
  134. char *ssc, *umount_cmd;
  135. FILE *sf;
  136. int rv;
  137. char seekscript[] =
  138. /* error message is annoying in help output */
  139. "exec 2>/dev/null; "
  140. "/usr/bin/fstat " FUSE_DEV_TRUNK "* | "
  141. "/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; "
  142. " { if ($3 == %d) print $10; }' | "
  143. "/usr/bin/sort | "
  144. "/usr/bin/uniq | "
  145. "/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'";
  146. (void) mountpoint;
  147. /*
  148. * If we don't know the fd, we have to resort to the scripted
  149. * solution -- iterating over the fd-s is unpractical, as we
  150. * don't know how many of open files we have. (This could be
  151. * looked up in procfs -- however, that's optional on FBSD; or
  152. * read out from the kmem -- however, that's bound to
  153. * privileges (in fact, that's what happens when we call the
  154. * setgid kmem fstat(1) utility).
  155. */
  156. if (asprintf(&ssc, seekscript, getpid()) == -1)
  157. return;
  158. errno = 0;
  159. sf = popen(ssc, "r");
  160. free(ssc);
  161. if (! sf)
  162. return;
  163. fgets(dev, sizeof(dev), sf);
  164. rv = pclose(sf);
  165. if (rv)
  166. return;
  167. if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1)
  168. return;
  169. system(umount_cmd);
  170. free(umount_cmd);
  171. }
  172. static void do_unmount(char *dev, int fd)
  173. {
  174. char device_path[SPECNAMELEN + 12];
  175. const char *argv[4];
  176. const char umount_cmd[] = "/sbin/umount";
  177. pid_t pid;
  178. snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
  179. argv[0] = umount_cmd;
  180. argv[1] = "-f";
  181. argv[2] = device_path;
  182. argv[3] = NULL;
  183. pid = fork();
  184. if (pid == -1)
  185. return;
  186. if (pid == 0) {
  187. close(fd);
  188. execvp(umount_cmd, (char **)argv);
  189. exit(1);
  190. }
  191. waitpid(pid, NULL, 0);
  192. }
  193. void fuse_kern_unmount(const char *mountpoint, int fd)
  194. {
  195. char *ep, dev[128];
  196. struct stat sbuf;
  197. (void)mountpoint;
  198. if (fstat(fd, &sbuf) == -1)
  199. goto out;
  200. devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
  201. if (strncmp(dev, "fuse", 4))
  202. goto out;
  203. strtol(dev + 4, &ep, 10);
  204. if (*ep != '\0')
  205. goto out;
  206. do_unmount(dev, fd);
  207. out:
  208. close(fd);
  209. }
  210. /* Check if kernel is doing init in background */
  211. static int init_backgrounded(void)
  212. {
  213. unsigned ibg, len;
  214. len = sizeof(ibg);
  215. if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
  216. return 0;
  217. return ibg;
  218. }
  219. static int fuse_mount_core(const char *mountpoint, const char *opts)
  220. {
  221. const char *mountprog = FUSERMOUNT_PROG;
  222. int fd;
  223. char *fdnam, *dev;
  224. pid_t pid, cpid;
  225. int status;
  226. fdnam = getenv("FUSE_DEV_FD");
  227. if (fdnam) {
  228. char *ep;
  229. fd = strtol(fdnam, &ep, 10);
  230. if (*ep != '\0') {
  231. fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
  232. return -1;
  233. }
  234. if (fd < 0)
  235. return -1;
  236. goto mount;
  237. }
  238. dev = getenv("FUSE_DEV_NAME");
  239. if (! dev)
  240. dev = (char *)FUSE_DEV_TRUNK;
  241. if ((fd = open(dev, O_RDWR)) < 0) {
  242. perror("fuse: failed to open fuse device");
  243. return -1;
  244. }
  245. mount:
  246. if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
  247. goto out;
  248. pid = fork();
  249. cpid = pid;
  250. if (pid == -1) {
  251. perror("fuse: fork() failed");
  252. close(fd);
  253. return -1;
  254. }
  255. if (pid == 0) {
  256. if (! init_backgrounded()) {
  257. /*
  258. * If init is not backgrounded, we have to
  259. * call the mount util backgrounded, to avoid
  260. * deadlock.
  261. */
  262. pid = fork();
  263. if (pid == -1) {
  264. perror("fuse: fork() failed");
  265. close(fd);
  266. exit(1);
  267. }
  268. }
  269. if (pid == 0) {
  270. const char *argv[32];
  271. int a = 0;
  272. if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
  273. perror("fuse: failed to assemble mount arguments");
  274. exit(1);
  275. }
  276. argv[a++] = mountprog;
  277. if (opts) {
  278. argv[a++] = "-o";
  279. argv[a++] = opts;
  280. }
  281. argv[a++] = fdnam;
  282. argv[a++] = mountpoint;
  283. argv[a++] = NULL;
  284. execvp(mountprog, (char **) argv);
  285. perror("fuse: failed to exec mount program");
  286. exit(1);
  287. }
  288. exit(0);
  289. }
  290. if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
  291. perror("fuse: failed to mount file system");
  292. close(fd);
  293. return -1;
  294. }
  295. out:
  296. return fd;
  297. }
  298. int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
  299. {
  300. struct mount_opts mo;
  301. int res = -1;
  302. memset(&mo, 0, sizeof(mo));
  303. /* mount util should not try to spawn the daemon */
  304. setenv("MOUNT_FUSEFS_SAFE", "1", 1);
  305. /* to notify the mount util it's called from lib */
  306. setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
  307. if (args &&
  308. fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
  309. return -1;
  310. if (mo.allow_other && mo.allow_root) {
  311. fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
  312. goto out;
  313. }
  314. if (mo.ishelp)
  315. return 0;
  316. res = fuse_mount_core(mountpoint, mo.kernel_opts);
  317. out:
  318. free(mo.kernel_opts);
  319. return res;
  320. }
  321. FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");