Skip to content

Instantly share code, notes, and snippets.

@bfleischer
Created May 20, 2011 00:32
Show Gist options
  • Save bfleischer/de320bcf06ec6be8069c to your computer and use it in GitHub Desktop.
Save bfleischer/de320bcf06ec6be8069c to your computer and use it in GitHub Desktop.
mount_hash problem
/*
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.
gcc -Wall `pkg-config fuse --cflags --libs` issue.c -o issue
then run
mkdir mountpoint && ./issue
*/
#define FUSE_USE_VERSION 26
#include <fuse_lowlevel.h>
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
static const char *hello_str = "Hello World!\n";
static const char *hello_name = "hello";
static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
{
stbuf->st_ino = ino;
switch (ino) {
case 1:
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
break;
case 2:
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(hello_str);
break;
default:
return -1;
}
return 0;
}
static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
struct stat stbuf;
(void) fi;
memset(&stbuf, 0, sizeof(stbuf));
if (hello_stat(ino, &stbuf) == -1)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, 1.0);
}
static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
struct fuse_entry_param e;
if (parent != 1 || strcmp(name, hello_name) != 0)
fuse_reply_err(req, ENOENT);
else {
memset(&e, 0, sizeof(e));
e.ino = 2;
e.attr_timeout = 1.0;
e.entry_timeout = 1.0;
hello_stat(e.ino, &e.attr);
fuse_reply_entry(req, &e);
}
}
struct dirbuf {
char *p;
size_t size;
};
static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
fuse_ino_t ino)
{
struct stat stbuf;
size_t oldsize = b->size;
b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
char *newp = realloc(b->p, b->size);
if (!newp) {
fprintf(stderr, "*** fatal error: cannot allocate memory\n");
abort();
}
b->p = newp;
memset(&stbuf, 0, sizeof(stbuf));
stbuf.st_ino = ino;
fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
b->size);
}
#define min(x, y) ((x) < (y) ? (x) : (y))
static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
off_t off, size_t maxsize)
{
if (off < bufsize)
return fuse_reply_buf(req, buf + off,
min(bufsize - off, maxsize));
else
return fuse_reply_buf(req, NULL, 0);
}
static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi)
{
(void) fi;
if (ino != 1)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, ".", 1);
dirbuf_add(req, &b, "..", 1);
dirbuf_add(req, &b, hello_name, 2);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
if (ino != 2)
fuse_reply_err(req, EISDIR);
else if ((fi->flags & 3) != O_RDONLY)
fuse_reply_err(req, EACCES);
else
fuse_reply_open(req, fi);
}
static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi)
{
(void) fi;
assert(ino == 2);
reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
}
static struct fuse_lowlevel_ops hello_ll_oper = {
.lookup = hello_ll_lookup,
.getattr = hello_ll_getattr,
.readdir = hello_ll_readdir,
.open = hello_ll_open,
.read = hello_ll_read,
};
/* Original main() function
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
char *mountpoint;
int err = -1;
if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 &&
(ch = fuse_mount(mountpoint, &args)) != NULL) {
struct fuse_session *se;
se = fuse_lowlevel_new(&args, &hello_ll_oper,
sizeof(hello_ll_oper), NULL);
if (se != NULL) {
if (fuse_set_signal_handlers(se) != -1) {
fuse_session_add_chan(se, ch);
err = fuse_session_loop(se);
fuse_remove_signal_handlers(se);
fuse_session_remove_chan(ch);
}
fuse_session_destroy(se);
}
fuse_unmount(mountpoint, ch);
}
fuse_opt_free_args(&args);
return err ? 1 : 0;
}
*/
static void *fuse_loop_thread(void *arg) {
fuse_session_loop(arg);
printf("exiting fuse_loop_thread()\n");
return NULL;
}
/**
Everything above is just a copy of hello_ll.c from macfuse code repository. The most interesting is below.
Compile it with
gcc -Wall `pkg-config fuse --cflags --libs` issue.c -o issue
then run
mkdir mountpoint && ./issue
*/
int main(void) {
struct fuse_chan *chan = fuse_mount("mountpoint", NULL);
struct fuse_session *se = fuse_lowlevel_new(NULL, &hello_ll_oper, sizeof(hello_ll_oper), NULL);
fuse_session_add_chan(se, chan);
pthread_t pid;
pthread_create(&pid, NULL, fuse_loop_thread, se);
int tries = 0;
struct stat buff;
while(1) {
/*
Issue #1 - on Linux fuse_new returns mounted filesystem, macfuse
returns unmounted filesystem and mounts some time later. This happens (I think) because
fuse_mount in macfuse is a background process, while in Linux it is foreground.
*/
if (stat("mountpoint/hello", &buff) < 0) {
if (errno == ENOENT) {
tries++;
} else {
perror("stat");
tries = -1; // error
break;
}
} else {
break;
}
}
if (tries == 0) {
printf("fuse_new works fine\n");
} else if (tries > 0) {
// The filesystem is available only after some short period of time
printf("The filesystem bacame available only after %d unsuccessful tries\n", tries);
}
fuse_session_exit(se); // exit the loop
pthread_join(pid, NULL);
printf("ready to unmount\n");
/*
Issue #2 - There is some deadlock problem in fuse_unmount that happens only when pthreads are used.
*/
fuse_unmount("link", chan);
printf("unmount took 60 seconds, wtf?\n");
fuse_session_destroy(se);
return EXIT_SUCCESS;
}
/*
* Amit Singh <singh@>
*
* Derived from mount_bsd.c from the fuse distribution.
*
* FUSE: Filesystem in Userspace
* Copyright (C) 2005-2006 Csaba Henk <csaba.henk@creo.hu>
*
* This program can be distributed under the terms of the GNU LGPLv2.
* See the file COPYING.LIB.
*/
#undef _POSIX_C_SOURCE
#include <sys/types.h>
#include <CoreFoundation/CoreFoundation.h>
#include "fuse_i.h"
#include "fuse_opt.h"
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <paths.h>
#include <libproc.h>
#include <sys/utsname.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <AssertMacros.h>
#include "fuse_darwin_private.h"
static int quiet_mode = 0;
long
fuse_os_version_major_np(void)
{
int ret = 0;
long major = 0;
char *c = NULL;
struct utsname u;
size_t oldlen;
oldlen = sizeof(u.release);
ret = sysctlbyname("kern.osrelease", u.release, &oldlen, NULL, 0);
if (ret != 0) {
return -1;
}
c = strchr(u.release, '.');
if (c == NULL) {
return -1;
}
*c = '\0';
errno = 0;
major = strtol(u.release, NULL, 10);
if ((errno == EINVAL) || (errno == ERANGE)) {
return -1;
}
return major;
}
int
fuse_running_under_rosetta(void)
{
int result = 0;
int is_native = 1;
size_t sz = sizeof(result);
int ret = sysctlbyname("sysctl.proc_native", &result, &sz, NULL, (size_t)0);
if ((ret == 0) && !result) {
is_native = 0;
}
return !is_native;
}
static int
loadkmod(void)
{
int result = -1;
pid_t pid, terminated_pid;
int status;
long major;
major = fuse_os_version_major_np();
if (major < 9) { /* not Mac OS X 10.5+ */
return EINVAL;
}
pid = fork();
if (pid == 0) {
result = execl(MACFUSE_LOAD_PROG, MACFUSE_LOAD_PROG, NULL);
/* exec failed */
goto Return;
}
require_action(pid != -1, Return, result = errno);
while ((terminated_pid = waitpid(pid, &status, 0)) < 0) {
/* retry if EINTR, else break out with error */
if (errno != EINTR) {
break;
}
}
if ((terminated_pid == pid) && (WIFEXITED(status))) {
result = WEXITSTATUS(status);
} else {
result = -1;
}
Return:
check_noerr_string(result, strerror(errno));
return result;
}
static int
post_notification(char *name,
char *udata_keys[],
char *udata_values[],
CFIndex nf_num)
{
CFIndex i;
CFStringRef nf_name = NULL;
CFStringRef nf_object = NULL;
CFMutableDictionaryRef nf_udata = NULL;
CFNotificationCenterRef distributedCenter;
CFStringEncoding encoding = kCFStringEncodingUTF8;
distributedCenter = CFNotificationCenterGetDistributedCenter();
if (!distributedCenter) {
return -1;
}
nf_name = CFStringCreateWithCString(kCFAllocatorDefault, name, encoding);
nf_object = CFStringCreateWithCString(kCFAllocatorDefault,
LIBFUSE_UNOTIFICATIONS_OBJECT,
encoding);
nf_udata = CFDictionaryCreateMutable(kCFAllocatorDefault,
nf_num,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!nf_name || !nf_object || !nf_udata) {
goto out;
}
for (i = 0; i < nf_num; i++) {
CFStringRef a_key = CFStringCreateWithCString(kCFAllocatorDefault,
udata_keys[i],
kCFStringEncodingUTF8);
CFStringRef a_value = CFStringCreateWithCString(kCFAllocatorDefault,
udata_values[i],
kCFStringEncodingUTF8);
CFDictionarySetValue(nf_udata, a_key, a_value);
CFRelease(a_key);
CFRelease(a_value);
}
CFNotificationCenterPostNotification(distributedCenter,
nf_name, nf_object, nf_udata, false);
out:
if (nf_name) {
CFRelease(nf_name);
}
if (nf_object) {
CFRelease(nf_object);
}
if (nf_udata) {
CFRelease(nf_udata);
}
return 0;
}
enum {
KEY_ALLOW_ROOT,
KEY_AUTO_CACHE,
KEY_DIO,
KEY_HELP,
KEY_IGNORED,
KEY_KERN,
KEY_QUIET,
KEY_RO,
KEY_VERSION,
KEY_VOLICON,
};
struct mount_opts {
int allow_other;
int allow_root;
int ishelp;
char *kernel_opts;
};
static const struct fuse_opt fuse_mount_opts[] = {
{ "allow_other", offsetof(struct mount_opts, allow_other), 1 },
{ "allow_root", offsetof(struct mount_opts, allow_root), 1 },
FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
FUSE_OPT_KEY("auto_cache", KEY_AUTO_CACHE),
FUSE_OPT_KEY("-r", KEY_RO),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
/* standard FreeBSD mount options */
FUSE_OPT_KEY("dev", KEY_KERN),
FUSE_OPT_KEY("async", KEY_KERN),
FUSE_OPT_KEY("atime", KEY_KERN),
FUSE_OPT_KEY("dev", KEY_KERN),
FUSE_OPT_KEY("exec", KEY_KERN),
FUSE_OPT_KEY("suid", KEY_KERN),
FUSE_OPT_KEY("symfollow", KEY_KERN),
FUSE_OPT_KEY("rdonly", KEY_KERN),
FUSE_OPT_KEY("sync", KEY_KERN),
FUSE_OPT_KEY("union", KEY_KERN),
FUSE_OPT_KEY("userquota", KEY_KERN),
FUSE_OPT_KEY("groupquota", KEY_KERN),
FUSE_OPT_KEY("clusterr", KEY_KERN),
FUSE_OPT_KEY("clusterw", KEY_KERN),
FUSE_OPT_KEY("suiddir", KEY_KERN),
FUSE_OPT_KEY("snapshot", KEY_KERN),
FUSE_OPT_KEY("multilabel", KEY_KERN),
FUSE_OPT_KEY("acls", KEY_KERN),
FUSE_OPT_KEY("force", KEY_KERN),
FUSE_OPT_KEY("update", KEY_KERN),
FUSE_OPT_KEY("ro", KEY_KERN),
FUSE_OPT_KEY("rw", KEY_KERN),
FUSE_OPT_KEY("auto", KEY_KERN),
/* options supported under both Linux and FBSD */
FUSE_OPT_KEY("allow_other", KEY_KERN),
FUSE_OPT_KEY("default_permissions", KEY_KERN),
/* FBSD FUSE specific mount options */
FUSE_OPT_KEY("private", KEY_KERN),
FUSE_OPT_KEY("neglect_shares", KEY_KERN),
FUSE_OPT_KEY("push_symlinks_in", KEY_KERN),
/* stock FBSD mountopt parsing routine lets anything be negated... */
FUSE_OPT_KEY("nodev", KEY_KERN),
FUSE_OPT_KEY("noasync", KEY_KERN),
FUSE_OPT_KEY("noatime", KEY_KERN),
FUSE_OPT_KEY("nodev", KEY_KERN),
FUSE_OPT_KEY("noexec", KEY_KERN),
FUSE_OPT_KEY("nosuid", KEY_KERN),
FUSE_OPT_KEY("nosymfollow", KEY_KERN),
FUSE_OPT_KEY("nordonly", KEY_KERN),
FUSE_OPT_KEY("nosync", KEY_KERN),
FUSE_OPT_KEY("nounion", KEY_KERN),
FUSE_OPT_KEY("nouserquota", KEY_KERN),
FUSE_OPT_KEY("nogroupquota", KEY_KERN),
FUSE_OPT_KEY("noclusterr", KEY_KERN),
FUSE_OPT_KEY("noclusterw", KEY_KERN),
FUSE_OPT_KEY("nosuiddir", KEY_KERN),
FUSE_OPT_KEY("nosnapshot", KEY_KERN),
FUSE_OPT_KEY("nomultilabel", KEY_KERN),
FUSE_OPT_KEY("noacls", KEY_KERN),
FUSE_OPT_KEY("noforce", KEY_KERN),
FUSE_OPT_KEY("noupdate", KEY_KERN),
FUSE_OPT_KEY("noro", KEY_KERN),
FUSE_OPT_KEY("norw", KEY_KERN),
FUSE_OPT_KEY("noauto", KEY_KERN),
FUSE_OPT_KEY("noallow_other", KEY_KERN),
FUSE_OPT_KEY("nodefault_permissions", KEY_KERN),
FUSE_OPT_KEY("noprivate", KEY_KERN),
FUSE_OPT_KEY("noneglect_shares", KEY_KERN),
FUSE_OPT_KEY("nopush_symlinks_in", KEY_KERN),
/* Mac OS X options */
FUSE_OPT_KEY("allow_recursion", KEY_KERN),
FUSE_OPT_KEY("allow_root", KEY_KERN), /* need to pass this on */
FUSE_OPT_KEY("auto_xattr", KEY_KERN),
FUSE_OPT_KEY("automounted", KEY_IGNORED),
FUSE_OPT_KEY("blocksize=", KEY_KERN),
FUSE_OPT_KEY("daemon_timeout=", KEY_KERN),
FUSE_OPT_KEY("default_permissions", KEY_KERN),
FUSE_OPT_KEY("defer_permissions", KEY_KERN),
FUSE_OPT_KEY("direct_io", KEY_DIO),
FUSE_OPT_KEY("extended_security", KEY_KERN),
FUSE_OPT_KEY("fsid=", KEY_KERN),
FUSE_OPT_KEY("fsname=", KEY_KERN),
FUSE_OPT_KEY("fssubtype=", KEY_KERN),
FUSE_OPT_KEY("fstypename=", KEY_KERN),
FUSE_OPT_KEY("init_timeout=", KEY_KERN),
FUSE_OPT_KEY("iosize=", KEY_KERN),
FUSE_OPT_KEY("jail_symlinks", KEY_KERN),
FUSE_OPT_KEY("kill_on_unmount", KEY_KERN),
FUSE_OPT_KEY("local", KEY_KERN),
FUSE_OPT_KEY("native_xattr", KEY_KERN),
FUSE_OPT_KEY("negative_vncache", KEY_KERN),
FUSE_OPT_KEY("noalerts", KEY_KERN),
FUSE_OPT_KEY("noappledouble", KEY_KERN),
FUSE_OPT_KEY("noapplexattr", KEY_KERN),
FUSE_OPT_KEY("noattrcache", KEY_KERN),
FUSE_OPT_KEY("nobrowse", KEY_KERN),
FUSE_OPT_KEY("nolocalcaches", KEY_KERN),
FUSE_OPT_KEY("noping_diskarb", KEY_IGNORED),
FUSE_OPT_KEY("noreadahead", KEY_KERN),
FUSE_OPT_KEY("nosynconclose", KEY_KERN),
FUSE_OPT_KEY("nosyncwrites", KEY_KERN),
FUSE_OPT_KEY("noubc", KEY_KERN),
FUSE_OPT_KEY("novncache", KEY_KERN),
FUSE_OPT_KEY("ping_diskarb", KEY_IGNORED),
FUSE_OPT_KEY("quiet", KEY_QUIET),
FUSE_OPT_KEY("slow_statfs", KEY_KERN),
FUSE_OPT_KEY("sparse", KEY_KERN),
FUSE_OPT_KEY("subtype=", KEY_IGNORED),
FUSE_OPT_KEY("volicon=", KEY_VOLICON),
FUSE_OPT_KEY("volname=", KEY_KERN),
FUSE_OPT_END
};
static void
mount_help(void)
{
system(MACFUSE_MOUNT_PROG " --help");
fputc('\n', stderr);
}
static void
mount_version(void)
{
system(MACFUSE_MOUNT_PROG " --version");
}
static int
fuse_mount_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct mount_opts *mo = data;
switch (key) {
case KEY_AUTO_CACHE:
if (fuse_opt_add_opt(&mo->kernel_opts, "auto_cache") == -1 ||
fuse_opt_add_arg(outargs, "-oauto_cache") == -1)
return -1;
return 0;
case KEY_ALLOW_ROOT:
if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
fuse_opt_add_arg(outargs, "-oallow_root") == -1)
return -1;
return 0;
case KEY_RO:
arg = "ro";
/* fall through */
case KEY_KERN:
return fuse_opt_add_opt(&mo->kernel_opts, arg);
case KEY_DIO:
if (fuse_opt_add_opt(&mo->kernel_opts, "direct_io") == -1 ||
(fuse_opt_add_arg(outargs, "-odirect_io") == -1))
return -1;
return 0;
case KEY_IGNORED:
return 0;
case KEY_QUIET:
quiet_mode = 1;
return 0;
case KEY_VOLICON:
{
char volicon_arg[MAXPATHLEN + 32];
char *volicon_path = strchr(arg, '=');
if (!volicon_path) {
return -1;
}
if (snprintf(volicon_arg, sizeof(volicon_arg),
"-omodules=volicon,iconpath%s", volicon_path) <= 0) {
return -1;
}
if (fuse_opt_add_arg(outargs, volicon_arg) == -1) {
return -1;
}
return 0;
}
case KEY_HELP:
mount_help();
mo->ishelp = 1;
break;
case KEY_VERSION:
mount_version();
mo->ishelp = 1;
break;
}
return 1;
}
static void
mount_hash_purge_helper(char *key, void *data)
{
free(key);
free(data);
}
void
fuse_kern_unmount(const char *mountpoint, int fd)
{
int ret;
struct stat sbuf;
char dev[128];
char resolved_path[PATH_MAX];
char *ep, *rp = NULL, *umount_cmd;
unsigned int hs_complete = 0;
pthread_mutex_lock(&mount_lock);
if ((mount_count > 0) && mountpoint) {
struct mount_info* mi =
hash_search(mount_hash, (char *)mountpoint, NULL, NULL);
if (mi) {
hash_destroy(mount_hash, (char *)mountpoint,
mount_hash_purge_helper);
--mount_count;
}
fprintf(stderr, "mount_count = %d (after unmount)\n", mount_count);
}
pthread_mutex_unlock(&mount_lock);
ret = ioctl(fd, FUSEDEVIOCGETHANDSHAKECOMPLETE, &hs_complete);
if (ret || !hs_complete) {
return;
}
if (fstat(fd, &sbuf) == -1) {
return;
}
devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
if (strncmp(dev, MACFUSE_DEVICE_BASENAME,
sizeof(MACFUSE_DEVICE_BASENAME) - 1)) {
return;
}
strtol(dev + 4, &ep, 10);
if (*ep != '\0') {
return;
}
close(fd);
unmount(mountpoint, MNT_FORCE);
return;
}
void
fuse_unmount_compat22(const char *mountpoint)
{
unmount(mountpoint, MNT_FORCE);
}
static int
fuse_mount_core(const char *mountpoint, const char *opts)
{
int fd;
int result;
char *fdnam, *dev;
pid_t pid, cpid;
int status;
const char *mountprog = MACFUSE_MOUNT_PROG;
if (!mountpoint) {
fprintf(stderr, "missing or invalid mount point\n");
return -1;
}
if (fuse_running_under_rosetta()) {
fprintf(stderr, "MacFUSE does not work under Rosetta\n");
return -1;
}
result = loadkmod();
if (result) {
CFOptionFlags responseFlags;
if (result == EINVAL) {
if (!quiet_mode) {
CFUserNotificationDisplayNotice(
(CFTimeInterval)0,
kCFUserNotificationCautionAlertLevel,
(CFURLRef)0,
(CFURLRef)0,
(CFURLRef)0,
CFSTR("Operating System Too Old"),
CFSTR("The installed MacFUSE version is too new for the operating system. Please downgrade your MacFUSE installation to one that is compatible with the currently running operating system."),
CFSTR("OK")
);
}
post_notification(
LIBFUSE_UNOTIFICATIONS_NOTIFY_OSISTOOOLD, NULL, NULL, 0);
} else if (result == EBUSY) {
if (!quiet_mode) {
CFUserNotificationDisplayNotice(
(CFTimeInterval)0,
kCFUserNotificationCautionAlertLevel,
(CFURLRef)0,
(CFURLRef)0,
(CFURLRef)0,
CFSTR("MacFUSE Version Mismatch"),
CFSTR("MacFUSE has been updated but an incompatible or old version of the MacFUSE kernel extension is already loaded. It failed to unload, possibly because a MacFUSE volume is currently mounted.\n\nPlease eject all MacFUSE volumes and try again, or simply restart the system for changes to take effect."),
CFSTR("OK")
);
}
post_notification(LIBFUSE_UNOTIFICATIONS_NOTIFY_VERSIONMISMATCH,
NULL, NULL, 0);
}
fprintf(stderr, "the MacFUSE file system is not available (%d)\n",
result);
return -1;
} else {
/* Module loaded, but now need to check for user<->kernel match. */
char version[MAXHOSTNAMELEN + 1] = { 0 };
size_t version_len = MAXHOSTNAMELEN;
size_t version_len_desired = 0;
result = sysctlbyname(SYSCTL_MACFUSE_VERSION_NUMBER, version,
&version_len, NULL, (size_t)0);
if (result == 0) {
/* sysctlbyname() includes the trailing '\0' in version_len */
version_len_desired = strlen(MACFUSE_VERSION) + 1;
if ((version_len != version_len_desired) ||
strncmp(MACFUSE_VERSION, version, version_len)) {
result = -1;
}
}
}
if (result) {
if (!quiet_mode) {
CFUserNotificationDisplayNotice(
(CFTimeInterval)0,
kCFUserNotificationCautionAlertLevel,
(CFURLRef)0,
(CFURLRef)0,
(CFURLRef)0,
CFSTR("MacFUSE Runtime Version Mismatch"),
CFSTR("The MacFUSE library version this program is using is incompatible with the loaded MacFUSE kernel extension."),
CFSTR("OK")
);
}
post_notification(LIBFUSE_UNOTIFICATIONS_NOTIFY_RUNTIMEVERSIONMISMATCH,
NULL, NULL, 0);
fprintf(stderr,
"this MacFUSE library version is incompatible with "
"the MacFUSE kernel extension\n");
return -1;
}
fdnam = getenv("FUSE_DEV_FD");
if (fdnam) {
char *ep;
fd = strtol(fdnam, &ep, 10);
if (*ep != '\0') {
fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
return -1;
}
if (fd < 0)
return -1;
goto mount;
}
dev = getenv("FUSE_DEV_NAME");
if (dev) {
if ((fd = open(dev, O_RDWR)) < 0) {
perror("MacFUSE: failed to open device");
return -1;
}
} else {
int r, devidx = -1;
char devpath[MAXPATHLEN];
for (r = 0; r < MACFUSE_NDEVICES; r++) {
snprintf(devpath, MAXPATHLEN - 1,
_PATH_DEV MACFUSE_DEVICE_BASENAME "%d", r);
fd = open(devpath, O_RDWR);
if (fd >= 0) {
dev = devpath;
devidx = r;
break;
}
}
if (devidx == -1) {
perror("MacFUSE: failed to open device");
return -1;
}
}
mount:
if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
goto out;
pid = fork();
cpid = pid;
if (pid == -1) {
perror("MacFUSE: fork() failed");
close(fd);
return -1;
}
if (pid == 0) {
pid = fork();
if (pid == -1) {
perror("MacFUSE: fork() failed");
close(fd);
exit(1);
}
if (pid == 0) {
const char *argv[32];
int a = 0;
if (! fdnam)
asprintf(&fdnam, "%d", fd);
argv[a++] = mountprog;
if (opts) {
argv[a++] = "-o";
argv[a++] = opts;
}
argv[a++] = fdnam;
argv[a++] = mountpoint;
argv[a++] = NULL;
{
char title[MAXPATHLEN + 1] = { 0 };
u_int32_t len = MAXPATHLEN;
int ret = proc_pidpath(getpid(), title, len);
if (ret) {
setenv("MOUNT_FUSEFS_DAEMON_PATH", title, 1);
}
}
execvp(mountprog, (char **) argv);
perror("MacFUSE: failed to exec mount program");
exit(1);
}
_exit(0);
}
if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
perror("MacFUSE: failed to mount file system");
close(fd);
return -1;
}
out:
return fd;
}
int
fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
{
struct mount_opts mo;
int res = -1;
memset(&mo, 0, sizeof(mo));
/* mount_fusefs should not try to spawn the daemon */
setenv("MOUNT_FUSEFS_SAFE", "1", 1);
/* to notify mount_fusefs it's called from lib */
setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
if (args &&
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1) {
return -1;
}
if (mo.allow_other && mo.allow_root) {
fprintf(stderr,
"MacFUSE: allow_other and allow_root are mutually exclusive\n");
goto out;
}
if (mo.ishelp) {
res = 0;
goto out;
}
pthread_mutex_lock(&mount_lock);
if (hash_search(mount_hash, (char *)mountpoint, NULL, NULL) != NULL) {
fprintf(stderr, "MacFUSE: attempt to remount on active mount point: %s",
mountpoint);
goto out_unlock;
}
if (did_daemonize && mount_count > 0) {
fprintf(stderr, "MacFUSE: attempt to multi-mount after daemonized: %s",
mountpoint);
goto out_unlock;
}
struct mount_info *mi = calloc(1, sizeof(struct mount_info));
if (!mi) {
goto out_unlock;
}
res = fuse_mount_core(mountpoint, mo.kernel_opts);
if (res < 0) {
free(mi);
} else {
mi->fd = res;
hash_search(mount_hash, (char *)mountpoint, mi, NULL);
++mount_count;
fprintf(stderr, "mount_count = %d (after mount)\n", mount_count);
}
out_unlock:
pthread_mutex_unlock(&mount_lock);
out:
free(mo.kernel_opts);
return res;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment