1N/A/*
1N/A * Fuse: Filesystem in Userspace
1N/A *
1N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
1N/A *
1N/A * This program can be distributed under the terms of the GNU LGPLv2.
1N/A * See the file COPYING.LIB
1N/A */
1N/A
1N/A#include "fuse.h"
1N/A#include "fuse_opt.h"
1N/A#include <libuvfs.h>
1N/A#include "fuse_impl.h"
1N/A
1N/A#include <libintl.h>
1N/A#include <assert.h>
1N/A#include <errno.h>
1N/A#include <fcntl.h>
1N/A#include <limits.h>
1N/A#include <stddef.h>
1N/A#include <stdio.h>
1N/A#include <stdlib.h>
1N/A#include <string.h>
1N/A#include <priv.h>
1N/A#include <sys/mntent.h>
1N/A#include <sys/mount.h>
1N/A#include <sys/poll.h>
1N/A#include <sys/socket.h>
1N/A#include <sys/un.h>
1N/A#include <sys/wait.h>
1N/A#include <unistd.h>
1N/A
1N/Aenum {
1N/A KEY_MOUNT_OPT,
1N/A};
1N/A
1N/Astatic const struct fuse_opt fuse_mount_opts[] = {
1N/A FUSE_OPT_KEY(MNTOPT_RO, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_RW, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_DEVICES, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NODEVICES, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_EXEC, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NOEXEC, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NBMAND, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NONBMAND, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_SETUID, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NOSETUID, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_SUID, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NOSUID, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_RSTCHOWN, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NORSTCHOWN, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_XATTR, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NOXATTR, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_ATIME, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NOATIME, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NOCTO, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_FORCEDIRECTIO, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NOFORCEDIRECTIO, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_DIRECTIO, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_DEFAULT_PERMS, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_ALLOW_OTHER, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY(MNTOPT_NOALLOW_OTHER, KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY("max_read=", KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY("max_write=", KEY_MOUNT_OPT),
1N/A FUSE_OPT_KEY("max_dthreads=", KEY_MOUNT_OPT),
1N/A FUSE_OPT_END
1N/A};
1N/A
1N/A#define PACKAGE_VERSION "2.7.4"
1N/A
1N/Aenum {
1N/A KEY_HELP,
1N/A KEY_VERSION,
1N/A};
1N/A
1N/Astruct helper_opts {
1N/A char *mountpoint;
1N/A};
1N/A
1N/A#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 }
1N/A
1N/Astatic const struct fuse_opt fuse_helper_opts[] = {
1N/A FUSE_OPT_KEY("-h", KEY_HELP),
1N/A FUSE_OPT_KEY("--help", KEY_HELP),
1N/A FUSE_OPT_KEY("-V", KEY_VERSION),
1N/A FUSE_OPT_KEY("--version", KEY_VERSION),
1N/A FUSE_OPT_END
1N/A};
1N/A
1N/A/*
1N/A * Non-failing strdup; see umem_alloc(3C). Note that the nofail callback is
1N/A * set in libfuse.
1N/A */
1N/A
1N/Achar *
1N/Alibfuse_strdup(const char *str)
1N/A{
1N/A int size = strlen(str) + 1;
1N/A char *rc;
1N/A
1N/A rc = umem_alloc(size, UMEM_NOFAIL);
1N/A
1N/A (void) strlcpy(rc, str, size);
1N/A
1N/A return (rc);
1N/A}
1N/A
1N/A/*
1N/A * Counterpart to libfuse_strdup().
1N/A */
1N/A
1N/Avoid
1N/Alibfuse_strfree(char *str)
1N/A{
1N/A int size = strlen(str) + 1;
1N/A
1N/A umem_free(str, size);
1N/A}
1N/A
1N/A/*
1N/A * Append an option to a buffer containing a comma-separated list of
1N/A * options. Return -1 on overflow, or 0 on success.
1N/A */
1N/A
1N/Astatic int
1N/Aappend_mount_opt(char *buffer, int bufsize, const char *opt)
1N/A{
1N/A if ((buffer[0] != '\0') && (strlcat(buffer, ",", bufsize) >= bufsize))
1N/A return (-1);
1N/A
1N/A if (strlcat(buffer, opt, bufsize) >= bufsize)
1N/A return (-1);
1N/A
1N/A return (0);
1N/A}
1N/A
1N/A/*
1N/A * Process a mount option for fuse. Returns zero if something is wrong,
1N/A * one otherwise. Called via the fuse_opt_parse framework.
1N/A */
1N/A
1N/A/*ARGSUSED*/
1N/Astatic int
1N/Afuse_mount_opt_proc(void *data, const char *arg, int key,
1N/A struct fuse_args *outargs)
1N/A{
1N/A const char *optionstr = arg;
1N/A int rc;
1N/A
1N/A if (key != KEY_MOUNT_OPT)
1N/A return (1);
1N/A
1N/A /*
1N/A * Convert "direct_io" into "forcedirectio"
1N/A */
1N/A
1N/A if (strcmp(optionstr, MNTOPT_DIRECTIO) == 0)
1N/A optionstr = MNTOPT_FORCEDIRECTIO;
1N/A
1N/A rc = append_mount_opt(data, MAX_MNTOPT_STR, optionstr);
1N/A if (rc == -1)
1N/A return (0);
1N/A
1N/A return (1);
1N/A}
1N/A
1N/A/*
1N/A * Process argument according to fuse_opt_parse() and the fuse_mount_opts.
1N/A */
1N/A
1N/Aint
1N/Afuse_mount_option_process(struct fuse_args *args, char *mount_opts)
1N/A{
1N/A return (fuse_opt_parse(args, mount_opts, fuse_mount_opts,
1N/A fuse_mount_opt_proc));
1N/A}
1N/A
1N/Astatic void
1N/Ausage(const char *progname)
1N/A{
1N/A (void) fprintf(stderr, gettext("usage: %s mountpoint [opts]\n\n"),
1N/A progname);
1N/A (void) fprintf(stderr, gettext("general options:\n"));
1N/A (void) fprintf(stderr, gettext(" -o opt,[opt...] "
1N/A "mount options\n"));
1N/A (void) fprintf(stderr, gettext(" -h --help "
1N/A "print help\n"));
1N/A (void) fprintf(stderr, gettext(" -V --version "
1N/A "print version\n"));
1N/A (void) fprintf(stderr, "\n");
1N/A}
1N/A
1N/Astatic void
1N/Ahelper_version(void)
1N/A{
1N/A (void) fprintf(stderr, gettext("FUSE library version: %s\n"),
1N/A PACKAGE_VERSION);
1N/A}
1N/A
1N/A/*
1N/A * Callback function for parsing the helper options via fuse_opt_parse().
1N/A */
1N/A
1N/Astatic int
1N/Afuse_helper_opt_proc(void *data, const char *arg, int key,
1N/A struct fuse_args *outargs)
1N/A{
1N/A struct helper_opts *hopts = data;
1N/A
1N/A switch (key) {
1N/A case KEY_HELP:
1N/A usage(outargs->argv[0]);
1N/A return (fuse_opt_add_arg(outargs, "-h"));
1N/A
1N/A case KEY_VERSION:
1N/A helper_version();
1N/A return (1);
1N/A
1N/A case FUSE_OPT_KEY_NONOPT:
1N/A if (!hopts->mountpoint) {
1N/A char mountpoint[PATH_MAX];
1N/A if (realpath(arg, mountpoint) == NULL) {
1N/A (void) fprintf(stderr,
1N/A gettext("bad mount point "));
1N/A (void) fprintf(stderr, gettext("%s: %s\n"), arg,
1N/A strerror(errno));
1N/A return (-1);
1N/A }
1N/A return (fuse_opt_add_opt(&hopts->mountpoint,
1N/A mountpoint));
1N/A } else {
1N/A (void) fprintf(stderr, gettext("invalid arg %s\n"),
1N/A arg);
1N/A return (-1);
1N/A }
1N/A
1N/A default:
1N/A return (1);
1N/A }
1N/A}
1N/A
1N/A/*
1N/A * Parse the command line according to fuse_helper_opts.
1N/A */
1N/A
1N/A/*ARGSUSED*/
1N/Aint
1N/Afuse_parse_cmdline(struct fuse_args *args, char **mountpoint,
1N/A int *multithreaded, int *foreground)
1N/A{
1N/A struct helper_opts hopts;
1N/A int rc;
1N/A
1N/A (void) memset(&hopts, 0, sizeof (hopts));
1N/A rc = fuse_opt_parse(args, &hopts, fuse_helper_opts,
1N/A fuse_helper_opt_proc);
1N/A if (rc == -1)
1N/A return (-1);
1N/A
1N/A if (mountpoint)
1N/A *mountpoint = hopts.mountpoint;
1N/A else
1N/A libfuse_strfree(hopts.mountpoint);
1N/A
1N/A return (0);
1N/A
1N/Aerr:
1N/A libfuse_strfree(hopts.mountpoint);
1N/A return (-1);
1N/A}
1N/A
1N/A/*
1N/A * Create the fuse_chan structure, containing the libuvfs_fs_t.
1N/A */
1N/A
1N/Astruct fuse_chan *
1N/Afuse_uvfs_fs_create(const char *mountp, boolean_t is_smf)
1N/A{
1N/A struct fuse_chan *fuse_chan;
1N/A uint64_t fsid;
1N/A
1N/A if (is_smf)
1N/A fsid = LIBUVFS_FSID_SVC;
1N/A else {
1N/A fsid = libuvfs_get_fsid(mountp);
1N/A if (fsid == 0)
1N/A return (0);
1N/A }
1N/A
1N/A fuse_chan = umem_zalloc(sizeof (struct fuse_chan), UMEM_NOFAIL);
1N/A fuse_chan->fuse_uvfs_fs = libuvfs_create_fs(LIBUVFS_VERSION, fsid);
1N/A
1N/A return (fuse_chan);
1N/A}
1N/A
1N/A/*
1N/A * Free a fuse_chan, releasing its underlying libuvfs_fs_t.
1N/A */
1N/A
1N/Astatic void
1N/Afuse_free_chan(struct fuse_chan *chan)
1N/A{
1N/A libuvfs_destroy_fs(chan->fuse_uvfs_fs);
1N/A umem_free(chan, sizeof (*chan));
1N/A}
1N/A
1N/A/*
1N/A * Perform another round of fuse_opt_parse, and call the mount(2) system
1N/A * call with the derived options. Returns a pointer to a fuse_chan
1N/A * structure on success, or a NULL on error.
1N/A */
1N/A
1N/Astruct fuse_chan *
1N/Afuse_mount(const char *mountp, struct fuse_args *args)
1N/A{
1N/A struct fuse_chan *fuse_chan;
1N/A char opts[BUFSIZ];
1N/A
1N/A opts[0] = '\0';
1N/A
1N/A if (fuse_mount_option_process(args, opts))
1N/A return (NULL);
1N/A
1N/A if (!priv_ineffect(PRIV_SYS_MOUNT)) {
1N/A (void) fprintf(stderr, gettext("insufficient privilege\n"));
1N/A return (NULL);
1N/A }
1N/A
1N/A if (mount(args->argv[0], mountp, MS_OPTIONSTR, "uvfs", NULL, 0,
1N/A opts, MAX_MNTOPT_STR) != 0)
1N/A return (NULL);
1N/A
1N/A fuse_chan = fuse_uvfs_fs_create(mountp, 0);
1N/A
1N/A return (fuse_chan);
1N/A}
1N/A
1N/A/*
1N/A * Unmount, and free the fuse_chan structure.
1N/A */
1N/A
1N/Avoid
1N/Afuse_unmount(const char *mountpoint, struct fuse_chan *chan)
1N/A{
1N/A fuse_free_chan(chan);
1N/A (void) umount2(mountpoint, 0);
1N/A}