fuse update for 5.17
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCYdw8xgAKCRDh3BK/laaZ PFy3AQCHSltzy6f234CcsFk3mtJn0im0tDbRoEYFD731JOR1YAD9HQKtJRn/sMCF r0PnZnOWJ35RWB3o8uEqptsgrZXJkQo= =HosR -----END PGP SIGNATURE----- Merge tag 'fuse-update-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse Pull fuse updates from Miklos Szeredi: - Fix a regression introduced in 5.15 - Extend the size of the FUSE_INIT request to accommodate for more flags. There's a slight possibility of a regression for obscure fuse servers; if this happens, then more complexity will need to be added to the protocol - Allow the DAX property to be controlled by the server on a per-inode basis in virtiofs - Allow sending security context to the server when creating a file or directory * tag 'fuse-update-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: Documentation/filesystem/dax: DAX on virtiofs fuse: mark inode DONT_CACHE when per inode DAX hint changes fuse: negotiate per inode DAX in FUSE_INIT fuse: enable per inode DAX fuse: support per inode DAX in fuse protocol fuse: make DAX mount option a tri-state fuse: add fuse_should_enable_dax() helper fuse: Pass correct lend value to filemap_write_and_wait_range() fuse: send security context of inode on file fuse: extend init flags
This commit is contained in:
Коммит
8975f89748
|
@ -23,8 +23,8 @@ on it as usual. The `DAX` code currently only supports files with a block
|
||||||
size equal to your kernel's `PAGE_SIZE`, so you may need to specify a block
|
size equal to your kernel's `PAGE_SIZE`, so you may need to specify a block
|
||||||
size when creating the filesystem.
|
size when creating the filesystem.
|
||||||
|
|
||||||
Currently 3 filesystems support `DAX`: ext2, ext4 and xfs. Enabling `DAX` on them
|
Currently 4 filesystems support `DAX`: ext2, ext4, xfs and virtiofs.
|
||||||
is different.
|
Enabling `DAX` on them is different.
|
||||||
|
|
||||||
Enabling DAX on ext2
|
Enabling DAX on ext2
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -168,6 +168,22 @@ if the underlying media does not support dax and/or the filesystem is
|
||||||
overridden with a mount option.
|
overridden with a mount option.
|
||||||
|
|
||||||
|
|
||||||
|
Enabling DAX on virtiofs
|
||||||
|
----------------------------
|
||||||
|
The semantic of DAX on virtiofs is basically equal to that on ext4 and xfs,
|
||||||
|
except that when '-o dax=inode' is specified, virtiofs client derives the hint
|
||||||
|
whether DAX shall be enabled or not from virtiofs server through FUSE protocol,
|
||||||
|
rather than the persistent `FS_XFLAG_DAX` flag. That is, whether DAX shall be
|
||||||
|
enabled or not is completely determined by virtiofs server, while virtiofs
|
||||||
|
server itself may deploy various algorithm making this decision, e.g. depending
|
||||||
|
on the persistent `FS_XFLAG_DAX` flag on the host.
|
||||||
|
|
||||||
|
It is still supported to set or clear persistent `FS_XFLAG_DAX` flag inside
|
||||||
|
guest, but it is not guaranteed that DAX will be enabled or disabled for
|
||||||
|
corresponding file then. Users inside guest still need to call statx(2) and
|
||||||
|
check the statx flag `STATX_ATTR_DAX` to see if DAX is enabled for this file.
|
||||||
|
|
||||||
|
|
||||||
Implementation Tips for Block Driver Writers
|
Implementation Tips for Block Driver Writers
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1279,11 +1279,14 @@ out_err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fuse_dax_conn_alloc(struct fuse_conn *fc, struct dax_device *dax_dev)
|
int fuse_dax_conn_alloc(struct fuse_conn *fc, enum fuse_dax_mode dax_mode,
|
||||||
|
struct dax_device *dax_dev)
|
||||||
{
|
{
|
||||||
struct fuse_conn_dax *fcd;
|
struct fuse_conn_dax *fcd;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
fc->dax_mode = dax_mode;
|
||||||
|
|
||||||
if (!dax_dev)
|
if (!dax_dev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1327,17 +1330,46 @@ static const struct address_space_operations fuse_dax_file_aops = {
|
||||||
.invalidatepage = noop_invalidatepage,
|
.invalidatepage = noop_invalidatepage,
|
||||||
};
|
};
|
||||||
|
|
||||||
void fuse_dax_inode_init(struct inode *inode)
|
static bool fuse_should_enable_dax(struct inode *inode, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
enum fuse_dax_mode dax_mode = fc->dax_mode;
|
||||||
|
|
||||||
|
if (dax_mode == FUSE_DAX_NEVER)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fc->dax may be NULL in 'inode' mode when filesystem device doesn't
|
||||||
|
* support DAX, in which case it will silently fallback to 'never' mode.
|
||||||
|
*/
|
||||||
if (!fc->dax)
|
if (!fc->dax)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (dax_mode == FUSE_DAX_ALWAYS)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* dax_mode is FUSE_DAX_INODE* */
|
||||||
|
return fc->inode_dax && (flags & FUSE_ATTR_DAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fuse_dax_inode_init(struct inode *inode, unsigned int flags)
|
||||||
|
{
|
||||||
|
if (!fuse_should_enable_dax(inode, flags))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
inode->i_flags |= S_DAX;
|
inode->i_flags |= S_DAX;
|
||||||
inode->i_data.a_ops = &fuse_dax_file_aops;
|
inode->i_data.a_ops = &fuse_dax_file_aops;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fuse_dax_dontcache(struct inode *inode, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
|
||||||
|
if (fuse_is_inode_dax_mode(fc->dax_mode) &&
|
||||||
|
((bool) IS_DAX(inode) != (bool) (flags & FUSE_ATTR_DAX)))
|
||||||
|
d_mark_dontcache(inode);
|
||||||
|
}
|
||||||
|
|
||||||
bool fuse_dax_check_alignment(struct fuse_conn *fc, unsigned int map_alignment)
|
bool fuse_dax_check_alignment(struct fuse_conn *fc, unsigned int map_alignment)
|
||||||
{
|
{
|
||||||
if (fc->dax && (map_alignment > FUSE_DAX_SHIFT)) {
|
if (fc->dax && (map_alignment > FUSE_DAX_SHIFT)) {
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
#include <linux/xattr.h>
|
#include <linux/xattr.h>
|
||||||
#include <linux/iversion.h>
|
#include <linux/iversion.h>
|
||||||
#include <linux/posix_acl.h>
|
#include <linux/posix_acl.h>
|
||||||
|
#include <linux/security.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
static void fuse_advise_use_readdirplus(struct inode *dir)
|
static void fuse_advise_use_readdirplus(struct inode *dir)
|
||||||
{
|
{
|
||||||
|
@ -456,6 +459,62 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int get_security_context(struct dentry *entry, umode_t mode,
|
||||||
|
void **security_ctx, u32 *security_ctxlen)
|
||||||
|
{
|
||||||
|
struct fuse_secctx *fctx;
|
||||||
|
struct fuse_secctx_header *header;
|
||||||
|
void *ctx = NULL, *ptr;
|
||||||
|
u32 ctxlen, total_len = sizeof(*header);
|
||||||
|
int err, nr_ctx = 0;
|
||||||
|
const char *name;
|
||||||
|
size_t namelen;
|
||||||
|
|
||||||
|
err = security_dentry_init_security(entry, mode, &entry->d_name,
|
||||||
|
&name, &ctx, &ctxlen);
|
||||||
|
if (err) {
|
||||||
|
if (err != -EOPNOTSUPP)
|
||||||
|
goto out_err;
|
||||||
|
/* No LSM is supporting this security hook. Ignore error */
|
||||||
|
ctxlen = 0;
|
||||||
|
ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctxlen) {
|
||||||
|
nr_ctx = 1;
|
||||||
|
namelen = strlen(name) + 1;
|
||||||
|
err = -EIO;
|
||||||
|
if (WARN_ON(namelen > XATTR_NAME_MAX + 1 || ctxlen > S32_MAX))
|
||||||
|
goto out_err;
|
||||||
|
total_len += FUSE_REC_ALIGN(sizeof(*fctx) + namelen + ctxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = -ENOMEM;
|
||||||
|
header = ptr = kzalloc(total_len, GFP_KERNEL);
|
||||||
|
if (!ptr)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
header->nr_secctx = nr_ctx;
|
||||||
|
header->size = total_len;
|
||||||
|
ptr += sizeof(*header);
|
||||||
|
if (nr_ctx) {
|
||||||
|
fctx = ptr;
|
||||||
|
fctx->size = ctxlen;
|
||||||
|
ptr += sizeof(*fctx);
|
||||||
|
|
||||||
|
strcpy(ptr, name);
|
||||||
|
ptr += namelen;
|
||||||
|
|
||||||
|
memcpy(ptr, ctx, ctxlen);
|
||||||
|
}
|
||||||
|
*security_ctxlen = total_len;
|
||||||
|
*security_ctx = header;
|
||||||
|
err = 0;
|
||||||
|
out_err:
|
||||||
|
kfree(ctx);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Atomic create+open operation
|
* Atomic create+open operation
|
||||||
*
|
*
|
||||||
|
@ -476,6 +535,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||||
struct fuse_entry_out outentry;
|
struct fuse_entry_out outentry;
|
||||||
struct fuse_inode *fi;
|
struct fuse_inode *fi;
|
||||||
struct fuse_file *ff;
|
struct fuse_file *ff;
|
||||||
|
void *security_ctx = NULL;
|
||||||
|
u32 security_ctxlen;
|
||||||
|
|
||||||
/* Userspace expects S_IFREG in create mode */
|
/* Userspace expects S_IFREG in create mode */
|
||||||
BUG_ON((mode & S_IFMT) != S_IFREG);
|
BUG_ON((mode & S_IFMT) != S_IFREG);
|
||||||
|
@ -517,7 +578,20 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||||
args.out_args[0].value = &outentry;
|
args.out_args[0].value = &outentry;
|
||||||
args.out_args[1].size = sizeof(outopen);
|
args.out_args[1].size = sizeof(outopen);
|
||||||
args.out_args[1].value = &outopen;
|
args.out_args[1].value = &outopen;
|
||||||
|
|
||||||
|
if (fm->fc->init_security) {
|
||||||
|
err = get_security_context(entry, mode, &security_ctx,
|
||||||
|
&security_ctxlen);
|
||||||
|
if (err)
|
||||||
|
goto out_put_forget_req;
|
||||||
|
|
||||||
|
args.in_numargs = 3;
|
||||||
|
args.in_args[2].size = security_ctxlen;
|
||||||
|
args.in_args[2].value = security_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
err = fuse_simple_request(fm, &args);
|
err = fuse_simple_request(fm, &args);
|
||||||
|
kfree(security_ctx);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_free_ff;
|
goto out_free_ff;
|
||||||
|
|
||||||
|
@ -620,6 +694,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
|
||||||
struct dentry *d;
|
struct dentry *d;
|
||||||
int err;
|
int err;
|
||||||
struct fuse_forget_link *forget;
|
struct fuse_forget_link *forget;
|
||||||
|
void *security_ctx = NULL;
|
||||||
|
u32 security_ctxlen;
|
||||||
|
|
||||||
if (fuse_is_bad(dir))
|
if (fuse_is_bad(dir))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -633,7 +709,22 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
|
||||||
args->out_numargs = 1;
|
args->out_numargs = 1;
|
||||||
args->out_args[0].size = sizeof(outarg);
|
args->out_args[0].size = sizeof(outarg);
|
||||||
args->out_args[0].value = &outarg;
|
args->out_args[0].value = &outarg;
|
||||||
|
|
||||||
|
if (fm->fc->init_security && args->opcode != FUSE_LINK) {
|
||||||
|
err = get_security_context(entry, mode, &security_ctx,
|
||||||
|
&security_ctxlen);
|
||||||
|
if (err)
|
||||||
|
goto out_put_forget_req;
|
||||||
|
|
||||||
|
BUG_ON(args->in_numargs != 2);
|
||||||
|
|
||||||
|
args->in_numargs = 3;
|
||||||
|
args->in_args[2].size = security_ctxlen;
|
||||||
|
args->in_args[2].value = security_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
err = fuse_simple_request(fm, args);
|
err = fuse_simple_request(fm, args);
|
||||||
|
kfree(security_ctx);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_put_forget_req;
|
goto out_put_forget_req;
|
||||||
|
|
||||||
|
|
|
@ -2910,7 +2910,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
|
|
||||||
static int fuse_writeback_range(struct inode *inode, loff_t start, loff_t end)
|
static int fuse_writeback_range(struct inode *inode, loff_t start, loff_t end)
|
||||||
{
|
{
|
||||||
int err = filemap_write_and_wait_range(inode->i_mapping, start, -1);
|
int err = filemap_write_and_wait_range(inode->i_mapping, start, LLONG_MAX);
|
||||||
|
|
||||||
if (!err)
|
if (!err)
|
||||||
fuse_sync_writes(inode);
|
fuse_sync_writes(inode);
|
||||||
|
@ -3169,7 +3169,7 @@ static const struct address_space_operations fuse_file_aops = {
|
||||||
.write_end = fuse_write_end,
|
.write_end = fuse_write_end,
|
||||||
};
|
};
|
||||||
|
|
||||||
void fuse_init_file_inode(struct inode *inode)
|
void fuse_init_file_inode(struct inode *inode, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||||
|
|
||||||
|
@ -3183,5 +3183,5 @@ void fuse_init_file_inode(struct inode *inode)
|
||||||
fi->writepages = RB_ROOT;
|
fi->writepages = RB_ROOT;
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_FUSE_DAX))
|
if (IS_ENABLED(CONFIG_FUSE_DAX))
|
||||||
fuse_dax_inode_init(inode);
|
fuse_dax_inode_init(inode, flags);
|
||||||
}
|
}
|
||||||
|
|
|
@ -480,6 +480,18 @@ struct fuse_dev {
|
||||||
struct list_head entry;
|
struct list_head entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum fuse_dax_mode {
|
||||||
|
FUSE_DAX_INODE_DEFAULT, /* default */
|
||||||
|
FUSE_DAX_ALWAYS, /* "-o dax=always" */
|
||||||
|
FUSE_DAX_NEVER, /* "-o dax=never" */
|
||||||
|
FUSE_DAX_INODE_USER, /* "-o dax=inode" */
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool fuse_is_inode_dax_mode(enum fuse_dax_mode mode)
|
||||||
|
{
|
||||||
|
return mode == FUSE_DAX_INODE_DEFAULT || mode == FUSE_DAX_INODE_USER;
|
||||||
|
}
|
||||||
|
|
||||||
struct fuse_fs_context {
|
struct fuse_fs_context {
|
||||||
int fd;
|
int fd;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
|
@ -497,7 +509,7 @@ struct fuse_fs_context {
|
||||||
bool no_control:1;
|
bool no_control:1;
|
||||||
bool no_force_umount:1;
|
bool no_force_umount:1;
|
||||||
bool legacy_opts_show:1;
|
bool legacy_opts_show:1;
|
||||||
bool dax:1;
|
enum fuse_dax_mode dax_mode;
|
||||||
unsigned int max_read;
|
unsigned int max_read;
|
||||||
unsigned int blksize;
|
unsigned int blksize;
|
||||||
const char *subtype;
|
const char *subtype;
|
||||||
|
@ -765,6 +777,12 @@ struct fuse_conn {
|
||||||
/* Propagate syncfs() to server */
|
/* Propagate syncfs() to server */
|
||||||
unsigned int sync_fs:1;
|
unsigned int sync_fs:1;
|
||||||
|
|
||||||
|
/* Initialize security xattrs when creating a new inode */
|
||||||
|
unsigned int init_security:1;
|
||||||
|
|
||||||
|
/* Does the filesystem support per inode DAX? */
|
||||||
|
unsigned int inode_dax:1;
|
||||||
|
|
||||||
/** The number of requests waiting for completion */
|
/** The number of requests waiting for completion */
|
||||||
atomic_t num_waiting;
|
atomic_t num_waiting;
|
||||||
|
|
||||||
|
@ -802,6 +820,9 @@ struct fuse_conn {
|
||||||
struct list_head devices;
|
struct list_head devices;
|
||||||
|
|
||||||
#ifdef CONFIG_FUSE_DAX
|
#ifdef CONFIG_FUSE_DAX
|
||||||
|
/* Dax mode */
|
||||||
|
enum fuse_dax_mode dax_mode;
|
||||||
|
|
||||||
/* Dax specific conn data, non-NULL if DAX is enabled */
|
/* Dax specific conn data, non-NULL if DAX is enabled */
|
||||||
struct fuse_conn_dax *dax;
|
struct fuse_conn_dax *dax;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1007,7 +1028,7 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
|
||||||
/**
|
/**
|
||||||
* Initialize file operations on a regular file
|
* Initialize file operations on a regular file
|
||||||
*/
|
*/
|
||||||
void fuse_init_file_inode(struct inode *inode);
|
void fuse_init_file_inode(struct inode *inode, unsigned int flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize inode operations on regular files and special files
|
* Initialize inode operations on regular files and special files
|
||||||
|
@ -1269,11 +1290,13 @@ ssize_t fuse_dax_read_iter(struct kiocb *iocb, struct iov_iter *to);
|
||||||
ssize_t fuse_dax_write_iter(struct kiocb *iocb, struct iov_iter *from);
|
ssize_t fuse_dax_write_iter(struct kiocb *iocb, struct iov_iter *from);
|
||||||
int fuse_dax_mmap(struct file *file, struct vm_area_struct *vma);
|
int fuse_dax_mmap(struct file *file, struct vm_area_struct *vma);
|
||||||
int fuse_dax_break_layouts(struct inode *inode, u64 dmap_start, u64 dmap_end);
|
int fuse_dax_break_layouts(struct inode *inode, u64 dmap_start, u64 dmap_end);
|
||||||
int fuse_dax_conn_alloc(struct fuse_conn *fc, struct dax_device *dax_dev);
|
int fuse_dax_conn_alloc(struct fuse_conn *fc, enum fuse_dax_mode mode,
|
||||||
|
struct dax_device *dax_dev);
|
||||||
void fuse_dax_conn_free(struct fuse_conn *fc);
|
void fuse_dax_conn_free(struct fuse_conn *fc);
|
||||||
bool fuse_dax_inode_alloc(struct super_block *sb, struct fuse_inode *fi);
|
bool fuse_dax_inode_alloc(struct super_block *sb, struct fuse_inode *fi);
|
||||||
void fuse_dax_inode_init(struct inode *inode);
|
void fuse_dax_inode_init(struct inode *inode, unsigned int flags);
|
||||||
void fuse_dax_inode_cleanup(struct inode *inode);
|
void fuse_dax_inode_cleanup(struct inode *inode);
|
||||||
|
void fuse_dax_dontcache(struct inode *inode, unsigned int flags);
|
||||||
bool fuse_dax_check_alignment(struct fuse_conn *fc, unsigned int map_alignment);
|
bool fuse_dax_check_alignment(struct fuse_conn *fc, unsigned int map_alignment);
|
||||||
void fuse_dax_cancel_work(struct fuse_conn *fc);
|
void fuse_dax_cancel_work(struct fuse_conn *fc);
|
||||||
|
|
||||||
|
|
|
@ -301,6 +301,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
||||||
if (inval)
|
if (inval)
|
||||||
invalidate_inode_pages2(inode->i_mapping);
|
invalidate_inode_pages2(inode->i_mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_FUSE_DAX))
|
||||||
|
fuse_dax_dontcache(inode, attr->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
|
static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
|
||||||
|
@ -313,7 +316,7 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
|
||||||
inode->i_ctime.tv_nsec = attr->ctimensec;
|
inode->i_ctime.tv_nsec = attr->ctimensec;
|
||||||
if (S_ISREG(inode->i_mode)) {
|
if (S_ISREG(inode->i_mode)) {
|
||||||
fuse_init_common(inode);
|
fuse_init_common(inode);
|
||||||
fuse_init_file_inode(inode);
|
fuse_init_file_inode(inode, attr->flags);
|
||||||
} else if (S_ISDIR(inode->i_mode))
|
} else if (S_ISDIR(inode->i_mode))
|
||||||
fuse_init_dir(inode);
|
fuse_init_dir(inode);
|
||||||
else if (S_ISLNK(inode->i_mode))
|
else if (S_ISLNK(inode->i_mode))
|
||||||
|
@ -767,8 +770,12 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
|
||||||
seq_printf(m, ",blksize=%lu", sb->s_blocksize);
|
seq_printf(m, ",blksize=%lu", sb->s_blocksize);
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_FUSE_DAX
|
#ifdef CONFIG_FUSE_DAX
|
||||||
if (fc->dax)
|
if (fc->dax_mode == FUSE_DAX_ALWAYS)
|
||||||
seq_puts(m, ",dax");
|
seq_puts(m, ",dax=always");
|
||||||
|
else if (fc->dax_mode == FUSE_DAX_NEVER)
|
||||||
|
seq_puts(m, ",dax=never");
|
||||||
|
else if (fc->dax_mode == FUSE_DAX_INODE_USER)
|
||||||
|
seq_puts(m, ",dax=inode");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1109,73 +1116,80 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||||
process_init_limits(fc, arg);
|
process_init_limits(fc, arg);
|
||||||
|
|
||||||
if (arg->minor >= 6) {
|
if (arg->minor >= 6) {
|
||||||
|
u64 flags = arg->flags | (u64) arg->flags2 << 32;
|
||||||
|
|
||||||
ra_pages = arg->max_readahead / PAGE_SIZE;
|
ra_pages = arg->max_readahead / PAGE_SIZE;
|
||||||
if (arg->flags & FUSE_ASYNC_READ)
|
if (flags & FUSE_ASYNC_READ)
|
||||||
fc->async_read = 1;
|
fc->async_read = 1;
|
||||||
if (!(arg->flags & FUSE_POSIX_LOCKS))
|
if (!(flags & FUSE_POSIX_LOCKS))
|
||||||
fc->no_lock = 1;
|
fc->no_lock = 1;
|
||||||
if (arg->minor >= 17) {
|
if (arg->minor >= 17) {
|
||||||
if (!(arg->flags & FUSE_FLOCK_LOCKS))
|
if (!(flags & FUSE_FLOCK_LOCKS))
|
||||||
fc->no_flock = 1;
|
fc->no_flock = 1;
|
||||||
} else {
|
} else {
|
||||||
if (!(arg->flags & FUSE_POSIX_LOCKS))
|
if (!(flags & FUSE_POSIX_LOCKS))
|
||||||
fc->no_flock = 1;
|
fc->no_flock = 1;
|
||||||
}
|
}
|
||||||
if (arg->flags & FUSE_ATOMIC_O_TRUNC)
|
if (flags & FUSE_ATOMIC_O_TRUNC)
|
||||||
fc->atomic_o_trunc = 1;
|
fc->atomic_o_trunc = 1;
|
||||||
if (arg->minor >= 9) {
|
if (arg->minor >= 9) {
|
||||||
/* LOOKUP has dependency on proto version */
|
/* LOOKUP has dependency on proto version */
|
||||||
if (arg->flags & FUSE_EXPORT_SUPPORT)
|
if (flags & FUSE_EXPORT_SUPPORT)
|
||||||
fc->export_support = 1;
|
fc->export_support = 1;
|
||||||
}
|
}
|
||||||
if (arg->flags & FUSE_BIG_WRITES)
|
if (flags & FUSE_BIG_WRITES)
|
||||||
fc->big_writes = 1;
|
fc->big_writes = 1;
|
||||||
if (arg->flags & FUSE_DONT_MASK)
|
if (flags & FUSE_DONT_MASK)
|
||||||
fc->dont_mask = 1;
|
fc->dont_mask = 1;
|
||||||
if (arg->flags & FUSE_AUTO_INVAL_DATA)
|
if (flags & FUSE_AUTO_INVAL_DATA)
|
||||||
fc->auto_inval_data = 1;
|
fc->auto_inval_data = 1;
|
||||||
else if (arg->flags & FUSE_EXPLICIT_INVAL_DATA)
|
else if (flags & FUSE_EXPLICIT_INVAL_DATA)
|
||||||
fc->explicit_inval_data = 1;
|
fc->explicit_inval_data = 1;
|
||||||
if (arg->flags & FUSE_DO_READDIRPLUS) {
|
if (flags & FUSE_DO_READDIRPLUS) {
|
||||||
fc->do_readdirplus = 1;
|
fc->do_readdirplus = 1;
|
||||||
if (arg->flags & FUSE_READDIRPLUS_AUTO)
|
if (flags & FUSE_READDIRPLUS_AUTO)
|
||||||
fc->readdirplus_auto = 1;
|
fc->readdirplus_auto = 1;
|
||||||
}
|
}
|
||||||
if (arg->flags & FUSE_ASYNC_DIO)
|
if (flags & FUSE_ASYNC_DIO)
|
||||||
fc->async_dio = 1;
|
fc->async_dio = 1;
|
||||||
if (arg->flags & FUSE_WRITEBACK_CACHE)
|
if (flags & FUSE_WRITEBACK_CACHE)
|
||||||
fc->writeback_cache = 1;
|
fc->writeback_cache = 1;
|
||||||
if (arg->flags & FUSE_PARALLEL_DIROPS)
|
if (flags & FUSE_PARALLEL_DIROPS)
|
||||||
fc->parallel_dirops = 1;
|
fc->parallel_dirops = 1;
|
||||||
if (arg->flags & FUSE_HANDLE_KILLPRIV)
|
if (flags & FUSE_HANDLE_KILLPRIV)
|
||||||
fc->handle_killpriv = 1;
|
fc->handle_killpriv = 1;
|
||||||
if (arg->time_gran && arg->time_gran <= 1000000000)
|
if (arg->time_gran && arg->time_gran <= 1000000000)
|
||||||
fm->sb->s_time_gran = arg->time_gran;
|
fm->sb->s_time_gran = arg->time_gran;
|
||||||
if ((arg->flags & FUSE_POSIX_ACL)) {
|
if ((flags & FUSE_POSIX_ACL)) {
|
||||||
fc->default_permissions = 1;
|
fc->default_permissions = 1;
|
||||||
fc->posix_acl = 1;
|
fc->posix_acl = 1;
|
||||||
fm->sb->s_xattr = fuse_acl_xattr_handlers;
|
fm->sb->s_xattr = fuse_acl_xattr_handlers;
|
||||||
}
|
}
|
||||||
if (arg->flags & FUSE_CACHE_SYMLINKS)
|
if (flags & FUSE_CACHE_SYMLINKS)
|
||||||
fc->cache_symlinks = 1;
|
fc->cache_symlinks = 1;
|
||||||
if (arg->flags & FUSE_ABORT_ERROR)
|
if (flags & FUSE_ABORT_ERROR)
|
||||||
fc->abort_err = 1;
|
fc->abort_err = 1;
|
||||||
if (arg->flags & FUSE_MAX_PAGES) {
|
if (flags & FUSE_MAX_PAGES) {
|
||||||
fc->max_pages =
|
fc->max_pages =
|
||||||
min_t(unsigned int, fc->max_pages_limit,
|
min_t(unsigned int, fc->max_pages_limit,
|
||||||
max_t(unsigned int, arg->max_pages, 1));
|
max_t(unsigned int, arg->max_pages, 1));
|
||||||
}
|
}
|
||||||
if (IS_ENABLED(CONFIG_FUSE_DAX) &&
|
if (IS_ENABLED(CONFIG_FUSE_DAX)) {
|
||||||
arg->flags & FUSE_MAP_ALIGNMENT &&
|
if (flags & FUSE_MAP_ALIGNMENT &&
|
||||||
!fuse_dax_check_alignment(fc, arg->map_alignment)) {
|
!fuse_dax_check_alignment(fc, arg->map_alignment)) {
|
||||||
ok = false;
|
ok = false;
|
||||||
|
}
|
||||||
|
if (flags & FUSE_HAS_INODE_DAX)
|
||||||
|
fc->inode_dax = 1;
|
||||||
}
|
}
|
||||||
if (arg->flags & FUSE_HANDLE_KILLPRIV_V2) {
|
if (flags & FUSE_HANDLE_KILLPRIV_V2) {
|
||||||
fc->handle_killpriv_v2 = 1;
|
fc->handle_killpriv_v2 = 1;
|
||||||
fm->sb->s_flags |= SB_NOSEC;
|
fm->sb->s_flags |= SB_NOSEC;
|
||||||
}
|
}
|
||||||
if (arg->flags & FUSE_SETXATTR_EXT)
|
if (flags & FUSE_SETXATTR_EXT)
|
||||||
fc->setxattr_ext = 1;
|
fc->setxattr_ext = 1;
|
||||||
|
if (flags & FUSE_SECURITY_CTX)
|
||||||
|
fc->init_security = 1;
|
||||||
} else {
|
} else {
|
||||||
ra_pages = fc->max_read / PAGE_SIZE;
|
ra_pages = fc->max_read / PAGE_SIZE;
|
||||||
fc->no_lock = 1;
|
fc->no_lock = 1;
|
||||||
|
@ -1203,13 +1217,14 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||||
void fuse_send_init(struct fuse_mount *fm)
|
void fuse_send_init(struct fuse_mount *fm)
|
||||||
{
|
{
|
||||||
struct fuse_init_args *ia;
|
struct fuse_init_args *ia;
|
||||||
|
u64 flags;
|
||||||
|
|
||||||
ia = kzalloc(sizeof(*ia), GFP_KERNEL | __GFP_NOFAIL);
|
ia = kzalloc(sizeof(*ia), GFP_KERNEL | __GFP_NOFAIL);
|
||||||
|
|
||||||
ia->in.major = FUSE_KERNEL_VERSION;
|
ia->in.major = FUSE_KERNEL_VERSION;
|
||||||
ia->in.minor = FUSE_KERNEL_MINOR_VERSION;
|
ia->in.minor = FUSE_KERNEL_MINOR_VERSION;
|
||||||
ia->in.max_readahead = fm->sb->s_bdi->ra_pages * PAGE_SIZE;
|
ia->in.max_readahead = fm->sb->s_bdi->ra_pages * PAGE_SIZE;
|
||||||
ia->in.flags |=
|
flags =
|
||||||
FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
|
FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
|
||||||
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
|
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
|
||||||
FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
|
FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
|
||||||
|
@ -1219,13 +1234,19 @@ void fuse_send_init(struct fuse_mount *fm)
|
||||||
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
|
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
|
||||||
FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
|
FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
|
||||||
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
|
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
|
||||||
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT;
|
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
|
||||||
|
FUSE_SECURITY_CTX;
|
||||||
#ifdef CONFIG_FUSE_DAX
|
#ifdef CONFIG_FUSE_DAX
|
||||||
if (fm->fc->dax)
|
if (fm->fc->dax)
|
||||||
ia->in.flags |= FUSE_MAP_ALIGNMENT;
|
flags |= FUSE_MAP_ALIGNMENT;
|
||||||
|
if (fuse_is_inode_dax_mode(fm->fc->dax_mode))
|
||||||
|
flags |= FUSE_HAS_INODE_DAX;
|
||||||
#endif
|
#endif
|
||||||
if (fm->fc->auto_submounts)
|
if (fm->fc->auto_submounts)
|
||||||
ia->in.flags |= FUSE_SUBMOUNTS;
|
flags |= FUSE_SUBMOUNTS;
|
||||||
|
|
||||||
|
ia->in.flags = flags;
|
||||||
|
ia->in.flags2 = flags >> 32;
|
||||||
|
|
||||||
ia->args.opcode = FUSE_INIT;
|
ia->args.opcode = FUSE_INIT;
|
||||||
ia->args.in_numargs = 1;
|
ia->args.in_numargs = 1;
|
||||||
|
@ -1514,7 +1535,7 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
|
||||||
sb->s_subtype = ctx->subtype;
|
sb->s_subtype = ctx->subtype;
|
||||||
ctx->subtype = NULL;
|
ctx->subtype = NULL;
|
||||||
if (IS_ENABLED(CONFIG_FUSE_DAX)) {
|
if (IS_ENABLED(CONFIG_FUSE_DAX)) {
|
||||||
err = fuse_dax_conn_alloc(fc, ctx->dax_dev);
|
err = fuse_dax_conn_alloc(fc, ctx->dax_mode, ctx->dax_dev);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,12 +88,21 @@ struct virtio_fs_req_work {
|
||||||
static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
|
static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
|
||||||
struct fuse_req *req, bool in_flight);
|
struct fuse_req *req, bool in_flight);
|
||||||
|
|
||||||
|
static const struct constant_table dax_param_enums[] = {
|
||||||
|
{"always", FUSE_DAX_ALWAYS },
|
||||||
|
{"never", FUSE_DAX_NEVER },
|
||||||
|
{"inode", FUSE_DAX_INODE_USER },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
OPT_DAX,
|
OPT_DAX,
|
||||||
|
OPT_DAX_ENUM,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct fs_parameter_spec virtio_fs_parameters[] = {
|
static const struct fs_parameter_spec virtio_fs_parameters[] = {
|
||||||
fsparam_flag("dax", OPT_DAX),
|
fsparam_flag("dax", OPT_DAX),
|
||||||
|
fsparam_enum("dax", OPT_DAX_ENUM, dax_param_enums),
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,7 +119,10 @@ static int virtio_fs_parse_param(struct fs_context *fsc,
|
||||||
|
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case OPT_DAX:
|
case OPT_DAX:
|
||||||
ctx->dax = 1;
|
ctx->dax_mode = FUSE_DAX_ALWAYS;
|
||||||
|
break;
|
||||||
|
case OPT_DAX_ENUM:
|
||||||
|
ctx->dax_mode = result.uint_32;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1326,8 +1338,8 @@ static int virtio_fs_fill_super(struct super_block *sb, struct fs_context *fsc)
|
||||||
|
|
||||||
/* virtiofs allocates and installs its own fuse devices */
|
/* virtiofs allocates and installs its own fuse devices */
|
||||||
ctx->fudptr = NULL;
|
ctx->fudptr = NULL;
|
||||||
if (ctx->dax) {
|
if (ctx->dax_mode != FUSE_DAX_NEVER) {
|
||||||
if (!fs->dax_dev) {
|
if (ctx->dax_mode == FUSE_DAX_ALWAYS && !fs->dax_dev) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
pr_err("virtio-fs: dax can't be enabled as filesystem"
|
pr_err("virtio-fs: dax can't be enabled as filesystem"
|
||||||
" device does not support it.\n");
|
" device does not support it.\n");
|
||||||
|
|
|
@ -187,6 +187,13 @@
|
||||||
*
|
*
|
||||||
* 7.35
|
* 7.35
|
||||||
* - add FOPEN_NOFLUSH
|
* - add FOPEN_NOFLUSH
|
||||||
|
*
|
||||||
|
* 7.36
|
||||||
|
* - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
|
||||||
|
* - add flags2 to fuse_init_in and fuse_init_out
|
||||||
|
* - add FUSE_SECURITY_CTX init flag
|
||||||
|
* - add security context to create, mkdir, symlink, and mknod requests
|
||||||
|
* - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LINUX_FUSE_H
|
#ifndef _LINUX_FUSE_H
|
||||||
|
@ -222,7 +229,7 @@
|
||||||
#define FUSE_KERNEL_VERSION 7
|
#define FUSE_KERNEL_VERSION 7
|
||||||
|
|
||||||
/** Minor version number of this interface */
|
/** Minor version number of this interface */
|
||||||
#define FUSE_KERNEL_MINOR_VERSION 35
|
#define FUSE_KERNEL_MINOR_VERSION 36
|
||||||
|
|
||||||
/** The node ID of the root inode */
|
/** The node ID of the root inode */
|
||||||
#define FUSE_ROOT_ID 1
|
#define FUSE_ROOT_ID 1
|
||||||
|
@ -341,6 +348,11 @@ struct fuse_file_lock {
|
||||||
* write/truncate sgid is killed only if file has group
|
* write/truncate sgid is killed only if file has group
|
||||||
* execute permission. (Same as Linux VFS behavior).
|
* execute permission. (Same as Linux VFS behavior).
|
||||||
* FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in
|
* FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in
|
||||||
|
* FUSE_INIT_EXT: extended fuse_init_in request
|
||||||
|
* FUSE_INIT_RESERVED: reserved, do not use
|
||||||
|
* FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and
|
||||||
|
* mknod
|
||||||
|
* FUSE_HAS_INODE_DAX: use per inode DAX
|
||||||
*/
|
*/
|
||||||
#define FUSE_ASYNC_READ (1 << 0)
|
#define FUSE_ASYNC_READ (1 << 0)
|
||||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||||
|
@ -372,6 +384,11 @@ struct fuse_file_lock {
|
||||||
#define FUSE_SUBMOUNTS (1 << 27)
|
#define FUSE_SUBMOUNTS (1 << 27)
|
||||||
#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
|
#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
|
||||||
#define FUSE_SETXATTR_EXT (1 << 29)
|
#define FUSE_SETXATTR_EXT (1 << 29)
|
||||||
|
#define FUSE_INIT_EXT (1 << 30)
|
||||||
|
#define FUSE_INIT_RESERVED (1 << 31)
|
||||||
|
/* bits 32..63 get shifted down 32 bits into the flags2 field */
|
||||||
|
#define FUSE_SECURITY_CTX (1ULL << 32)
|
||||||
|
#define FUSE_HAS_INODE_DAX (1ULL << 33)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CUSE INIT request/reply flags
|
* CUSE INIT request/reply flags
|
||||||
|
@ -454,8 +471,10 @@ struct fuse_file_lock {
|
||||||
* fuse_attr flags
|
* fuse_attr flags
|
||||||
*
|
*
|
||||||
* FUSE_ATTR_SUBMOUNT: Object is a submount root
|
* FUSE_ATTR_SUBMOUNT: Object is a submount root
|
||||||
|
* FUSE_ATTR_DAX: Enable DAX for this file in per inode DAX mode
|
||||||
*/
|
*/
|
||||||
#define FUSE_ATTR_SUBMOUNT (1 << 0)
|
#define FUSE_ATTR_SUBMOUNT (1 << 0)
|
||||||
|
#define FUSE_ATTR_DAX (1 << 1)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open flags
|
* Open flags
|
||||||
|
@ -741,6 +760,8 @@ struct fuse_init_in {
|
||||||
uint32_t minor;
|
uint32_t minor;
|
||||||
uint32_t max_readahead;
|
uint32_t max_readahead;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
uint32_t flags2;
|
||||||
|
uint32_t unused[11];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FUSE_COMPAT_INIT_OUT_SIZE 8
|
#define FUSE_COMPAT_INIT_OUT_SIZE 8
|
||||||
|
@ -757,7 +778,8 @@ struct fuse_init_out {
|
||||||
uint32_t time_gran;
|
uint32_t time_gran;
|
||||||
uint16_t max_pages;
|
uint16_t max_pages;
|
||||||
uint16_t map_alignment;
|
uint16_t map_alignment;
|
||||||
uint32_t unused[8];
|
uint32_t flags2;
|
||||||
|
uint32_t unused[7];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CUSE_INIT_INFO_MAX 4096
|
#define CUSE_INIT_INFO_MAX 4096
|
||||||
|
@ -865,9 +887,12 @@ struct fuse_dirent {
|
||||||
char name[];
|
char name[];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
|
/* Align variable length records to 64bit boundary */
|
||||||
#define FUSE_DIRENT_ALIGN(x) \
|
#define FUSE_REC_ALIGN(x) \
|
||||||
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
|
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
|
||||||
|
|
||||||
|
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
|
||||||
|
#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
|
||||||
#define FUSE_DIRENT_SIZE(d) \
|
#define FUSE_DIRENT_SIZE(d) \
|
||||||
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
|
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
|
||||||
|
|
||||||
|
@ -984,4 +1009,26 @@ struct fuse_syncfs_in {
|
||||||
uint64_t padding;
|
uint64_t padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For each security context, send fuse_secctx with size of security context
|
||||||
|
* fuse_secctx will be followed by security context name and this in turn
|
||||||
|
* will be followed by actual context label.
|
||||||
|
* fuse_secctx, name, context
|
||||||
|
*/
|
||||||
|
struct fuse_secctx {
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Contains the information about how many fuse_secctx structures are being
|
||||||
|
* sent and what's the total size of all security contexts (including
|
||||||
|
* size of fuse_secctx_header).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct fuse_secctx_header {
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t nr_secctx;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _LINUX_FUSE_H */
|
#endif /* _LINUX_FUSE_H */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче