aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog42
-rw-r--r--configure.in4
-rw-r--r--include/fuse.h36
-rw-r--r--include/fuse_common.h11
-rw-r--r--include/fuse_compat.h56
-rw-r--r--include/fuse_lowlevel.h36
-rw-r--r--include/fuse_lowlevel_compat.h57
-rw-r--r--include/fuse_opt.h41
-rw-r--r--kernel/configure.ac2
-rw-r--r--kernel/file.c9
-rw-r--r--kernel/fuse_i.h3
-rw-r--r--kernel/fuse_kernel.h16
-rw-r--r--kernel/inode.c21
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/fuse.c34
-rw-r--r--lib/fuse_i.h2
-rw-r--r--lib/fuse_lowlevel.c117
-rw-r--r--lib/fuse_opt.c55
-rw-r--r--lib/fuse_versionscript24
-rw-r--r--lib/helper.c38
-rw-r--r--lib/mount.c1
21 files changed, 495 insertions, 112 deletions
diff --git a/ChangeLog b/ChangeLog
index 1298d09..0ea0d2e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+2006-01-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fuse_opt: add new helper constants FUSE_OPT_KEY_KEEP and
+ FUSE_OPT_KEY_DISCARD
+
+ * Add options 'max_readahead', 'sync_read' and 'async_read'
+
+ * Kernel ABI version 7.6:
+
+ * Negotiate the 'max_readahead' value and 'async_read' flags with
+ userspace in the INIT method
+
+ * Add connection info to ->init() methods to both lowlevel and
+ highlevel API
+
+ * Fall back to synchronous read() behavior if either library or
+ userspace filesystem is using the old interface version. This is
+ needed so non-updated filesystems won't be confused by the
+ different read() behavior
+
+2006-01-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: if "fsname=" option was given, pass it to fusermount
+
+ * fuse_opt: add new fuse_opt_insert_arg() function, which is
+ needed by filesystems to implement some argument manipulations
+ correctly
+
+ * fuse_opt: fix memory leak in handling "--" option
+
+2006-01-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: fix detection of case when fuse is not configured into
+ the kernel either as module or built-in
+
+ * fuse_opt.h: fix incompatibility with C++ compilers by renaming
+ 'template' structure member to 'templ'. Reported by Takashi Iwai
+
+ * fuse.h: fix compatibility bugs. Patch by Yura Pakhuchiy
+
+ * kernel: support version 2.6.16 (i_sem -> i_mutex)
+
2006-01-16 Miklos Szeredi <miklos@szeredi.hu>
* Added (again) asynchronous readpages support
diff --git a/configure.in b/configure.in
index d14db87..4e29570 100644
--- a/configure.in
+++ b/configure.in
@@ -1,4 +1,4 @@
-AC_INIT(fuse, 2.5.0)
+AC_INIT(fuse, 2.7.0-pre0)
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER(include/config.h)
@@ -23,7 +23,7 @@ esac
if test "$ac_env_CFLAGS_set" != set; then
CFLAGS="-Wall -W -g -O2"
fi
-CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=25"
+CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=27"
AC_ARG_ENABLE(kernel-module,
[ --enable-kernel-module Compile kernel module ])
diff --git a/include/fuse.h b/include/fuse.h
index a94c61a..3c3a98f 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -295,7 +295,7 @@ struct fuse_operations {
*
* Introduced in version 2.3
*/
- void *(*init) (void);
+ void *(*init) (struct fuse_conn_info *conn);
/**
* Clean up filesystem
@@ -535,13 +535,25 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
* Compatibility stuff *
* ----------------------------------------------------------- */
-#ifndef __FreeBSD__
+#ifdef __FreeBSD__
+# if FUSE_USE_VERSION < 25
+# error On FreeBSD API version 25 or greater must be used
+# endif
+#endif
-#if FUSE_USE_VERSION == 22 || FUSE_USE_VERSION == 21 || FUSE_USE_VERSION == 11
+#if FUSE_USE_VERSION == 25 || FUSE_USE_VERSION == 22 || \
+ FUSE_USE_VERSION == 21 || FUSE_USE_VERSION == 11
# include "fuse_compat.h"
# undef FUSE_MINOR_VERSION
# undef fuse_main
-# if FUSE_USE_VERSION == 22
+# if FUSE_USE_VERSION == 25
+# define FUSE_MINOR_VERSION 6
+# define fuse_main(argc, argv, op) \
+ fuse_main_real_compat25(argc, argv, op, sizeof(*(op)))
+# define fuse_new fuse_new_compat25
+# define fuse_setup fuse_setup_compat25
+# define fuse_operations fuse_operations_compat25
+# elif FUSE_USE_VERSION == 22
# define FUSE_MINOR_VERSION 4
# define fuse_main(argc, argv, op) \
fuse_main_real_compat22(argc, argv, op, sizeof(*(op)))
@@ -549,13 +561,14 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
# define fuse_setup fuse_setup_compat22
# define fuse_operations fuse_operations_compat22
# define fuse_file_info fuse_file_info_compat22
+# define fuse_mount fuse_mount_compat22
# else
# define fuse_dirfil_t fuse_dirfil_t_compat
# define __fuse_read_cmd fuse_read_cmd
# define __fuse_process_cmd fuse_process_cmd
# define __fuse_loop_mt fuse_loop_mt_proc
# if FUSE_USE_VERSION == 21
-# define FUSE_MAJOR_VERSION 2
+# define FUSE_MINOR_VERSION 1
# define fuse_operations fuse_operations_compat2
# define fuse_main fuse_main_compat2
# define fuse_new fuse_new_compat2
@@ -563,6 +576,7 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
# define __fuse_teardown fuse_teardown
# define __fuse_exited fuse_exited
# define __fuse_set_getcontext_func fuse_set_getcontext_func
+# define fuse_mount fuse_mount_compat22
# else
# warning Compatibility with API version 11 is deprecated
# undef FUSE_MAJOR_VERSION
@@ -576,18 +590,10 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
# define FUSE_DEBUG FUSE_DEBUG_COMPAT1
# endif
# endif
-#elif FUSE_USE_VERSION < 25
-# error Compatibility with API version other than 21, 22 and 11 not supported
+#elif FUSE_USE_VERSION < 27
+# error Compatibility with API version other than 21, 22, 25 and 11 not supported
#endif
-#else /* __FreeBSD__ */
-
-#if FUSE_USE_VERSION < 25
-# error On FreeBSD API version 25 or greater must be used
-#endif
-
-#endif /* __FreeBSD__ */
-
#ifdef __cplusplus
}
#endif
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 0f35ea6..4304462 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -20,7 +20,7 @@
#define FUSE_MAJOR_VERSION 2
/** Minor version of FUSE library interface */
-#define FUSE_MINOR_VERSION 5
+#define FUSE_MINOR_VERSION 7
#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
@@ -66,6 +66,15 @@ struct fuse_file_info {
uint64_t fh;
};
+struct fuse_conn_info {
+ unsigned proto_major;
+ unsigned proto_minor;
+ unsigned async_read;
+ unsigned max_write;
+ unsigned max_readahead;
+ unsigned reserved[27];
+};
+
/**
* Create a FUSE mountpoint
*
diff --git a/include/fuse_compat.h b/include/fuse_compat.h
index 220ef07..97f7b12 100644
--- a/include/fuse_compat.h
+++ b/include/fuse_compat.h
@@ -9,6 +9,60 @@
/* these definitions provide source compatibility to prior versions.
Do not include this file directly! */
+struct fuse_operations_compat25 {
+ 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) (void);
+ 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 *);
+};
+
+struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
+ const struct fuse_operations_compat25 *op,
+ size_t op_size);
+
+int fuse_main_real_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size);
+
+struct fuse *fuse_setup_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd);
+
+#ifndef __FreeBSD__
#include <sys/statfs.h>
struct fuse_file_info_compat22 {
@@ -145,3 +199,5 @@ int fuse_mount_compat1(const char *mountpoint, const char *args[]);
struct fuse *fuse_new_compat1(int fd, int flags, const struct fuse_operations_compat1 *op);
void fuse_main_compat1(int argc, char *argv[], const struct fuse_operations_compat1 *op);
+
+#endif /* __FreeBSD__ */
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index e45968c..0c4672f 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -141,7 +141,7 @@ struct fuse_lowlevel_ops {
*
* @param userdata the user data passed to fuse_lowlevel_new()
*/
- void (*init) (void *userdata);
+ void (*init) (void *userdata, struct fuse_conn_info *conn);
/**
* Clean up filesystem
@@ -1235,27 +1235,29 @@ void fuse_remove_signal_handlers(struct fuse_session *se);
* Compatibility stuff *
* ----------------------------------------------------------- */
-#ifndef __FreeBSD__
+#ifdef __FreeBSD__
+# if FUSE_USE_VERSION < 25
+# error On FreeBSD API version 25 or greater must be used
+# endif
+#endif
-#if FUSE_USE_VERSION == 24
+#if FUSE_USE_VERSION == 25 || FUSE_USE_VERSION == 24
# include "fuse_lowlevel_compat.h"
# undef FUSE_MINOR_VERSION
-# define FUSE_MINOR_VERSION 4
-# define fuse_file_info fuse_file_info_compat
-# define fuse_reply_statfs fuse_reply_statfs_compat
-# define fuse_reply_open fuse_reply_open_compat
-#elif FUSE_USE_VERSION < 25
-# error Compatibility with low level API version other than 24 not supported
+# if FUSE_USE_VERSION == 25
+# define FUSE_MINOR_VERSION 6
+# define fuse_lowlevel_ops fuse_lowlevel_ops_compat25
+# define fuse_lowlevel_new fuse_lowlevel_new_compat25
+# else
+# define FUSE_MINOR_VERSION 4
+# define fuse_file_info fuse_file_info_compat
+# define fuse_reply_statfs fuse_reply_statfs_compat
+# define fuse_reply_open fuse_reply_open_compat
+# endif
+#elif FUSE_USE_VERSION < 27
+# error Compatibility with low level API version other than 24 and 25 not supported
#endif
-#else /* __FreeBSD__ */
-
-#if FUSE_USE_VERSION < 25
-# error On FreeBSD API version 25 or greater must be used
-#endif
-
-#endif /* __FreeBSD__ */
-
#ifdef __cplusplus
}
#endif
diff --git a/include/fuse_lowlevel_compat.h b/include/fuse_lowlevel_compat.h
index ccb7d01..0ba0c96 100644
--- a/include/fuse_lowlevel_compat.h
+++ b/include/fuse_lowlevel_compat.h
@@ -9,6 +9,61 @@
/* these definitions provide source compatibility to prior versions.
Do not include this file directly! */
+struct fuse_lowlevel_ops_compat25 {
+ void (*init) (void *userdata);
+ void (*destroy) (void *userdata);
+ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+ void (*getattr) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+ int to_set, struct fuse_file_info *fi);
+ void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, dev_t rdev);
+ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode);
+ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+ const char *name);
+ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ fuse_ino_t newparent, const char *newname);
+ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+ const char *newname);
+ void (*open) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+ struct fuse_file_info *fi);
+ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+ size_t size, off_t off, struct fuse_file_info *fi);
+ void (*flush) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+ void (*release) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+ void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *fi);
+ void (*opendir) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+ void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+ struct fuse_file_info *fi);
+ void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *fi);
+ void (*statfs) (fuse_req_t req);
+ void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ const char *value, size_t size, int flags);
+ void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ size_t size);
+ void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
+ void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
+ void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
+ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, struct fuse_file_info *fi);
+};
+
+struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
+ const struct fuse_lowlevel_ops_compat25 *op,
+ size_t op_size, void *userdata);
+
+#ifndef __FreeBSD__
+
#include <sys/statfs.h>
struct fuse_file_info_compat {
@@ -27,3 +82,5 @@ int fuse_reply_open_compat(fuse_req_t req,
struct fuse_session *fuse_lowlevel_new_compat(const char *opts,
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata);
+
+#endif /* __FreeBSD__ */
diff --git a/include/fuse_opt.h b/include/fuse_opt.h
index 2ef5d56..9e159d5 100644
--- a/include/fuse_opt.h
+++ b/include/fuse_opt.h
@@ -72,7 +72,7 @@ extern "C" {
*/
struct fuse_opt {
/** Matching template and optional parameter formatting */
- const char *template;
+ const char *templ;
/**
* Offset of variable within 'data' parameter of fuse_opt_parse()
@@ -82,7 +82,7 @@ struct fuse_opt {
/**
* Value to set the variable to, or to be passed as 'key' to the
- * processing function. Ignored if template a format
+ * processing function. Ignored if template has a format
*/
int value;
};
@@ -91,13 +91,13 @@ struct fuse_opt {
* Key option. In case of a match, the processing function will be
* called with the specified key.
*/
-#define FUSE_OPT_KEY(template, key) { template, -1U, key }
+#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
/**
* Last option. An array of 'struct fuse_opt' must end with a NULL
* template value
*/
-#define FUSE_OPT_END { .template = NULL }
+#define FUSE_OPT_END { .templ = NULL }
/**
* Argument list
@@ -120,7 +120,7 @@ struct fuse_args {
/**
* Key value passed to the processing function if an option did not
- * match any templated
+ * match any template
*/
#define FUSE_OPT_KEY_OPT -1
@@ -133,6 +133,22 @@ struct fuse_args {
#define FUSE_OPT_KEY_NONOPT -2
/**
+ * Special key value for options to keep
+ *
+ * Argument is not passed to processing function, but behave as if the
+ * processing function returned 1
+ */
+#define FUSE_OPT_KEY_KEEP -3
+
+/**
+ * Special key value for options to discard
+ *
+ * Argument is not passed to processing function, but behave as if the
+ * processing function returned zero
+ */
+#define FUSE_OPT_KEY_DISCARD -4
+
+/**
* Processing function
*
* This function is called if
@@ -202,6 +218,21 @@ int fuse_opt_add_opt(char **opts, const char *opt);
int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
/**
+ * Add an argument at the specified position in a NULL terminated
+ * argument vector
+ *
+ * Adds the argument to the N-th position. This is useful for adding
+ * options at the beggining of the array which must not come after the
+ * special '--' option.
+ *
+ * @param args is the structure containing the current argument list
+ * @param pos is the position at which to add the argument
+ * @param arg is the new argument to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
+
+/**
* Free the contents of argument list
*
* The structure itself is not freed
diff --git a/kernel/configure.ac b/kernel/configure.ac
index 0ef06f8..cdb2669 100644
--- a/kernel/configure.ac
+++ b/kernel/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT(fuse-kernel, 2.5.0)
+AC_INIT(fuse-kernel, 2.7.0-pre0)
AC_CONFIG_HEADERS([config.h])
AC_PROG_INSTALL
diff --git a/kernel/file.c b/kernel/file.c
index e8dae79..be9cc39 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -350,9 +350,14 @@ static void fuse_send_readpages(struct fuse_req *req, struct file *file,
loff_t pos = page_offset(req->pages[0]);
size_t count = req->num_pages << PAGE_CACHE_SHIFT;
req->out.page_zeroing = 1;
- req->end = fuse_readpages_end;
fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
- request_send_background(fc, req);
+ if (fc->async_read) {
+ req->end = fuse_readpages_end;
+ request_send_background(fc, req);
+ } else {
+ request_send(fc, req);
+ fuse_readpages_end(fc, req);
+ }
}
struct fuse_readpages_data {
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index 4979fac..b37b6aa 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -369,6 +369,9 @@ struct fuse_conn {
reply, before any other request, and never cleared */
unsigned conn_error : 1;
+ /** Do readpages asynchronously? Only set in INIT */
+ unsigned async_read : 1;
+
/*
* The following bitfields are only for optimization purposes
* and hence races in setting them will not cause malfunction
diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h
index 191b97d..54b1933 100644
--- a/kernel/fuse_kernel.h
+++ b/kernel/fuse_kernel.h
@@ -49,7 +49,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 5
+#define FUSE_KERNEL_MINOR_VERSION 6
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -93,6 +93,9 @@ struct fuse_kstatfs {
__u32 spare[6];
};
+/**
+ * Bitmasks for fuse_setattr_in.valid
+ */
#define FATTR_MODE (1 << 0)
#define FATTR_UID (1 << 1)
#define FATTR_GID (1 << 2)
@@ -110,6 +113,11 @@ struct fuse_kstatfs {
#define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1)
+/**
+ * INIT request/reply flags
+ */
+#define FUSE_ASYNC_READ (1 << 0)
+
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
@@ -282,12 +290,16 @@ struct fuse_access_in {
struct fuse_init_in {
__u32 major;
__u32 minor;
+ __u32 max_readahead;
+ __u32 flags;
};
struct fuse_init_out {
__u32 major;
__u32 minor;
- __u32 unused[3];
+ __u32 max_readahead;
+ __u32 flags;
+ __u32 unused;
__u32 max_write;
};
diff --git a/kernel/inode.c b/kernel/inode.c
index f10990f..00a13cc 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -646,6 +646,19 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION)
fc->conn_error = 1;
else {
+#ifdef KERNEL_2_6
+ unsigned long ra_pages;
+
+ if (arg->minor >= 6) {
+ ra_pages = arg->max_readahead / PAGE_CACHE_SIZE;
+ fc->async_read = arg->flags & FUSE_ASYNC_READ;
+ } else {
+ ra_pages = fc->max_read / PAGE_CACHE_SIZE;
+ fc->async_read = 0;
+ }
+
+ fc->bdi.ra_pages = min(fc->bdi.ra_pages, ra_pages);
+#endif
fc->minor = arg->minor;
fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
}
@@ -669,6 +682,10 @@ static void fuse_send_init(struct fuse_conn *fc)
arg->major = FUSE_KERNEL_VERSION;
arg->minor = FUSE_KERNEL_MINOR_VERSION;
+#ifdef KERNEL_2_6
+ arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE;
+ arg->flags |= FUSE_ASYNC_READ;
+#endif
req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg);
@@ -732,10 +749,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
fc->user_id = d.user_id;
fc->group_id = d.group_id;
fc->max_read = d.max_read;
-#ifdef KERNEL_2_6
- if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages)
- fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE;
-#endif
/* Used by get_root_inode() */
sb->s_fs_info = fc;
diff --git a/lib/Makefile.am b/lib/Makefile.am
index d58cfed..366fcb0 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -22,7 +22,7 @@ libfuse_la_SOURCES = \
helper.c \
$(mount_source)
-libfuse_la_LDFLAGS = -lpthread -version-number 2:5:0 \
+libfuse_la_LDFLAGS = -lpthread -version-number 2:7:0 \
-Wl,--version-script,fuse_versionscript
EXTRA_DIST = fuse_versionscript
diff --git a/lib/fuse.c b/lib/fuse.c
index 7d66a7d..2cfb436 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -553,7 +553,7 @@ static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
reply_err(req, err);
}
-static void fuse_data_init(void *data)
+static void fuse_data_init(void *data, struct fuse_conn_info *conn)
{
struct fuse *f = (struct fuse *) data;
struct fuse_context *c = fuse_get_context();
@@ -562,7 +562,7 @@ static void fuse_data_init(void *data)
c->fuse = f;
if (f->op.init)
- f->user_data = f->op.init();
+ f->user_data = f->op.init(conn);
}
static void fuse_data_destroy(void *data)
@@ -1808,7 +1808,6 @@ void fuse_set_getcontext_func(struct fuse_context *(*func)(void))
enum {
KEY_HELP,
- KEY_KEEP
};
#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
@@ -1816,8 +1815,8 @@ enum {
static const struct fuse_opt fuse_lib_opts[] = {
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
- FUSE_OPT_KEY("debug", KEY_KEEP),
- FUSE_OPT_KEY("-d", KEY_KEEP),
+ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
FUSE_LIB_OPT("debug", debug, 1),
FUSE_LIB_OPT("-d", debug, 1),
FUSE_LIB_OPT("hard_remove", hard_remove, 1),
@@ -1906,6 +1905,11 @@ struct fuse *fuse_new_common(int fd, struct fuse_args *args,
f->conf.readdir_ino = 1;
#endif
+ if (compat && compat <= 25) {
+ if (fuse_sync_compat_args(args) == -1)
+ goto out_free;
+ }
+
f->se = fuse_lowlevel_new(args, &fuse_path_ops, sizeof(fuse_path_ops), f);
if (f->se == NULL)
goto out_free;
@@ -2012,13 +2016,13 @@ void fuse_destroy(struct fuse *f)
free(f);
}
-#ifndef __FreeBSD__
-
#include "fuse_compat.h"
+#ifndef __FreeBSD__
+
static int fuse_do_open(struct fuse *f, char *path, struct fuse_file_info *fi)
{
- if (!f->compat)
+ if (!f->compat || f->compat >= 25)
return f->op.open(path, fi);
else if (f->compat == 22) {
int err;
@@ -2045,7 +2049,7 @@ static void fuse_do_release(struct fuse *f, char *path,
static int fuse_do_opendir(struct fuse *f, char *path,
struct fuse_file_info *fi)
{
- if (!f->compat) {
+ if (!f->compat || f->compat >= 25) {
return f->op.opendir(path, fi);
} else {
int err;
@@ -2085,7 +2089,7 @@ static int fuse_do_statfs(struct fuse *f, char *path, struct statvfs *buf)
{
int err;
- if (!f->compat) {
+ if (!f->compat || f->compat >= 25) {
err = f->op.statfs(path, buf);
} else if (f->compat > 11) {
struct statfs oldbuf;
@@ -2179,3 +2183,13 @@ static int fuse_do_statfs(struct fuse *f, char *path, struct statvfs *buf)
}
#endif /* __FreeBSD__ */
+
+struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
+ const struct fuse_operations_compat25 *op,
+ size_t op_size)
+{
+ return fuse_new_common(fd, args, (struct fuse_operations *) op,
+ op_size, 25);
+}
+
+__asm__(".symver fuse_new_compat25,fuse_new@FUSE_2.5");
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index efbabf7..79c3cbf 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -22,3 +22,5 @@ struct fuse_session *fuse_get_session(struct fuse *f);
struct fuse *fuse_new_common(int fd, struct fuse_args *args,
const struct fuse_operations *op,
size_t op_size, int compat);
+
+int fuse_sync_compat_args(struct fuse_args *args);
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 0bd6c99..13ddab9 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -10,6 +10,7 @@
#include "fuse_lowlevel.h"
#include "fuse_kernel.h"
#include "fuse_opt.h"
+#include "fuse_i.h"
#include <stdio.h>
#include <stdlib.h>
@@ -27,9 +28,8 @@ struct fuse_ll {
struct fuse_lowlevel_ops op;
int got_init;
void *userdata;
- int major;
- int minor;
uid_t owner;
+ struct fuse_conn_info conn;
};
struct fuse_req {
@@ -263,7 +263,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
/* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
negative entry */
- if (!e->ino && req->f->minor < 4)
+ if (!e->ino && req->f->conn.proto_minor < 4)
return fuse_reply_err(req, ENOENT);
memset(&arg, 0, sizeof(arg));
@@ -330,7 +330,7 @@ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
{
struct fuse_statfs_out arg;
- size_t size = req->f->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
+ size_t size = req->f->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
memset(&arg, 0, sizeof(arg));
convert_statfs(stbuf, &arg.st);
@@ -693,27 +693,51 @@ static void do_init(fuse_req_t req, struct fuse_init_in *arg)
if (f->debug) {
printf("INIT: %u.%u\n", arg->major, arg->minor);
+ if (arg->major > 7 || (arg->major == 7 && arg->minor >= 6)) {
+ printf("flags=0x%08x\n", arg->flags);
+ printf("max_readahead=0x%08x\n", arg->max_readahead);
+ }
fflush(stdout);
}
- f->got_init = 1;
- if (f->op.init)
- f->op.init(f->userdata);
-
- f->major = FUSE_KERNEL_VERSION;
- f->minor = arg->minor;
+ f->conn.proto_major = arg->major;
+ f->conn.proto_minor = arg->minor;
+
+ if (arg->major > 7 || (arg->major == 7 && arg->minor >= 6)) {
+ if (f->conn.async_read)
+ f->conn.async_read = arg->flags & FUSE_ASYNC_READ;
+ if (arg->max_readahead < f->conn.max_readahead)
+ f->conn.max_readahead = arg->max_readahead;
+ } else {
+ f->conn.async_read = 0;
+ f->conn.max_readahead = 0;
+ }
if (bufsize < FUSE_MIN_READ_BUFFER) {
fprintf(stderr, "fuse: warning: buffer size too small: %i\n", bufsize);
bufsize = FUSE_MIN_READ_BUFFER;
}
+ bufsize -= 4096;
+ if (bufsize < f->conn.max_write)
+ f->conn.max_write = bufsize;
+
+ f->got_init = 1;
+ if (f->op.init)
+ f->op.init(f->userdata, &f->conn);
+
memset(&outarg, 0, sizeof(outarg));
- outarg.major = f->major;
+ outarg.major = FUSE_KERNEL_VERSION;
outarg.minor = FUSE_KERNEL_MINOR_VERSION;
- outarg.max_write = bufsize - 4096;
+ if (f->conn.async_read)
+ outarg.flags |= FUSE_ASYNC_READ;
+ outarg.max_readahead = f->conn.max_readahead;
+ outarg.max_write = f->conn.max_write;
if (f->debug) {
printf(" INIT: %u.%u\n", outarg.major, outarg.minor);
+ printf(" flags=0x%08x\n", outarg.flags);
+ printf(" max_readahead=0x%08x\n", outarg.max_readahead);
+ printf(" max_write=0x%08x\n", outarg.max_write);
fflush(stdout);
}
@@ -902,6 +926,11 @@ static struct fuse_opt fuse_ll_opts[] = {
{ "debug", offsetof(struct fuse_ll, debug), 1 },
{ "-d", offsetof(struct fuse_ll, debug), 1 },
{ "allow_root", offsetof(struct fuse_ll, allow_root), 1 },
+ { "max_write=%u", offsetof(struct fuse_ll, conn.max_write), 0 },
+ { "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 },
+ { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 },
+ { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 },
+ FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-V", KEY_VERSION),
@@ -915,6 +944,15 @@ static void fuse_ll_version(void)
FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
}
+static void fuse_ll_help(void)
+{
+ fprintf(stderr,
+" -o max_write=N set maximum size of write requests\n"
+" -o max_readahead=N set maximum readahead\n"
+" -o async_read perform reads asynchronously (default)\n"
+" -o sync_read perform reads synchronously\n");
+}
+
static int fuse_ll_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
@@ -922,6 +960,7 @@ static int fuse_ll_opt_proc(void *data, const char *arg, int key,
switch (key) {
case KEY_HELP:
+ fuse_ll_help();
break;
case KEY_VERSION:
@@ -972,6 +1011,10 @@ struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
goto out;
}
+ f->conn.async_read = 1;
+ f->conn.max_write = UINT_MAX;
+ f->conn.max_readahead = UINT_MAX;
+
if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
goto out_free;
@@ -991,10 +1034,10 @@ struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
return NULL;
}
-#ifndef __FreeBSD__
-
#include "fuse_lowlevel_compat.h"
+#ifndef __FreeBSD__
+
static void fill_open_compat(struct fuse_open_out *arg,
const struct fuse_file_info_compat *f)
{
@@ -1062,3 +1105,49 @@ __asm__(".symver fuse_reply_open_compat,fuse_reply_open@FUSE_2.4");
__asm__(".symver fuse_lowlevel_new_compat,fuse_lowlevel_new@FUSE_2.4");
#endif /* __FreeBSD__ */
+
+struct fuse_ll_compat_conf {
+ unsigned max_read;
+ int set_max_read;
+};
+
+static const struct fuse_opt fuse_ll_opts_compat[] = {
+ { "max_read=", offsetof(struct fuse_ll_compat_conf, set_max_read), 1 },
+ { "max_read=%u", offsetof(struct fuse_ll_compat_conf, max_read), 0 },
+ FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_END
+};
+
+int fuse_sync_compat_args(struct fuse_args *args)
+{
+ struct fuse_ll_compat_conf conf;
+
+ if (fuse_opt_parse(args, &conf, fuse_ll_opts_compat, NULL) == -1)
+ return -1;
+
+ if (fuse_opt_insert_arg(args, 1, "-osync_read"))
+ return -1;
+
+ if (conf.set_max_read) {
+ char tmpbuf[64];
+
+ sprintf(tmpbuf, "-omax_readahead=%u", conf.max_read);
+ if (fuse_opt_insert_arg(args, 1, tmpbuf) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
+ const struct fuse_lowlevel_ops_compat25 *op,
+ size_t op_size, void *userdata)
+{
+ if (fuse_sync_compat_args(args) == -1)
+ return NULL;
+
+ return fuse_lowlevel_new(args, (const struct fuse_lowlevel_ops *) op,
+ op_size, userdata);
+}
+
+
+__asm__(".symver fuse_lowlevel_new_compat25,fuse_lowlevel_new@FUSE_2.5");
diff --git a/lib/fuse_opt.c b/lib/fuse_opt.c
index 2ac499c..582c6ad 100644
--- a/lib/fuse_opt.c
+++ b/lib/fuse_opt.c
@@ -62,6 +62,21 @@ int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
return 0;
}
+int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
+{
+ assert(pos <= args->argc);
+ if (fuse_opt_add_arg(args, arg) == -1)
+ return -1;
+
+ if (pos != args->argc - 1) {
+ char *newarg = args->argv[args->argc - 1];
+ memmove(&args->argv[pos + 1], &args->argv[pos],
+ sizeof(char *) * (args->argc - pos - 1));
+ args->argv[pos] = newarg;
+ }
+ return 0;
+}
+
static int next_arg(struct fuse_opt_context *ctx, const char *opt)
{
if (ctx->argctr + 1 >= ctx->argc) {
@@ -102,25 +117,13 @@ static int add_opt(struct fuse_opt_context *ctx, const char *opt)
return fuse_opt_add_opt(&ctx->opts, opt);
}
-static int insert_arg(struct fuse_opt_context *ctx, int pos, const char *arg)
-{
- assert(pos <= ctx->outargs.argc);
- if (add_arg(ctx, arg) == -1)
- return -1;
-
- if (pos != ctx->outargs.argc - 1) {
- char *newarg = ctx->outargs.argv[ctx->outargs.argc - 1];
- memmove(&ctx->outargs.argv[pos + 1], &ctx->outargs.argv[pos],
- sizeof(char *) * (ctx->outargs.argc - pos - 1));
- ctx->outargs.argv[pos] = newarg;
- }
- return 0;
-}
-
static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
int iso)
{
- if (ctx->proc) {
+ if (key == FUSE_OPT_KEY_DISCARD)
+ return 0;
+
+ if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
if (res == -1 || !res)
return res;
@@ -155,8 +158,8 @@ static int match_template(const char *t, const char *arg, unsigned *sepp)
static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
const char *arg, unsigned *sepp)
{
- for (; opt && opt->template; opt++)
- if (match_template(opt->template, arg, sepp))
+ for (; opt && opt->templ; opt++)
+ if (match_template(opt->templ, arg, sepp))
return opt;
return NULL;
}
@@ -195,11 +198,11 @@ static int process_opt(struct fuse_opt_context *ctx,
return -1;
} else {
void *var = ctx->data + opt->offset;
- if (sep && opt->template[sep + 1]) {
+ if (sep && opt->templ[sep + 1]) {
const char *param = arg + sep;
- if (opt->template[sep] == '=')
+ if (opt->templ[sep] == '=')
param ++;
- if (process_opt_param(var, opt->template + sep + 1,
+ if (process_opt_param(var, opt->templ + sep + 1,
param, arg) == -1)
return -1;
} else
@@ -239,7 +242,7 @@ static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
if (opt) {
for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
int res;
- if (sep && opt->template[sep] == ' ' && !arg[sep])
+ if (sep && opt->templ[sep] == ' ' && !arg[sep])
res = process_opt_sep_arg(ctx, opt, sep, arg, iso);
else
res = process_opt(ctx, opt, sep, arg, iso);
@@ -321,12 +324,14 @@ static int opt_parse(struct fuse_opt_context *ctx)
return -1;
if (ctx->opts) {
- if (insert_arg(ctx, 1, "-o") == -1 ||
- insert_arg(ctx, 2, ctx->opts) == -1)
+ if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
+ fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
return -1;
}
- if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc)
+ if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) {
+ free(ctx->outargs.argv[ctx->outargs.argc - 1]);
ctx->outargs.argv[--ctx->outargs.argc] = NULL;
+ }
return 0;
}
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 9927158..421228e 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -61,18 +61,16 @@ FUSE_2.4 {
FUSE_2.5 {
global:
- fuse_lowlevel_new;
fuse_lowlevel_new_compat;
- fuse_main_real;
fuse_main_real_compat22;
fuse_mount;
fuse_mount_compat22;
- fuse_new;
fuse_new_compat22;
fuse_opt_parse;
fuse_opt_add_opt;
fuse_opt_add_arg;
fuse_opt_free_args;
+ fuse_opt_insert_arg;
fuse_opt_match;
fuse_parse_cmdline;
fuse_remove_signal_handlers;
@@ -81,10 +79,26 @@ FUSE_2.5 {
fuse_reply_open_compat;
fuse_reply_statfs;
fuse_reply_statfs_compat;
- fuse_setup;
fuse_setup_compat22;
fuse_set_signal_handlers;
+} FUSE_2.4;
+
+FUSE_2.6 {
+ global:
+ fuse_opt_insert_arg;
+} FUSE_2.5;
+
+FUSE_2.7 {
+ global:
+ fuse_lowlevel_new;
+ fuse_lowlevel_new_compat25;
+ fuse_main_real;
+ fuse_main_real_compat25;
+ fuse_new;
+ fuse_new_compat25;
+ fuse_setup;
+ fuse_setup_compat25;
local:
*;
-} FUSE_2.4;
+} FUSE_2.6;
diff --git a/lib/helper.c b/lib/helper.c
index b7f77be..f937366 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -22,7 +22,6 @@ enum {
KEY_HELP,
KEY_HELP_NOHEADER,
KEY_VERSION,
- KEY_KEEP,
};
struct helper_opts {
@@ -46,8 +45,9 @@ static const struct fuse_opt fuse_helper_opts[] = {
FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
- FUSE_OPT_KEY("-d", KEY_KEEP),
- FUSE_OPT_KEY("debug", KEY_KEEP),
+ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_END
};
@@ -100,11 +100,12 @@ static int fuse_helper_opt_proc(void *data, const char *arg, int key,
case FUSE_OPT_KEY_NONOPT:
if (!hopts->mountpoint)
return fuse_opt_add_opt(&hopts->mountpoint, arg);
-
- /* fall through */
+ else {
+ fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
+ return -1;
+ }
default:
- case KEY_KEEP:
return 1;
}
}
@@ -289,10 +290,10 @@ int fuse_main(void)
return -1;
}
-#ifndef __FreeBSD__
-
#include "fuse_compat.h"
+#ifndef __FreeBSD__
+
struct fuse *fuse_setup_compat22(int argc, char *argv[],
const struct fuse_operations_compat22 *op,
size_t op_size, char **mountpoint,
@@ -341,3 +342,24 @@ __asm__(".symver fuse_main_compat2,fuse_main@");
__asm__(".symver fuse_main_real_compat22,fuse_main_real@FUSE_2.2");
#endif /* __FreeBSD__ */
+
+
+struct fuse *fuse_setup_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd)
+{
+ return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+ op_size, mountpoint, multithreaded, fd, 25);
+}
+
+int fuse_main_real_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size)
+{
+ return fuse_main_common(argc, argv, (struct fuse_operations *) op, op_size,
+ 25);
+}
+
+__asm__(".symver fuse_setup_compat25,fuse_setup@FUSE_2.5");
+__asm__(".symver fuse_main_real_compat25,fuse_main_real@FUSE_2.5");
diff --git a/lib/mount.c b/lib/mount.c
index 219ee14..2840a54 100644
--- a/lib/mount.c
+++ b/lib/mount.c
@@ -48,6 +48,7 @@ static const struct fuse_opt fuse_mount_opts[] = {
FUSE_OPT_KEY("fsname=", KEY_KERN),
FUSE_OPT_KEY("large_read", KEY_KERN),
FUSE_OPT_KEY("max_read=", KEY_KERN),
+ FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("-r", KEY_RO),
FUSE_OPT_KEY("ro", KEY_KERN),
FUSE_OPT_KEY("rw", KEY_KERN),