diff options
Diffstat (limited to 'lib/libfuse')
-rw-r--r-- | lib/libfuse/Makefile | 52 | ||||
-rw-r--r-- | lib/libfuse/debug.c | 40 | ||||
-rw-r--r-- | lib/libfuse/debug.h | 49 | ||||
-rw-r--r-- | lib/libfuse/dict.c | 106 | ||||
-rw-r--r-- | lib/libfuse/fuse.3 | 241 | ||||
-rw-r--r-- | lib/libfuse/fuse.c | 331 | ||||
-rw-r--r-- | lib/libfuse/fuse.h | 152 | ||||
-rw-r--r-- | lib/libfuse/fuse_ops.c | 884 | ||||
-rw-r--r-- | lib/libfuse/fuse_private.h | 120 | ||||
-rw-r--r-- | lib/libfuse/fuse_subr.c | 140 | ||||
-rw-r--r-- | lib/libfuse/generate_pkgconfig.sh | 70 | ||||
-rw-r--r-- | lib/libfuse/shlib_version | 2 | ||||
-rw-r--r-- | lib/libfuse/tree.c | 100 |
13 files changed, 2287 insertions, 0 deletions
diff --git a/lib/libfuse/Makefile b/lib/libfuse/Makefile new file mode 100644 index 00000000000..5be536881f0 --- /dev/null +++ b/lib/libfuse/Makefile @@ -0,0 +1,52 @@ +# $OpenBSD: Makefile,v 1.1 2013/06/03 16:00:50 tedu Exp $ + +LIB= fuse +MAN= fuse.3 + +MLINKS= fuse.3 fuse_main.3 \ + fuse.3 fuse_new.3 \ + fuse.3 fuse_parse_cmdline.3 \ + fuse.3 fuse_mount.3 \ + fuse.3 fuse_remove_signal_handlers.3 \ + fuse.3 fuse_set_signal_handlers.3 \ + fuse.3 fuse_get_session.3 \ + fuse.3 fuse_is_lib_option.3 \ + fuse.3 fuse_loop.3 \ + fuse.3 fuse_loop_mt.3 \ + fuse.3 fuse_chan_fd.3 \ + fuse.3 fuse_unmount.3 \ + fuse.3 fuse_daemonize.3 \ + fuse.3 fuse_destroy.3 + +CFLAGS+= -Wall -g -Werror -Wshadow -Wmissing-prototypes +CFLAGS+= -Wstrict-prototypes -Wsign-compare + +CDIAGFLAGS+= -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 +CDIAGFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations +CDIAGFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare +CDIAGFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align + +# XXX Shouldn't we use a common fuse.h with proper ifdef _KERNEL part? +CFLAGS+= -I${.CURDIR} -DDEBUG + +SRCS= debug.c dict.c fuse.c fuse_ops.c fuse_subr.c tree.c +HDRS= fuse.h + +PC_FILES=fuse.pc +CLEANFILES+=${PC_FILES} + +includes: + @cd ${.CURDIR}; for i in ${HDRS}; do \ + j="cmp -s $$i ${DESTDIR}/usr/include/$$i || \ + ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m 444 \ + $$i ${DESTDIR}/usr/include"; \ + echo $$j; \ + eval "$$j"; \ + done + +beforeinstall: + /bin/sh ${.CURDIR}/generate_pkgconfig.sh -c ${.CURDIR} -o ${.OBJDIR} + ${INSTALL} ${INSTALL_COPY} -o root -g ${SHAREGRP} \ + -m ${SHAREMODE} ${.OBJDIR}/${PC_FILES} ${DESTDIR}/usr/lib/pkgconfig/ + +.include <bsd.lib.mk> diff --git a/lib/libfuse/debug.c b/lib/libfuse/debug.c new file mode 100644 index 00000000000..e6262136451 --- /dev/null +++ b/lib/libfuse/debug.c @@ -0,0 +1,40 @@ +/* $OpenBSD: debug.c,v 1.1 2013/06/03 16:00:50 tedu Exp $ */ +/* + * Copyright (c) 2011 Alexandre Ratchov <alex@caoua.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <unistd.h> + +#include "debug.h" + +#ifdef DEBUG +/* + * debug level, -1 means uninitialized + */ +int ifuse_debug = -1; + +void +ifuse_debug_init(void) +{ + char *dbg; + + if (ifuse_debug < 0) { + dbg = issetugid() ? NULL : getenv("FUSE_DEBUG"); + if (!dbg || sscanf(dbg, "%u", &ifuse_debug) != 1) + ifuse_debug = 0; + } +} +#endif diff --git a/lib/libfuse/debug.h b/lib/libfuse/debug.h new file mode 100644 index 00000000000..4a23e9af1b1 --- /dev/null +++ b/lib/libfuse/debug.h @@ -0,0 +1,49 @@ +/* $OpenBSD: debug.h,v 1.1 2013/06/03 16:00:50 tedu Exp $ */ +/* + * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#ifdef DEBUG +#include <stdio.h> + +#define DPRINTFN(n, ...) \ + do { \ + if (ifuse_debug >= (n)) \ + fprintf(stderr, __VA_ARGS__); \ + } while(0) + +#define DPRINTF(...) \ + do { \ + if (ifuse_debug > 0) \ + fprintf(stderr, __VA_ARGS__); \ + } while(0) + +#define DPERROR(s) \ + do { \ + if (ifuse_debug > 0) \ + perror(s); \ + } while(0) + +void ifuse_debug_init(void); +extern int ifuse_debug; +#else +#define DPRINTF(...) do {} while(0) +#define DPRINTFN(...) do {} while(0) +#define DPERROR(s) do {} while(0) +#endif + +#endif /* _DEBUG_H_ */ diff --git a/lib/libfuse/dict.c b/lib/libfuse/dict.c new file mode 100644 index 00000000000..a30532b1f1a --- /dev/null +++ b/lib/libfuse/dict.c @@ -0,0 +1,106 @@ +/* $OpenBSD: dict.c,v 1.1 2013/06/03 16:00:50 tedu Exp $ */ + +/* + * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <err.h> +#include <stdlib.h> +#include <string.h> + +#include "fuse_private.h" + +#define MAX_DICTKEY_SIZE NAME_MAX +struct dictentry { + SPLAY_ENTRY(dictentry) entry; + char key[MAX_DICTKEY_SIZE]; + void *data; +}; + +static int dictentry_cmp(struct dictentry *, struct dictentry *); + +SPLAY_PROTOTYPE(dict, dictentry, entry, dictentry_cmp); + +int +dict_check(struct dict *d, const char *k) +{ + struct dictentry key; + + if (strlcpy(key.key, k, sizeof key.key) >= sizeof key.key) + errx(1, "dict_check(%p, %s): key too large", d, k); + + return (SPLAY_FIND(dict, d, &key) != NULL); +} + +void * +dict_set(struct dict *d, const char *k, void *data) +{ + struct dictentry *entry, key; + + if (strlcpy(key.key, k, sizeof key.key) >= sizeof key.key) + errx(1, "dict_set(%p, %s): key too large", d, k); + if ((entry = SPLAY_FIND(dict, d, &key)) == NULL) { + entry = malloc(sizeof *entry); + if (entry == NULL) + return (NULL); + + strlcpy(entry->key, k, sizeof entry->key); + SPLAY_INSERT(dict, d, entry); + } + + entry->data = data; + + return (entry); +} + +void * +dict_get(struct dict *d, const char *k) +{ + struct dictentry key, *entry; + + if (strlcpy(key.key, k, sizeof key.key) >= sizeof key.key) + errx(1, "dict_get(%p, %s): key too large", d, k); + if ((entry = SPLAY_FIND(dict, d, &key)) == NULL) + return (NULL); + + return (entry->data); +} + +void * +dict_pop(struct dict *d, const char *k) +{ + struct dictentry key, *entry; + void *data; + + if (strlcpy(key.key, k, sizeof key.key) >= sizeof key.key) + errx(1, "dict_pop(%p, %s): key too large", d, k); + if ((entry = SPLAY_FIND(dict, d, &key)) == NULL) + return (NULL); + + data = entry->data; + SPLAY_REMOVE(dict, d, entry); + free(entry); + + return (data); +} + +static int +dictentry_cmp(struct dictentry *a, struct dictentry *b) +{ + return strcmp(a->key, b->key); +} + +SPLAY_GENERATE(dict, dictentry, entry, dictentry_cmp); diff --git a/lib/libfuse/fuse.3 b/lib/libfuse/fuse.3 new file mode 100644 index 00000000000..931cb0d2f55 --- /dev/null +++ b/lib/libfuse/fuse.3 @@ -0,0 +1,241 @@ +.\" +.\" Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: June 3 2013 $ +.Dt FUSE 3 +.Os +.Sh NAME +.Nm fuse , +.Nm fuse_main , +.Nm fuse_new , +.Nm fuse_parse_cmdline , +.Nm fuse_mount , +.Nm fuse_remove_signal_handlers , +.Nm fuse_set_signal_handlers , +.Nm fuse_get_session , +.Nm fuse_is_lib_option , +.Nm fuse_loop , +.Nm fuse_loop_mt , +.Nm fuse_chan_fd , +.Nm fuse_unmount , +.Nm fuse_daemonize, +.Nm fuse_destroy +.Nd FUSE implementation routines +.Sh SYNOPSIS +.Fd #include \*[Lt]fuse.h\*[Gt] +.Ft int +.Fn fuse_main "int argc" "char **argv" "const struct fuse_operations *ops" \ + "void *data" +.Ft struct fuse * +.Fn fuse_new "struct fuse_chan *fc" "struct fuse_args *args" \ + "const struct fuse_operations *ops" "size_t size" "void *userdata" +.Ft int +.Fn fuse_parse_cmdline "struct fuse_args *args" "char **mp" "int *mt" "int *fg" +.Ft struct fuse_chan * +.Fn fuse_mount "const char *dir" "struct fuse_args *args" +.Ft void +.Fn fuse_remove_signal_handlers "struct fuse_session *se" +.Ft int +.Fn fuse_set_signal_handlers "struct fuse_session *se" +.Ft struct fuse_session * +.Fn fuse_get_session "struct fuse *f" +.Ft int +.Fn fuse_is_lib_option "const char *opt" +.Ft int +.Fn fuse_loop "struct fuse *fuse" +.Ft int +.Fn fuse_loop_mt "struct fuse *fuse" +.Ft int +.Fn fuse_chan_fd "struct fuse_chan *ch" +.Ft void +.Fn fuse_unmount "const char *dir" "struct fuse_chan *ch" +.Ft int +.Fn fuse_daemonize "int foreground" +.Ft void +.Fn fuse_destroy "struct fuse *f" +.Sh DESCRIPTION +The +.Nm +library provides routine to implement a filesystem in userspace. +.Ss INTRODUCTION +There is two way of Implementing a Fuse filesystem. You can implement a fs only +by calling +.Fn fuse_main +and giving this function the +.Em ops +argument containing all the callbacks of your filesystems. Or you can use all +others functions. +.Ss FUNCTIONS +.Fn fuse_new +Fuse new return a +.Fa struct fuse +That will be needed by +.Fn fuse_loop +.Pp +.Fn fuse_parse_cmdline +This function will parse the +.Fa struct fuse_args +given by the user and will set +.Fa mp +with a char * containing the mountpoint directory. +.Pp +.Fn fuse_mount +Will look for an empty +.Xr fuse 4 +device to create a connection. If this function find an available device it will +open it to use it for fuse communication with the fuse VFS driver and will mount +the filesystem. This function will return a +.Fa struct fuse_chan +that will be needed by +.Fn fuse_new +.Pp +.Fn fuse_remove_signal_handlers +I do not know yet what this function is supposed to do... +.Pp +.Fn fuse_set_signal_handlers +I do not know yet what this function is supposed to do... +.Pp +.Fn fuse_get_session +returns a pointer on the structure +.Fa fuse_session +contained in a +.Fa struct fuse +.Pp +.Fn fuse_is_lib_option +check if the string +.Fa opt +is an option handled by the libfuse or by the fuse client. It returns 1 if it is +handled by the lib and 0 in the other case. +.Pp +.Fn fuse_loop_mt +Do not use! It is not supported yet (and we will probably never need it...) +.Pp +.Fn fuse_loop +This is the mainloop of a fuse program. This function will look for fuse +request from kernel and will respond to it using the +.Fa struct fuse_operation +present in the structure +.Fa fuse. +It will return only if something goes wrong +or if the kernel send a +.Fa FUSE_DESTROY +opcode to libfuse. +.Pp +.Fn fuse_chan_fd +returns the filedescriptor used by the given +.Fa fuse_chan +structure. +.Pp +.Fn fuse_unmount +unmount the +.Fa dir +mountpoint. +.Pp +.Fn fuse_daemonize +daemonise the fuse library. It will permit to background the fuse program that +will continue to handles kernel filesystems opcodes. +.Pp +.Fn fuse_destroy +I do not know yet what this function is supposed to do... +.Sh EXAMPLE +Here is a simple example of a fuse implementation: +.Bd -literal +#include <errno.h> +#include <fuse.h> +#include <string.h> + +static int +fs_readdir(const char *path, void *data, fuse_fill_dir_t filler, + off_t off, struct fuse_file_info *ffi) +{ + if (strcmp(path, "/") != 0) + return (-ENOENT); + + filler(data, ".", NULL, 0); + filler(data, "..", NULL, 0); + filler(data, "file", NULL, 0); + return (0); +} + +static int +fs_read(const char *path, char *buf, size_t size, off_t off, + struct fuse_file_info *ffi) +{ + if (off >= 5) + return (0); + + size = 5 - off; + memcpy(buf, "data." + off, size); + return (size); +} + +static int +fs_open(const char *path, struct fuse_file_info *ffi) +{ + if (strncmp(path, "/file", 10) != 0) + return (-ENOENT); + + if ((ffi->flags & 3) != O_RDONLY) + return (-EACCES); + + return (0); +} + +static int +fs_getattr(const char *path, struct stat *st) +{ + if (strcmp(path, "/") == 0) { + st->st_blksize = 512; + st->st_mode = 0755; + st->st_nlink = 2; + } else if (strcmp(path, "/file") == 0) { + st->st_mode = 0644; + st->st_blksize = 512; + st->st_nlink = 1; + st->st_size = 5; + } else { + return (-ENOENT); + } + + return (0); +} + +struct fuse_operations fsops = { + .readdir = fs_readdir, + .read = fs_read, + .open = fs_open, + .getattr = fs_getattr, +}; + +int +main(int ac, char **av) +{ + return (fuse_main(ac, av, &fsops, NULL)); +} +.Ed +.Sh SEE ALSO +The +.Tn FUSE +specifications and orignal implementation can be found at: +.Lk http://fuse.sourceforge.net/ +.Pp +.Xr fuse 4 +.Sh HISTORY +The +.Nm +library first appeared in +.Ox ??? . +.Sh BUGS +This man page is woefully incomplete diff --git a/lib/libfuse/fuse.c b/lib/libfuse/fuse.c new file mode 100644 index 00000000000..24099d60e66 --- /dev/null +++ b/lib/libfuse/fuse.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/wait.h> + +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "fuse_private.h" +#include "debug.h" + +static struct fuse_session *sigse; + +int +fuse_loop(struct fuse *fuse) +{ + struct fusebuf fbuf; + struct kevent ev; + int error = 0; + size_t len = 0; + int ret; + + fuse->fc->kq = kqueue(); + if (fuse->fc->kq == -1) + return (-1); + + EV_SET(&fuse->fc->event, fuse->fc->fd, EVFILT_READ, EV_ADD | + EV_ENABLE, 0, 0, 0); + + while (!fuse->fc->dead) { + ret = kevent(fuse->fc->kq, &fuse->fc->event, 1, &ev, 1, NULL); + if (ret == -1) + DPERROR(__func__); + else if (ret > 0) { + error = read(fuse->fc->fd, &fbuf, sizeof(fbuf.fb_hdr)); + if (error != sizeof(fbuf.fb_hdr)) { + DPRINTF("%s: bad hdr read\n", __func__); + errno = EINVAL; + return (-1); + } + + if (fbuf.fb_len != 0) { + error = read(fuse->fc->fd, (char *)&fbuf.F_dat, + fbuf.fb_len); + + if (error != (int)fbuf.fb_len) { + errno = EINVAL; + return (-1); + } + } + + ret = ifuse_exec_opcode(fuse, &fbuf); + if (ret) { + return (ret); + } + + len = sizeof(fbuf.fb_hdr) + fbuf.fb_len; + ret = write(fuse->fc->fd, &fbuf, len); + + if (ret != (int)len) { + errno = EINVAL; + return (-1); + } + } + } + + return (0); +} + +#define DEVPATH "/dev/" +#define FUSEDEV DEVPATH "fuse" + +struct fuse_chan * +fuse_mount(const char *dir, unused struct fuse_args *args) +{ + struct fusefs_args fargs; + struct fuse_chan *fc; + struct stat st; + char busnode[16]; + dev_t minor; + int i; + + fc = calloc(1, sizeof(*fc)); + if (fc == NULL) + return (NULL); + + fc->dir = strdup(dir); + if (fc->dir == NULL) + goto bad; + + for (i = 0; i < 8 ; i++) { + minor = -1; + snprintf(busnode, sizeof(busnode), FUSEDEV "%d", i); + + DPRINTF("trying %s\n", busnode); + if ((fc->fd = open(busnode, O_RDWR)) < 0) { + if (errno == EBUSY) + DPRINTF("device %s already opened\n", busnode); + else if (errno != ENOENT && errno != ENXIO) + DPRINTF("could not open %s\n", busnode); + continue; + } + + if (fstat(fc->fd, &st) != 0) + goto bad; + + minor = st.st_rdev; + break; + } + + if (minor == -1) { + fprintf(stderr, "%s: Cannot find a suitable fuse device\n", + __func__); + goto bad; + } + + fargs.dev = minor; + if (mount(MOUNT_FUSEFS, dir, 0, &fargs)) { + if (errno == EOPNOTSUPP) + fprintf(stderr, + "%s: %s: FS not supported by kernel\n", __func__, + dir); + else + perror("fuse_mount failure:"); + + goto bad; + } + + return (fc); +bad: + if (fc->fd > 0) + close(fc->fd); + if (fc->dir != NULL) + free(fc->dir); + return (NULL); +} + +void +fuse_unmount(const char *dir, unused struct fuse_chan *ch) +{ + int ret; + + if (ch->dead) + return ; + + if ((ret = unmount(dir, MNT_UPDATE)) == -1) + DPERROR(__func__); + + return ; +} + +int +fuse_is_lib_option(unused const char *opt) +{ + DPRINTF("option %s\n", opt); + return (0); +} + +int +fuse_chan_fd(struct fuse_chan *ch) +{ + return (ch->fd); +} + +struct fuse_session * +fuse_get_session(struct fuse *f) +{ + return (&f->se); +} + +int +fuse_loop_mt(unused struct fuse *fuse) +{ + return (0); +} + +struct fuse * +fuse_new(struct fuse_chan *fc, unused struct fuse_args *args, + const struct fuse_operations *ops, unused size_t size, + unused void *userdata) +{ + struct fuse *fuse; + struct fuse_vnode *root; + + if ((fuse = calloc(1, sizeof(*fuse))) == NULL) + return (NULL); + + /* copy fuse ops to their own structure */ + memcpy(&fuse->op, ops, sizeof(fuse->op)); + + fuse->fc = fc; + fuse->max_ino = FUSE_ROOT_INO; + fuse->se.args = fuse; + + if ((root = alloc_vn(fuse, "/", FUSE_ROOT_INO, 0)) == NULL) { + free(fuse); + return (NULL); + } + + tree_init(&fuse->vnode_tree); + tree_init(&fuse->name_tree); + if (!set_vn(fuse, root)) { + free(fuse); + return (NULL); + } + + return (fuse); +} + +int +fuse_daemonize(unused int foreground) +{ +#ifdef DEBUG + return (daemon(0,1)); +#else + return (daemon(0,0)); +#endif +} + +void +fuse_destroy(unused struct fuse *f) +{ + close(f->fc->fd); + free(f->fc->dir); + free(f->fc); + free(f); +} + +static void +ifuse_get_signal(unused int num) +{ + struct fuse *f; + pid_t child; + int status; + + if (sigse != NULL) { + child = fork(); + + if (child < 0) + return ; + + f = sigse->args; + if (child == 0) { + fuse_unmount(f->fc->dir, f->fc); + sigse = NULL; + exit(0); + } + + fuse_loop(f); + wait(&status); + } +} + +void +fuse_remove_signal_handlers(unused struct fuse_session *se) +{ + sigse = NULL; + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_DFL); +} + +int +fuse_set_signal_handlers(unused struct fuse_session *se) +{ + sigse = se; + signal(SIGHUP, ifuse_get_signal); + signal(SIGINT, ifuse_get_signal); + signal(SIGTERM, ifuse_get_signal); + signal(SIGPIPE, SIG_IGN); + return (0); +} + +int +fuse_parse_cmdline(struct fuse_args *args, char **mp, int *mt, unused int *fg) +{ + int i; + +#ifdef DEBUG + ifuse_debug_init(); +#endif + + for (i = args->argc - 1 ; i > 0 && *args->argv[i] == '-' ; --i); + *mp = args->argv[i]; + *mt = 0; + + return (0); +} + +int +fuse_main(int argc, char **argv, const struct fuse_operations *ops, void *data) +{ + struct fuse *fuse; + struct fuse_chan *fc; + struct fuse_args args; + char *mountpoint; + int mt, fg; + + args.argc = argc; + args.argv = argv; + fuse_parse_cmdline(&args, &mountpoint, &mt, &fg); + + fuse_daemonize(0); + + if ((fc = fuse_mount(mountpoint, NULL)) == NULL) + return (-1); + + if ((fuse = fuse_new(fc, NULL, ops, sizeof(*(ops)), data)) == NULL) { + free(fc); + return (-1); + } + + return (fuse_loop(fuse)); +} diff --git a/lib/libfuse/fuse.h b/lib/libfuse/fuse.h new file mode 100644 index 00000000000..1ac2133f193 --- /dev/null +++ b/lib/libfuse/fuse.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FUSE_H_ +#define _FUSE_H_ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/statvfs.h> + +#include <fcntl.h> +#include <utime.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct fuse_chan; +struct fuse_args; +struct fuse_session; + +struct fuse_file_info { + int32_t flags; /* open(2) flags */ + uint32_t fh_old; /* old file handle */ + int32_t writepage; + uint32_t direct_io:1; + uint32_t keep_cache:1; + uint32_t flush:1; + uint32_t __padd:29; + uint64_t fh; /* file handle */ + uint64_t lock_owner; +}; + +struct fuse_conn_info { + uint32_t proto_major; + uint32_t proto_minor; + uint32_t async_read; + uint32_t max_write; + uint32_t max_readahead; + uint32_t reserved[27]; +}; + +typedef ino_t fuse_ino_t; +typedef int (*fuse_fill_dir_t)(void *, const char *, const struct stat *, + off_t); + +typedef struct fuse_dirhandle { + fuse_fill_dir_t filler; + void *buf; + int filled; + int full; + int isgetdir; + uint32_t size; + uint32_t start; + uint32_t idx; +} *fuse_dirh_t; + +typedef int (*fuse_dirfil_t)(fuse_dirh_t, const char *, int, ino_t); + +struct fuse_operations { + int (*getattr)(const char *, struct stat *); + int (*readlink)(const char *, char *, size_t); + int (*getdir)(const char *, fuse_dirh_t, fuse_dirfil_t); + int (*mknod)(const char *, mode_t, dev_t); + int (*mkdir)(const char *, mode_t); + int (*unlink)(const char *); + int (*rmdir)(const char *); + int (*symlink)(const char *, const char *); + int (*rename)(const char *, const char *); + int (*link)(const char *, const char *); + int (*chmod)(const char *, mode_t); + int (*chown)(const char *, uid_t, gid_t); + int (*truncate)(const char *, off_t); + int (*utime)(const char *, struct utimbuf *); + int (*open)(const char *, struct fuse_file_info *); + int (*read)(const char *, char *, size_t, off_t, + struct fuse_file_info *); + int (*write)(const char *, const char *, size_t, off_t, + struct fuse_file_info *); + int (*statfs)(const char *, struct statvfs *); + int (*flush)(const char *, struct fuse_file_info *); + int (*release)(const char *, struct fuse_file_info *); + int (*fsync)(const char *, int, struct fuse_file_info *); + int (*setxattr)(const char *, const char *, const char *, size_t, + int); + int (*getxattr)(const char *, const char *, char *, size_t); + int (*listxattr)(const char *, char *, size_t); + int (*removexattr)(const char *, const char *); + int (*opendir)(const char *, struct fuse_file_info *); + int (*readdir)(const char *, void *, fuse_fill_dir_t, off_t, + struct fuse_file_info *); + int (*releasedir)(const char *, struct fuse_file_info *); + int (*fsyncdir)(const char *, int, struct fuse_file_info *); + void *(*init)(struct fuse_conn_info *); + void (*destroy)(void *); + int (*access)(const char *, int); + int (*create)(const char *, mode_t, struct fuse_file_info *); + int (*ftruncate)(const char *, off_t, struct fuse_file_info *); + int (*fgetattr)(const char *, struct stat *, struct fuse_file_info *); + int (*lock)(const char *, struct fuse_file_info *, int, struct flock *); + int (*utimens)(const char *, const struct timespec *); + int (*bmap)(const char *, size_t , uint64_t *); +}; + +#ifndef FUSE_USE_VERSION +#define FUSE_USE_VERSION 26 +#endif + +#if FUSE_USE_VERSION >= 26 +#define FUSE_VERSION 26 +#else +#error "Fuse version < 26 not supported" +#endif + + +/* + * API prototypes + */ +int fuse_main(int, char **, const struct fuse_operations *, void *); +struct fuse *fuse_new(struct fuse_chan *, struct fuse_args *, + const struct fuse_operations *, size_t, void *); +int fuse_parse_cmdline(struct fuse_args *, char **, int *, int *); +struct fuse_chan *fuse_mount(const char *, struct fuse_args *); +void fuse_remove_signal_handlers(struct fuse_session *); +int fuse_set_signal_handlers(struct fuse_session *); +struct fuse_session *fuse_get_session(struct fuse *); +int fuse_is_lib_option(const char *); +int fuse_loop(struct fuse *); +int fuse_loop_mt(struct fuse *); +int fuse_chan_fd(struct fuse_chan *); +void fuse_unmount(const char *, struct fuse_chan *); +int fuse_daemonize(int); +void fuse_destroy(struct fuse *); + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_H_ */ diff --git a/lib/libfuse/fuse_ops.c b/lib/libfuse/fuse_ops.c new file mode 100644 index 00000000000..ec85e00c0ca --- /dev/null +++ b/lib/libfuse/fuse_ops.c @@ -0,0 +1,884 @@ +/* + * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#include "fuse_private.h" +#include "debug.h" + +#define CHECK_OPT(opname) DPRINTF("Opcode:\t%s\n", #opname); \ + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); \ + if (!f->op.opname) { \ + fbuf->fb_err = -ENOSYS; \ + fbuf->fb_len = 0; \ + DPRINTF("\n"); \ + return (0); \ + } + +static void +stat2attr(struct vattr *v, struct stat *st) +{ + v->va_fileid = st->st_ino; + v->va_bytes = st->st_blocks; + v->va_mode = st->st_mode; + v->va_nlink = st->st_nlink; + v->va_uid = st->st_uid; + v->va_gid = st->st_gid; + v->va_rdev = st->st_rdev; + v->va_size = st->st_size; + v->va_blocksize = st->st_blksize; + v->va_atime.tv_sec = st->st_atime; + v->va_atime.tv_nsec = st->st_atimensec; + v->va_mtime.tv_sec = st->st_mtime; + v->va_mtime.tv_nsec = st->st_mtimensec; + v->va_ctime.tv_sec = st->st_ctime; + v->va_ctime.tv_nsec = st->st_ctimensec; +} + +static int +update_vattr(struct fuse *f, struct vattr *attr, const char *realname, + struct fuse_vnode *vn) +{ + struct stat st; + int ret; + + bzero(&st, sizeof(st)); + ret = f->op.getattr(realname, &st); + + st.st_ino = vn->ino; + + if (f->conf.set_mode) + st.st_mode = (st.st_mode & S_IFMT) | (0777 & ~f->conf.umask); + + if (f->conf.set_uid) + st.st_uid = f->conf.uid; + + if (f->conf.set_gid) + st.st_gid = f->conf.gid; + + stat2attr(attr, &st); + + return (ret); +} + +static int +ifuse_ops_init(struct fusebuf *fbuf) +{ + DPRINTF("Opcode:\tinit\n"); + fbuf->fb_len = 0; + DPRINTF("\n"); + + return (0); +} + +static int +ifuse_ops_getattr(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + struct stat st; + char *realname; + + DPRINTF("Opcode:\tgetattr\n"); + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); + bzero(&st, sizeof(st)); + + fbuf->fb_len = FUSEFDSIZE; + bzero(&fbuf->fb_vattr, sizeof(fbuf->fb_vattr)); + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + fbuf->fb_err = update_vattr(f, &fbuf->fb_vattr, realname, vn); + free(realname); + + if (fbuf->fb_err) + fbuf->fb_len = 0; + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_access(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + char *realname; + + CHECK_OPT(access); + + fbuf->fb_len = 0; + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.access(realname, fbuf->fb_io_mode); + free(realname); + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_open(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_file_info ffi; + struct fuse_vnode *vn; + char *realname; + + DPRINTF("Opcode:\topen\n"); + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); + + CHECK_OPT(open); + + bzero(&ffi, sizeof(ffi)); + ffi.flags = fbuf->fb_io_flags; + + fbuf->fb_len = FUSEFDSIZE; + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.open(realname, &ffi); + free(realname); + + if (!fbuf->fb_err) + fbuf->fb_io_fd = ffi.fh; + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_opendir(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_file_info ffi; + struct fuse_vnode *vn; + char *realname; + + DPRINTF("Opcode:\topendir\n"); + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); + + memset(&ffi, 0, sizeof(ffi)); + ffi.flags = fbuf->fb_io_flags; + + fbuf->fb_len = 0; + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + if (f->op.opendir) { + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.opendir(realname, &ffi); + free(realname); + } + + if (!fbuf->fb_err) { + fbuf->fb_io_fd = ffi.fh; + fbuf->fb_len = FUSEFDSIZE; + + vn->fd = calloc(1, sizeof(*vn->fd)); + if (vn->fd == NULL) { + DPRINTF("\n"); + return (errno); + } + + vn->fd->filled = 0; + vn->fd->size = 0; + vn->fd->start = 0; + } + + DPRINTF("\n"); + return (0); +} + +#define GENERIC_DIRSIZ(NLEN) \ +((sizeof (struct dirent) - (MAXNAMLEN+1)) + ((NLEN+1 + 3) &~ 3)) + +static int +ifuse_fill_readdir(void *dh, const char *name, const struct stat *stbuf, + off_t off) +{ + struct fuse_dirhandle *fd = dh; + struct fusebuf *fbuf; + struct dirent *dir; + uint32_t namelen; + uint32_t len; + + fbuf = fd->buf; + namelen = strnlen(name, MAXNAMLEN); + len = GENERIC_DIRSIZ(namelen); + + if (fd->full || fbdatsize(fbuf) + len > fd->size) { + fd->full = 1; + return (0); + } + + if (fd->isgetdir && fd->start != 0 && fd->idx < fd->start) { + fd->idx += len; + return (0); + } + + dir = (struct dirent *) &fbuf->fb_dat[fbdatsize(fbuf)]; + + if (!off) + fd->filled = 0; + + if (stbuf) { + dir->d_fileno = stbuf->st_ino; + dir->d_type = stbuf->st_mode; + } else { + dir->d_fileno = 0xffffffff; + dir->d_type = 0; + } + dir->d_namlen = namelen; + dir->d_reclen = len; + memcpy(dir->d_name, name, namelen); + + fbuf->fb_len += len; + if (fd->isgetdir) { + fd->start += len; + fd->idx += len; + } + return (0); +} + +static int +ifuse_fill_getdir(fuse_dirh_t fd, const char *name, int type, ino_t ino) +{ + struct stat st; + + bzero(&st, sizeof(st)); + st.st_mode = type << 12; + if (ino == 0) + st.st_ino = 0xffffffff; + else + st.st_ino = ino; + + return (fd->filler(fd, name, &st, 0)); +} + +static int +ifuse_ops_readdir(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_file_info ffi; + struct fuse_vnode *vn; + char *realname; + uint64_t offset; + uint32_t size; + uint32_t startsave; + + DPRINTF("Opcode:\treaddir\n"); + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); + DPRINTF("Offset:\t%llu\n", fbuf->fb_io_off); + DPRINTF("Size:\t%lu\n", fbuf->fb_io_len); + + bzero(&ffi, sizeof(ffi)); + ffi.fh = fbuf->fb_io_fd; + offset = fbuf->fb_io_off; + size = fbuf->fb_io_len; + startsave = 0; + + fbuf->fb_len = FUSEFDSIZE; + bzero(fbuf->fb_dat, FUSELEN); + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + if (!vn->fd->filled) { + vn->fd->filler = ifuse_fill_readdir; + vn->fd->buf = fbuf; + vn->fd->filled = 0; + vn->fd->full = 0; + vn->fd->isgetdir = 0; + vn->fd->size = size; + + realname = build_realname(f, vn->ino); + if (f->op.readdir) + fbuf->fb_err = f->op.readdir(realname, vn->fd, + ifuse_fill_readdir, offset, &ffi); + else if (f->op.getdir) { + vn->fd->isgetdir = 1; + vn->fd->idx = 0; + startsave = vn->fd->start; + fbuf->fb_err = f->op.getdir(realname, vn->fd, + ifuse_fill_getdir); + } else + fbuf->fb_err = -ENOSYS; + free(realname); + } + + if (!vn->fd->full && vn->fd->isgetdir && vn->fd->start == startsave) + vn->fd->filled = 1; + + if (fbuf->fb_err) { + fbuf->fb_len = 0; + vn->fd->filled = 1; + } + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_releasedir(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_file_info ffi; + struct fuse_vnode *vn; + char *realname; + + DPRINTF("Opcode:\treleasedir\n"); + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); + + bzero(&ffi, sizeof(ffi)); + ffi.fh = fbuf->fb_io_fd; + ffi.fh_old = ffi.fh; + ffi.flags = fbuf->fb_io_flags; + + fbuf->fb_len = 0; + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + if (f->op.releasedir) { + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.releasedir(realname, &ffi); + free(realname); + } else + fbuf->fb_err = 0; + + if (!fbuf->fb_err) + free(vn->fd); + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_release(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_file_info ffi; + struct fuse_vnode *vn; + char *realname; + + CHECK_OPT(release); + + bzero(&ffi, sizeof(ffi)); + ffi.fh = fbuf->fb_io_fd; + ffi.fh_old = ffi.fh; + ffi.flags = fbuf->fb_io_flags; + + fbuf->fb_len = 0; + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.release(realname, &ffi); + free(realname); + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_lookup(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + char *realname; + + DPRINTF("Opcode:\tlookup\n"); + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); + DPRINTF("For file %s\n", fbuf->fb_dat); + + vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino); + if (vn == NULL) { + vn = alloc_vn(f, fbuf->fb_dat, -1, fbuf->fb_ino); + if (vn == NULL) { + fbuf->fb_err = -ENOMEM; + fbuf->fb_len = 0; + DPRINTF("\n"); + return (0); + } + set_vn(f, vn); /*XXX*/ + } + + DPRINTF("new ino %i\n", vn->ino); + fbuf->fb_len = FUSEFDSIZE; + + realname = build_realname(f, vn->ino); + fbuf->fb_err = update_vattr(f, &fbuf->fb_vattr, realname, vn); + free(realname); + + if (fbuf->fb_err) + fbuf->fb_len = 0; + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_read(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_file_info ffi; + struct fuse_vnode *vn; + char *realname; + uint64_t offset; + uint32_t size; + int ret; + + CHECK_OPT(read); + + bzero(&ffi, sizeof(ffi)); + ffi.fh = fbuf->fb_io_fd; + size = fbuf->fb_io_len; + offset = fbuf->fb_io_off; + + fbuf->fb_len = FUSEFDSIZE + size; + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + ret = f->op.read(realname, fbuf->fb_dat, size, offset, &ffi); + free(realname); + if (ret >= 0) { + fbuf->fb_len = FUSEFDSIZE + ret; + fbuf->fb_err = 0; + } else { + fbuf->fb_len = 0; + fbuf->fb_err = ret; + } + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_write(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_file_info ffi; + struct fuse_vnode *vn; + char *realname; + uint64_t offset; + uint32_t size; + int ret; + + CHECK_OPT(write); + + bzero(&ffi, sizeof(ffi)); + ffi.fh = fbuf->fb_io_fd; + ffi.fh_old = ffi.fh; + ffi.writepage = fbuf->fb_io_flags & 1; + size = fbuf->fb_io_len; + offset = fbuf->fb_io_off; + + fbuf->fb_len = 0; + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + ret = f->op.write(realname, fbuf->fb_dat, size, offset, &ffi); + free(realname); + + if (ret >= 0) { + fbuf->fb_io_len = ret; + fbuf->fb_err = 0; + + fbuf->fb_len = FUSEFDSIZE; + } else + fbuf->fb_err = ret; + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_create(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_file_info ffi; + struct fuse_vnode *vn; + uint32_t mode; + + char *realname; + + CHECK_OPT(create); + + bzero(&ffi, sizeof(ffi)); + ffi.flags = fbuf->fb_io_flags; + mode = fbuf->fb_io_mode; + vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino); + + fbuf->fb_len = 0; + + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.create(realname, mode, &ffi); + + if (!fbuf->fb_err) { + fbuf->fb_err = update_vattr(f, &fbuf->fb_vattr, realname, vn); + fbuf->fb_ino = fbuf->fb_vattr.va_fileid; + fbuf->fb_io_mode = fbuf->fb_vattr.va_mode; + + if (!fbuf->fb_err) + fbuf->fb_len = FUSEFDSIZE; + } + free(realname); + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_mkdir(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + char *realname; + uint32_t mode; + + CHECK_OPT(mkdir); + + mode = fbuf->fb_io_mode; + vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino); + + fbuf->fb_len = 0; + + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.mkdir(realname, mode); + + if (!fbuf->fb_err) { + fbuf->fb_err = update_vattr(f, &fbuf->fb_vattr, realname, vn); + fbuf->fb_io_mode = fbuf->fb_vattr.va_mode; + fbuf->fb_ino = vn->ino; + + if (!fbuf->fb_err) + fbuf->fb_len = FUSEFDSIZE; + } + free(realname); + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_rmdir(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + char *realname; + + CHECK_OPT(rmdir); + + fbuf->fb_len = 0; + + vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.rmdir(realname); + free(realname); + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_readlink(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + char *realname; + char name[PATH_MAX + 1]; + int len, ret; + + DPRINTF("Opcode:\treadlink\n"); + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + if (f->op.readlink) + ret = f->op.readlink(realname, name, sizeof(name)); + else + ret = -ENOSYS; + free(realname); + + if (!ret) + len = strnlen(name, PATH_MAX); + else + len = -1; + + fbuf->fb_len = FUSEFDSIZE + len + 1; + fbuf->fb_err = ret; + + if (!ret) { + memcpy(fbuf->fb_dat, name, len); + fbuf->fb_dat[len] = '\0'; + } else + fbuf->fb_len = 0; + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_unlink(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + char *realname; + + fbuf->fb_len = 0; + + CHECK_OPT(unlink); + + vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.unlink(realname); + free(realname); + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_statfs(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + char *realname; + + fbuf->fb_len = FUSEFDSIZE; + bzero(&fbuf->fb_stat, sizeof(fbuf->fb_stat)); + + CHECK_OPT(statfs); + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + + realname = build_realname(f, vn->ino); + fbuf->fb_err = f->op.statfs(realname, &fbuf->fb_stat); + free(realname); + + if (fbuf->fb_err) + fbuf->fb_len = 0; + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_link(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + char *realname; + char *realname_ln; + ino_t oldnodeid; + + CHECK_OPT(link); + oldnodeid = fbuf->fb_io_ino; + vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino); + + fbuf->fb_len = 0; + + realname = build_realname(f, oldnodeid); + realname_ln = build_realname(f, vn->ino); + fbuf->fb_err = f->op.link(realname, realname_ln); + free(realname); + free(realname_ln); + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_setattr(struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + struct timespec ts[2]; + struct utimbuf tbuf; + struct fb_io *io; + char *realname; + uid_t uid; + gid_t gid; + + DPRINTF("Opcode:\tsetattr\n"); + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); + + vn = tree_get(&f->vnode_tree, fbuf->fb_ino); + realname = build_realname(f, vn->ino); + io = fbtod(fbuf, struct fb_io *); + + if (io->fi_flags & FUSE_FATTR_MODE) { + if (f->op.chmod) + fbuf->fb_err = f->op.chmod(realname, + fbuf->fb_vattr.va_mode); + else + fbuf->fb_err = -ENOSYS; + } + + if (!fbuf->fb_err && (io->fi_flags & FUSE_FATTR_UID || + io->fi_flags & FUSE_FATTR_GID) ) { + uid = (io->fi_flags & FUSE_FATTR_UID) ? + fbuf->fb_vattr.va_uid : (gid_t)-1; + gid = (io->fi_flags & FUSE_FATTR_GID) ? + fbuf->fb_vattr.va_gid : (uid_t)-1; + if (f->op.chown) + fbuf->fb_err = f->op.chown(realname, uid, gid); + else + fbuf->fb_err = -ENOSYS; + } + + if (!fbuf->fb_err && ( io->fi_flags & FUSE_FATTR_MTIME || + io->fi_flags & FUSE_FATTR_ATIME)) { + ts[0].tv_sec = fbuf->fb_vattr.va_atime.tv_sec; + ts[0].tv_nsec = fbuf->fb_vattr.va_atime.tv_nsec; + ts[1].tv_sec = fbuf->fb_vattr.va_mtime.tv_sec; + ts[1].tv_nsec = fbuf->fb_vattr.va_mtime.tv_nsec; + tbuf.actime = ts[0].tv_sec; + tbuf.modtime = ts[1].tv_sec; + + if (f->op.utimens) + fbuf->fb_err = f->op.utimens(realname, ts); + else if (f->op.utime) + fbuf->fb_err = f->op.utime(realname, &tbuf); + else + fbuf->fb_err = -ENOSYS; + } + + fbuf->fb_len = FUSEFDSIZE; + bzero(&fbuf->fb_vattr, sizeof(fbuf->fb_vattr)); + + if (!fbuf->fb_err) + fbuf->fb_err = update_vattr(f, &fbuf->fb_vattr, realname, vn); + free(realname); + + if (fbuf->fb_err) + fbuf->fb_len = 0; + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_symlink(unused struct fuse *f, struct fusebuf *fbuf) +{ + struct fuse_vnode *vn; + char *realname; + int len; + + CHECK_OPT(symlink); + + vn = get_vn_by_name_and_parent(f, fbuf->fb_dat, fbuf->fb_ino); + len = strlen(fbuf->fb_dat); + fbuf->fb_len = 0; + + realname = build_realname(f, vn->ino); + /* fuse invert the symlink params */ + fbuf->fb_err = f->op.symlink(&fbuf->fb_dat[len + 1], realname); + fbuf->fb_ino = vn->ino; + free(realname); + + DPRINTF("\n"); + return (0); +} + +static int +ifuse_ops_destroy(struct fuse *f, struct fusebuf *fbuf) +{ + DPRINTF("Opcode:\tdestroy\n"); + + fbuf->fb_len = 0; + + fbuf->fb_err = 0; + f->fc->dead = 1; + + DPRINTF("\n"); + return (0); +} + +int +ifuse_exec_opcode(struct fuse *f, struct fusebuf *fbuf) +{ + int ret = 0; + + switch (fbuf->fb_type) { + case FBT_LOOKUP: + ret = ifuse_ops_lookup(f, fbuf); + break; + case FBT_GETATTR: + ret = ifuse_ops_getattr(f, fbuf); + break; + case FBT_SETATTR: + ret = ifuse_ops_setattr(f, fbuf); + break; + case FBT_READLINK: + ret = ifuse_ops_readlink(f, fbuf); + break; + case FBT_MKDIR: + ret = ifuse_ops_mkdir(f, fbuf); + break; + case FBT_UNLINK: + ret = ifuse_ops_unlink(f, fbuf); + break; + case FBT_RMDIR: + ret = ifuse_ops_rmdir(f, fbuf); + break; + case FBT_LINK: + ret = ifuse_ops_link(f, fbuf); + break; + case FBT_OPEN: + ret = ifuse_ops_open(f, fbuf); + break; + case FBT_READ: + ret = ifuse_ops_read(f, fbuf); + break; + case FBT_WRITE: + ret = ifuse_ops_write(f, fbuf); + break; + case FBT_STATFS: + ret = ifuse_ops_statfs(f, fbuf); + break; + case FBT_RELEASE: + ret = ifuse_ops_release(f, fbuf); + break; + case FBT_INIT: + ret = ifuse_ops_init(fbuf); + break; + case FBT_OPENDIR: + ret = ifuse_ops_opendir(f, fbuf); + break; + case FBT_READDIR: + ret = ifuse_ops_readdir(f, fbuf); + break; + case FBT_RELEASEDIR: + ret = ifuse_ops_releasedir(f, fbuf); + break; + case FBT_ACCESS: + ret = ifuse_ops_access(f, fbuf); + break; + case FBT_CREATE: + ret = ifuse_ops_create(f, fbuf); + break; + case FBT_SYMLINK: + ret = ifuse_ops_symlink(f, fbuf); + break; + case FBT_DESTROY: + ret = ifuse_ops_destroy(f, fbuf); + break; + default: + DPRINTF("Opcode:\t%i not supported\n", fbuf->fb_type); + DPRINTF("Inode:\t%i\n", fbuf->fb_ino); + + fbuf->fb_err = -ENOSYS; + fbuf->fb_len = 0; + DPRINTF("\n"); + } + + /* fuse api use negative errno */ + fbuf->fb_err = -fbuf->fb_err; + return (ret); +} diff --git a/lib/libfuse/fuse_private.h b/lib/libfuse/fuse_private.h new file mode 100644 index 00000000000..7ce0d5426d7 --- /dev/null +++ b/lib/libfuse/fuse_private.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FUSE_SUBR_H_ +#define _FUSE_SUBR_H_ + +#include <sys/param.h> +#include <sys/dirent.h> +#include <sys/mount.h> +#include <sys/statvfs.h> +#include <sys/vnode.h> +#include <sys/fusebuf.h> + +#include "fuse.h" + +struct fuse_dirhandle; + +struct fuse_vnode { + ino_t ino; + ino_t parent; + + char path[NAME_MAX]; + struct fuse_dirhandle *fd; + + SIMPLEQ_ENTRY(fuse_vnode) node; /* for dict */ +}; + +SIMPLEQ_HEAD(fuse_vn_head, fuse_vnode); +SPLAY_HEAD(dict, dictentry); +SPLAY_HEAD(tree, treeentry); + +struct fuse_args { + int argc; + char **argv; + int allocated; +}; + +struct fuse_session { + void *args; +}; + +struct fuse_chan { + char *dir; + struct fuse_args *args; + + int fd; + int dead; + + /* kqueue stuff */ + int kq; + struct kevent event; +}; + +struct fuse_config { + uid_t uid; + gid_t gid; + mode_t umask; + int set_mode; + int set_uid; + int set_gid; +}; + +struct fuse { + struct fuse_chan *fc; + struct fuse_operations op; + + int compat; + + struct tree vnode_tree; + struct dict name_tree; + uint64_t max_ino; + + struct fuse_config conf; + struct fuse_session se; +}; + +#define FUSE_MAX_OPS 39 +#define FUSE_ROOT_INO ((ino_t)1) + +/* fuse_ops.c */ +int ifuse_exec_opcode(struct fuse *, struct fusebuf *); + +/* fuse_subr.c */ +struct fuse_vnode *alloc_vn(struct fuse *, const char *, ino_t, ino_t); +struct fuse_vnode *get_vn_by_name_and_parent(struct fuse *, const char *, + ino_t); +int set_vn(struct fuse *, struct fuse_vnode *); +char *build_realname(struct fuse *, ino_t); + +/* tree.c */ +#define tree_init(t) SPLAY_INIT((t)) +#define tree_empty(t) SPLAY_EMPTY((t)) +int tree_check(struct tree *, uint64_t); +void *tree_set(struct tree *, uint64_t, void *); +void *tree_get(struct tree *, uint64_t);; +void *tree_pop(struct tree *, uint64_t); + +/* dict.c */ +int dict_check(struct dict *, const char *); +void *dict_set(struct dict *, const char *, void *); +void *dict_get(struct dict *, const char *);; +void *dict_pop(struct dict *, const char *); + +#define FUSE_VERSION_PKG_INFO "2.6.9" +#define unused __attribute__ ((unused)) + +#endif /* _FUSE_SUBR_ */ diff --git a/lib/libfuse/fuse_subr.c b/lib/libfuse/fuse_subr.c new file mode 100644 index 00000000000..2124043697c --- /dev/null +++ b/lib/libfuse/fuse_subr.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "fuse_private.h" +#include "debug.h" + +struct fuse_vnode * +alloc_vn(struct fuse *f, const char *path, ino_t ino, ino_t parent) +{ + struct fuse_vnode *vn; + + DPRINTF("%s %i@%i = %s\n", __func__, ino, parent, path); + if ((vn = malloc(sizeof(*vn))) == NULL) { + DPERROR("alloc_vn:"); + return (NULL); + } + + vn->ino = ino; + vn->parent = parent; + strncpy(vn->path, path, NAME_MAX); + vn->path[NAME_MAX - 1] = '\0'; + if (ino == (ino_t)-1) { + f->max_ino++; + vn->ino = f->max_ino; + } + + return (vn); +} + +int +set_vn(struct fuse *f, struct fuse_vnode *v) +{ + struct fuse_vn_head *vn_head; + struct fuse_vnode *vn; + + DPRINTF("%s: create or update vnode %i%i = %s\n", __func__, v->ino, + v->parent, v->path); + + if (tree_set(&f->vnode_tree, v->ino, v) == NULL) + return (0); + + if (!dict_check(&f->name_tree, v->path)) { + vn_head = malloc(sizeof(*vn_head)); + if (vn_head == NULL) + return (0); + SIMPLEQ_INIT(vn_head); + } else { + vn_head = dict_get(&f->name_tree, v->path); + if (vn_head == NULL) + return (0); + } + + SIMPLEQ_FOREACH(vn, vn_head, node) { + if (v->parent == vn->parent && v->ino == vn->ino) + return (1); + } + + SIMPLEQ_INSERT_TAIL(vn_head, v, node); + dict_set(&f->name_tree, v->path, vn_head); + + return (1); +} + +struct fuse_vnode * +get_vn_by_name_and_parent(struct fuse *f, const char *path, ino_t parent) +{ + struct fuse_vn_head *vn_head; + struct fuse_vnode *v = NULL; + + DPRINTF("%s %i = %s\n", __func__, parent, path); + vn_head = dict_get(&f->name_tree, path); + + if (vn_head == NULL) + return (NULL); + + SIMPLEQ_FOREACH(v, vn_head, node) + if (v->parent == parent) + return (v); + + return (NULL); +} + +char * +build_realname(struct fuse *f, ino_t ino) +{ + struct fuse_vnode *vn; + char *name = strdup("/"); + char *tmp = NULL; + int firstshot = 0; + + vn = tree_get(&f->vnode_tree, ino); + if (!vn || !name) { + free(name); + return (NULL); + } + + while (vn->parent != 0) { + if (firstshot++) + asprintf(&tmp, "/%s%s", vn->path, name); + else + asprintf(&tmp, "/%s", vn->path); + + if (tmp == NULL) { + free(name); + return (NULL); + } + + free(name); + name = tmp; + tmp = NULL; + vn = tree_get(&f->vnode_tree, vn->parent); + + if (!vn) + return (NULL); + } + + if (ino == (ino_t)0) + DPRINTF("%s: NULL ino\n", __func__); + + DPRINTF("realname %s\n", name); + return (name); +} diff --git a/lib/libfuse/generate_pkgconfig.sh b/lib/libfuse/generate_pkgconfig.sh new file mode 100644 index 00000000000..15c44b297b9 --- /dev/null +++ b/lib/libfuse/generate_pkgconfig.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# +# $OpenBSD: generate_pkgconfig.sh,v 1.1 2013/06/03 16:00:50 tedu Exp $ +# +# Copyright (c) 2010,2011 Jasper Lievisse Adriaanse <jasper@openbsd.org> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Generate pkg-config file for zlib. + +usage() { + echo "usage: ${0##*/} -c current_directory -o obj_directory" + exit 1 +} + +curdir= +objdir= +while getopts "c:o:" flag; do + case "$flag" in + c) + curdir=$OPTARG + ;; + o) + objdir=$OPTARG + ;; + *) + usage + ;; + esac +done + +[ -n "${curdir}" ] || usage +if [ ! -d "${curdir}" ]; then + echo "${0##*/}: ${curdir}: not found" + exit 1 +fi +[ -n "${objdir}" ] || usage +if [ ! -w "${objdir}" ]; then + echo "${0##*/}: ${objdir}: not found or not writable" + exit 1 +fi + +version_re="s/^#define[[:blank:]]+FUSE_VERSION_PKG_INFO[[:blank:]]+\"(.*)\".*/\1/p" +version_file=${curdir}/fuse_private.h +lib_version=$(sed -nE ${version_re} ${version_file}) + +pc_file="${objdir}/fuse.pc" +cat > ${pc_file} << __EOF__ +prefix=/usr +exec_prefix=\${prefix} +libdir=\${exec_prefix}/lib +includedir=\${prefix}/include + +Name: fuse +Description: fuse filesystem library +Version: ${lib_version} +Requires: +Libs: -L\${libdir} -lfuse +Cflags: -I\${includedir} +__EOF__ diff --git a/lib/libfuse/shlib_version b/lib/libfuse/shlib_version new file mode 100644 index 00000000000..97c9f92d6b8 --- /dev/null +++ b/lib/libfuse/shlib_version @@ -0,0 +1,2 @@ +major=0 +minor=0 diff --git a/lib/libfuse/tree.c b/lib/libfuse/tree.c new file mode 100644 index 00000000000..8050f6da6e9 --- /dev/null +++ b/lib/libfuse/tree.c @@ -0,0 +1,100 @@ +/* $OpenBSD: tree.c,v 1.1 2013/06/03 16:00:50 tedu Exp $ */ + +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> + +#include "fuse_private.h" + +struct treeentry { + SPLAY_ENTRY(treeentry) entry; + uint64_t id; + void *data; +}; + +static int treeentry_cmp(struct treeentry *, struct treeentry *); + +SPLAY_PROTOTYPE(tree, treeentry, entry, treeentry_cmp); + +int +tree_check(struct tree *t, uint64_t id) +{ + struct treeentry key; + + key.id = id; + return (SPLAY_FIND(tree, t, &key) != NULL); +} + +void * +tree_set(struct tree *t, uint64_t id, void *data) +{ + struct treeentry *entry, key; + + key.id = id; + if ((entry = SPLAY_FIND(tree, t, &key)) == NULL) { + entry = malloc(sizeof *entry); + if (entry == NULL) + return (NULL); + entry->id = id; + SPLAY_INSERT(tree, t, entry); + } + + entry->data = data; + + return (entry); +} + +void * +tree_get(struct tree *t, uint64_t id) +{ + struct treeentry key, *entry; + + key.id = id; + if ((entry = SPLAY_FIND(tree, t, &key)) == NULL) + return (NULL); + + return (entry->data); +} + +void * +tree_pop(struct tree *t, uint64_t id) +{ + struct treeentry key, *entry; + void *data; + + key.id = id; + if ((entry = SPLAY_FIND(tree, t, &key)) == NULL) + return (NULL); + + data = entry->data; + SPLAY_REMOVE(tree, t, entry); + free(entry); + + return (data); +} + +static int +treeentry_cmp(struct treeentry *a, struct treeentry *b) +{ + if (a->id < b->id) + return (-1); + if (a->id > b->id) + return (1); + return (0); +} + +SPLAY_GENERATE(tree, treeentry, entry, treeentry_cmp); |