Logo Search packages:      
Sourcecode: fuse version File versions  Download package

mount_bsd.c

/*
  FUSE: Filesystem in Userspace
  Copyright (C) 2005-2006 Csaba Henk <csaba.henk@creo.hu>

  This program can be distributed under the terms of the GNU LGPLv2.
  See the file COPYING.LIB.
*/

#include "fuse_i.h"
#include "fuse_misc.h"
#include "fuse_opt.h"

#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <paths.h>

#define FUSERMOUNT_PROG       "mount_fusefs"
#define FUSE_DEV_TRUNK        "/dev/fuse"

enum {
      KEY_ALLOW_ROOT,
      KEY_RO,
      KEY_HELP,
      KEY_VERSION,
      KEY_KERN
};

struct mount_opts {
      int allow_other;
      int allow_root;
      int ishelp;
      char *kernel_opts;
};

static const struct fuse_opt fuse_mount_opts[] = {
      { "allow_other", offsetof(struct mount_opts, allow_other), 1 },
      { "allow_root", offsetof(struct mount_opts, allow_root), 1 },
      FUSE_OPT_KEY("allow_root",        KEY_ALLOW_ROOT),
      FUSE_OPT_KEY("-r",                KEY_RO),
      FUSE_OPT_KEY("-h",                KEY_HELP),
      FUSE_OPT_KEY("--help",            KEY_HELP),
      FUSE_OPT_KEY("-V",                KEY_VERSION),
      FUSE_OPT_KEY("--version",         KEY_VERSION),
      /* standard FreeBSD mount options */
      FUSE_OPT_KEY("dev",               KEY_KERN),
      FUSE_OPT_KEY("async",             KEY_KERN),
      FUSE_OPT_KEY("atime",             KEY_KERN),
      FUSE_OPT_KEY("dev",               KEY_KERN),
      FUSE_OPT_KEY("exec",              KEY_KERN),
      FUSE_OPT_KEY("suid",              KEY_KERN),
      FUSE_OPT_KEY("symfollow",         KEY_KERN),
      FUSE_OPT_KEY("rdonly",            KEY_KERN),
      FUSE_OPT_KEY("sync",              KEY_KERN),
      FUSE_OPT_KEY("union",             KEY_KERN),
      FUSE_OPT_KEY("userquota",         KEY_KERN),
      FUSE_OPT_KEY("groupquota",        KEY_KERN),
      FUSE_OPT_KEY("clusterr",          KEY_KERN),
      FUSE_OPT_KEY("clusterw",          KEY_KERN),
      FUSE_OPT_KEY("suiddir",           KEY_KERN),
      FUSE_OPT_KEY("snapshot",          KEY_KERN),
      FUSE_OPT_KEY("multilabel",        KEY_KERN),
      FUSE_OPT_KEY("acls",              KEY_KERN),
      FUSE_OPT_KEY("force",             KEY_KERN),
      FUSE_OPT_KEY("update",            KEY_KERN),
      FUSE_OPT_KEY("ro",                KEY_KERN),
      FUSE_OPT_KEY("rw",                KEY_KERN),
      FUSE_OPT_KEY("auto",              KEY_KERN),
      /* options supported under both Linux and FBSD */
      FUSE_OPT_KEY("allow_other",       KEY_KERN),
      FUSE_OPT_KEY("default_permissions", KEY_KERN),
      /* FBSD FUSE specific mount options */
      FUSE_OPT_KEY("private",           KEY_KERN),
      FUSE_OPT_KEY("neglect_shares",          KEY_KERN),
      FUSE_OPT_KEY("push_symlinks_in",    KEY_KERN),
      /* stock FBSD mountopt parsing routine lets anything be negated... */
      FUSE_OPT_KEY("nodev",             KEY_KERN),
      FUSE_OPT_KEY("noasync",           KEY_KERN),
      FUSE_OPT_KEY("noatime",           KEY_KERN),
      FUSE_OPT_KEY("nodev",             KEY_KERN),
      FUSE_OPT_KEY("noexec",            KEY_KERN),
      FUSE_OPT_KEY("nosuid",            KEY_KERN),
      FUSE_OPT_KEY("nosymfollow",       KEY_KERN),
      FUSE_OPT_KEY("nordonly",          KEY_KERN),
      FUSE_OPT_KEY("nosync",            KEY_KERN),
      FUSE_OPT_KEY("nounion",           KEY_KERN),
      FUSE_OPT_KEY("nouserquota",       KEY_KERN),
      FUSE_OPT_KEY("nogroupquota",      KEY_KERN),
      FUSE_OPT_KEY("noclusterr",        KEY_KERN),
      FUSE_OPT_KEY("noclusterw",        KEY_KERN),
      FUSE_OPT_KEY("nosuiddir",         KEY_KERN),
      FUSE_OPT_KEY("nosnapshot",        KEY_KERN),
      FUSE_OPT_KEY("nomultilabel",      KEY_KERN),
      FUSE_OPT_KEY("noacls",            KEY_KERN),
      FUSE_OPT_KEY("noforce",           KEY_KERN),
      FUSE_OPT_KEY("noupdate",          KEY_KERN),
      FUSE_OPT_KEY("noro",              KEY_KERN),
      FUSE_OPT_KEY("norw",              KEY_KERN),
      FUSE_OPT_KEY("noauto",            KEY_KERN),
      FUSE_OPT_KEY("noallow_other",     KEY_KERN),
      FUSE_OPT_KEY("nodefault_permissions", KEY_KERN),
      FUSE_OPT_KEY("noprivate",         KEY_KERN),
      FUSE_OPT_KEY("noneglect_shares",    KEY_KERN),
      FUSE_OPT_KEY("nopush_symlinks_in",  KEY_KERN),
      /*
       * Linux specific mount options, but let just the mount util
       * handle them
       */
      FUSE_OPT_KEY("fsname=",           KEY_KERN),
      FUSE_OPT_KEY("nonempty",          KEY_KERN),
      FUSE_OPT_KEY("large_read",        KEY_KERN),
      FUSE_OPT_KEY("max_read=",         KEY_KERN),
      FUSE_OPT_END
};

