ocfs2: Support xfs style space reservation ioctls
We re-use the RESVSP/UNRESVSP ioctls from xfs which allow the user to allocate and deallocate regions to a file without zeroing data or changing i_size. Though renamed, the structure passed in from user is identical to struct xfs_flock64. The three fields that are actually used right now are l_whence, l_start and l_len. This should get ocfs2 immediate compatibility with userspace software using the pre-existing xfs ioctls. Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
This commit is contained in:
parent
063c4561f5
commit
b25801038d
182
fs/ocfs2/file.c
182
fs/ocfs2/file.c
@ -1111,17 +1111,16 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ocfs2_write_remove_suid(struct inode *inode)
|
||||
static int __ocfs2_write_remove_suid(struct inode *inode,
|
||||
struct buffer_head *bh)
|
||||
{
|
||||
int ret;
|
||||
struct buffer_head *bh = NULL;
|
||||
struct ocfs2_inode_info *oi = OCFS2_I(inode);
|
||||
handle_t *handle;
|
||||
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
||||
struct ocfs2_dinode *di;
|
||||
|
||||
mlog_entry("(Inode %llu, mode 0%o)\n",
|
||||
(unsigned long long)oi->ip_blkno, inode->i_mode);
|
||||
(unsigned long long)OCFS2_I(inode)->ip_blkno, inode->i_mode);
|
||||
|
||||
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
|
||||
if (handle == NULL) {
|
||||
@ -1130,17 +1129,11 @@ static int ocfs2_write_remove_suid(struct inode *inode)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ocfs2_read_block(osb, oi->ip_blkno, &bh, OCFS2_BH_CACHED, inode);
|
||||
if (ret < 0) {
|
||||
mlog_errno(ret);
|
||||
goto out_trans;
|
||||
}
|
||||
|
||||
ret = ocfs2_journal_access(handle, inode, bh,
|
||||
OCFS2_JOURNAL_ACCESS_WRITE);
|
||||
if (ret < 0) {
|
||||
mlog_errno(ret);
|
||||
goto out_bh;
|
||||
goto out_trans;
|
||||
}
|
||||
|
||||
inode->i_mode &= ~S_ISUID;
|
||||
@ -1153,8 +1146,7 @@ static int ocfs2_write_remove_suid(struct inode *inode)
|
||||
ret = ocfs2_journal_dirty(handle, bh);
|
||||
if (ret < 0)
|
||||
mlog_errno(ret);
|
||||
out_bh:
|
||||
brelse(bh);
|
||||
|
||||
out_trans:
|
||||
ocfs2_commit_trans(osb, handle);
|
||||
out:
|
||||
@ -1200,6 +1192,25 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ocfs2_write_remove_suid(struct inode *inode)
|
||||
{
|
||||
int ret;
|
||||
struct buffer_head *bh = NULL;
|
||||
struct ocfs2_inode_info *oi = OCFS2_I(inode);
|
||||
|
||||
ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
|
||||
oi->ip_blkno, &bh, OCFS2_BH_CACHED, inode);
|
||||
if (ret < 0) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = __ocfs2_write_remove_suid(inode, bh);
|
||||
out:
|
||||
brelse(bh);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate enough extents to cover the region starting at byte offset
|
||||
* start for len bytes. Existing extents are skipped, any extents
|
||||
@ -1490,6 +1501,151 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parts of this function taken from xfs_change_file_space()
|
||||
*/
|
||||
int ocfs2_change_file_space(struct file *file, unsigned int cmd,
|
||||
struct ocfs2_space_resv *sr)
|
||||
{
|
||||
int ret;
|
||||
s64 llen;
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
||||
struct buffer_head *di_bh = NULL;
|
||||
handle_t *handle;
|
||||
unsigned long long max_off = ocfs2_max_file_offset(inode->i_sb->s_blocksize_bits);
|
||||
|
||||
if ((cmd == OCFS2_IOC_RESVSP || cmd == OCFS2_IOC_RESVSP64) &&
|
||||
!ocfs2_writes_unwritten_extents(osb))
|
||||
return -ENOTTY;
|
||||
else if ((cmd == OCFS2_IOC_UNRESVSP || cmd == OCFS2_IOC_UNRESVSP64) &&
|
||||
!ocfs2_sparse_alloc(osb))
|
||||
return -ENOTTY;
|
||||
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(file->f_mode & FMODE_WRITE))
|
||||
return -EBADF;
|
||||
|
||||
if (ocfs2_is_hard_readonly(osb) || ocfs2_is_soft_readonly(osb))
|
||||
return -EROFS;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
/*
|
||||
* This prevents concurrent writes on other nodes
|
||||
*/
|
||||
ret = ocfs2_rw_lock(inode, 1);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ocfs2_meta_lock(inode, &di_bh, 1);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out_rw_unlock;
|
||||
}
|
||||
|
||||
if (inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
|
||||
ret = -EPERM;
|
||||
goto out_meta_unlock;
|
||||
}
|
||||
|
||||
switch (sr->l_whence) {
|
||||
case 0: /*SEEK_SET*/
|
||||
break;
|
||||
case 1: /*SEEK_CUR*/
|
||||
sr->l_start += file->f_pos;
|
||||
break;
|
||||
case 2: /*SEEK_END*/
|
||||
sr->l_start += i_size_read(inode);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_meta_unlock;
|
||||
}
|
||||
sr->l_whence = 0;
|
||||
|
||||
llen = sr->l_len > 0 ? sr->l_len - 1 : sr->l_len;
|
||||
|
||||
if (sr->l_start < 0
|
||||
|| sr->l_start > max_off
|
||||
|| (sr->l_start + llen) < 0
|
||||
|| (sr->l_start + llen) > max_off) {
|
||||
ret = -EINVAL;
|
||||
goto out_meta_unlock;
|
||||
}
|
||||
|
||||
if (cmd == OCFS2_IOC_RESVSP || cmd == OCFS2_IOC_RESVSP64) {
|
||||
if (sr->l_len <= 0) {
|
||||
ret = -EINVAL;
|
||||
goto out_meta_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (should_remove_suid(file->f_path.dentry)) {
|
||||
ret = __ocfs2_write_remove_suid(inode, di_bh);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out_meta_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
down_write(&OCFS2_I(inode)->ip_alloc_sem);
|
||||
switch (cmd) {
|
||||
case OCFS2_IOC_RESVSP:
|
||||
case OCFS2_IOC_RESVSP64:
|
||||
/*
|
||||
* This takes unsigned offsets, but the signed ones we
|
||||
* pass have been checked against overflow above.
|
||||
*/
|
||||
ret = ocfs2_allocate_unwritten_extents(inode, sr->l_start,
|
||||
sr->l_len);
|
||||
break;
|
||||
case OCFS2_IOC_UNRESVSP:
|
||||
case OCFS2_IOC_UNRESVSP64:
|
||||
ret = ocfs2_remove_inode_range(inode, di_bh, sr->l_start,
|
||||
sr->l_len);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
up_write(&OCFS2_I(inode)->ip_alloc_sem);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out_meta_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* We update c/mtime for these changes
|
||||
*/
|
||||
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
|
||||
if (IS_ERR(handle)) {
|
||||
ret = PTR_ERR(handle);
|
||||
mlog_errno(ret);
|
||||
goto out_meta_unlock;
|
||||
}
|
||||
|
||||
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
|
||||
ret = ocfs2_mark_inode_dirty(handle, inode, di_bh);
|
||||
if (ret < 0)
|
||||
mlog_errno(ret);
|
||||
|
||||
ocfs2_commit_trans(osb, handle);
|
||||
|
||||
out_meta_unlock:
|
||||
brelse(di_bh);
|
||||
ocfs2_meta_unlock(inode, 1);
|
||||
out_rw_unlock:
|
||||
ocfs2_rw_unlock(inode, 1);
|
||||
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
|
||||
loff_t *ppos,
|
||||
size_t count,
|
||||
|
@ -62,4 +62,7 @@ int ocfs2_should_update_atime(struct inode *inode,
|
||||
int ocfs2_update_inode_atime(struct inode *inode,
|
||||
struct buffer_head *bh);
|
||||
|
||||
int ocfs2_change_file_space(struct file *file, unsigned int cmd,
|
||||
struct ocfs2_space_resv *sr);
|
||||
|
||||
#endif /* OCFS2_FILE_H */
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "ocfs2.h"
|
||||
#include "alloc.h"
|
||||
#include "dlmglue.h"
|
||||
#include "file.h"
|
||||
#include "inode.h"
|
||||
#include "journal.h"
|
||||
|
||||
@ -115,6 +116,7 @@ int ocfs2_ioctl(struct inode * inode, struct file * filp,
|
||||
{
|
||||
unsigned int flags;
|
||||
int status;
|
||||
struct ocfs2_space_resv sr;
|
||||
|
||||
switch (cmd) {
|
||||
case OCFS2_IOC_GETFLAGS:
|
||||
@ -130,6 +132,14 @@ int ocfs2_ioctl(struct inode * inode, struct file * filp,
|
||||
|
||||
return ocfs2_set_inode_attr(inode, flags,
|
||||
OCFS2_FL_MODIFIABLE);
|
||||
case OCFS2_IOC_RESVSP:
|
||||
case OCFS2_IOC_RESVSP64:
|
||||
case OCFS2_IOC_UNRESVSP:
|
||||
case OCFS2_IOC_UNRESVSP64:
|
||||
if (copy_from_user(&sr, (int __user *) arg, sizeof(sr)))
|
||||
return -EFAULT;
|
||||
|
||||
return ocfs2_change_file_space(filp, cmd, &sr);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
@ -148,6 +158,11 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
|
||||
case OCFS2_IOC32_SETFLAGS:
|
||||
cmd = OCFS2_IOC_SETFLAGS;
|
||||
break;
|
||||
case OCFS2_IOC_RESVSP:
|
||||
case OCFS2_IOC_RESVSP64:
|
||||
case OCFS2_IOC_UNRESVSP:
|
||||
case OCFS2_IOC_UNRESVSP64:
|
||||
break;
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
@ -174,6 +174,32 @@
|
||||
#define OCFS2_IOC32_GETFLAGS _IOR('f', 1, int)
|
||||
#define OCFS2_IOC32_SETFLAGS _IOW('f', 2, int)
|
||||
|
||||
/*
|
||||
* Space reservation / allocation / free ioctls and argument structure
|
||||
* are designed to be compatible with XFS.
|
||||
*
|
||||
* ALLOCSP* and FREESP* are not and will never be supported, but are
|
||||
* included here for completeness.
|
||||
*/
|
||||
struct ocfs2_space_resv {
|
||||
__s16 l_type;
|
||||
__s16 l_whence;
|
||||
__s64 l_start;
|
||||
__s64 l_len; /* len == 0 means until end of file */
|
||||
__s32 l_sysid;
|
||||
__u32 l_pid;
|
||||
__s32 l_pad[4]; /* reserve area */
|
||||
};
|
||||
|
||||
#define OCFS2_IOC_ALLOCSP _IOW ('X', 10, struct ocfs2_space_resv)
|
||||
#define OCFS2_IOC_FREESP _IOW ('X', 11, struct ocfs2_space_resv)
|
||||
#define OCFS2_IOC_RESVSP _IOW ('X', 40, struct ocfs2_space_resv)
|
||||
#define OCFS2_IOC_UNRESVSP _IOW ('X', 41, struct ocfs2_space_resv)
|
||||
#define OCFS2_IOC_ALLOCSP64 _IOW ('X', 36, struct ocfs2_space_resv)
|
||||
#define OCFS2_IOC_FREESP64 _IOW ('X', 37, struct ocfs2_space_resv)
|
||||
#define OCFS2_IOC_RESVSP64 _IOW ('X', 42, struct ocfs2_space_resv)
|
||||
#define OCFS2_IOC_UNRESVSP64 _IOW ('X', 43, struct ocfs2_space_resv)
|
||||
|
||||
/*
|
||||
* Journal Flags (ocfs2_dinode.id1.journal1.i_flags)
|
||||
*/
|
||||
|
@ -115,8 +115,6 @@ static void ocfs2_write_super(struct super_block *sb);
|
||||
static struct inode *ocfs2_alloc_inode(struct super_block *sb);
|
||||
static void ocfs2_destroy_inode(struct inode *inode);
|
||||
|
||||
static unsigned long long ocfs2_max_file_offset(unsigned int blockshift);
|
||||
|
||||
static const struct super_operations ocfs2_sops = {
|
||||
.statfs = ocfs2_statfs,
|
||||
.alloc_inode = ocfs2_alloc_inode,
|
||||
@ -321,7 +319,7 @@ static void ocfs2_destroy_inode(struct inode *inode)
|
||||
/* From xfs_super.c:xfs_max_file_offset
|
||||
* Copyright (c) 2000-2004 Silicon Graphics, Inc.
|
||||
*/
|
||||
static unsigned long long ocfs2_max_file_offset(unsigned int blockshift)
|
||||
unsigned long long ocfs2_max_file_offset(unsigned int blockshift)
|
||||
{
|
||||
unsigned int pagefactor = 1;
|
||||
unsigned int bitshift = BITS_PER_LONG - 1;
|
||||
|
@ -45,4 +45,6 @@ void __ocfs2_abort(struct super_block *sb,
|
||||
|
||||
#define ocfs2_abort(sb, fmt, args...) __ocfs2_abort(sb, __PRETTY_FUNCTION__, fmt, ##args)
|
||||
|
||||
unsigned long long ocfs2_max_file_offset(unsigned int blockshift);
|
||||
|
||||
#endif /* OCFS2_SUPER_H */
|
||||
|
Loading…
Reference in New Issue
Block a user