Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: FUSE: Notifying the kernel of deletion. fuse: support ioctl on directories fuse: Use kcalloc instead of kzalloc to allocate array fuse: llseek optimize SEEK_CUR and SEEK_SET
This commit is contained in:
commit
6733e54b66
5 changed files with 149 additions and 50 deletions
|
@ -1378,7 +1378,59 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
|
||||||
down_read(&fc->killsb);
|
down_read(&fc->killsb);
|
||||||
err = -ENOENT;
|
err = -ENOENT;
|
||||||
if (fc->sb)
|
if (fc->sb)
|
||||||
err = fuse_reverse_inval_entry(fc->sb, outarg.parent, &name);
|
err = fuse_reverse_inval_entry(fc->sb, outarg.parent, 0, &name);
|
||||||
|
up_read(&fc->killsb);
|
||||||
|
kfree(buf);
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err:
|
||||||
|
kfree(buf);
|
||||||
|
fuse_copy_finish(cs);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
|
||||||
|
struct fuse_copy_state *cs)
|
||||||
|
{
|
||||||
|
struct fuse_notify_delete_out outarg;
|
||||||
|
int err = -ENOMEM;
|
||||||
|
char *buf;
|
||||||
|
struct qstr name;
|
||||||
|
|
||||||
|
buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
err = -EINVAL;
|
||||||
|
if (size < sizeof(outarg))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
||||||
|
if (err)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
err = -ENAMETOOLONG;
|
||||||
|
if (outarg.namelen > FUSE_NAME_MAX)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
err = -EINVAL;
|
||||||
|
if (size != sizeof(outarg) + outarg.namelen + 1)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
name.name = buf;
|
||||||
|
name.len = outarg.namelen;
|
||||||
|
err = fuse_copy_one(cs, buf, outarg.namelen + 1);
|
||||||
|
if (err)
|
||||||
|
goto err;
|
||||||
|
fuse_copy_finish(cs);
|
||||||
|
buf[outarg.namelen] = 0;
|
||||||
|
name.hash = full_name_hash(name.name, name.len);
|
||||||
|
|
||||||
|
down_read(&fc->killsb);
|
||||||
|
err = -ENOENT;
|
||||||
|
if (fc->sb)
|
||||||
|
err = fuse_reverse_inval_entry(fc->sb, outarg.parent,
|
||||||
|
outarg.child, &name);
|
||||||
up_read(&fc->killsb);
|
up_read(&fc->killsb);
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return err;
|
return err;
|
||||||
|
@ -1597,6 +1649,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
|
||||||
case FUSE_NOTIFY_RETRIEVE:
|
case FUSE_NOTIFY_RETRIEVE:
|
||||||
return fuse_notify_retrieve(fc, size, cs);
|
return fuse_notify_retrieve(fc, size, cs);
|
||||||
|
|
||||||
|
case FUSE_NOTIFY_DELETE:
|
||||||
|
return fuse_notify_delete(fc, size, cs);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fuse_copy_finish(cs);
|
fuse_copy_finish(cs);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -868,7 +868,7 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
|
||||||
}
|
}
|
||||||
|
|
||||||
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
|
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
|
||||||
struct qstr *name)
|
u64 child_nodeid, struct qstr *name)
|
||||||
{
|
{
|
||||||
int err = -ENOTDIR;
|
int err = -ENOTDIR;
|
||||||
struct inode *parent;
|
struct inode *parent;
|
||||||
|
@ -895,8 +895,36 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
|
||||||
|
|
||||||
fuse_invalidate_attr(parent);
|
fuse_invalidate_attr(parent);
|
||||||
fuse_invalidate_entry(entry);
|
fuse_invalidate_entry(entry);
|
||||||
dput(entry);
|
|
||||||
|
if (child_nodeid != 0 && entry->d_inode) {
|
||||||
|
mutex_lock(&entry->d_inode->i_mutex);
|
||||||
|
if (get_node_id(entry->d_inode) != child_nodeid) {
|
||||||
|
err = -ENOENT;
|
||||||
|
goto badentry;
|
||||||
|
}
|
||||||
|
if (d_mountpoint(entry)) {
|
||||||
|
err = -EBUSY;
|
||||||
|
goto badentry;
|
||||||
|
}
|
||||||
|
if (S_ISDIR(entry->d_inode->i_mode)) {
|
||||||
|
shrink_dcache_parent(entry);
|
||||||
|
if (!simple_empty(entry)) {
|
||||||
|
err = -ENOTEMPTY;
|
||||||
|
goto badentry;
|
||||||
|
}
|
||||||
|
entry->d_inode->i_flags |= S_DEAD;
|
||||||
|
}
|
||||||
|
dont_mount(entry);
|
||||||
|
clear_nlink(entry->d_inode);
|
||||||
err = 0;
|
err = 0;
|
||||||
|
badentry:
|
||||||
|
mutex_unlock(&entry->d_inode->i_mutex);
|
||||||
|
if (!err)
|
||||||
|
d_delete(entry);
|
||||||
|
} else {
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
dput(entry);
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
mutex_unlock(&parent->i_mutex);
|
mutex_unlock(&parent->i_mutex);
|
||||||
|
@ -1182,6 +1210,30 @@ static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end,
|
||||||
return fuse_fsync_common(file, start, end, datasync, 1);
|
return fuse_fsync_common(file, start, end, datasync, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long fuse_dir_ioctl(struct file *file, unsigned int cmd,
|
||||||
|
unsigned long arg)
|
||||||
|
{
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(file->f_mapping->host);
|
||||||
|
|
||||||
|
/* FUSE_IOCTL_DIR only supported for API version >= 7.18 */
|
||||||
|
if (fc->minor < 18)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
return fuse_ioctl_common(file, cmd, arg, FUSE_IOCTL_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd,
|
||||||
|
unsigned long arg)
|
||||||
|
{
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(file->f_mapping->host);
|
||||||
|
|
||||||
|
if (fc->minor < 18)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
return fuse_ioctl_common(file, cmd, arg,
|
||||||
|
FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
static bool update_mtime(unsigned ivalid)
|
static bool update_mtime(unsigned ivalid)
|
||||||
{
|
{
|
||||||
/* Always update if mtime is explicitly set */
|
/* Always update if mtime is explicitly set */
|
||||||
|
@ -1596,6 +1648,8 @@ static const struct file_operations fuse_dir_operations = {
|
||||||
.open = fuse_dir_open,
|
.open = fuse_dir_open,
|
||||||
.release = fuse_dir_release,
|
.release = fuse_dir_release,
|
||||||
.fsync = fuse_dir_fsync,
|
.fsync = fuse_dir_fsync,
|
||||||
|
.unlocked_ioctl = fuse_dir_ioctl,
|
||||||
|
.compat_ioctl = fuse_dir_compat_ioctl,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct inode_operations fuse_common_inode_operations = {
|
static const struct inode_operations fuse_common_inode_operations = {
|
||||||
|
|
|
@ -1555,48 +1555,16 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
|
||||||
loff_t retval;
|
loff_t retval;
|
||||||
struct inode *inode = file->f_path.dentry->d_inode;
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
/* No i_mutex protection necessary for SEEK_CUR and SEEK_SET */
|
||||||
if (origin != SEEK_CUR && origin != SEEK_SET) {
|
if (origin == SEEK_CUR || origin == SEEK_SET)
|
||||||
retval = fuse_update_attributes(inode, NULL, file, NULL);
|
return generic_file_llseek(file, offset, origin);
|
||||||
if (retval)
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (origin) {
|
mutex_lock(&inode->i_mutex);
|
||||||
case SEEK_END:
|
retval = fuse_update_attributes(inode, NULL, file, NULL);
|
||||||
offset += i_size_read(inode);
|
if (!retval)
|
||||||
break;
|
retval = generic_file_llseek(file, offset, origin);
|
||||||
case SEEK_CUR:
|
|
||||||
if (offset == 0) {
|
|
||||||
retval = file->f_pos;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
offset += file->f_pos;
|
|
||||||
break;
|
|
||||||
case SEEK_DATA:
|
|
||||||
if (offset >= i_size_read(inode)) {
|
|
||||||
retval = -ENXIO;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SEEK_HOLE:
|
|
||||||
if (offset >= i_size_read(inode)) {
|
|
||||||
retval = -ENXIO;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
offset = i_size_read(inode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
retval = -EINVAL;
|
|
||||||
if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {
|
|
||||||
if (offset != file->f_pos) {
|
|
||||||
file->f_pos = offset;
|
|
||||||
file->f_version = 0;
|
|
||||||
}
|
|
||||||
retval = offset;
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1808,7 +1776,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
|
||||||
BUILD_BUG_ON(sizeof(struct fuse_ioctl_iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE);
|
BUILD_BUG_ON(sizeof(struct fuse_ioctl_iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE);
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
pages = kzalloc(sizeof(pages[0]) * FUSE_MAX_PAGES_PER_REQ, GFP_KERNEL);
|
pages = kcalloc(FUSE_MAX_PAGES_PER_REQ, sizeof(pages[0]), GFP_KERNEL);
|
||||||
iov_page = (struct iovec *) __get_free_page(GFP_KERNEL);
|
iov_page = (struct iovec *) __get_free_page(GFP_KERNEL);
|
||||||
if (!pages || !iov_page)
|
if (!pages || !iov_page)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1958,7 +1926,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fuse_do_ioctl);
|
EXPORT_SYMBOL_GPL(fuse_do_ioctl);
|
||||||
|
|
||||||
static long fuse_file_ioctl_common(struct file *file, unsigned int cmd,
|
long fuse_ioctl_common(struct file *file, unsigned int cmd,
|
||||||
unsigned long arg, unsigned int flags)
|
unsigned long arg, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode = file->f_dentry->d_inode;
|
struct inode *inode = file->f_dentry->d_inode;
|
||||||
|
@ -1976,13 +1944,13 @@ static long fuse_file_ioctl_common(struct file *file, unsigned int cmd,
|
||||||
static long fuse_file_ioctl(struct file *file, unsigned int cmd,
|
static long fuse_file_ioctl(struct file *file, unsigned int cmd,
|
||||||
unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
return fuse_file_ioctl_common(file, cmd, arg, 0);
|
return fuse_ioctl_common(file, cmd, arg, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
|
static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
|
||||||
unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
return fuse_file_ioctl_common(file, cmd, arg, FUSE_IOCTL_COMPAT);
|
return fuse_ioctl_common(file, cmd, arg, FUSE_IOCTL_COMPAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -755,9 +755,15 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
|
||||||
/**
|
/**
|
||||||
* File-system tells the kernel to invalidate parent attributes and
|
* File-system tells the kernel to invalidate parent attributes and
|
||||||
* the dentry matching parent/name.
|
* the dentry matching parent/name.
|
||||||
|
*
|
||||||
|
* If the child_nodeid is non-zero and:
|
||||||
|
* - matches the inode number for the dentry matching parent/name,
|
||||||
|
* - is not a mount point
|
||||||
|
* - is a file or oan empty directory
|
||||||
|
* then the dentry is unhashed (d_delete()).
|
||||||
*/
|
*/
|
||||||
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
|
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
|
||||||
struct qstr *name);
|
u64 child_nodeid, struct qstr *name);
|
||||||
|
|
||||||
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
|
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
|
||||||
bool isdir);
|
bool isdir);
|
||||||
|
@ -765,6 +771,8 @@ ssize_t fuse_direct_io(struct file *file, const char __user *buf,
|
||||||
size_t count, loff_t *ppos, int write);
|
size_t count, loff_t *ppos, int write);
|
||||||
long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
|
long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
|
||||||
unsigned int flags);
|
unsigned int flags);
|
||||||
|
long fuse_ioctl_common(struct file *file, unsigned int cmd,
|
||||||
|
unsigned long arg, unsigned int flags);
|
||||||
unsigned fuse_file_poll(struct file *file, poll_table *wait);
|
unsigned fuse_file_poll(struct file *file, poll_table *wait);
|
||||||
int fuse_dev_release(struct inode *inode, struct file *file);
|
int fuse_dev_release(struct inode *inode, struct file *file);
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,10 @@
|
||||||
*
|
*
|
||||||
* 7.17
|
* 7.17
|
||||||
* - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK
|
* - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK
|
||||||
|
*
|
||||||
|
* 7.18
|
||||||
|
* - add FUSE_IOCTL_DIR flag
|
||||||
|
* - add FUSE_NOTIFY_DELETE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LINUX_FUSE_H
|
#ifndef _LINUX_FUSE_H
|
||||||
|
@ -81,7 +85,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 17
|
#define FUSE_KERNEL_MINOR_VERSION 18
|
||||||
|
|
||||||
/** The node ID of the root inode */
|
/** The node ID of the root inode */
|
||||||
#define FUSE_ROOT_ID 1
|
#define FUSE_ROOT_ID 1
|
||||||
|
@ -214,6 +218,7 @@ struct fuse_file_lock {
|
||||||
* FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
|
* FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
|
||||||
* FUSE_IOCTL_RETRY: retry with new iovecs
|
* FUSE_IOCTL_RETRY: retry with new iovecs
|
||||||
* FUSE_IOCTL_32BIT: 32bit ioctl
|
* FUSE_IOCTL_32BIT: 32bit ioctl
|
||||||
|
* FUSE_IOCTL_DIR: is a directory
|
||||||
*
|
*
|
||||||
* FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
|
* FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
|
||||||
*/
|
*/
|
||||||
|
@ -221,6 +226,7 @@ struct fuse_file_lock {
|
||||||
#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
|
#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
|
||||||
#define FUSE_IOCTL_RETRY (1 << 2)
|
#define FUSE_IOCTL_RETRY (1 << 2)
|
||||||
#define FUSE_IOCTL_32BIT (1 << 3)
|
#define FUSE_IOCTL_32BIT (1 << 3)
|
||||||
|
#define FUSE_IOCTL_DIR (1 << 4)
|
||||||
|
|
||||||
#define FUSE_IOCTL_MAX_IOV 256
|
#define FUSE_IOCTL_MAX_IOV 256
|
||||||
|
|
||||||
|
@ -283,6 +289,7 @@ enum fuse_notify_code {
|
||||||
FUSE_NOTIFY_INVAL_ENTRY = 3,
|
FUSE_NOTIFY_INVAL_ENTRY = 3,
|
||||||
FUSE_NOTIFY_STORE = 4,
|
FUSE_NOTIFY_STORE = 4,
|
||||||
FUSE_NOTIFY_RETRIEVE = 5,
|
FUSE_NOTIFY_RETRIEVE = 5,
|
||||||
|
FUSE_NOTIFY_DELETE = 6,
|
||||||
FUSE_NOTIFY_CODE_MAX,
|
FUSE_NOTIFY_CODE_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -606,6 +613,13 @@ struct fuse_notify_inval_entry_out {
|
||||||
__u32 padding;
|
__u32 padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct fuse_notify_delete_out {
|
||||||
|
__u64 parent;
|
||||||
|
__u64 child;
|
||||||
|
__u32 namelen;
|
||||||
|
__u32 padding;
|
||||||
|
};
|
||||||
|
|
||||||
struct fuse_notify_store_out {
|
struct fuse_notify_store_out {
|
||||||
__u64 nodeid;
|
__u64 nodeid;
|
||||||
__u64 offset;
|
__u64 offset;
|
||||||
|
|
Loading…
Reference in a new issue