static void mount_help(void)
{
      fprintf(stderr,
            "    -o allow_root          allow access to root\n"
            );
      system(FUSERMOUNT_PROG " --help");
      fputc('\n', stderr);
}

static void mount_version(void)
{
      system(FUSERMOUNT_PROG " --version");
}

static int fuse_mount_opt_proc(void *data, const char *arg, int key,
                         struct fuse_args *outargs)
{
      struct mount_opts *mo = data;

      switch (key) {
      case KEY_ALLOW_ROOT:
            if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
                fuse_opt_add_arg(outargs, "-oallow_root") == -1)
                  return -1;
            return 0;

      case KEY_RO:
            arg = "ro";
            /* fall through */

      case KEY_KERN:
            return fuse_opt_add_opt(&mo->kernel_opts, arg);

      case KEY_HELP:
            mount_help();
            mo->ishelp = 1;
            break;

      case KEY_VERSION:
            mount_version();
            mo->ishelp = 1;
            break;
      }
      return 1;
}

void fuse_unmount_compat22(const char *mountpoint)
{
      char dev[128];
      char *ssc, *umount_cmd;
      FILE *sf;
      int rv;
      char *seekscript =
            /* error message is annoying in help output */
            "exec 2>/dev/null; "
            "/usr/bin/fstat " FUSE_DEV_TRUNK "* | "
            "/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; "
            "            { if ($3 == %d) print $10; }' | "
            "/usr/bin/sort | "
            "/usr/bin/uniq | "
            "/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'";

      (void) mountpoint;

      /*
       * If we don't know the fd, we have to resort to the scripted
       * solution -- iterating over the fd-s is unpractical, as we
       * don't know how many of open files we have. (This could be
       * looked up in procfs -- however, that's optional on FBSD; or
       * read out from the kmem -- however, that's bound to
       * privileges (in fact, that's what happens when we call the
       * setgid kmem fstat(1) utility).
       */
      asprintf(&ssc, seekscript, getpid());

      errno = 0;
      sf = popen(ssc, "r");
      if (! sf)
            return;

      fgets(dev, sizeof(dev), sf);
      rv = pclose(sf);
      if (rv)
            return;

      asprintf(&umount_cmd, "/sbin/umount %s", dev);
      system(umount_cmd);
}

