diff --git a/configure.ac b/configure.ac index d887a4b2..29602749 100644 --- a/configure.ac +++ b/configure.ac @@ -212,7 +212,7 @@ esac AC_MSG_CHECKING([fuse compatibility]) case "${target_os}" in -linux*) +linux*|solaris*) AC_ARG_WITH( [fuse], [AS_HELP_STRING([--with-fuse=],[Select FUSE library: internal or external @<:@default=internal@:>@])], @@ -220,7 +220,7 @@ linux*) [with_fuse="internal"] ) ;; -darwin*|netbsd*|solaris*|kfreebsd*-gnu) +darwin*|netbsd*|kfreebsd*-gnu) with_fuse="external" ;; freebsd*) diff --git a/include/fuse-lite/fuse.h b/include/fuse-lite/fuse.h index 16c4ea9c..13c82251 100644 --- a/include/fuse-lite/fuse.h +++ b/include/fuse-lite/fuse.h @@ -624,6 +624,47 @@ void fuse_fs_destroy(struct fuse_fs *fs); struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *user_data); +#ifdef __SOLARIS__ + +/** + * Filesystem module + * + * Filesystem modules are registered with the FUSE_REGISTER_MODULE() + * macro. + * + * If the "-omodules=modname:..." option is present, filesystem + * objects are created and pushed onto the stack with the 'factory' + * function. + */ +struct fuse_module { + /** + * Name of filesystem + */ + const char *name; + + /** + * Factory for creating filesystem objects + * + * The function may use and remove options from 'args' that belong + * to this module. + * + * For now the 'fs' vector always contains exactly one filesystem. + * This is the filesystem which will be below the newly created + * filesystem in the stack. + * + * @param args the command line arguments + * @param fs NULL terminated filesystem object vector + * @return the new filesystem object + */ + struct fuse_fs *(*factory)(struct fuse_args *args, struct fuse_fs *fs[]); + + struct fuse_module *next; + struct fusemod_so *so; + int ctr; +}; + +#endif /* __SOLARIS__ */ + /* ----------------------------------------------------------- * * Advanced API for event handling, don't worry about this... * * ----------------------------------------------------------- */ diff --git a/include/fuse-lite/fuse_common.h b/include/fuse-lite/fuse_common.h index 7139b9b1..69d3c973 100644 --- a/include/fuse-lite/fuse_common.h +++ b/include/fuse-lite/fuse_common.h @@ -32,6 +32,11 @@ #define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) +/* This interface uses 64 bit off_t */ +#if defined(__SOLARIS__) && !defined(__x86_64__) && (_FILE_OFFSET_BITS != 64) +#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags! +#endif + #ifdef __cplusplus extern "C" { #endif @@ -149,6 +154,41 @@ struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args); */ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch); +#ifdef __SOLARIS__ +/** + * Parse common options + * + * The following options are parsed: + * + * '-f' foreground + * '-d' '-odebug' foreground, but keep the debug option + * '-s' single threaded + * '-h' '--help' help + * '-ho' help without header + * '-ofsname=..' file system name, if not present, then set to the program + * name + * + * All parameters may be NULL + * + * @param args argument vector + * @param mountpoint the returned mountpoint, should be freed after use + * @param multithreaded set to 1 unless the '-s' option is present + * @param foreground set to 1 if one of the relevant options is present + * @return 0 on success, -1 on failure + */ +int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, + int *multithreaded, int *foreground); + +/** + * Go into the background + * + * @param foreground if true, stay in the foreground + * @return 0 on success, -1 on failure + */ +int fuse_daemonize(int foreground); + +#endif /* __SOLARIS__ */ + /** * Get the version of the library * diff --git a/include/fuse-lite/fuse_lowlevel.h b/include/fuse-lite/fuse_lowlevel.h index 0d4eb19f..f8b0bfc2 100644 --- a/include/fuse-lite/fuse_lowlevel.h +++ b/include/fuse-lite/fuse_lowlevel.h @@ -1079,6 +1079,13 @@ int fuse_req_interrupted(fuse_req_t req); * Filesystem setup * * ----------------------------------------------------------- */ +#ifdef __SOLARIS__ + +/* Deprecated, don't use */ +int fuse_lowlevel_is_lib_option(const char *opt); + +#endif /* __SOLARIS__ */ + /** * Create a low level session * diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index c02e4a32..d55c9df5 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -6,6 +6,11 @@ See the file COPYING.LIB */ +#ifdef __SOLARIS__ +/* For pthread_rwlock_t */ +#define _GNU_SOURCE +#endif /* __SOLARIS__ */ + #include "config.h" #include "fuse_i.h" #include "fuse_lowlevel.h" @@ -28,6 +33,10 @@ #include #include +#ifdef __SOLARIS__ +#define FUSE_MAX_PATH 4096 +#endif /* __SOLARIS__ */ + #define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1 #define FUSE_UNKNOWN_INO 0xffffffff @@ -54,13 +63,27 @@ struct fuse_config { int intr; int intr_signal; int help; +#ifdef __SOLARIS__ + int auto_cache; + char *modules; +#endif /* __SOLARIS__ */ }; struct fuse_fs { struct fuse_operations op; void *user_data; +#ifdef __SOLARIS__ + struct fuse_module *m; +#endif /* __SOLARIS__ */ }; +#ifdef __SOLARIS__ +struct fusemod_so { + void *handle; + int ctr; +}; +#endif /* __SOLARIS__ */ + struct fuse { struct fuse_session *se; struct node **name_table; @@ -98,6 +121,12 @@ struct node { uint64_t nlookup; int open_count; int is_hidden; +#ifdef __SOLARIS__ + struct timespec stat_updated; + struct timespec mtime; + off_t size; + int cache_valid; +#endif /* __SOLARIS__ */ struct lock *locks; }; @@ -125,6 +154,107 @@ static pthread_key_t fuse_context_key; static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER; static int fuse_context_ref; +#ifdef __SOLARIS__ + +static struct fusemod_so *fuse_current_so; +static struct fuse_module *fuse_modules; + +static int fuse_load_so_name(const char *soname) +{ + struct fusemod_so *so; + + so = calloc(1, sizeof(struct fusemod_so)); + if (!so) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + + fuse_current_so = so; + so->handle = dlopen(soname, RTLD_NOW); + fuse_current_so = NULL; + if (!so->handle) { + fprintf(stderr, "fuse: %s\n", dlerror()); + goto err; + } + if (!so->ctr) { + fprintf(stderr, "fuse: %s did not register any modules", soname); + goto err; + } + return 0; + + err: + if (so->handle) + dlclose(so->handle); + free(so); + return -1; +} + +static int fuse_load_so_module(const char *module) +{ + int res; + char *soname = malloc(strlen(module) + 64); + if (!soname) { + fprintf(stderr, "fuse: memory allocation failed\n"); + return -1; + } + sprintf(soname, "libfusemod_%s.so", module); + res = fuse_load_so_name(soname); + free(soname); + return res; +} + +static struct fuse_module *fuse_find_module(const char *module) +{ + struct fuse_module *m; + for (m = fuse_modules; m; m = m->next) { + if (strcmp(module, m->name) == 0) { + m->ctr++; + break; + } + } + return m; +} + +static struct fuse_module *fuse_get_module(const char *module) +{ + struct fuse_module *m; + + pthread_mutex_lock(&fuse_context_lock); + m = fuse_find_module(module); + if (!m) { + int err = fuse_load_so_module(module); + if (!err) + m = fuse_find_module(module); + } + pthread_mutex_unlock(&fuse_context_lock); + return m; +} + +static void fuse_put_module(struct fuse_module *m) +{ + pthread_mutex_lock(&fuse_context_lock); + assert(m->ctr > 0); + m->ctr--; + if (!m->ctr && m->so) { + struct fusemod_so *so = m->so; + assert(so->ctr > 0); + so->ctr--; + if (!so->ctr) { + struct fuse_module **mp; + for (mp = &fuse_modules; *mp;) { + if ((*mp)->so == so) + *mp = (*mp)->next; + else + mp = &(*mp)->next; + } + dlclose(so->handle); + free(so); + } + } + pthread_mutex_unlock(&fuse_context_lock); +} +#endif /* __SOLARIS__ */ + static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid) { size_t hash = nodeid % f->id_table_size; @@ -297,10 +427,15 @@ static struct node *find_node(struct fuse *f, fuse_ino_t parent, return node; } +#ifndef __SOLARIS__ static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name) +#else /* __SOLARIS__ */ +static char *add_name(char *buf, char *s, const char *name) +#endif /* __SOLARIS__ */ { size_t len = strlen(name); +#ifndef __SOLARIS__ if (s - len <= *buf) { unsigned pathlen = *bufsize - (s - *buf); unsigned newbufsize = *bufsize; @@ -323,6 +458,13 @@ static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name) *bufsize = newbufsize; } s -= len; +#else /* ! __SOLARIS__ */ + s -= len; + if (s <= buf) { + fprintf(stderr, "fuse: path too long: ...%s\n", s + len); + return NULL; + } +#endif /* __SOLARIS__ */ strncpy(s, name, len); s--; *s = '/'; @@ -332,6 +474,42 @@ static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name) static char *get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name) { +#ifdef __SOLARIS__ + char buf[FUSE_MAX_PATH]; + char *s = buf + FUSE_MAX_PATH - 1; + struct node *node; + + *s = '\0'; + + if (name != NULL) { + s = add_name(buf, s, name); + if (s == NULL) + return NULL; + } + + pthread_mutex_lock(&f->lock); + for (node = get_node(f, nodeid); node && node->nodeid != FUSE_ROOT_ID; + node = node->parent) { + if (node->name == NULL) { + s = NULL; + break; + } + + s = add_name(buf, s, node->name); + if (s == NULL) + break; + } + pthread_mutex_unlock(&f->lock); + + if (node == NULL || s == NULL) + return NULL; + else if (*s == '\0') + return strdup("/"); + else + return strdup(s); + +#else /* __SOLARIS__ */ + unsigned bufsize = 256; char *buf; char *s; @@ -376,6 +554,7 @@ static char *get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name) out_free: free(buf); return NULL; +#endif /* __SOLARIS__ */ } static char *get_path(struct fuse *f, fuse_ino_t nodeid) @@ -930,6 +1109,44 @@ static int hide_node(struct fuse *f, const char *oldpath, return err; } +#ifdef __SOLARIS__ + +static int mtime_eq(const struct stat *stbuf, const struct timespec *ts) +{ + return stbuf->st_mtime == ts->tv_sec && ST_MTIM_NSEC(stbuf) == ts->tv_nsec; +} + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC CLOCK_REALTIME +#endif + +static void curr_time(struct timespec *now) +{ + static clockid_t clockid = CLOCK_MONOTONIC; + int res = clock_gettime(clockid, now); + if (res == -1 && errno == EINVAL) { + clockid = CLOCK_REALTIME; + res = clock_gettime(clockid, now); + } + if (res == -1) { + perror("fuse: clock_gettime"); + abort(); + } +} + +static void update_stat(struct node *node, const struct stat *stbuf) +{ + if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) || + stbuf->st_size != node->size)) + node->cache_valid = 0; + node->mtime.tv_sec = stbuf->st_mtime; + node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf); + node->size = stbuf->st_size; + curr_time(&node->stat_updated); +} + +#endif /* __SOLARIS__ */ + static int lookup_path(struct fuse *f, fuse_ino_t nodeid, const char *name, const char *path, struct fuse_entry_param *e, struct fuse_file_info *fi) @@ -952,6 +1169,13 @@ static int lookup_path(struct fuse *f, fuse_ino_t nodeid, e->generation = node->generation; e->entry_timeout = f->conf.entry_timeout; e->attr_timeout = f->conf.attr_timeout; +#ifdef __SOLARIS__ + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(node, &e->attr); + pthread_mutex_unlock(&f->lock); + } +#endif /* __SOLARIS__ */ set_stat(f, e->ino, &e->attr); if (f->conf.debug) fprintf(stderr, " NODEID: %lu\n", (unsigned long) e->ino); @@ -1028,7 +1252,11 @@ static struct fuse *req_fuse_prepare(fuse_req_t req) return c->ctx.fuse; } +#ifndef __SOLARIS__ static void reply_err(fuse_req_t req, int err) +#else /* __SOLARIS__ */ +static inline void reply_err(fuse_req_t req, int err) +#endif /* __SOLARIS__ */ { /* fuse_reply_err() uses non-negated errno values */ fuse_reply_err(req, -err); @@ -1067,6 +1295,10 @@ void fuse_fs_destroy(struct fuse_fs *fs) fuse_get_context()->private_data = fs->user_data; if (fs->op.destroy) fs->op.destroy(fs->user_data); +#ifdef __SOLARIS__ + if (fs->m) + fuse_put_module(fs->m); +#endif /* __SOLARIS__ */ free(fs); } @@ -1143,6 +1375,13 @@ static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, } pthread_rwlock_unlock(&f->tree_lock); if (!err) { +#ifdef __SOLARIS__ + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(get_node(f, ino), &buf); + pthread_mutex_unlock(&f->lock); + } +#endif /* __SOLARIS__ */ set_stat(f, ino, &buf); fuse_reply_attr(req, &buf, f->conf.attr_timeout); } else @@ -1227,6 +1466,13 @@ static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, } pthread_rwlock_unlock(&f->tree_lock); if (!err) { +#ifdef __SOLARIS__ + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(get_node(f, ino), &buf); + pthread_mutex_unlock(&f->lock); + } +#endif /* __SOLARIS__ */ set_stat(f, ino, &buf); fuse_reply_attr(req, &buf, f->conf.attr_timeout); } else @@ -1571,6 +1817,47 @@ static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent, pthread_rwlock_unlock(&f->tree_lock); } +#ifdef __SOLARIS__ + +static double diff_timespec(const struct timespec *t1, + const struct timespec *t2) +{ + return (t1->tv_sec - t2->tv_sec) + + ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0; +} + +static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path, + struct fuse_file_info *fi) +{ + struct node *node; + + pthread_mutex_lock(&f->lock); + node = get_node(f, ino); + if (node->cache_valid) { + struct timespec now; + + curr_time(&now); + if (diff_timespec(&now, &node->stat_updated) > f->conf.ac_attr_timeout) { + struct stat stbuf; + int err; + pthread_mutex_unlock(&f->lock); + err = fuse_fs_fgetattr(f->fs, path, &stbuf, fi); + pthread_mutex_lock(&f->lock); + if (!err) + update_stat(node, &stbuf); + else + node->cache_valid = 0; + } + } + if (node->cache_valid) + fi->keep_cache = 1; + + node->cache_valid = 1; + pthread_mutex_unlock(&f->lock); +} + +#endif /* __SOLARIS__ */ + static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { @@ -1590,6 +1877,11 @@ static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino, fi->direct_io = 1; if (f->conf.kernel_cache) fi->keep_cache = 1; +#ifdef __SOLARIS__ + + if (f->conf.auto_cache) + open_auto_cache(f, ino, path, fi); +#endif /* __SOLARIS__ */ } fuse_finish_interrupt(f, req, &d); } @@ -1775,7 +2067,9 @@ static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino, } } else { reply_err(req, err); +#ifndef __SOLARIS__ pthread_mutex_destroy(&dh->lock); +#endif /* ! __SOLARIS__ */ free(dh); } free(path); @@ -1789,12 +2083,17 @@ static int extend_contents(struct fuse_dh *dh, unsigned minsize) unsigned newsize = dh->size; if (!newsize) newsize = 1024; +#ifndef __SOLARIS__ while (newsize < minsize) { if (newsize >= 0x80000000) newsize = 0xffffffff; else newsize *= 2; } +#else /* __SOLARIS__ */ + while (newsize < minsize) + newsize *= 2; +#endif /* __SOLARIS__ */ newptr = (char *) realloc(dh->contents, newsize); if (!newptr) { @@ -2501,6 +2800,10 @@ static const struct fuse_opt fuse_lib_opts[] = { FUSE_LIB_OPT("readdir_ino", readdir_ino, 1), FUSE_LIB_OPT("direct_io", direct_io, 1), FUSE_LIB_OPT("kernel_cache", kernel_cache, 1), +#ifdef __SOLARIS__ + FUSE_LIB_OPT("auto_cache", auto_cache, 1), + FUSE_LIB_OPT("noauto_cache", auto_cache, 0), +#endif /* __SOLARIS__ */ FUSE_LIB_OPT("umask=", set_mode, 1), FUSE_LIB_OPT("umask=%o", umask, 0), FUSE_LIB_OPT("uid=", set_uid, 1), @@ -2514,6 +2817,9 @@ static const struct fuse_opt fuse_lib_opts[] = { FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0), FUSE_LIB_OPT("intr", intr, 1), FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0), +#ifdef __SOLARIS__ + FUSE_LIB_OPT("modules=%s", modules, 0), +#endif /* __SOLARIS__ */ FUSE_OPT_END }; @@ -2525,6 +2831,9 @@ static void fuse_lib_help(void) " -o readdir_ino try to fill in d_ino in readdir\n" " -o direct_io use direct I/O\n" " -o kernel_cache cache files in kernel\n" +#ifdef __SOLARIS__ +" -o [no]auto_cache enable caching based on modification times\n" +#endif /* __SOLARIS__ */ " -o umask=M set file permissions (octal)\n" " -o uid=N set file owner\n" " -o gid=N set file group\n" @@ -2534,9 +2843,42 @@ static void fuse_lib_help(void) " -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n" " -o intr allow requests to be interrupted\n" " -o intr_signal=NUM signal to send on interrupt (%i)\n" +#ifdef __SOLARIS__ +" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n" +#endif /* __SOLARIS__ */ "\n", FUSE_DEFAULT_INTR_SIGNAL); } +#ifdef __SOLARIS__ + +static void fuse_lib_help_modules(void) +{ + struct fuse_module *m; + fprintf(stderr, "\nModule options:\n"); + pthread_mutex_lock(&fuse_context_lock); + for (m = fuse_modules; m; m = m->next) { + struct fuse_fs *fs = NULL; + struct fuse_fs *newfs; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + if (fuse_opt_add_arg(&args, "") != -1 && + fuse_opt_add_arg(&args, "-h") != -1) { + fprintf(stderr, "\n[%s]\n", m->name); + newfs = m->factory(&args, &fs); + assert(newfs == NULL); + } + fuse_opt_free_args(&args); + } + pthread_mutex_unlock(&fuse_context_lock); +} + +int fuse_is_lib_option(const char *opt) +{ + return fuse_lowlevel_is_lib_option(opt) || + fuse_opt_match(fuse_lib_opts, opt); +} + +#endif /* __SOLARIS__ */ + static int fuse_lib_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { @@ -2585,6 +2927,32 @@ static void fuse_restore_intr_signal(int signum) sigaction(signum, &sa, NULL); } +#ifdef __SOLARIS__ + +static int fuse_push_module(struct fuse *f, const char *module, + struct fuse_args *args) +{ + struct fuse_fs *newfs; + struct fuse_module *m = fuse_get_module(module); + struct fuse_fs *fs[2]; + + fs[0] = f->fs; + fs[1] = NULL; + if (!m) + return -1; + + newfs = m->factory(args, fs); + if (!newfs) { + fuse_put_module(m); + return -1; + } + newfs->m = m; + f->fs = newfs; + return 0; +} + +#endif /* __SOLARIS__ */ + struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *user_data) { @@ -2646,6 +3014,22 @@ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, if (fuse_opt_parse(args, &f->conf, fuse_lib_opts, fuse_lib_opt_proc) == -1) goto out_free_fs; +#ifdef __SOLARIS__ + if (f->conf.modules) { + char *module; + char *next; + + for (module = f->conf.modules; module; module = next) { + char *p; + for (p = module; *p && *p != ':'; p++); + next = *p ? p + 1 : NULL; + *p = '\0'; + if (module[0] && fuse_push_module(f, module, args) == -1) + goto out_free_fs; + } + } +#endif /* __SOLARIS__ */ + if (!f->conf.ac_attr_timeout_set) f->conf.ac_attr_timeout = f->conf.attr_timeout; @@ -2658,14 +3042,21 @@ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, #endif f->se = fuse_lowlevel_new(args, &llop, sizeof(llop), f); + if (f->se == NULL) { +#ifdef __SOLARIS__ + if (f->conf.help) + fuse_lib_help_modules(); +#endif /* __SOLARIS__ */ goto out_free_fs; } fuse_session_add_chan(f->se, ch); +#ifndef __SOLARIS__ if (f->conf.debug) fprintf(stderr, "utime_omit_ok: %i\n", f->utime_omit_ok); +#endif /* ! __SOLARIS__ */ f->ctr = 0; f->generation = 0; /* FIXME: Dynamic hash table */ @@ -2728,6 +3119,9 @@ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, called on the filesystem without init being called first */ fs->op.destroy = NULL; fuse_fs_destroy(f->fs); +#ifdef __SOLARIS__ + free(f->conf.modules); +#endif /* __SOLARIS__ */ out_free: free(f); out_delete_context_key: @@ -2777,7 +3171,9 @@ void fuse_destroy(struct fuse *f) pthread_mutex_destroy(&f->lock); pthread_rwlock_destroy(&f->tree_lock); fuse_session_destroy(f->se); +#ifdef __SOLARIS__ + free(f->conf.modules); +#endif /* __SOLARIS__ */ free(f); fuse_delete_context_key(); } - diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c index 48a4262e..880cdf01 100644 --- a/libfuse-lite/fuse_lowlevel.c +++ b/libfuse-lite/fuse_lowlevel.c @@ -546,7 +546,7 @@ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) name = (const char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE; if (req->f->op.mknod) - req->f->op.mknod(req, nodeid, name, arg->mode, arg->rdev); + req->f->op.mknod(req, nodeid, name, arg->mode, arg->rdev); else fuse_reply_err(req, ENOSYS); } @@ -621,7 +621,7 @@ static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { - const struct fuse_create_in *arg = (const struct fuse_create_in *) inarg; + const struct fuse_create_in *arg = (const struct fuse_create_in *) inarg; if (req->f->op.create) { struct fuse_file_info fi; @@ -1130,18 +1130,6 @@ const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) return &req->ctx; } -/* - * The size of fuse_ctx got extended, so need to be careful about - * incompatibility (i.e. a new binary cannot work with an old - * library). - */ -const struct fuse_ctx *fuse_req_ctx_compat24(fuse_req_t req); -const struct fuse_ctx *fuse_req_ctx_compat24(fuse_req_t req) -{ - return fuse_req_ctx(req); -} -//FUSE_SYMVER(".symver fuse_req_ctx_compat24,fuse_req_ctx@FUSE_2.4"); - void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, void *data) { @@ -1327,6 +1315,15 @@ static int fuse_ll_opt_proc(void *data, const char *arg, int key, return -1; } +#ifdef __SOLARIS__ + +int fuse_lowlevel_is_lib_option(const char *opt) +{ + return fuse_opt_match(fuse_ll_opts, opt); +} + +#endif /* __SOLARIS__ */ + static void fuse_ll_destroy(void *data) { struct fuse_ll *f = (struct fuse_ll *) data; @@ -1387,4 +1384,3 @@ struct fuse_session *fuse_lowlevel_new(struct fuse_args *args, out: return NULL; } - diff --git a/libfuse-lite/fuse_session.c b/libfuse-lite/fuse_session.c index c4e544d1..3773303a 100644 --- a/libfuse-lite/fuse_session.c +++ b/libfuse-lite/fuse_session.c @@ -165,9 +165,20 @@ struct fuse_session *fuse_chan_session(struct fuse_chan *ch) 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); } +#ifdef __SOLARIS__ +int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size) +{ + int res; + + res = fuse_chan_recv(&ch, buf, size); + return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0; +} +#endif /* __SOLARIS__ */ + int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count) { return ch->op.send(ch, iov, count); @@ -180,4 +191,3 @@ void fuse_chan_destroy(struct fuse_chan *ch) ch->op.destroy(ch); free(ch); } - diff --git a/libfuse-lite/fusermount.c b/libfuse-lite/fusermount.c index 6537e444..4e724dbc 100644 --- a/libfuse-lite/fusermount.c +++ b/libfuse-lite/fusermount.c @@ -19,14 +19,20 @@ #include #include #include + +#ifdef __SOLARIS__ +#include +#else /* __SOLARIS__ */ +#include #include +#include +#endif /* __SOLARIS__ */ + #include #include #include -#include #include #include -#include #define FUSE_DEV_NEW "/dev/fuse" @@ -41,6 +47,32 @@ static int mount_max = 1000; int drop_privs(void); int restore_privs(void); +#ifdef __SOLARIS__ + +/* + * fusermount is not implemented in fuse-lite for Solaris, + * only the minimal functions are provided. + */ + +/* + * Solaris doesn't have setfsuid/setfsgid. + * This doesn't really matter anyway as this program shouldn't be made + * suid on Solaris. It should instead be used via a profile with the + * sys_mount privilege. + */ + +int drop_privs(void) +{ + return (0); +} + +int restore_privs(void) +{ + return (0); +} + +#else /* __SOLARIS__ */ + static const char *get_user_name(void) { struct passwd *pw = getpwuid(getuid()); @@ -668,3 +700,5 @@ out: free(mnt); return res; } + +#endif /* __SOLARIS__ */ diff --git a/libfuse-lite/helper.c b/libfuse-lite/helper.c index 6c640aec..ebd7d77e 100644 --- a/libfuse-lite/helper.c +++ b/libfuse-lite/helper.c @@ -15,6 +15,18 @@ struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args) struct fuse_chan *ch; int fd; +#ifdef __SOLARIS__ + /* + * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos + * would ensue. + */ + do { + fd = open("/dev/null", O_RDWR); + if (fd > 2) + close(fd); + } while (fd >= 0 && fd <= 2); +#endif /* __SOLARIS__ */ + fd = fuse_kern_mount(mountpoint, args); if (fd == -1) return NULL; @@ -38,3 +50,12 @@ int fuse_version(void) return FUSE_VERSION; } +#ifdef __SOLARIS__ +#undef fuse_main +int fuse_main(void); +int fuse_main(void) +{ + fprintf(stderr, "fuse_main(): This function does not exist\n"); + return -1; +} +#endif /* __SOLARIS__ */ diff --git a/libfuse-lite/mount.c b/libfuse-lite/mount.c index 07f5ead2..70454f4e 100644 --- a/libfuse-lite/mount.c +++ b/libfuse-lite/mount.c @@ -12,6 +12,7 @@ #include "mount_util.h" #include +#include #include #include #include @@ -23,6 +24,21 @@ #include #include +#ifdef __SOLARIS__ + +#define FUSERMOUNT_PROG "fusermount" +#define FUSE_COMMFD_ENV "_FUSE_COMMFD" + +#ifndef FUSERMOUNT_DIR +#define FUSERMOUNT_DIR "/usr" +#endif /* FUSERMOUNT_DIR */ + +#ifndef HAVE_FORK +#define fork() vfork() +#endif + +#endif /* __SOLARIS__ */ + #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 #endif @@ -44,8 +60,16 @@ struct mount_opts { int allow_root; int ishelp; int flags; +#ifdef __SOLARIS__ + int nonempty; int blkdev; char *fsname; + char *subtype; + char *subtype_opt; +#else + int blkdev; + char *fsname; +#endif char *mtab_opts; char *fusermount_opts; char *kernel_opts; @@ -54,6 +78,42 @@ struct mount_opts { #define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 } static const struct fuse_opt fuse_mount_opts[] = { +#ifdef __SOLARIS__ + FUSE_MOUNT_OPT("allow_other", allow_other), + FUSE_MOUNT_OPT("allow_root", allow_root), + FUSE_MOUNT_OPT("nonempty", nonempty), + FUSE_MOUNT_OPT("blkdev", blkdev), + FUSE_MOUNT_OPT("fsname=%s", fsname), + FUSE_MOUNT_OPT("subtype=%s", subtype), + FUSE_OPT_KEY("allow_other", KEY_KERN_OPT), + FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT), + FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT), + FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_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("-g", KEY_KERN_FLAG), + FUSE_OPT_KEY("-m", KEY_KERN_FLAG), + FUSE_OPT_KEY("-O", KEY_KERN_FLAG), + FUSE_OPT_KEY("setuid", KEY_KERN_OPT), + FUSE_OPT_KEY("nosetuid", KEY_KERN_OPT), + FUSE_OPT_KEY("devices", KEY_KERN_OPT), + FUSE_OPT_KEY("nodevices", KEY_KERN_OPT), + FUSE_OPT_KEY("exec", KEY_KERN_OPT), + FUSE_OPT_KEY("noexec", KEY_KERN_OPT), + FUSE_OPT_KEY("nbmand", KEY_KERN_OPT), + FUSE_OPT_KEY("nonbmand", KEY_KERN_OPT), +#else /* __SOLARIS__ */ FUSE_MOUNT_OPT("allow_other", allow_other), FUSE_MOUNT_OPT("allow_root", allow_root), FUSE_MOUNT_OPT("blkdev", blkdev), @@ -83,6 +143,7 @@ static const struct fuse_opt fuse_mount_opts[] = { FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG), FUSE_OPT_KEY("atime", KEY_KERN_FLAG), FUSE_OPT_KEY("noatime", KEY_KERN_FLAG), +#endif /* __SOLARIS__ */ FUSE_OPT_KEY("-h", KEY_HELP), FUSE_OPT_KEY("--help", KEY_HELP), FUSE_OPT_KEY("-V", KEY_VERSION), @@ -90,6 +151,42 @@ static const struct fuse_opt fuse_mount_opts[] = { FUSE_OPT_END }; +#ifdef __SOLARIS__ + +static void mount_help(void) +{ + fprintf(stderr, + " -o allow_other allow access to other users\n" + " -o allow_root allow access to root\n" + " -o nonempty allow mounts over non-empty file/dir\n" + " -o default_permissions enable permission checking by kernel\n" + " -o fsname=NAME set filesystem name\n" + " -o subtype=NAME set filesystem type\n" + " -o large_read issue large read requests (2.4 only)\n" + " -o max_read=N set maximum size of read requests\n" + "\n" + ); +} + +static void exec_fusermount(const char *argv[]) +{ + execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv); + execvp(FUSERMOUNT_PROG, (char **) argv); +} + +static void mount_version(void) +{ + int pid = fork(); + if (!pid) { + const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL }; + exec_fusermount(argv); + _exit(1); + } else if (pid != -1) + waitpid(pid, NULL, 0); +} + +#endif /* __SOLARIS__ */ + struct mount_flags { const char *opt; unsigned long flag; @@ -101,6 +198,7 @@ static struct mount_flags mount_flags[] = { {"ro", MS_RDONLY, 1}, {"suid", MS_NOSUID, 0}, {"nosuid", MS_NOSUID, 1}, +#ifndef __SOLARIS__ {"dev", MS_NODEV, 0}, {"nodev", MS_NODEV, 1}, {"exec", MS_NOEXEC, 0}, @@ -110,9 +208,42 @@ static struct mount_flags mount_flags[] = { {"atime", MS_NOATIME, 0}, {"noatime", MS_NOATIME, 1}, {"dirsync", MS_DIRSYNC, 1}, +#else /* __SOLARIS__ */ + {"-g", MS_GLOBAL, 1}, /* 1eaf4 */ + {"-m", MS_NOMNTTAB, 1}, /* 1eb00 */ + {"-O", MS_OVERLAY, 1}, /* 1eb0c */ +#endif /* __SOLARIS__ */ {NULL, 0, 0} }; +#ifdef __SOLARIS__ + +/* + * See comments in fuse_kern_mount() + */ +struct solaris_mount_opts { + int nosuid; + int setuid; + int nosetuid; + int devices; + int nodevices; +}; + +#define SOLARIS_MOUNT_OPT(t, p, n) \ + { t, offsetof(struct solaris_mount_opts, p), n } +static const struct fuse_opt solaris_mnt_opts[] = { + SOLARIS_MOUNT_OPT("suid", setuid, 1), + SOLARIS_MOUNT_OPT("suid", devices, 1), + SOLARIS_MOUNT_OPT("nosuid", nosuid, 1), + SOLARIS_MOUNT_OPT("setuid", setuid, 1), + SOLARIS_MOUNT_OPT("nosetuid", nosetuid, 1), + SOLARIS_MOUNT_OPT("devices", devices, 1), + SOLARIS_MOUNT_OPT("nodevices", nodevices, 1), + FUSE_OPT_END +}; + +#endif /* __SOLARIS__ */ + static void set_mount_flag(const char *s, int *flags) { int i; @@ -156,23 +287,85 @@ static int fuse_mount_opt_proc(void *data, const char *arg, int key, case KEY_FUSERMOUNT_OPT: return fuse_opt_add_opt(&mo->fusermount_opts, arg); +#ifdef __SOLARIS__ + case KEY_SUBTYPE_OPT: + return fuse_opt_add_opt(&mo->subtype_opt, arg); +#endif /* __SOLARIS__ */ + case KEY_MTAB_OPT: return fuse_opt_add_opt(&mo->mtab_opts, arg); case KEY_HELP: +#ifdef __SOLARIS__ + mount_help(); +#endif /* __SOLARIS__ */ mo->ishelp = 1; break; case KEY_VERSION: +#ifdef __SOLARIS__ + mount_version(); +#endif /* __SOLARIS__ */ mo->ishelp = 1; break; } return 1; } +#ifdef __SOLARIS__ + +/* return value: + * >= 0 => fd + * -1 => error + */ +static int receive_fd(int fd) +{ + struct msghdr msg; + struct iovec iov; + char buf[1]; + int rv; + size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)]; + struct cmsghdr *cmsg; + + iov.iov_base = buf; + iov.iov_len = 1; + + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + /* old BSD implementations should use msg_accrights instead of + * msg_control; the interface is different. */ + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof(ccmsg); + + while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR); + if (rv == -1) { + perror("recvmsg"); + return -1; + } + if(!rv) { + /* EOF */ + return -1; + } + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg->cmsg_type == SCM_RIGHTS) { + fprintf(stderr, "got control message of unknown type %d\n", + cmsg->cmsg_type); + return -1; + } + return *(int*)CMSG_DATA(cmsg); +} + +#endif /* __SOLARIS__ */ + void fuse_kern_unmount(const char *mountpoint, int fd) { int res; +#ifdef __SOLARIS__ + int pid; +#endif /* __SOLARIS__ */ if (!mountpoint) return; @@ -188,11 +381,214 @@ void fuse_kern_unmount(const char *mountpoint, int fd) if (res == 1 && (pfd.revents & POLLERR)) return; } +#ifndef __SOLARIS__ close(fd); fusermount(1, 0, 1, "", mountpoint); +#else /* __SOLARIS__ */ + if (geteuid() == 0) { + fuse_mnt_umount("fuse", mountpoint, 1); + return; + } + + res = umount2(mountpoint, 2); + if (res == 0) + return; + + pid = fork(); + if(pid == -1) + return; + + if(pid == 0) { + const char *argv[] = + { FUSERMOUNT_PROG, "-u", "-q", "-z", "--", mountpoint, NULL }; + + exec_fusermount(argv); + _exit(1); + } + waitpid(pid, NULL, 0); +#endif /* __SOLARIS__ */ } +#ifdef __SOLARIS__ + +static int fuse_mount_fusermount(const char *mountpoint, const char *opts, + int quiet) +{ + int fds[2], pid; + int res; + int rv; + + if (!mountpoint) { + fprintf(stderr, "fuse: missing mountpoint\n"); + return -1; + } + + res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); + if(res == -1) { + perror("fuse: socketpair() failed"); + return -1; + } + + pid = fork(); + if(pid == -1) { + perror("fuse: fork() failed"); + close(fds[0]); + close(fds[1]); + return -1; + } + + if(pid == 0) { + char env[10]; + const char *argv[32]; + int a = 0; + + if (quiet) { + int fd = open("/dev/null", O_RDONLY); + dup2(fd, 1); + dup2(fd, 2); + } + + argv[a++] = FUSERMOUNT_PROG; + if (opts) { + argv[a++] = "-o"; + argv[a++] = opts; + } + argv[a++] = "--"; + argv[a++] = mountpoint; + argv[a++] = NULL; + + close(fds[1]); + fcntl(fds[0], F_SETFD, 0); + snprintf(env, sizeof(env), "%i", fds[0]); + setenv(FUSE_COMMFD_ENV, env, 1); + exec_fusermount(argv); + perror("fuse: failed to exec fusermount"); + _exit(1); + } + + close(fds[0]); + rv = receive_fd(fds[1]); + close(fds[1]); + waitpid(pid, NULL, 0); /* bury zombie */ + + return rv; +} + +static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, + const char *mnt_opts) +{ + char tmp[128]; + const char *devname = "/dev/fuse"; + char *source = NULL; + char *type = NULL; + struct stat stbuf; + int fd; + int res; + + if (!mnt) { + fprintf(stderr, "fuse: missing mountpoint\n"); + return -1; + } + + res = lstat(mnt, &stbuf); + if (res == -1) { + fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n", + mnt, strerror(errno)); + return -1; + } + + if (!mo->nonempty) { + res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode, stbuf.st_size); + if (res == -1) + return -1; + } + + fd = open(devname, O_RDWR); + if (fd == -1) { + if (errno == ENODEV || errno == ENOENT) + fprintf(stderr, + "fuse: device not found, try 'modprobe fuse' first\n"); + else + fprintf(stderr, "fuse: failed to open %s: %s\n", devname, + strerror(errno)); + return -1; + } + + snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%i,group_id=%i", fd, + stbuf.st_mode & S_IFMT, getuid(), getgid()); + + res = fuse_opt_add_opt(&mo->kernel_opts, tmp); + if (res == -1) + goto out_close; + + source = malloc((mo->fsname ? strlen(mo->fsname) : 0) + + (mo->subtype ? strlen(mo->subtype) : 0) + + strlen(devname) + 32); + + type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32); + if (!type || !source) { + fprintf(stderr, "fuse: failed to allocate memory\n"); + goto out_close; + } + + strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); + if (mo->subtype) { + strcat(type, "."); + strcat(type, mo->subtype); + } + strcpy(source, + mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname)); + + /* JPA added two final zeroes */ + res = mount(source, mnt, MS_OPTIONSTR|mo->flags, type, NULL, 0, + mo->kernel_opts, MAX_MNTOPT_STR, 0, 0); + + if (res == -1 && errno == EINVAL && mo->subtype) { + /* Probably missing subtype support */ + strcpy(type, mo->blkdev ? "fuseblk" : "fuse"); + if (mo->fsname) { + if (!mo->blkdev) + sprintf(source, "%s#%s", mo->subtype, mo->fsname); + } else { + strcpy(source, type); + } + /* JPA two null args added */ + res = mount(source, mnt, MS_OPTIONSTR|mo->flags, type, NULL, 0, + mo->kernel_opts, MAX_MNTOPT_STR, 0, 0); + } + if (res == -1) { + /* + * Maybe kernel doesn't support unprivileged mounts, in this + * case try falling back to fusermount + */ + if (errno == EPERM) { + res = -2; + } else { + int errno_save = errno; + if (mo->blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk()) + fprintf(stderr, "fuse: 'fuseblk' support missing\n"); + else + fprintf(stderr, "fuse: mount failed: %s\n", + strerror(errno_save)); + } + + goto out_close; + } + + return fd; + + out_umount: + umount2(mnt, 2); /* lazy umount */ + out_close: + free(type); + free(source); + close(fd); + return res; +} + +#endif /* __SOLARIS__ */ + static int get_mnt_flag_opts(char **mnt_optsp, int flags) { int i; @@ -202,8 +598,8 @@ static int get_mnt_flag_opts(char **mnt_optsp, int flags) 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; + fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1) + return -1; } return 0; } @@ -213,14 +609,59 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) struct mount_opts mo; int res = -1; char *mnt_opts = NULL; +#ifdef __SOLARIS__ + struct solaris_mount_opts smo; + struct fuse_args sa = FUSE_ARGS_INIT(0, NULL); +#endif /* __SOLARIS__ */ memset(&mo, 0, sizeof(mo)); +#ifndef __SOLARIS__ if (getuid()) mo.flags = MS_NOSUID | MS_NODEV; +#else /* __SOLARIS__ */ + mo.flags = 0; + memset(&smo, 0, sizeof(smo)); + if (args != NULL) { + while (args->argv[sa.argc] != NULL) + fuse_opt_add_arg(&sa, args->argv[sa.argc]); + } +#endif /* __SOLARIS__ */ if (args && fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) +#ifndef __SOLARIS__ return -1; +#else /* __SOLARIS__ */ + goto out; /* if SOLARIS, clean up 'sa' */ + + /* + * In Solaris, nosuid is equivalent to nosetuid + nodevices. We only + * have MS_NOSUID for mount flags (no MS_(NO)SETUID, etc.). But if + * we set that as a default, it restricts specifying just nosetuid + * or nodevices; there is no way for the user to specify setuid + + * nodevices or vice-verse. So we parse the existing options, then + * add restrictive defaults if needed. + */ + if (fuse_opt_parse(&sa, &smo, solaris_mnt_opts, NULL) == -1) + goto out; + if (smo.nosuid || (!smo.nodevices && !smo.devices + && !smo.nosetuid && !smo.setuid)) { + mo.flags |= MS_NOSUID; + } else { + /* + * Defaults; if neither nodevices|devices,nosetuid|setuid has + * been specified, add the default negative option string. If + * both have been specified (i.e., -osuid,nosuid), leave them + * alone; the last option will have precedence. + */ + if (!smo.nodevices && !smo.devices) + if (fuse_opt_add_opt(&mo.kernel_opts, "nodevices") == -1) + goto out; + if (!smo.nosetuid && !smo.setuid) + if (fuse_opt_add_opt(&mo.kernel_opts, "nosetuid") == -1) + goto out; + } +#endif /* __SOLARIS__ */ if (mo.allow_other && mo.allow_root) { fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n"); @@ -233,6 +674,7 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) res = -1; if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1) goto out; +#ifndef __SOLARIS__ 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) @@ -243,15 +685,48 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) 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); - +#else /* __SOLARIS__ */ + 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; + res = fuse_mount_sys(mountpoint, &mo, mnt_opts); + if (res == -2) { + if (mo.fusermount_opts && + fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1) + goto out; + + if (mo.subtype) { + char *tmp_opts = NULL; + + res = -1; + if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 || + fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) { + free(tmp_opts); + goto out; + } + + res = fuse_mount_fusermount(mountpoint, tmp_opts, 1); + free(tmp_opts); + if (res == -1) + res = fuse_mount_fusermount(mountpoint, mnt_opts, 0); + } else { + res = fuse_mount_fusermount(mountpoint, mnt_opts, 0); + } + } +#endif /* __SOLARIS__ */ + out: free(mnt_opts); +#ifdef __SOLARIS__ + fuse_opt_free_args(&sa); + free(mo.subtype); + free(mo.subtype_opt); +#endif /* __SOLARIS__ */ free(mo.fsname); free(mo.fusermount_opts); free(mo.kernel_opts); free(mo.mtab_opts); return res; } - diff --git a/libfuse-lite/mount_util.c b/libfuse-lite/mount_util.c index 75a7ee63..724a042d 100644 --- a/libfuse-lite/mount_util.c +++ b/libfuse-lite/mount_util.c @@ -15,11 +15,245 @@ #include #include #include -#include #include #include +#ifdef __SOLARIS__ +#else /* __SOLARIS__ */ +#include #include #include +#endif /* __SOLARIS__ */ + +#ifdef __SOLARIS__ + +char *mkdtemp(char *template); + +#ifndef _PATH_MOUNTED +#define _PATH_MOUNTED "/etc/mnttab" +#endif /* _PATH_MOUNTED */ + +#ifndef IGNORE_MTAB +static int mtab_needs_update(const char *mnt) +{ + 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; + + if (lstat(_PATH_MOUNTED, &stbuf) != -1 && S_ISLNK(stbuf.st_mode)) + return 0; + + return 1; +} +#endif /* IGNORE_MTAB */ + +int fuse_mnt_add_mount(const char *progname, const char *fsname, + const char *mnt, const char *type, const char *opts) +{ + int res; + int status; + +#ifndef IGNORE_MTAB + if (!mtab_needs_update(mnt)) + return 0; +#endif /* IGNORE_MTAB */ + + res = fork(); + if (res == -1) { + fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); + return -1; + } + 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("/sbin/mount", "/sbin/mount", "-F", type, "-o", opts, + fsname, mnt, NULL); + fprintf(stderr, "%s: failed to execute /sbin/mount: %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; +} + +int fuse_mnt_umount(const char *progname, const char *mnt, int lazy) +{ + int res; + int status; + +#ifndef IGNORE_MTAB + if (!mtab_needs_update(mnt)) + return 0; +#endif /* IGNORE_MTAB */ + + res = fork(); + if (res == -1) { + fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno)); + return -1; + } + if (res == 0) { + setuid(geteuid()); + execl("/sbin/umount", "/sbin/umount", !lazy ? "-f" : NULL, mnt, + NULL); + fprintf(stderr, "%s: failed to execute /sbin/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_empty(const char *progname, const char *mnt, + mode_t rootmode, off_t rootsize) +{ + int isempty = 1; + + if (S_ISDIR(rootmode)) { + struct dirent *ent; + DIR *dp = opendir(mnt); + if (dp == NULL) { + fprintf(stderr, "%s: failed to open mountpoint for reading: %s\n", + progname, strerror(errno)); + return -1; + } + while ((ent = readdir(dp)) != NULL) { + if (strcmp(ent->d_name, ".") != 0 && + strcmp(ent->d_name, "..") != 0) { + isempty = 0; + break; + } + } + closedir(dp); + } else if (rootsize) + isempty = 0; + + if (!isempty) { + fprintf(stderr, "%s: mountpoint is not empty\n", progname); + fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname); + return -1; + } + return 0; +} + +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; +} + +#else /* __SOLARIS__ */ static int mtab_needs_update(const char *mnt) { @@ -217,3 +451,5 @@ int fuse_mnt_check_fuseblk(void) fclose(f); return 0; } + +#endif /* __SOLARIS__ */ diff --git a/libfuse-lite/mount_util.h b/libfuse-lite/mount_util.h index a7559675..0318385f 100644 --- a/libfuse-lite/mount_util.h +++ b/libfuse-lite/mount_util.h @@ -14,6 +14,14 @@ 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); +#ifdef __SOLARIS__ + +int fuse_mnt_check_empty(const char *progname, const char *mnt, + mode_t rootmode, off_t rootsize); + +#else /* __SOLARIS__ */ + int fusermount(int unmount, int quiet, int lazy, const char *opts, const char *origmnt); +#endif /* __SOLARIS__ */