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: Ankit Jain <me@ankitjain.org> Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
		
					parent
					
						
							
								01c031945f
							
						
					
				
			
			
				commit
				
					
						3e63cbb1ef
					
				
			
		
					 5 changed files with 139 additions and 29 deletions
				
			
		| 
						 | 
					@ -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)
 | 
				
			||||||
	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,
 | 
				
			||||||
	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:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										35
									
								
								fs/ioctl.c
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								fs/ioctl.c
									
										
									
									
									
								
							| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#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,
 | 
				
			||||||
		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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										58
									
								
								fs/open.c
									
										
									
									
									
								
							
							
						
						
									
										58
									
								
								fs/open.c
									
										
									
									
									
								
							| 
						 | 
					@ -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 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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,
 | 
				
			||||||
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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue