Commit 3e63cbb1efca7dd3137de1bb475e2e068e38ef23
Committed by
Al Viro
1 parent
01c031945f
Exists in
master
and in
39 other branches
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>
Showing 5 changed files with 139 additions and 29 deletions Side-by-side Diff
fs/compat_ioctl.c
... | ... | @@ -31,6 +31,7 @@ |
31 | 31 | #include <linux/skbuff.h> |
32 | 32 | #include <linux/netlink.h> |
33 | 33 | #include <linux/vt.h> |
34 | +#include <linux/falloc.h> | |
34 | 35 | #include <linux/fs.h> |
35 | 36 | #include <linux/file.h> |
36 | 37 | #include <linux/ppp_defs.h> |
37 | 38 | |
... | ... | @@ -1779,7 +1780,42 @@ |
1779 | 1780 | return sys_ioctl(fd, cmd, (unsigned long)tn); |
1780 | 1781 | } |
1781 | 1782 | |
1783 | +/* on ia32 l_start is on a 32-bit boundary */ | |
1784 | +#if defined(CONFIG_IA64) || defined(CONFIG_X86_64) | |
1785 | +struct space_resv_32 { | |
1786 | + __s16 l_type; | |
1787 | + __s16 l_whence; | |
1788 | + __s64 l_start __attribute__((packed)); | |
1789 | + /* len == 0 means until end of file */ | |
1790 | + __s64 l_len __attribute__((packed)); | |
1791 | + __s32 l_sysid; | |
1792 | + __u32 l_pid; | |
1793 | + __s32 l_pad[4]; /* reserve area */ | |
1794 | +}; | |
1782 | 1795 | |
1796 | +#define FS_IOC_RESVSP_32 _IOW ('X', 40, struct space_resv_32) | |
1797 | +#define FS_IOC_RESVSP64_32 _IOW ('X', 42, struct space_resv_32) | |
1798 | + | |
1799 | +/* just account for different alignment */ | |
1800 | +static int compat_ioctl_preallocate(struct file *file, unsigned long arg) | |
1801 | +{ | |
1802 | + struct space_resv_32 __user *p32 = (void __user *)arg; | |
1803 | + struct space_resv __user *p = compat_alloc_user_space(sizeof(*p)); | |
1804 | + | |
1805 | + if (copy_in_user(&p->l_type, &p32->l_type, sizeof(s16)) || | |
1806 | + copy_in_user(&p->l_whence, &p32->l_whence, sizeof(s16)) || | |
1807 | + copy_in_user(&p->l_start, &p32->l_start, sizeof(s64)) || | |
1808 | + copy_in_user(&p->l_len, &p32->l_len, sizeof(s64)) || | |
1809 | + copy_in_user(&p->l_sysid, &p32->l_sysid, sizeof(s32)) || | |
1810 | + copy_in_user(&p->l_pid, &p32->l_pid, sizeof(u32)) || | |
1811 | + copy_in_user(&p->l_pad, &p32->l_pad, 4*sizeof(u32))) | |
1812 | + return -EFAULT; | |
1813 | + | |
1814 | + return ioctl_preallocate(file, p); | |
1815 | +} | |
1816 | +#endif | |
1817 | + | |
1818 | + | |
1783 | 1819 | typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, |
1784 | 1820 | unsigned long, struct file *); |
1785 | 1821 | |
... | ... | @@ -2755,6 +2791,18 @@ |
2755 | 2791 | case FIOASYNC: |
2756 | 2792 | case FIOQSIZE: |
2757 | 2793 | break; |
2794 | + | |
2795 | +#if defined(CONFIG_IA64) || defined(CONFIG_X86_64) | |
2796 | + case FS_IOC_RESVSP_32: | |
2797 | + case FS_IOC_RESVSP64_32: | |
2798 | + error = compat_ioctl_preallocate(filp, arg); | |
2799 | + goto out_fput; | |
2800 | +#else | |
2801 | + case FS_IOC_RESVSP: | |
2802 | + case FS_IOC_RESVSP64: | |
2803 | + error = ioctl_preallocate(filp, (void __user *)arg); | |
2804 | + goto out_fput; | |
2805 | +#endif | |
2758 | 2806 | |
2759 | 2807 | case FIBMAP: |
2760 | 2808 | case FIGETBSZ: |
fs/ioctl.c
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | #include <linux/uaccess.h> |
16 | 16 | #include <linux/writeback.h> |
17 | 17 | #include <linux/buffer_head.h> |
18 | +#include <linux/falloc.h> | |
18 | 19 | |
19 | 20 | #include <asm/ioctls.h> |
20 | 21 | |
... | ... | @@ -403,6 +404,37 @@ |
403 | 404 | |
404 | 405 | #endif /* CONFIG_BLOCK */ |
405 | 406 | |
407 | +/* | |
408 | + * This provides compatibility with legacy XFS pre-allocation ioctls | |
409 | + * which predate the fallocate syscall. | |
410 | + * | |
411 | + * Only the l_start, l_len and l_whence fields of the 'struct space_resv' | |
412 | + * are used here, rest are ignored. | |
413 | + */ | |
414 | +int ioctl_preallocate(struct file *filp, void __user *argp) | |
415 | +{ | |
416 | + struct inode *inode = filp->f_path.dentry->d_inode; | |
417 | + struct space_resv sr; | |
418 | + | |
419 | + if (copy_from_user(&sr, argp, sizeof(sr))) | |
420 | + return -EFAULT; | |
421 | + | |
422 | + switch (sr.l_whence) { | |
423 | + case SEEK_SET: | |
424 | + break; | |
425 | + case SEEK_CUR: | |
426 | + sr.l_start += filp->f_pos; | |
427 | + break; | |
428 | + case SEEK_END: | |
429 | + sr.l_start += i_size_read(inode); | |
430 | + break; | |
431 | + default: | |
432 | + return -EINVAL; | |
433 | + } | |
434 | + | |
435 | + return do_fallocate(filp, FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len); | |
436 | +} | |
437 | + | |
406 | 438 | static int file_ioctl(struct file *filp, unsigned int cmd, |
407 | 439 | unsigned long arg) |
408 | 440 | { |
... | ... | @@ -414,6 +446,9 @@ |
414 | 446 | return ioctl_fibmap(filp, p); |
415 | 447 | case FIONREAD: |
416 | 448 | return put_user(i_size_read(inode) - filp->f_pos, p); |
449 | + case FS_IOC_RESVSP: | |
450 | + case FS_IOC_RESVSP64: | |
451 | + return ioctl_preallocate(filp, p); | |
417 | 452 | } |
418 | 453 | |
419 | 454 | return vfs_ioctl(filp, cmd, arg); |
fs/open.c
... | ... | @@ -378,63 +378,63 @@ |
378 | 378 | #endif |
379 | 379 | #endif /* BITS_PER_LONG == 32 */ |
380 | 380 | |
381 | -SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len) | |
381 | + | |
382 | +int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) | |
382 | 383 | { |
383 | - struct file *file; | |
384 | - struct inode *inode; | |
385 | - long ret = -EINVAL; | |
384 | + struct inode *inode = file->f_path.dentry->d_inode; | |
385 | + long ret; | |
386 | 386 | |
387 | 387 | if (offset < 0 || len <= 0) |
388 | - goto out; | |
388 | + return -EINVAL; | |
389 | 389 | |
390 | 390 | /* Return error if mode is not supported */ |
391 | - ret = -EOPNOTSUPP; | |
392 | 391 | if (mode && !(mode & FALLOC_FL_KEEP_SIZE)) |
393 | - goto out; | |
392 | + return -EOPNOTSUPP; | |
394 | 393 | |
395 | - ret = -EBADF; | |
396 | - file = fget(fd); | |
397 | - if (!file) | |
398 | - goto out; | |
399 | 394 | if (!(file->f_mode & FMODE_WRITE)) |
400 | - goto out_fput; | |
395 | + return -EBADF; | |
401 | 396 | /* |
402 | 397 | * Revalidate the write permissions, in case security policy has |
403 | 398 | * changed since the files were opened. |
404 | 399 | */ |
405 | 400 | ret = security_file_permission(file, MAY_WRITE); |
406 | 401 | if (ret) |
407 | - goto out_fput; | |
402 | + return ret; | |
408 | 403 | |
409 | - inode = file->f_path.dentry->d_inode; | |
410 | - | |
411 | - ret = -ESPIPE; | |
412 | 404 | if (S_ISFIFO(inode->i_mode)) |
413 | - goto out_fput; | |
405 | + return -ESPIPE; | |
414 | 406 | |
415 | - ret = -ENODEV; | |
416 | 407 | /* |
417 | 408 | * Let individual file system decide if it supports preallocation |
418 | 409 | * for directories or not. |
419 | 410 | */ |
420 | 411 | if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) |
421 | - goto out_fput; | |
412 | + return -ENODEV; | |
422 | 413 | |
423 | - ret = -EFBIG; | |
424 | 414 | /* Check for wrap through zero too */ |
425 | 415 | if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) |
426 | - goto out_fput; | |
416 | + return -EFBIG; | |
427 | 417 | |
428 | - if (inode->i_op->fallocate) | |
429 | - ret = inode->i_op->fallocate(inode, mode, offset, len); | |
430 | - else | |
431 | - ret = -EOPNOTSUPP; | |
418 | + if (!inode->i_op->fallocate) | |
419 | + return -EOPNOTSUPP; | |
432 | 420 | |
433 | -out_fput: | |
434 | - fput(file); | |
435 | -out: | |
436 | - return ret; | |
421 | + return inode->i_op->fallocate(inode, mode, offset, len); | |
437 | 422 | } |
423 | + | |
424 | +SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len) | |
425 | +{ | |
426 | + struct file *file; | |
427 | + int error = -EBADF; | |
428 | + | |
429 | + file = fget(fd); | |
430 | + if (file) { | |
431 | + error = do_fallocate(file, mode, offset, len); | |
432 | + fput(file); | |
433 | + } | |
434 | + | |
435 | + return error; | |
436 | +} | |
437 | + | |
438 | 438 | #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS |
439 | 439 | asmlinkage long SyS_fallocate(long fd, long mode, loff_t offset, loff_t len) |
440 | 440 | { |
include/linux/falloc.h
... | ... | @@ -3,5 +3,26 @@ |
3 | 3 | |
4 | 4 | #define FALLOC_FL_KEEP_SIZE 0x01 /* default is extend size */ |
5 | 5 | |
6 | +#ifdef __KERNEL__ | |
7 | + | |
8 | +/* | |
9 | + * Space reservation ioctls and argument structure | |
10 | + * are designed to be compatible with the legacy XFS ioctls. | |
11 | + */ | |
12 | +struct space_resv { | |
13 | + __s16 l_type; | |
14 | + __s16 l_whence; | |
15 | + __s64 l_start; | |
16 | + __s64 l_len; /* len == 0 means until end of file */ | |
17 | + __s32 l_sysid; | |
18 | + __u32 l_pid; | |
19 | + __s32 l_pad[4]; /* reserved area */ | |
20 | +}; | |
21 | + | |
22 | +#define FS_IOC_RESVSP _IOW('X', 40, struct space_resv) | |
23 | +#define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv) | |
24 | + | |
25 | +#endif /* __KERNEL__ */ | |
26 | + | |
6 | 27 | #endif /* _FALLOC_H_ */ |
include/linux/fs.h
... | ... | @@ -1906,6 +1906,8 @@ |
1906 | 1906 | |
1907 | 1907 | extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, |
1908 | 1908 | struct file *filp); |
1909 | +extern int do_fallocate(struct file *file, int mode, loff_t offset, | |
1910 | + loff_t len); | |
1909 | 1911 | extern long do_sys_open(int dfd, const char __user *filename, int flags, |
1910 | 1912 | int mode); |
1911 | 1913 | extern struct file *filp_open(const char *, int, int); |
... | ... | @@ -1913,6 +1915,10 @@ |
1913 | 1915 | const struct cred *); |
1914 | 1916 | extern int filp_close(struct file *, fl_owner_t id); |
1915 | 1917 | extern char * getname(const char __user *); |
1918 | + | |
1919 | +/* fs/ioctl.c */ | |
1920 | + | |
1921 | +extern int ioctl_preallocate(struct file *filp, void __user *argp); | |
1916 | 1922 | |
1917 | 1923 | /* fs/dcache.c */ |
1918 | 1924 | extern void __init vfs_caches_init_early(void); |