add missing fuse-lite files
parent
892f763d31
commit
34ac269f49
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
/* these definitions provide source compatibility to prior versions.
|
||||
Do not include this file directly! */
|
||||
|
||||
size_t fuse_dirent_size(size_t namelen);
|
||||
|
||||
char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
|
||||
off_t off);
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#ifndef _FUSE_OPT_H_
|
||||
#define _FUSE_OPT_H_
|
||||
|
||||
/** @file
|
||||
*
|
||||
* This file defines the option parsing interface of FUSE
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Option description
|
||||
*
|
||||
* This structure describes a single option, and and action associated
|
||||
* with it, in case it matches.
|
||||
*
|
||||
* More than one such match may occur, in which case the action for
|
||||
* each match is executed.
|
||||
*
|
||||
* There are three possible actions in case of a match:
|
||||
*
|
||||
* i) An integer (int or unsigned) variable determined by 'offset' is
|
||||
* set to 'value'
|
||||
*
|
||||
* ii) The processing function is called, with 'value' as the key
|
||||
*
|
||||
* iii) An integer (any) or string (char *) variable determined by
|
||||
* 'offset' is set to the value of an option parameter
|
||||
*
|
||||
* 'offset' should normally be either set to
|
||||
*
|
||||
* - 'offsetof(struct foo, member)' actions i) and iii)
|
||||
*
|
||||
* - -1 action ii)
|
||||
*
|
||||
* The 'offsetof()' macro is defined in the <stddef.h> header.
|
||||
*
|
||||
* The template determines which options match, and also have an
|
||||
* effect on the action. Normally the action is either i) or ii), but
|
||||
* if a format is present in the template, then action iii) is
|
||||
* performed.
|
||||
*
|
||||
* The types of templates are:
|
||||
*
|
||||
* 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only
|
||||
* themselves. Invalid values are "--" and anything beginning
|
||||
* with "-o"
|
||||
*
|
||||
* 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or
|
||||
* the relevant option in a comma separated option list
|
||||
*
|
||||
* 3) "bar=", "--foo=", etc. These are variations of 1) and 2)
|
||||
* which have a parameter
|
||||
*
|
||||
* 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform
|
||||
* action iii).
|
||||
*
|
||||
* 5) "-x ", etc. Matches either "-xparam" or "-x param" as
|
||||
* two separate arguments
|
||||
*
|
||||
* 6) "-x %s", etc. Combination of 4) and 5)
|
||||
*
|
||||
* If the format is "%s", memory is allocated for the string unlike
|
||||
* with scanf().
|
||||
*/
|
||||
struct fuse_opt {
|
||||
/** Matching template and optional parameter formatting */
|
||||
const char *templ;
|
||||
|
||||
/**
|
||||
* Offset of variable within 'data' parameter of fuse_opt_parse()
|
||||
* or -1
|
||||
*/
|
||||
unsigned long offset;
|
||||
|
||||
/**
|
||||
* Value to set the variable to, or to be passed as 'key' to the
|
||||
* processing function. Ignored if template has a format
|
||||
*/
|
||||
int value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Key option. In case of a match, the processing function will be
|
||||
* called with the specified key.
|
||||
*/
|
||||
#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
|
||||
|
||||
/**
|
||||
* Last option. An array of 'struct fuse_opt' must end with a NULL
|
||||
* template value
|
||||
*/
|
||||
#define FUSE_OPT_END { .templ = NULL }
|
||||
|
||||
/**
|
||||
* Argument list
|
||||
*/
|
||||
struct fuse_args {
|
||||
/** Argument count */
|
||||
int argc;
|
||||
|
||||
/** Argument vector. NULL terminated */
|
||||
char **argv;
|
||||
|
||||
/** Is 'argv' allocated? */
|
||||
int allocated;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializer for 'struct fuse_args'
|
||||
*/
|
||||
#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
|
||||
|
||||
/**
|
||||
* Key value passed to the processing function if an option did not
|
||||
* match any template
|
||||
*/
|
||||
#define FUSE_OPT_KEY_OPT -1
|
||||
|
||||
/**
|
||||
* Key value passed to the processing function for all non-options
|
||||
*
|
||||
* Non-options are the arguments beginning with a charater other than
|
||||
* '-' or all arguments after the special '--' option
|
||||
*/
|
||||
#define FUSE_OPT_KEY_NONOPT -2
|
||||
|
||||
/**
|
||||
* Special key value for options to keep
|
||||
*
|
||||
* Argument is not passed to processing function, but behave as if the
|
||||
* processing function returned 1
|
||||
*/
|
||||
#define FUSE_OPT_KEY_KEEP -3
|
||||
|
||||
/**
|
||||
* Special key value for options to discard
|
||||
*
|
||||
* Argument is not passed to processing function, but behave as if the
|
||||
* processing function returned zero
|
||||
*/
|
||||
#define FUSE_OPT_KEY_DISCARD -4
|
||||
|
||||
/**
|
||||
* Processing function
|
||||
*
|
||||
* This function is called if
|
||||
* - option did not match any 'struct fuse_opt'
|
||||
* - argument is a non-option
|
||||
* - option did match and offset was set to -1
|
||||
*
|
||||
* The 'arg' parameter will always contain the whole argument or
|
||||
* option including the parameter if exists. A two-argument option
|
||||
* ("-x foo") is always converted to single arguemnt option of the
|
||||
* form "-xfoo" before this function is called.
|
||||
*
|
||||
* Options of the form '-ofoo' are passed to this function without the
|
||||
* '-o' prefix.
|
||||
*
|
||||
* The return value of this function determines whether this argument
|
||||
* is to be inserted into the output argument vector, or discarded.
|
||||
*
|
||||
* @param data is the user data passed to the fuse_opt_parse() function
|
||||
* @param arg is the whole argument or option
|
||||
* @param key determines why the processing function was called
|
||||
* @param outargs the current output argument list
|
||||
* @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
|
||||
*/
|
||||
typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs);
|
||||
|
||||
/**
|
||||
* Option parsing function
|
||||
*
|
||||
* If 'args' was returned from a previous call to fuse_opt_parse() or
|
||||
* it was constructed from
|
||||
*
|
||||
* A NULL 'args' is equivalent to an empty argument vector
|
||||
*
|
||||
* A NULL 'opts' is equivalent to an 'opts' array containing a single
|
||||
* end marker
|
||||
*
|
||||
* A NULL 'proc' is equivalent to a processing function always
|
||||
* returning '1'
|
||||
*
|
||||
* @param args is the input and output argument list
|
||||
* @param data is the user data
|
||||
* @param opts is the option description array
|
||||
* @param proc is the processing function
|
||||
* @return -1 on error, 0 on success
|
||||
*/
|
||||
int fuse_opt_parse(struct fuse_args *args, void *data,
|
||||
const struct fuse_opt opts[], fuse_opt_proc_t proc);
|
||||
|
||||
/**
|
||||
* Add an option to a comma separated option list
|
||||
*
|
||||
* @param opts is a pointer to an option list, may point to a NULL value
|
||||
* @param opt is the option to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_add_opt(char **opts, const char *opt);
|
||||
|
||||
/**
|
||||
* Add an argument to a NULL terminated argument vector
|
||||
*
|
||||
* @param args is the structure containing the current argument list
|
||||
* @param arg is the new argument to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
|
||||
|
||||
/**
|
||||
* Add an argument at the specified position in a NULL terminated
|
||||
* argument vector
|
||||
*
|
||||
* Adds the argument to the N-th position. This is useful for adding
|
||||
* options at the beggining of the array which must not come after the
|
||||
* special '--' option.
|
||||
*
|
||||
* @param args is the structure containing the current argument list
|
||||
* @param pos is the position at which to add the argument
|
||||
* @param arg is the new argument to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
|
||||
|
||||
/**
|
||||
* Free the contents of argument list
|
||||
*
|
||||
* The structure itself is not freed
|
||||
*
|
||||
* @param args is the structure containing the argument list
|
||||
*/
|
||||
void fuse_opt_free_args(struct fuse_args *args);
|
||||
|
||||
|
||||
/**
|
||||
* Check if an option matches
|
||||
*
|
||||
* @param opts is the option description array
|
||||
* @param opt is the option to match
|
||||
* @return 1 if a match is found, 0 if not
|
||||
*/
|
||||
int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FUSE_OPT_H_ */
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
struct fuse_session;
|
||||
struct fuse_chan;
|
||||
struct fuse_lowlevel_ops;
|
||||
struct fuse_req;
|
||||
|
||||
struct fuse_cmd {
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
struct fuse_chan *ch;
|
||||
};
|
||||
|
||||
struct fuse_chan *fuse_kern_chan_new(int fd);
|
||||
|
||||
void fuse_kern_unmount(const char *mountpoint, int fd);
|
||||
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args);
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_i.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
|
||||
size_t size)
|
||||
{
|
||||
struct fuse_chan *ch = *chp;
|
||||
int err;
|
||||
ssize_t res;
|
||||
struct fuse_session *se = fuse_chan_session(ch);
|
||||
assert(se != NULL);
|
||||
|
||||
restart:
|
||||
res = read(fuse_chan_fd(ch), buf, size);
|
||||
err = errno;
|
||||
|
||||
if (fuse_session_exited(se))
|
||||
return 0;
|
||||
if (res == -1) {
|
||||
/* ENOENT means the operation was interrupted, it's safe
|
||||
to restart */
|
||||
if (err == ENOENT)
|
||||
goto restart;
|
||||
|
||||
if (err == ENODEV) {
|
||||
fuse_session_exit(se);
|
||||
return 0;
|
||||
}
|
||||
/* Errors occuring during normal operation: EINTR (read
|
||||
interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
|
||||
umounted) */
|
||||
if (err != EINTR && err != EAGAIN)
|
||||
perror("fuse: reading device");
|
||||
return -err;
|
||||
}
|
||||
if ((size_t) res < sizeof(struct fuse_in_header)) {
|
||||
fprintf(stderr, "short read on fuse device\n");
|
||||
return -EIO;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
|
||||
size_t count)
|
||||
{
|
||||
if (iov) {
|
||||
ssize_t res = writev(fuse_chan_fd(ch), iov, count);
|
||||
int err = errno;
|
||||
|
||||
if (res == -1) {
|
||||
struct fuse_session *se = fuse_chan_session(ch);
|
||||
|
||||
assert(se != NULL);
|
||||
|
||||
/* ENOENT means the operation was interrupted */
|
||||
if (!fuse_session_exited(se) && err != ENOENT)
|
||||
perror("fuse: writing device");
|
||||
return -err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fuse_kern_chan_destroy(struct fuse_chan *ch)
|
||||
{
|
||||
close(fuse_chan_fd(ch));
|
||||
}
|
||||
|
||||
#define MIN_BUFSIZE 0x21000
|
||||
|
||||
struct fuse_chan *fuse_kern_chan_new(int fd)
|
||||
{
|
||||
struct fuse_chan_ops op = {
|
||||
.receive = fuse_kern_chan_receive,
|
||||
.send = fuse_kern_chan_send,
|
||||
.destroy = fuse_kern_chan_destroy,
|
||||
};
|
||||
size_t bufsize = getpagesize() + 0x1000;
|
||||
bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize;
|
||||
return fuse_chan_new(&op, fd, bufsize, NULL);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
int fuse_session_loop(struct fuse_session *se)
|
||||
{
|
||||
int res = 0;
|
||||
struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
|
||||
size_t bufsize = fuse_chan_bufsize(ch);
|
||||
char *buf = (char *) malloc(bufsize);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "fuse: failed to allocate read buffer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!fuse_session_exited(se)) {
|
||||
struct fuse_chan *tmpch = ch;
|
||||
res = fuse_chan_recv(&tmpch, buf, bufsize);
|
||||
if (res == -EINTR)
|
||||
continue;
|
||||
if (res <= 0)
|
||||
break;
|
||||
fuse_session_process(se, buf, res, tmpch);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
fuse_session_reset(se);
|
||||
return res < 0 ? -1 : 0;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#ifndef USE_UCLIBC
|
||||
#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL)
|
||||
#else
|
||||
static inline void fuse_mutex_init(pthread_mutex_t *mut)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
|
||||
pthread_mutex_init(mut, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRUCT_STAT_ST_ATIM
|
||||
/* Linux */
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val)
|
||||
#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val)
|
||||
#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
|
||||
/* FreeBSD */
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val)
|
||||
#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val)
|
||||
#else
|
||||
#define ST_ATIM_NSEC(stbuf) 0
|
||||
#define ST_CTIM_NSEC(stbuf) 0
|
||||
#define ST_MTIM_NSEC(stbuf) 0
|
||||
#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0)
|
||||
#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0)
|
||||
#endif
|
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "fuse_opt.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct fuse_opt_context {
|
||||
void *data;
|
||||
const struct fuse_opt *opt;
|
||||
fuse_opt_proc_t proc;
|
||||
int argctr;
|
||||
int argc;
|
||||
char **argv;
|
||||
struct fuse_args outargs;
|
||||
char *opts;
|
||||
int nonopt;
|
||||
};
|
||||
|
||||
void fuse_opt_free_args(struct fuse_args *args)
|
||||
{
|
||||
if (args) {
|
||||
if (args->argv && args->allocated) {
|
||||
int i;
|
||||
for (i = 0; i < args->argc; i++)
|
||||
free(args->argv[i]);
|
||||
free(args->argv);
|
||||
}
|
||||
args->argc = 0;
|
||||
args->argv = NULL;
|
||||
args->allocated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int alloc_failed(void)
|
||||
{
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
|
||||
{
|
||||
char **newargv;
|
||||
char *newarg;
|
||||
|
||||
assert(!args->argv || args->allocated);
|
||||
|
||||
newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
|
||||
newarg = newargv ? strdup(arg) : NULL;
|
||||
if (!newargv || !newarg)
|
||||
return alloc_failed();
|
||||
|
||||
args->argv = newargv;
|
||||
args->allocated = 1;
|
||||
args->argv[args->argc++] = newarg;
|
||||
args->argv[args->argc] = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
|
||||
{
|
||||
assert(pos <= args->argc);
|
||||
if (fuse_opt_add_arg(args, arg) == -1)
|
||||
return -1;
|
||||
|
||||
if (pos != args->argc - 1) {
|
||||
char *newarg = args->argv[args->argc - 1];
|
||||
memmove(&args->argv[pos + 1], &args->argv[pos],
|
||||
sizeof(char *) * (args->argc - pos - 1));
|
||||
args->argv[pos] = newarg;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int next_arg(struct fuse_opt_context *ctx, const char *opt)
|
||||
{
|
||||
if (ctx->argctr + 1 >= ctx->argc) {
|
||||
fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
|
||||
return -1;
|
||||
}
|
||||
ctx->argctr++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_arg(struct fuse_opt_context *ctx, const char *arg)
|
||||
{
|
||||
return fuse_opt_add_arg(&ctx->outargs, arg);
|
||||
}
|
||||
|
||||
int fuse_opt_add_opt(char **opts, const char *opt)
|
||||
{
|
||||
char *newopts;
|
||||
if (!*opts)
|
||||
newopts = strdup(opt);
|
||||
else {
|
||||
unsigned oldlen = strlen(*opts);
|
||||
newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1);
|
||||
if (newopts) {
|
||||
newopts[oldlen] = ',';
|
||||
strcpy(newopts + oldlen + 1, opt);
|
||||
}
|
||||
}
|
||||
if (!newopts)
|
||||
return alloc_failed();
|
||||
|
||||
*opts = newopts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_opt(struct fuse_opt_context *ctx, const char *opt)
|
||||
{
|
||||
return fuse_opt_add_opt(&ctx->opts, opt);
|
||||
}
|
||||
|
||||
static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
|
||||
int iso)
|
||||
{
|
||||
if (key == FUSE_OPT_KEY_DISCARD)
|
||||
return 0;
|
||||
|
||||
if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
|
||||
int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
|
||||
if (res == -1 || !res)
|
||||
return res;
|
||||
}
|
||||
if (iso)
|
||||
return add_opt(ctx, arg);
|
||||
else
|
||||
return add_arg(ctx, arg);
|
||||
}
|
||||
|
||||
static int match_template(const char *t, const char *arg, unsigned *sepp)
|
||||
{
|
||||
int arglen = strlen(arg);
|
||||
const char *sep = strchr(t, '=');
|
||||
sep = sep ? sep : strchr(t, ' ');
|
||||
if (sep && (!sep[1] || sep[1] == '%')) {
|
||||
int tlen = sep - t;
|
||||
if (sep[0] == '=')
|
||||
tlen ++;
|
||||
if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
|
||||
*sepp = sep - t;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (strcmp(t, arg) == 0) {
|
||||
*sepp = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
|
||||
const char *arg, unsigned *sepp)
|
||||
{
|
||||
for (; opt && opt->templ; opt++)
|
||||
if (match_template(opt->templ, arg, sepp))
|
||||
return opt;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
|
||||
{
|
||||
unsigned dummy;
|
||||
return find_opt(opts, opt, &dummy) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int process_opt_param(void *var, const char *format, const char *param,
|
||||
const char *arg)
|
||||
{
|
||||
assert(format[0] == '%');
|
||||
if (format[1] == 's') {
|
||||
char *copy = strdup(param);
|
||||
if (!copy)
|
||||
return alloc_failed();
|
||||
|
||||
*(char **) var = copy;
|
||||
} else {
|
||||
if (sscanf(param, format, var) != 1) {
|
||||
fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_opt(struct fuse_opt_context *ctx,
|
||||
const struct fuse_opt *opt, unsigned sep,
|
||||
const char *arg, int iso)
|
||||
{
|
||||
if (opt->offset == -1U) {
|
||||
if (call_proc(ctx, arg, opt->value, iso) == -1)
|
||||
return -1;
|
||||
} else {
|
||||
void *var = (char *)ctx->data + opt->offset;
|
||||
if (sep && opt->templ[sep + 1]) {
|
||||
const char *param = arg + sep;
|
||||
if (opt->templ[sep] == '=')
|
||||
param ++;
|
||||
if (process_opt_param(var, opt->templ + sep + 1,
|
||||
param, arg) == -1)
|
||||
return -1;
|
||||
} else
|
||||
*(int *)var = opt->value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_opt_sep_arg(struct fuse_opt_context *ctx,
|
||||
const struct fuse_opt *opt, unsigned sep,
|
||||
const char *arg, int iso)
|
||||
{
|
||||
int res;
|
||||
char *newarg;
|
||||
char *param;
|
||||
|
||||
if (next_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
|
||||
param = ctx->argv[ctx->argctr];
|
||||
newarg = malloc(sep + strlen(param) + 1);
|
||||
if (!newarg)
|
||||
return alloc_failed();
|
||||
|
||||
memcpy(newarg, arg, sep);
|
||||
strcpy(newarg + sep, param);
|
||||
res = process_opt(ctx, opt, sep, newarg, iso);
|
||||
free(newarg);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
|
||||
{
|
||||
unsigned sep;
|
||||
const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
|
||||
if (opt) {
|
||||
for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
|
||||
int res;
|
||||
if (sep && opt->templ[sep] == ' ' && !arg[sep])
|
||||
res = process_opt_sep_arg(ctx, opt, sep, arg, iso);
|
||||
else
|
||||
res = process_opt(ctx, opt, sep, arg, iso);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else
|
||||
return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
|
||||
}
|
||||
|
||||
static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
|
||||
{
|
||||
char *sep;
|
||||
|
||||
do {
|
||||
int res;
|
||||
sep = strchr(opts, ',');
|
||||
if (sep)
|
||||
*sep = '\0';
|
||||
res = process_gopt(ctx, opts, 1);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
opts = sep + 1;
|
||||
} while (sep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
|
||||
{
|
||||
int res;
|
||||
char *copy;
|
||||
const char *sep = strchr(opts, ',');
|
||||
if (!sep)
|
||||
return process_gopt(ctx, opts, 1);
|
||||
|
||||
copy = strdup(opts);
|
||||
if (!copy) {
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
res = process_real_option_group(ctx, copy);
|
||||
free(copy);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int process_one(struct fuse_opt_context *ctx, const char *arg)
|
||||
{
|
||||
if (ctx->nonopt || arg[0] != '-')
|
||||
return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
|
||||
else if (arg[1] == 'o') {
|
||||
if (arg[2])
|
||||
return process_option_group(ctx, arg + 2);
|
||||
else {
|
||||
if (next_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
|
||||
return process_option_group(ctx, ctx->argv[ctx->argctr]);
|
||||
}
|
||||
} else if (arg[1] == '-' && !arg[2]) {
|
||||
if (add_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
ctx->nonopt = ctx->outargs.argc;
|
||||
return 0;
|
||||
} else
|
||||
return process_gopt(ctx, arg, 0);
|
||||
}
|
||||
|
||||
static int opt_parse(struct fuse_opt_context *ctx)
|
||||
{
|
||||
if (ctx->argc) {
|
||||
if (add_arg(ctx, ctx->argv[0]) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
|
||||
if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
|
||||
return -1;
|
||||
|
||||
if (ctx->opts) {
|
||||
if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
|
||||
fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
|
||||
return -1;
|
||||
}
|
||||
if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) {
|
||||
free(ctx->outargs.argv[ctx->outargs.argc - 1]);
|
||||
ctx->outargs.argv[--ctx->outargs.argc] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_opt_parse(struct fuse_args *args, void *data,
|
||||
const struct fuse_opt opts[], fuse_opt_proc_t proc)
|
||||
{
|
||||
int res;
|
||||
struct fuse_opt_context ctx = {
|
||||
.data = data,
|
||||
.opt = opts,
|
||||
.proc = proc,
|
||||
};
|
||||
|
||||
if (!args || !args->argv || !args->argc)
|
||||
return 0;
|
||||
|
||||
ctx.argc = args->argc;
|
||||
ctx.argv = args->argv;
|
||||
|
||||
res = opt_parse(&ctx);
|
||||
if (res != -1) {
|
||||
struct fuse_args tmp = *args;
|
||||
*args = ctx.outargs;
|
||||
ctx.outargs = tmp;
|
||||
}
|
||||
free(ctx.opts);
|
||||
fuse_opt_free_args(&ctx.outargs);
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_lowlevel_compat.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
struct fuse_session {
|
||||
struct fuse_session_ops op;
|
||||
|
||||
void *data;
|
||||
|
||||
volatile int exited;
|
||||
|
||||
struct fuse_chan *ch;
|
||||
};
|
||||
|
||||
struct fuse_chan {
|
||||
struct fuse_chan_ops op;
|
||||
|
||||
struct fuse_session *se;
|
||||
|
||||
int fd;
|
||||
|
||||
size_t bufsize;
|
||||
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
|
||||
{
|
||||
struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
|
||||
if (se == NULL) {
|
||||
fprintf(stderr, "fuse: failed to allocate session\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(se, 0, sizeof(*se));
|
||||
se->op = *op;
|
||||
se->data = data;
|
||||
|
||||
return se;
|
||||
}
|
||||
|
||||
void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
|
||||
{
|
||||
assert(se->ch == NULL);
|
||||
assert(ch->se == NULL);
|
||||
se->ch = ch;
|
||||
ch->se = se;
|
||||
}
|
||||
|
||||
void fuse_session_remove_chan(struct fuse_chan *ch)
|
||||
{
|
||||
struct fuse_session *se = ch->se;
|
||||
if (se) {
|
||||
assert(se->ch == ch);
|
||||
se->ch = NULL;
|
||||
ch->se = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
|
||||
struct fuse_chan *ch)
|
||||
{
|
||||
assert(ch == NULL || ch == se->ch);
|
||||
if (ch == NULL)
|
||||
return se->ch;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
|
||||
struct fuse_chan *ch)
|
||||
{
|
||||
se->op.process(se->data, buf, len, ch);
|
||||
}
|
||||
|
||||
void fuse_session_destroy(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.destroy)
|
||||
se->op.destroy(se->data);
|
||||
if (se->ch != NULL)
|
||||
fuse_chan_destroy(se->ch);
|
||||
free(se);
|
||||
}
|
||||
|
||||
void fuse_session_exit(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.exit)
|
||||
se->op.exit(se->data, 1);
|
||||
se->exited = 1;
|
||||
}
|
||||
|
||||
void fuse_session_reset(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.exit)
|
||||
se->op.exit(se->data, 0);
|
||||
se->exited = 0;
|
||||
}
|
||||
|
||||
int fuse_session_exited(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.exited)
|
||||
return se->op.exited(se->data);
|
||||
else
|
||||
return se->exited;
|
||||
}
|
||||
|
||||
static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd,
|
||||
size_t bufsize, void *data)
|
||||
{
|
||||
struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
|
||||
if (ch == NULL) {
|
||||
fprintf(stderr, "fuse: failed to allocate channel\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(ch, 0, sizeof(*ch));
|
||||
ch->op = *op;
|
||||
ch->fd = fd;
|
||||
ch->bufsize = bufsize;
|
||||
ch->data = data;
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
|
||||
size_t bufsize, void *data)
|
||||
{
|
||||
return fuse_chan_new_common(op, fd, bufsize, data);
|
||||
}
|
||||
|
||||
int fuse_chan_fd(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->fd;
|
||||
}
|
||||
|
||||
size_t fuse_chan_bufsize(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->bufsize;
|
||||
}
|
||||
|
||||
void *fuse_chan_data(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->data;
|
||||
}
|
||||
|
||||
struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->se;
|
||||
}
|
||||
|
||||
int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)
|
||||
{
|
||||
struct fuse_chan *ch = *chp;
|
||||
return ch->op.receive(chp, buf, size);
|
||||
}
|
||||
|
||||
int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
|
||||
{
|
||||
return ch->op.send(ch, iov, count);
|
||||
}
|
||||
|
||||
void fuse_chan_destroy(struct fuse_chan *ch)
|
||||
{
|
||||
fuse_session_remove_chan(ch);
|
||||
if (ch->op.destroy)
|
||||
ch->op.destroy(ch);
|
||||
free(ch);
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
static struct fuse_session *fuse_instance;
|
||||
|
||||
static void exit_handler(int sig)
|
||||
{
|
||||
(void) sig;
|
||||
if (fuse_instance)
|
||||
fuse_session_exit(fuse_instance);
|
||||
}
|
||||
|
||||
static int set_one_signal_handler(int sig, void (*handler)(int))
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct sigaction old_sa;
|
||||
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = handler;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
|
||||
if (sigaction(sig, NULL, &old_sa) == -1) {
|
||||
perror("fuse: cannot get old signal handler");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (old_sa.sa_handler == SIG_DFL &&
|
||||
sigaction(sig, &sa, NULL) == -1) {
|
||||
perror("fuse: cannot set signal handler");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_set_signal_handlers(struct fuse_session *se)
|
||||
{
|
||||
if (set_one_signal_handler(SIGHUP, exit_handler) == -1 ||
|
||||
set_one_signal_handler(SIGINT, exit_handler) == -1 ||
|
||||
set_one_signal_handler(SIGTERM, exit_handler) == -1 ||
|
||||
set_one_signal_handler(SIGPIPE, SIG_IGN) == -1)
|
||||
return -1;
|
||||
|
||||
fuse_instance = se;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fuse_remove_signal_handlers(struct fuse_session *se)
|
||||
{
|
||||
if (fuse_instance != se)
|
||||
fprintf(stderr,
|
||||
"fuse: fuse_remove_signal_handlers: unknown session\n");
|
||||
else
|
||||
fuse_instance = NULL;
|
||||
|
||||
set_one_signal_handler(SIGHUP, SIG_DFL);
|
||||
set_one_signal_handler(SIGINT, SIG_DFL);
|
||||
set_one_signal_handler(SIGTERM, SIG_DFL);
|
||||
set_one_signal_handler(SIGPIPE, SIG_DFL);
|
||||
}
|
||||
|
|
@ -0,0 +1,669 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
/* This program does the mounting and unmounting of FUSE filesystems */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "mount_util.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <mntent.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/fsuid.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <grp.h>
|
||||
|
||||
#define FUSE_DEV_NEW "/dev/fuse"
|
||||
|
||||
#ifndef MS_DIRSYNC
|
||||
#define MS_DIRSYNC 128
|
||||
#endif
|
||||
|
||||
static const char *progname = "ntfs-3g-mount";
|
||||
|
||||
static int mount_max = 1000;
|
||||
|
||||
int drop_privs(void);
|
||||
int restore_privs(void);
|
||||
|
||||
static const char *get_user_name(void)
|
||||
{
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
if (pw != NULL && pw->pw_name != NULL)
|
||||
return pw->pw_name;
|
||||
else {
|
||||
fprintf(stderr, "%s: could not determine username\n", progname);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int drop_privs(void)
|
||||
{
|
||||
if (!getegid()) {
|
||||
|
||||
gid_t new_gid = getgid();
|
||||
|
||||
if (setresgid(-1, new_gid, getegid()) < 0) {
|
||||
perror("priv drop: setresgid failed");
|
||||
return -1;
|
||||
}
|
||||
if (getegid() != new_gid){
|
||||
perror("dropping group privilege failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!geteuid()) {
|
||||
|
||||
uid_t new_uid = getuid();
|
||||
|
||||
if (setresuid(-1, new_uid, geteuid()) < 0) {
|
||||
perror("priv drop: setresuid failed");
|
||||
return -1;
|
||||
}
|
||||
if (geteuid() != new_uid){
|
||||
perror("dropping user privilege failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int restore_privs(void)
|
||||
{
|
||||
if (geteuid()) {
|
||||
|
||||
uid_t ruid, euid, suid;
|
||||
|
||||
if (getresuid(&ruid, &euid, &suid) < 0) {
|
||||
perror("priv restore: getresuid failed");
|
||||
return -1;
|
||||
}
|
||||
if (setresuid(-1, suid, -1) < 0) {
|
||||
perror("priv restore: setresuid failed");
|
||||
return -1;
|
||||
}
|
||||
if (geteuid() != suid) {
|
||||
perror("restoring privilege failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (getegid()) {
|
||||
|
||||
gid_t rgid, egid, sgid;
|
||||
|
||||
if (getresgid(&rgid, &egid, &sgid) < 0) {
|
||||
perror("priv restore: getresgid failed");
|
||||
return -1;
|
||||
}
|
||||
if (setresgid(-1, sgid, -1) < 0) {
|
||||
perror("priv restore: setresgid failed");
|
||||
return -1;
|
||||
}
|
||||
if (getegid() != sgid){
|
||||
perror("restoring group privilege failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef IGNORE_MTAB
|
||||
static int add_mount(const char *source, const char *mnt, const char *type,
|
||||
const char *opts)
|
||||
{
|
||||
return fuse_mnt_add_mount(progname, source, mnt, type, opts);
|
||||
}
|
||||
|
||||
static int count_fuse_fs(void)
|
||||
{
|
||||
struct mntent *entp;
|
||||
int count = 0;
|
||||
const char *mtab = _PATH_MOUNTED;
|
||||
FILE *fp = setmntent(mtab, "r");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
while ((entp = getmntent(fp)) != NULL) {
|
||||
if (strcmp(entp->mnt_type, "fuse") == 0 ||
|
||||
strncmp(entp->mnt_type, "fuse.", 5) == 0)
|
||||
count ++;
|
||||
}
|
||||
endmntent(fp);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
#else /* IGNORE_MTAB */
|
||||
static int count_fuse_fs()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_mount(const char *source, const char *mnt, const char *type,
|
||||
const char *opts)
|
||||
{
|
||||
(void) source;
|
||||
(void) mnt;
|
||||
(void) type;
|
||||
(void) opts;
|
||||
return 0;
|
||||
}
|
||||
#endif /* IGNORE_MTAB */
|
||||
|
||||
static int begins_with(const char *s, const char *beg)
|
||||
{
|
||||
if (strncmp(s, beg, strlen(beg)) == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mount_flags {
|
||||
const char *opt;
|
||||
unsigned long flag;
|
||||
int on;
|
||||
int safe;
|
||||
};
|
||||
|
||||
static struct mount_flags mount_flags[] = {
|
||||
{"rw", MS_RDONLY, 0, 1},
|
||||
{"ro", MS_RDONLY, 1, 1},
|
||||
{"suid", MS_NOSUID, 0, 0},
|
||||
{"nosuid", MS_NOSUID, 1, 1},
|
||||
{"dev", MS_NODEV, 0, 0},
|
||||
{"nodev", MS_NODEV, 1, 1},
|
||||
{"exec", MS_NOEXEC, 0, 1},
|
||||
{"noexec", MS_NOEXEC, 1, 1},
|
||||
{"async", MS_SYNCHRONOUS, 0, 1},
|
||||
{"sync", MS_SYNCHRONOUS, 1, 1},
|
||||
{"atime", MS_NOATIME, 0, 1},
|
||||
{"noatime", MS_NOATIME, 1, 1},
|
||||
{"dirsync", MS_DIRSYNC, 1, 1},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
const char *opt = mount_flags[i].opt;
|
||||
if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
|
||||
*on = mount_flags[i].on;
|
||||
*flag = mount_flags[i].flag;
|
||||
if (!mount_flags[i].safe && getuid() != 0) {
|
||||
*flag = 0;
|
||||
fprintf(stderr, "%s: unsafe option '%s' ignored\n",
|
||||
progname, opt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_option(char **optsp, const char *opt, unsigned expand)
|
||||
{
|
||||
char *newopts;
|
||||
if (*optsp == NULL)
|
||||
newopts = strdup(opt);
|
||||
else {
|
||||
unsigned oldsize = strlen(*optsp);
|
||||
unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
|
||||
newopts = (char *) realloc(*optsp, newsize);
|
||||
if (newopts)
|
||||
sprintf(newopts + oldsize, ",%s", opt);
|
||||
}
|
||||
if (newopts == NULL) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return -1;
|
||||
}
|
||||
*optsp = newopts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
|
||||
{
|
||||
int i;
|
||||
int l;
|
||||
|
||||
if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
|
||||
return -1;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
|
||||
add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (add_option(mnt_optsp, opts, 0) == -1)
|
||||
return -1;
|
||||
/* remove comma from end of opts*/
|
||||
l = strlen(*mnt_optsp);
|
||||
if ((*mnt_optsp)[l-1] == ',')
|
||||
(*mnt_optsp)[l-1] = '\0';
|
||||
if (getuid() != 0) {
|
||||
const char *user = get_user_name();
|
||||
if (user == NULL)
|
||||
return -1;
|
||||
|
||||
if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
|
||||
return -1;
|
||||
strcat(*mnt_optsp, user);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_eq(const char *s, unsigned len, const char *opt)
|
||||
{
|
||||
if(strlen(opt) == len && strncmp(s, opt, len) == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_string_opt(const char *s, unsigned len, const char *opt,
|
||||
char **val)
|
||||
{
|
||||
unsigned opt_len = strlen(opt);
|
||||
|
||||
if (*val)
|
||||
free(*val);
|
||||
*val = (char *) malloc(len - opt_len + 1);
|
||||
if (!*val) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(*val, s + opt_len, len - opt_len);
|
||||
(*val)[len - opt_len] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_mount(const char *mnt, char **typep, mode_t rootmode,
|
||||
int fd, const char *opts, const char *dev, char **sourcep,
|
||||
char **mnt_optsp)
|
||||
{
|
||||
int res;
|
||||
int flags = MS_NOSUID | MS_NODEV;
|
||||
char *optbuf;
|
||||
char *mnt_opts = NULL;
|
||||
const char *s;
|
||||
char *d;
|
||||
char *fsname = NULL;
|
||||
char *source = NULL;
|
||||
char *type = NULL;
|
||||
int blkdev = 0;
|
||||
|
||||
optbuf = (char *) malloc(strlen(opts) + 128);
|
||||
if (!optbuf) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (s = opts, d = optbuf; *s;) {
|
||||
unsigned len;
|
||||
const char *fsname_str = "fsname=";
|
||||
for (len = 0; s[len] && s[len] != ','; len++);
|
||||
if (begins_with(s, fsname_str)) {
|
||||
if (!get_string_opt(s, len, fsname_str, &fsname))
|
||||
goto err;
|
||||
} else if (opt_eq(s, len, "blkdev")) {
|
||||
blkdev = 1;
|
||||
} else if (!begins_with(s, "fd=") &&
|
||||
!begins_with(s, "rootmode=") &&
|
||||
!begins_with(s, "user_id=") &&
|
||||
!begins_with(s, "group_id=")) {
|
||||
int on;
|
||||
int flag;
|
||||
int skip_option = 0;
|
||||
if (opt_eq(s, len, "large_read")) {
|
||||
struct utsname utsname;
|
||||
unsigned kmaj, kmin;
|
||||
res = uname(&utsname);
|
||||
if (res == 0 &&
|
||||
sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
|
||||
(kmaj > 2 || (kmaj == 2 && kmin > 4))) {
|
||||
fprintf(stderr, "%s: note: 'large_read' mount option is "
|
||||
"deprecated for %i.%i kernels\n", progname, kmaj, kmin);
|
||||
skip_option = 1;
|
||||
}
|
||||
}
|
||||
if (!skip_option) {
|
||||
if (find_mount_flag(s, len, &on, &flag)) {
|
||||
if (on)
|
||||
flags |= flag;
|
||||
else
|
||||
flags &= ~flag;
|
||||
} else {
|
||||
memcpy(d, s, len);
|
||||
d += len;
|
||||
*d++ = ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
s += len;
|
||||
if (*s)
|
||||
s++;
|
||||
}
|
||||
*d = '\0';
|
||||
res = get_mnt_opts(flags, optbuf, &mnt_opts);
|
||||
if (res == -1)
|
||||
goto err;
|
||||
|
||||
sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
|
||||
fd, rootmode, getuid(), getgid());
|
||||
|
||||
source = malloc((fsname ? strlen(fsname) : 0) + strlen(dev) + 32);
|
||||
|
||||
type = malloc(32);
|
||||
if (!type || !source) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
goto err;
|
||||
}
|
||||
|
||||
strcpy(type, blkdev ? "fuseblk" : "fuse");
|
||||
|
||||
if (fsname)
|
||||
strcpy(source, fsname);
|
||||
else
|
||||
strcpy(source, dev);
|
||||
|
||||
if (restore_privs())
|
||||
goto err;
|
||||
|
||||
res = mount(source, mnt, type, flags, optbuf);
|
||||
if (res == -1 && errno == EINVAL) {
|
||||
/* It could be an old version not supporting group_id */
|
||||
sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid());
|
||||
res = mount(source, mnt, type, flags, optbuf);
|
||||
}
|
||||
|
||||
if (drop_privs())
|
||||
goto err;
|
||||
|
||||
if (res == -1) {
|
||||
int errno_save = errno;
|
||||
if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
|
||||
fprintf(stderr, "%s: 'fuseblk' support missing\n", progname);
|
||||
else {
|
||||
fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save));
|
||||
if (errno_save == EPERM)
|
||||
fprintf(stderr, "User doesn't have privilege to mount. "
|
||||
"For more information\nplease see: "
|
||||
"http://ntfs-3g.org/support.html#unprivileged\n");
|
||||
}
|
||||
goto err;
|
||||
} else {
|
||||
*sourcep = source;
|
||||
*typep = type;
|
||||
*mnt_optsp = mnt_opts;
|
||||
}
|
||||
out:
|
||||
free(fsname);
|
||||
free(optbuf);
|
||||
return res;
|
||||
err:
|
||||
free(source);
|
||||
free(type);
|
||||
free(mnt_opts);
|
||||
res = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
|
||||
int *mountpoint_fd)
|
||||
{
|
||||
int res;
|
||||
const char *mnt = *mntp;
|
||||
const char *origmnt = mnt;
|
||||
|
||||
res = stat(mnt, stbuf);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
|
||||
progname, mnt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* No permission checking is done for root */
|
||||
if (getuid() == 0)
|
||||
return 0;
|
||||
|
||||
if (S_ISDIR(stbuf->st_mode)) {
|
||||
*currdir_fd = open(".", O_RDONLY);
|
||||
if (*currdir_fd == -1) {
|
||||
fprintf(stderr, "%s: failed to open current directory: %s\n",
|
||||
progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
res = chdir(mnt);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
|
||||
progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
mnt = *mntp = ".";
|
||||
res = lstat(mnt, stbuf);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
|
||||
progname, origmnt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
|
||||
fprintf(stderr, "%s: mountpoint %s not owned by user\n",
|
||||
progname, origmnt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = access(mnt, W_OK);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
|
||||
progname, origmnt);
|
||||
return -1;
|
||||
}
|
||||
} else if (S_ISREG(stbuf->st_mode)) {
|
||||
static char procfile[256];
|
||||
*mountpoint_fd = open(mnt, O_WRONLY);
|
||||
if (*mountpoint_fd == -1) {
|
||||
fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
res = fstat(*mountpoint_fd, stbuf);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
|
||||
progname, mnt, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISREG(stbuf->st_mode)) {
|
||||
fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n",
|
||||
progname, mnt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
|
||||
*mntp = procfile;
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"%s: mountpoint %s is not a directory or a regular file\n",
|
||||
progname, mnt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int try_open(const char *dev, char **devp)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (restore_privs())
|
||||
return -1;
|
||||
fd = open(dev, O_RDWR);
|
||||
if (drop_privs())
|
||||
return -1;
|
||||
if (fd != -1) {
|
||||
*devp = strdup(dev);
|
||||
if (*devp == NULL) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
} else if (errno == ENODEV ||
|
||||
errno == ENOENT) /* check for ENOENT too, for the udev case */
|
||||
return -2;
|
||||
else {
|
||||
fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
|
||||
strerror(errno));
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int open_fuse_device(char **devp)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = try_open(FUSE_DEV_NEW, devp);
|
||||
if (fd >= -1)
|
||||
return fd;
|
||||
|
||||
fprintf(stderr, "%s: fuse device is missing, try 'modprobe fuse' as root\n",
|
||||
progname);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int mount_fuse(const char *mnt, const char *opts)
|
||||
{
|
||||
int res;
|
||||
int fd;
|
||||
char *dev;
|
||||
struct stat stbuf;
|
||||
char *type = NULL;
|
||||
char *source = NULL;
|
||||
char *mnt_opts = NULL;
|
||||
const char *real_mnt = mnt;
|
||||
int currdir_fd = -1;
|
||||
int mountpoint_fd = -1;
|
||||
|
||||
fd = open_fuse_device(&dev);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
if (getuid() != 0 && mount_max != -1) {
|
||||
if (count_fuse_fs() >= mount_max) {
|
||||
fprintf(stderr, "%s: too many mounted FUSE filesystems (%d+)\n",
|
||||
progname, mount_max);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
|
||||
if (res != -1)
|
||||
res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, dev,
|
||||
&source, &mnt_opts);
|
||||
|
||||
if (currdir_fd != -1) {
|
||||
fchdir(currdir_fd);
|
||||
close(currdir_fd);
|
||||
}
|
||||
if (mountpoint_fd != -1)
|
||||
close(mountpoint_fd);
|
||||
|
||||
if (res == -1)
|
||||
goto err;
|
||||
|
||||
if (restore_privs())
|
||||
goto err;
|
||||
|
||||
if (geteuid() == 0) {
|
||||
|
||||
if (setgroups(0, NULL) == -1) {
|
||||
perror("priv drop: setgroups failed");
|
||||
goto err;
|
||||
}
|
||||
|
||||
res = add_mount(source, mnt, type, mnt_opts);
|
||||
if (res == -1) {
|
||||
umount2(mnt, 2); /* lazy umount */
|
||||
drop_privs();
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (drop_privs())
|
||||
goto err;
|
||||
out:
|
||||
free(source);
|
||||
free(type);
|
||||
free(mnt_opts);
|
||||
free(dev);
|
||||
|
||||
return fd;
|
||||
err:
|
||||
close(fd);
|
||||
fd = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
int fusermount(int unmount, int quiet, int lazy, const char *opts,
|
||||
const char *origmnt)
|
||||
{
|
||||
int res = -1;
|
||||
char *mnt;
|
||||
mode_t old_umask;
|
||||
|
||||
mnt = fuse_mnt_resolve_path(progname, origmnt);
|
||||
if (mnt == NULL)
|
||||
return -1;
|
||||
|
||||
old_umask = umask(033);
|
||||
|
||||
if (unmount) {
|
||||
|
||||
if (restore_privs())
|
||||
goto out;
|
||||
|
||||
if (geteuid() == 0)
|
||||
res = fuse_mnt_umount(progname, mnt, lazy);
|
||||
else {
|
||||
res = umount2(mnt, lazy ? 2 : 0);
|
||||
if (res == -1 && !quiet)
|
||||
fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
|
||||
mnt, strerror(errno));
|
||||
}
|
||||
|
||||
if (drop_privs())
|
||||
res = -1;
|
||||
|
||||
} else
|
||||
res = mount_fuse(mnt, opts);
|
||||
out:
|
||||
umask(old_umask);
|
||||
free(mnt);
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args)
|
||||
{
|
||||
struct fuse_chan *ch;
|
||||
int fd;
|
||||
|
||||
fd = fuse_kern_mount(mountpoint, args);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
|
||||
ch = fuse_kern_chan_new(fd);
|
||||
if (!ch)
|
||||
fuse_kern_unmount(mountpoint, fd);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
|
||||
{
|
||||
int fd = ch ? fuse_chan_fd(ch) : -1;
|
||||
fuse_kern_unmount(mountpoint, fd);
|
||||
fuse_chan_destroy(ch);
|
||||
}
|
||||
|
||||
int fuse_version(void)
|
||||
{
|
||||
return FUSE_VERSION;
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_opt.h"
|
||||
#include "mount_util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#ifndef MS_DIRSYNC
|
||||
#define MS_DIRSYNC 128
|
||||
#endif
|
||||
|
||||
enum {
|
||||
KEY_KERN_FLAG,
|
||||
KEY_KERN_OPT,
|
||||
KEY_FUSERMOUNT_OPT,
|
||||
KEY_SUBTYPE_OPT,
|
||||
KEY_MTAB_OPT,
|
||||
KEY_ALLOW_ROOT,
|
||||
KEY_RO,
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
};
|
||||
|
||||
struct mount_opts {
|
||||
int allow_other;
|
||||
int allow_root;
|
||||
int ishelp;
|
||||
int flags;
|
||||
int blkdev;
|
||||
char *fsname;
|
||||
char *mtab_opts;
|
||||
char *fusermount_opts;
|
||||
char *kernel_opts;
|
||||
};
|
||||
|
||||
#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
|
||||
|
||||
static const struct fuse_opt fuse_mount_opts[] = {
|
||||
FUSE_MOUNT_OPT("allow_other", allow_other),
|
||||
FUSE_MOUNT_OPT("allow_root", allow_root),
|
||||
FUSE_MOUNT_OPT("blkdev", blkdev),
|
||||
FUSE_MOUNT_OPT("fsname=%s", fsname),
|
||||
FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
|
||||
FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
|
||||
FUSE_OPT_KEY("-r", KEY_RO),
|
||||
FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("async", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
struct mount_flags {
|
||||
const char *opt;
|
||||
unsigned long flag;
|
||||
int on;
|
||||
};
|
||||
|
||||
static struct mount_flags mount_flags[] = {
|
||||
{"rw", MS_RDONLY, 0},
|
||||
{"ro", MS_RDONLY, 1},
|
||||
{"suid", MS_NOSUID, 0},
|
||||
{"nosuid", MS_NOSUID, 1},
|
||||
{"dev", MS_NODEV, 0},
|
||||
{"nodev", MS_NODEV, 1},
|
||||
{"exec", MS_NOEXEC, 0},
|
||||
{"noexec", MS_NOEXEC, 1},
|
||||
{"async", MS_SYNCHRONOUS, 0},
|
||||
{"sync", MS_SYNCHRONOUS, 1},
|
||||
{"atime", MS_NOATIME, 0},
|
||||
{"noatime", MS_NOATIME, 1},
|
||||
{"dirsync", MS_DIRSYNC, 1},
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static void set_mount_flag(const char *s, int *flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
const char *opt = mount_flags[i].opt;
|
||||
if (strcmp(opt, s) == 0) {
|
||||
if (mount_flags[i].on)
|
||||
*flags |= mount_flags[i].flag;
|
||||
else
|
||||
*flags &= ~mount_flags[i].flag;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "fuse: internal error, can't find mount flag\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
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_FLAG:
|
||||
set_mount_flag(arg, &mo->flags);
|
||||
return 0;
|
||||
|
||||
case KEY_KERN_OPT:
|
||||
return fuse_opt_add_opt(&mo->kernel_opts, arg);
|
||||
|
||||
case KEY_FUSERMOUNT_OPT:
|
||||
return fuse_opt_add_opt(&mo->fusermount_opts, arg);
|
||||
|
||||
case KEY_MTAB_OPT:
|
||||
return fuse_opt_add_opt(&mo->mtab_opts, arg);
|
||||
|
||||
case KEY_HELP:
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
|
||||
case KEY_VERSION:
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void fuse_kern_unmount(const char *mountpoint, int fd)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (!mountpoint)
|
||||
return;
|
||||
|
||||
if (fd != -1) {
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = fd;
|
||||
pfd.events = 0;
|
||||
res = poll(&pfd, 1, 0);
|
||||
/* If file poll returns POLLERR on the device file descriptor,
|
||||
then the filesystem is already unmounted */
|
||||
if (res == 1 && (pfd.revents & POLLERR))
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
fusermount(1, 0, 1, "", mountpoint);
|
||||
}
|
||||
|
||||
static int get_mnt_flag_opts(char **mnt_optsp, int flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
|
||||
return -1;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
|
||||
fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
|
||||
{
|
||||
struct mount_opts mo;
|
||||
int res = -1;
|
||||
char *mnt_opts = NULL;
|
||||
|
||||
memset(&mo, 0, sizeof(mo));
|
||||
if (getuid())
|
||||
mo.flags = MS_NOSUID | MS_NODEV;
|
||||
|
||||
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;
|
||||
}
|
||||
res = 0;
|
||||
if (mo.ishelp)
|
||||
goto out;
|
||||
|
||||
res = -1;
|
||||
if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
|
||||
goto out;
|
||||
if (!(mo.flags & MS_NODEV) && fuse_opt_add_opt(&mnt_opts, "dev") == -1)
|
||||
goto out;
|
||||
if (!(mo.flags & MS_NOSUID) && fuse_opt_add_opt(&mnt_opts, "suid") == -1)
|
||||
goto out;
|
||||
if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
|
||||
goto out;
|
||||
if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
|
||||
goto out;
|
||||
if (mo.fusermount_opts && fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) < 0)
|
||||
goto out;
|
||||
|
||||
res = fusermount(0, 0, 0, mnt_opts ? mnt_opts : "", mountpoint);
|
||||
|
||||
out:
|
||||
free(mnt_opts);
|
||||
free(mo.fsname);
|
||||
free(mo.fusermount_opts);
|
||||
free(mo.kernel_opts);
|
||||
free(mo.mtab_opts);
|
||||
return res;
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "mount_util.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <mntent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
static int mtab_needs_update(const char *mnt)
|
||||
{
|
||||
int res;
|
||||
struct stat stbuf;
|
||||
|
||||
/* If mtab is within new mount, don't touch it */
|
||||
if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
|
||||
_PATH_MOUNTED[strlen(mnt)] == '/')
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Skip mtab update if /etc/mtab:
|
||||
*
|
||||
* - doesn't exist,
|
||||
* - is a symlink,
|
||||
* - is on a read-only filesystem.
|
||||
*/
|
||||
res = lstat(_PATH_MOUNTED, &stbuf);
|
||||
if (res == -1) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
} else {
|
||||
if (S_ISLNK(stbuf.st_mode))
|
||||
return 0;
|
||||
|
||||
res = access(_PATH_MOUNTED, W_OK);
|
||||
if (res == -1 && errno == EROFS)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fuse_mnt_add_mount(const char *progname, const char *fsname,
|
||||
const char *mnt, const char *type, const char *opts)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (!mtab_needs_update(mnt))
|
||||
return 0;
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
if (res == 0) {
|
||||
char templ[] = "/tmp/fusermountXXXXXX";
|
||||
char *tmp;
|
||||
|
||||
setuid(geteuid());
|
||||
|
||||
/*
|
||||
* hide in a directory, where mount isn't able to resolve
|
||||
* fsname as a valid path
|
||||
*/
|
||||
tmp = mkdtemp(templ);
|
||||
if (!tmp) {
|
||||
fprintf(stderr, "%s: failed to create temporary directory\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
if (chdir(tmp)) {
|
||||
fprintf(stderr, "%s: failed to chdir to %s: %s\n",
|
||||
progname, tmp, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
rmdir(tmp);
|
||||
execl("/bin/mount", "/bin/mount", "-i", "-f", "-t", type, "-o", opts,
|
||||
fsname, mnt, NULL);
|
||||
fprintf(stderr, "%s: failed to execute /bin/mount: %s\n", progname,
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_mnt_umount(const char *progname, const char *mnt, int lazy)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
|
||||
if (!mtab_needs_update(mnt)) {
|
||||
res = umount2(mnt, lazy ? 2 : 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
|
||||
mnt, strerror(errno));
|
||||
return res;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (res == 0) {
|
||||
setuid(geteuid());
|
||||
execl("/bin/umount", "/bin/umount", "-i", mnt, lazy ? "-l" : NULL,
|
||||
NULL);
|
||||
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n", progname,
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (status != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *fuse_mnt_resolve_path(const char *progname, const char *orig)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char *copy;
|
||||
char *dst;
|
||||
char *end;
|
||||
char *lastcomp;
|
||||
const char *toresolv;
|
||||
|
||||
if (!orig[0]) {
|
||||
fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname, orig);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copy = strdup(orig);
|
||||
if (copy == NULL) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toresolv = copy;
|
||||
lastcomp = NULL;
|
||||
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
|
||||
if (end[0] != '/') {
|
||||
char *tmp;
|
||||
end[1] = '\0';
|
||||
tmp = strrchr(copy, '/');
|
||||
if (tmp == NULL) {
|
||||
lastcomp = copy;
|
||||
toresolv = ".";
|
||||
} else {
|
||||
lastcomp = tmp + 1;
|
||||
if (tmp == copy)
|
||||
toresolv = "/";
|
||||
}
|
||||
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
|
||||
lastcomp = NULL;
|
||||
toresolv = copy;
|
||||
}
|
||||
else if (tmp)
|
||||
tmp[0] = '\0';
|
||||
}
|
||||
if (realpath(toresolv, buf) == NULL) {
|
||||
fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
|
||||
strerror(errno));
|
||||
free(copy);
|
||||
return NULL;
|
||||
}
|
||||
if (lastcomp == NULL)
|
||||
dst = strdup(buf);
|
||||
else {
|
||||
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
|
||||
if (dst) {
|
||||
unsigned buflen = strlen(buf);
|
||||
if (buflen && buf[buflen-1] == '/')
|
||||
sprintf(dst, "%s%s", buf, lastcomp);
|
||||
else
|
||||
sprintf(dst, "%s/%s", buf, lastcomp);
|
||||
}
|
||||
}
|
||||
free(copy);
|
||||
if (dst == NULL)
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return dst;
|
||||
}
|
||||
|
||||
int fuse_mnt_check_fuseblk(void)
|
||||
{
|
||||
char buf[256];
|
||||
FILE *f = fopen("/proc/filesystems", "r");
|
||||
if (!f)
|
||||
return 1;
|
||||
|
||||
while (fgets(buf, sizeof(buf), f))
|
||||
if (strstr(buf, "fuseblk\n")) {
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
int fuse_mnt_add_mount(const char *progname, const char *fsname,
|
||||
const char *mnt, const char *type, const char *opts);
|
||||
int fuse_mnt_umount(const char *progname, const char *mnt, int lazy);
|
||||
char *fuse_mnt_resolve_path(const char *progname, const char *orig);
|
||||
int fuse_mnt_check_fuseblk(void);
|
||||
|
||||
int fusermount(int unmount, int quiet, int lazy, const char *opts,
|
||||
const char *origmnt);
|
||||
|
Loading…
Reference in New Issue