diff options
author | Ankit Jain <me@ankitjain.org> | 2009-06-19 14:28:07 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-06-24 08:15:27 -0400 |
commit | 3e63cbb1efca7dd3137de1bb475e2e068e38ef23 (patch) | |
tree | 5a7964068266e778e94c1348192d3c85eba4ac48 | |
parent | 01c031945f2755c7afaaf456088543312f2b72ea (diff) |
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>
-rw-r--r-- | fs/compat_ioctl.c | 48 | ||||
-rw-r--r-- | fs/ioctl.c | 35 | ||||
-rw-r--r-- | fs/open.c | 58 | ||||
-rw-r--r-- | include/linux/falloc.h | 21 | ||||
-rw-r--r-- | include/linux/fs.h | 6 |
5 files changed, 139 insertions, 29 deletions
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index c135202c38b3..626c7483b4de 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/skbuff.h> | 31 | #include <linux/skbuff.h> |
32 | #include <linux/netlink.h> | 32 | #include <linux/netlink.h> |
33 | #include <linux/vt.h> | 33 | #include <linux/vt.h> |
34 | #include <linux/falloc.h> | ||
34 | #include <linux/fs.h> | 35 | #include <linux/fs.h> |
35 | #include <linux/file.h> | 36 | #include <linux/file.h> |
36 | #include <linux/ppp_defs.h> | 37 | #include <linux/ppp_defs.h> |
@@ -1779,6 +1780,41 @@ lp_timeout_trans(unsigned int fd, unsigned int cmd, unsigned long arg) | |||
1779 | return sys_ioctl(fd, cmd, (unsigned long)tn); | 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 | }; | ||
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 | |||
1782 | 1818 | ||
1783 | typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, | 1819 | typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, |
1784 | unsigned long, struct file *); | 1820 | unsigned long, struct file *); |
@@ -2756,6 +2792,18 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, | |||
2756 | case FIOQSIZE: | 2792 | case FIOQSIZE: |
2757 | break; | 2793 | break; |
2758 | 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 | ||
2806 | |||
2759 | case FIBMAP: | 2807 | case FIBMAP: |
2760 | case FIGETBSZ: | 2808 | case FIGETBSZ: |
2761 | case FIONREAD: | 2809 | case FIONREAD: |
diff --git a/fs/ioctl.c b/fs/ioctl.c index 001f8d3118f2..5612880fcbe7 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/uaccess.h> | 15 | #include <linux/uaccess.h> |
16 | #include <linux/writeback.h> | 16 | #include <linux/writeback.h> |
17 | #include <linux/buffer_head.h> | 17 | #include <linux/buffer_head.h> |
18 | #include <linux/falloc.h> | ||
18 | 19 | ||
19 | #include <asm/ioctls.h> | 20 | #include <asm/ioctls.h> |
20 | 21 | ||
@@ -403,6 +404,37 @@ EXPORT_SYMBOL(generic_block_fiemap); | |||
403 | 404 | ||
404 | #endif /* CONFIG_BLOCK */ | 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 | static int file_ioctl(struct file *filp, unsigned int cmd, | 438 | static int file_ioctl(struct file *filp, unsigned int cmd, |
407 | unsigned long arg) | 439 | unsigned long arg) |
408 | { | 440 | { |
@@ -414,6 +446,9 @@ static int file_ioctl(struct file *filp, unsigned int cmd, | |||
414 | return ioctl_fibmap(filp, p); | 446 | return ioctl_fibmap(filp, p); |
415 | case FIONREAD: | 447 | case FIONREAD: |
416 | return put_user(i_size_read(inode) - filp->f_pos, p); | 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 | return vfs_ioctl(filp, cmd, arg); | 454 | return vfs_ioctl(filp, cmd, arg); |
@@ -378,63 +378,63 @@ SYSCALL_ALIAS(sys_ftruncate64, SyS_ftruncate64); | |||
378 | #endif | 378 | #endif |
379 | #endif /* BITS_PER_LONG == 32 */ | 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 = file->f_path.dentry->d_inode; |
384 | struct inode *inode; | 385 | long ret; |
385 | long ret = -EINVAL; | ||
386 | 386 | ||
387 | if (offset < 0 || len <= 0) | 387 | if (offset < 0 || len <= 0) |
388 | goto out; | 388 | return -EINVAL; |
389 | 389 | ||
390 | /* Return error if mode is not supported */ | 390 | /* Return error if mode is not supported */ |
391 | ret = -EOPNOTSUPP; | ||
392 | if (mode && !(mode & FALLOC_FL_KEEP_SIZE)) | 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 | if (!(file->f_mode & FMODE_WRITE)) | 394 | if (!(file->f_mode & FMODE_WRITE)) |
400 | goto out_fput; | 395 | return -EBADF; |
401 | /* | 396 | /* |
402 | * Revalidate the write permissions, in case security policy has | 397 | * Revalidate the write permissions, in case security policy has |
403 | * changed since the files were opened. | 398 | * changed since the files were opened. |
404 | */ | 399 | */ |
405 | ret = security_file_permission(file, MAY_WRITE); | 400 | ret = security_file_permission(file, MAY_WRITE); |
406 | if (ret) | 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 | if (S_ISFIFO(inode->i_mode)) | 404 | if (S_ISFIFO(inode->i_mode)) |
413 | goto out_fput; | 405 | return -ESPIPE; |
414 | 406 | ||
415 | ret = -ENODEV; | ||
416 | /* | 407 | /* |
417 | * Let individual file system decide if it supports preallocation | 408 | * Let individual file system decide if it supports preallocation |
418 | * for directories or not. | 409 | * for directories or not. |
419 | */ | 410 | */ |
420 | if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) | 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 | /* Check for wrap through zero too */ | 414 | /* Check for wrap through zero too */ |
425 | if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) | 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) | 418 | if (!inode->i_op->fallocate) |
429 | ret = inode->i_op->fallocate(inode, mode, offset, len); | 419 | return -EOPNOTSUPP; |
430 | else | ||
431 | ret = -EOPNOTSUPP; | ||
432 | 420 | ||
433 | out_fput: | 421 | return inode->i_op->fallocate(inode, mode, offset, len); |
434 | fput(file); | ||
435 | out: | ||
436 | return ret; | ||
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 | #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS | 438 | #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS |
439 | asmlinkage long SyS_fallocate(long fd, long mode, loff_t offset, loff_t len) | 439 | asmlinkage long SyS_fallocate(long fd, long mode, loff_t offset, loff_t len) |
440 | { | 440 | { |
diff --git a/include/linux/falloc.h b/include/linux/falloc.h index 8e912ab6a072..3c155107d61f 100644 --- a/include/linux/falloc.h +++ b/include/linux/falloc.h | |||
@@ -3,4 +3,25 @@ | |||
3 | 3 | ||
4 | #define FALLOC_FL_KEEP_SIZE 0x01 /* default is extend size */ | 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 | #endif /* _FALLOC_H_ */ | 27 | #endif /* _FALLOC_H_ */ |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 1ff5e4e01952..79e302ddde0f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1906,6 +1906,8 @@ static inline int break_lease(struct inode *inode, unsigned int mode) | |||
1906 | 1906 | ||
1907 | extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, | 1907 | extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, |
1908 | struct file *filp); | 1908 | struct file *filp); |
1909 | extern int do_fallocate(struct file *file, int mode, loff_t offset, | ||
1910 | loff_t len); | ||
1909 | extern long do_sys_open(int dfd, const char __user *filename, int flags, | 1911 | extern long do_sys_open(int dfd, const char __user *filename, int flags, |
1910 | int mode); | 1912 | int mode); |
1911 | extern struct file *filp_open(const char *, int, int); | 1913 | extern struct file *filp_open(const char *, int, int); |
@@ -1914,6 +1916,10 @@ extern struct file * dentry_open(struct dentry *, struct vfsmount *, int, | |||
1914 | extern int filp_close(struct file *, fl_owner_t id); | 1916 | extern int filp_close(struct file *, fl_owner_t id); |
1915 | extern char * getname(const char __user *); | 1917 | extern char * getname(const char __user *); |
1916 | 1918 | ||
1919 | /* fs/ioctl.c */ | ||
1920 | |||
1921 | extern int ioctl_preallocate(struct file *filp, void __user *argp); | ||
1922 | |||
1917 | /* fs/dcache.c */ | 1923 | /* fs/dcache.c */ |
1918 | extern void __init vfs_caches_init_early(void); | 1924 | extern void __init vfs_caches_init_early(void); |
1919 | extern void __init vfs_caches_init(unsigned long); | 1925 | extern void __init vfs_caches_init(unsigned long); |