void fuse_kern_unmount(const char *mountpoint, int fd)
{
      char *ep, *umount_cmd, dev[128];
      struct stat sbuf;

      (void)mountpoint;

      if (fstat(fd, &sbuf) == -1)
            return;

      devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);

      if (strncmp(dev, "fuse", 4))
            return;

      strtol(dev + 4, &ep, 10);
      if (*ep != '\0')
            return;

      asprintf(&umount_cmd, "/sbin/umount " _PATH_DEV "%s", dev);
      system(umount_cmd);
}

/* Check if kernel is doing init in background */
static int init_backgrounded(void)
{
      int ibg, len;

      len = sizeof(ibg);

      if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
            return 0;

      return ibg;
}


static int fuse_mount_core(const char *mountpoint, const char *opts)
{
      const char *mountprog = FUSERMOUNT_PROG;
      int fd;
      char *fdnam, *dev;
      int pid;

      fdnam = getenv("FUSE_DEV_FD");

      if (fdnam) {
            char *ep;

            fd = strtol(fdnam, &ep, 10);

            if (*ep != '\0') {
                  fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
                  return -1;
            }

            if (fd < 0)
                  return -1;

            goto mount;
      }

      dev = getenv("FUSE_DEV_NAME");

      if (! dev)
            dev = FUSE_DEV_TRUNK;

      if ((fd = open(dev, O_RDWR)) < 0) {
            perror("fuse: failed to open fuse device");
            return -1;
      }

mount:
      if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
            goto out;

      pid = fork();

      if (pid == -1) {
            perror("fuse: fork() failed");
            close(fd);
            return -1;
      }

      if (pid == 0) {
            if (! init_backgrounded()) {
                  /*
                   * If init is not backgrounded, we have to
                   * call the mount util backgrounded, to avoid
                   * deadlock.
                   */

                  pid = fork();

                  if (pid == -1) {
                        perror("fuse: fork() failed");
                        close(fd);
                        exit(1);
                  }
            }

            if (pid == 0) {
                  const char *argv[32];
                  int a = 0;

                  if (! fdnam)
                        asprintf(&fdnam, "%d", fd);

                  argv[a++] = mountprog;
                  if (opts) {
                        argv[a++] = "-o";
                        argv[a++] = opts;
                  }
                  argv[a++] = fdnam;
                  argv[a++] = mountpoint;
                  argv[a++] = NULL;
                  execvp(mountprog, (char **) argv);
                  perror("fuse: failed to exec mount program");
                  exit(1);
            }

            exit(0);
      }

      waitpid(pid, NULL, 0);

out:
      return fd;
}

int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
{
      struct mount_opts mo;
      int res = -1;

      memset(&mo, 0, sizeof(mo));
      /* mount util should not try to spawn the daemon */
      setenv("MOUNT_FUSEFS_SAFE", "1", 1);
      /* to notify the mount util it's called from lib */
      setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);

      if (args &&
          fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
            return -1;

      if (mo.allow_other && mo.allow_root) {
            fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
            goto out;
      }
      if (mo.ishelp)
            return 0;

      res = fuse_mount_core(mountpoint, mo.kernel_opts);
out:
      free(mo.kernel_opts);
      return res;
}

FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");

Generated by  Doxygen 1.6.0   Back to index