Commit 3e63cbb1 authored by Ankit Jain's avatar Ankit Jain Committed by Al Viro

fs: Add new pre-allocation ioctls to vfs for compatibility with legacy xfs ioctls

This patch adds ioctls to vfs for compatibility with legacy XFS
pre-allocation ioctls (XFS_IOC_*RESVP*). The implementation
effectively invokes sys_fallocate for the new ioctls.
Also handles the compat_ioctl case.
Note: These legacy ioctls are also implemented by OCFS2.

[AV: folded fixes from hch]
Signed-off-by: default avatarAnkit Jain <me@ankitjain.org>
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 01c03194
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/vt.h> #include <linux/vt.h>
#include <linux/falloc.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/ppp_defs.h> #include <linux/ppp_defs.h>
...@@ -1779,6 +1780,41 @@ lp_timeout_trans(unsigned int fd, unsigned int cmd, unsigned long arg) ...@@ -1779,6 +1780,41 @@ lp_timeout_trans(unsigned int fd, unsigned int cmd, unsigned long arg)
return sys_ioctl(fd, cmd, (unsigned long)tn); return sys_ioctl(fd, cmd, (unsigned long)tn);
} }
/* on ia32 l_start is on a 32-bit boundary */
#if defined(CONFIG_IA64) || defined(CONFIG_X86_64)
struct space_resv_32 {
__s16 l_type;
__s16 l_whence;
__s64 l_start __attribute__((packed));
/* len == 0 means until end of file */
__s64 l_len __attribute__((packed));
__s32 l_sysid;
__u32 l_pid;
__s32 l_pad[4]; /* reserve area */
};
#define FS_IOC_RESVSP_32 _IOW ('X', 40, struct space_resv_32)
#define FS_IOC_RESVSP64_32 _IOW ('X', 42, struct space_resv_32)
/* just account for different alignment */
static int compat_ioctl_preallocate(struct file *file, unsigned long arg)
{
struct space_resv_32 __user *p32 = (void __user *)arg;
struct space_resv __user *p = compat_alloc_user_space(sizeof(*p));
if (copy_in_user(&p->l_type, &p32->l_type, sizeof(s16)) ||
copy_in_user(&p->l_whence, &p32->l_whence, sizeof(s16)) ||
copy_in_user(&p->l_start, &p32->l_start, sizeof(s64)) ||
copy_in_user(&p->l_len, &p32->l_len, sizeof(s64)) ||
copy_in_user(&p->l_sysid, &p32->l_sysid, sizeof(s32)) ||
copy_in_user(&p->l_pid, &p32->l_pid, sizeof(u32)) ||
copy_in_user(&p->l_pad, &p32->l_pad, 4*sizeof(u32)))
return -EFAULT;
return ioctl_preallocate(file, p);
}
#endif
typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int,
unsigned long, struct file *); unsigned long, struct file *);
...@@ -2756,6 +2792,18 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, ...@@ -2756,6 +2792,18 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
case FIOQSIZE: case FIOQSIZE:
break; break;
#if defined(CONFIG_IA64) || defined(CONFIG_X86_64)
case FS_IOC_RESVSP_32:
case FS_IOC_RESVSP64_32:
error = compat_ioctl_preallocate(filp, arg);
goto out_fput;
#else
case FS_IOC_RESVSP:
case FS_IOC_RESVSP64:
error = ioctl_preallocate(filp, (void __user *)arg);
goto out_fput;
#endif
case FIBMAP: case FIBMAP:
case FIGETBSZ: case FIGETBSZ:
case FIONREAD: case FIONREAD:
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/writeback.h> #include <linux/writeback.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/falloc.h>
#include <asm/ioctls.h> #include <asm/ioctls.h>
...@@ -403,6 +404,37 @@ EXPORT_SYMBOL(generic_block_fiemap); ...@@ -403,6 +404,37 @@ EXPORT_SYMBOL(generic_block_fiemap);
#endif /* CONFIG_BLOCK */ #endif /* CONFIG_BLOCK */
/*
* This provides compatibility with legacy XFS pre-allocation ioctls
* which predate the fallocate syscall.
*
* Only the l_start, l_len and l_whence fields of the 'struct space_resv'
* are used here, rest are ignored.
*/
int ioctl_preallocate(struct file *filp, void __user *argp)
{
struct inode *inode = filp->f_path.dentry->d_inode;
struct space_resv sr;
if (copy_from_user(&sr, argp, sizeof(sr)))
return -EFAULT;
switch (sr.l_whence) {
case SEEK_SET:
break;
case SEEK_CUR:
sr.l_start += filp->f_pos;
break;
case SEEK_END:
sr.l_start += i_size_read(inode);
break;
default:
return -EINVAL;
}
return do_fallocate(filp, FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len);
}
static int file_ioctl(struct file *filp, unsigned int cmd, static int file_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
...@@ -414,6 +446,9 @@ static int file_ioctl(struct file *filp, unsigned int cmd, ...@@ -414,6 +446,9 @@ static int file_ioctl(struct file *filp, unsigned int cmd,
return ioctl_fibmap(filp, p); return ioctl_fibmap(filp, p);
case FIONREAD: case FIONREAD:
return put_user(i_size_read(inode) - filp->f_pos, p); return put_user(i_size_read(inode) - filp->f_pos, p);
case FS_IOC_RESVSP:
case FS_IOC_RESVSP64:
return ioctl_preallocate(filp, p);
} }
return vfs_ioctl(filp, cmd, arg); return vfs_ioctl(filp, cmd, arg);
......
...@@ -378,63 +378,63 @@ SYSCALL_ALIAS(sys_ftruncate64, SyS_ftruncate64); ...@@ -378,63 +378,63 @@ SYSCALL_ALIAS(sys_ftruncate64, SyS_ftruncate64);
#endif #endif
#endif /* BITS_PER_LONG == 32 */ #endif /* BITS_PER_LONG == 32 */
SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len)
int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
{ {
struct file *file; struct inode *inode = file->f_path.dentry->d_inode;
struct inode *inode; long ret;
long ret = -EINVAL;
if (offset < 0 || len <= 0) if (offset < 0 || len <= 0)
goto out; return -EINVAL;
/* Return error if mode is not supported */ /* Return error if mode is not supported */
ret = -EOPNOTSUPP;
if (mode && !(mode & FALLOC_FL_KEEP_SIZE)) if (mode && !(mode & FALLOC_FL_KEEP_SIZE))
goto out; return -EOPNOTSUPP;
ret = -EBADF;
file = fget(fd);
if (!file)
goto out;
if (!(file->f_mode & FMODE_WRITE)) if (!(file->f_mode & FMODE_WRITE))
goto out_fput; return -EBADF;
/* /*
* Revalidate the write permissions, in case security policy has * Revalidate the write permissions, in case security policy has
* changed since the files were opened. * changed since the files were opened.
*/ */
ret = security_file_permission(file, MAY_WRITE); ret = security_file_permission(file, MAY_WRITE);
if (ret) if (ret)
goto out_fput; return ret;
inode = file->f_path.dentry->d_inode;
ret = -ESPIPE;
if (S_ISFIFO(inode->i_mode)) if (S_ISFIFO(inode->i_mode))
goto out_fput; return -ESPIPE;
ret = -ENODEV;
/* /*
* Let individual file system decide if it supports preallocation * Let individual file system decide if it supports preallocation
* for directories or not. * for directories or not.
*/ */
if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
goto out_fput; return -ENODEV;
ret = -EFBIG;
/* Check for wrap through zero too */ /* Check for wrap through zero too */
if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0))
goto out_fput; return -EFBIG;
if (inode->i_op->fallocate) if (!inode->i_op->fallocate)
ret = inode->i_op->fallocate(inode, mode, offset, len); return -EOPNOTSUPP;
else
ret = -EOPNOTSUPP;
out_fput: return inode->i_op->fallocate(inode, mode, offset, len);
fput(file);
out:
return ret;
} }
SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len)
{
struct file *file;
int error = -EBADF;
file = fget(fd);
if (file) {
error = do_fallocate(file, mode, offset, len);
fput(file);
}
return error;
}
#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
asmlinkage long SyS_fallocate(long fd, long mode, loff_t offset, loff_t len) asmlinkage long SyS_fallocate(long fd, long mode, loff_t offset, loff_t len)
{ {
......
...@@ -3,4 +3,25 @@ ...@@ -3,4 +3,25 @@
#define FALLOC_FL_KEEP_SIZE 0x01 /* default is extend size */ #define FALLOC_FL_KEEP_SIZE 0x01 /* default is extend size */
#ifdef __KERNEL__
/*
* Space reservation ioctls and argument structure
* are designed to be compatible with the legacy XFS ioctls.
*/
struct 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]; /* reserved area */
};
#define FS_IOC_RESVSP _IOW('X', 40, struct space_resv)
#define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv)
#endif /* __KERNEL__ */
#endif /* _FALLOC_H_ */ #endif /* _FALLOC_H_ */
...@@ -1906,6 +1906,8 @@ static inline int break_lease(struct inode *inode, unsigned int mode) ...@@ -1906,6 +1906,8 @@ static inline int break_lease(struct inode *inode, unsigned int mode)
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
struct file *filp); struct file *filp);
extern int do_fallocate(struct file *file, int mode, loff_t offset,
loff_t len);
extern long do_sys_open(int dfd, const char __user *filename, int flags, extern long do_sys_open(int dfd, const char __user *filename, int flags,
int mode); int mode);
extern struct file *filp_open(const char *, int, int); extern struct file *filp_open(const char *, int, int);
...@@ -1914,6 +1916,10 @@ extern struct file * dentry_open(struct dentry *, struct vfsmount *, int, ...@@ -1914,6 +1916,10 @@ extern struct file * dentry_open(struct dentry *, struct vfsmount *, int,
extern int filp_close(struct file *, fl_owner_t id); extern int filp_close(struct file *, fl_owner_t id);
extern char * getname(const char __user *); extern char * getname(const char __user *);
/* fs/ioctl.c */
extern int ioctl_preallocate(struct file *filp, void __user *argp);
/* fs/dcache.c */ /* fs/dcache.c */
extern void __init vfs_caches_init_early(void); extern void __init vfs_caches_init_early(void);
extern void __init vfs_caches_init(unsigned long); extern void __init vfs_caches_init(unsigned long);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment