diff options
| -rw-r--r-- | fs/file_table.c | 1 | ||||
| -rw-r--r-- | fs/namei.c | 2 | ||||
| -rw-r--r-- | fs/open.c | 4 | ||||
| -rw-r--r-- | fs/read_write.c | 54 | ||||
| -rw-r--r-- | include/linux/file.h | 6 | ||||
| -rw-r--r-- | include/linux/fs.h | 6 |
6 files changed, 55 insertions, 18 deletions
diff --git a/fs/file_table.c b/fs/file_table.c index 5fff9030be34..5b24008ea4f6 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
| @@ -135,6 +135,7 @@ struct file *get_empty_filp(void) | |||
| 135 | atomic_long_set(&f->f_count, 1); | 135 | atomic_long_set(&f->f_count, 1); |
| 136 | rwlock_init(&f->f_owner.lock); | 136 | rwlock_init(&f->f_owner.lock); |
| 137 | spin_lock_init(&f->f_lock); | 137 | spin_lock_init(&f->f_lock); |
| 138 | mutex_init(&f->f_pos_lock); | ||
| 138 | eventpoll_init_file(f); | 139 | eventpoll_init_file(f); |
| 139 | /* f->f_version: 0 */ | 140 | /* f->f_version: 0 */ |
| 140 | return f; | 141 | return f; |
diff --git a/fs/namei.c b/fs/namei.c index 385f7817bfcc..2f730ef9b4b3 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
| @@ -1884,7 +1884,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, | |||
| 1884 | 1884 | ||
| 1885 | nd->path = f.file->f_path; | 1885 | nd->path = f.file->f_path; |
| 1886 | if (flags & LOOKUP_RCU) { | 1886 | if (flags & LOOKUP_RCU) { |
| 1887 | if (f.need_put) | 1887 | if (f.flags & FDPUT_FPUT) |
| 1888 | *fp = f.file; | 1888 | *fp = f.file; |
| 1889 | nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq); | 1889 | nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq); |
| 1890 | rcu_read_lock(); | 1890 | rcu_read_lock(); |
| @@ -705,6 +705,10 @@ static int do_dentry_open(struct file *f, | |||
| 705 | return 0; | 705 | return 0; |
| 706 | } | 706 | } |
| 707 | 707 | ||
| 708 | /* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */ | ||
| 709 | if (S_ISREG(inode->i_mode)) | ||
| 710 | f->f_mode |= FMODE_ATOMIC_POS; | ||
| 711 | |||
| 708 | f->f_op = fops_get(inode->i_fop); | 712 | f->f_op = fops_get(inode->i_fop); |
| 709 | if (unlikely(WARN_ON(!f->f_op))) { | 713 | if (unlikely(WARN_ON(!f->f_op))) { |
| 710 | error = -ENODEV; | 714 | error = -ENODEV; |
diff --git a/fs/read_write.c b/fs/read_write.c index edc5746a902a..932bb3414a96 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
| @@ -264,10 +264,36 @@ loff_t vfs_llseek(struct file *file, loff_t offset, int whence) | |||
| 264 | } | 264 | } |
| 265 | EXPORT_SYMBOL(vfs_llseek); | 265 | EXPORT_SYMBOL(vfs_llseek); |
| 266 | 266 | ||
| 267 | /* | ||
| 268 | * We only lock f_pos if we have threads or if the file might be | ||
| 269 | * shared with another process. In both cases we'll have an elevated | ||
| 270 | * file count (done either by fdget() or by fork()). | ||
| 271 | */ | ||
| 272 | static inline struct fd fdget_pos(int fd) | ||
| 273 | { | ||
| 274 | struct fd f = fdget(fd); | ||
| 275 | struct file *file = f.file; | ||
| 276 | |||
| 277 | if (file && (file->f_mode & FMODE_ATOMIC_POS)) { | ||
| 278 | if (file_count(file) > 1) { | ||
| 279 | f.flags |= FDPUT_POS_UNLOCK; | ||
| 280 | mutex_lock(&file->f_pos_lock); | ||
| 281 | } | ||
| 282 | } | ||
| 283 | return f; | ||
| 284 | } | ||
| 285 | |||
| 286 | static inline void fdput_pos(struct fd f) | ||
| 287 | { | ||
| 288 | if (f.flags & FDPUT_POS_UNLOCK) | ||
| 289 | mutex_unlock(&f.file->f_pos_lock); | ||
| 290 | fdput(f); | ||
| 291 | } | ||
| 292 | |||
| 267 | SYSCALL_DEFINE3(lseek, unsigned int, fd, off_t, offset, unsigned int, whence) | 293 | SYSCALL_DEFINE3(lseek, unsigned int, fd, off_t, offset, unsigned int, whence) |
| 268 | { | 294 | { |
| 269 | off_t retval; | 295 | off_t retval; |
| 270 | struct fd f = fdget(fd); | 296 | struct fd f = fdget_pos(fd); |
| 271 | if (!f.file) | 297 | if (!f.file) |
| 272 | return -EBADF; | 298 | return -EBADF; |
| 273 | 299 | ||
| @@ -278,7 +304,7 @@ SYSCALL_DEFINE3(lseek, unsigned int, fd, off_t, offset, unsigned int, whence) | |||
| 278 | if (res != (loff_t)retval) | 304 | if (res != (loff_t)retval) |
| 279 | retval = -EOVERFLOW; /* LFS: should only happen on 32 bit platforms */ | 305 | retval = -EOVERFLOW; /* LFS: should only happen on 32 bit platforms */ |
| 280 | } | 306 | } |
| 281 | fdput(f); | 307 | fdput_pos(f); |
| 282 | return retval; | 308 | return retval; |
| 283 | } | 309 | } |
| 284 | 310 | ||
| @@ -498,7 +524,7 @@ static inline void file_pos_write(struct file *file, loff_t pos) | |||
| 498 | 524 | ||
| 499 | SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) | 525 | SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) |
| 500 | { | 526 | { |
| 501 | struct fd f = fdget(fd); | 527 | struct fd f = fdget_pos(fd); |
| 502 | ssize_t ret = -EBADF; | 528 | ssize_t ret = -EBADF; |
| 503 | 529 | ||
| 504 | if (f.file) { | 530 | if (f.file) { |
| @@ -506,7 +532,7 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) | |||
| 506 | ret = vfs_read(f.file, buf, count, &pos); | 532 | ret = vfs_read(f.file, buf, count, &pos); |
| 507 | if (ret >= 0) | 533 | if (ret >= 0) |
| 508 | file_pos_write(f.file, pos); | 534 | file_pos_write(f.file, pos); |
| 509 | fdput(f); | 535 | fdput_pos(f); |
| 510 | } | 536 | } |
| 511 | return ret; | 537 | return ret; |
| 512 | } | 538 | } |
| @@ -514,7 +540,7 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) | |||
| 514 | SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, | 540 | SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, |
| 515 | size_t, count) | 541 | size_t, count) |
| 516 | { | 542 | { |
| 517 | struct fd f = fdget(fd); | 543 | struct fd f = fdget_pos(fd); |
| 518 | ssize_t ret = -EBADF; | 544 | ssize_t ret = -EBADF; |
| 519 | 545 | ||
| 520 | if (f.file) { | 546 | if (f.file) { |
| @@ -522,7 +548,7 @@ SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, | |||
| 522 | ret = vfs_write(f.file, buf, count, &pos); | 548 | ret = vfs_write(f.file, buf, count, &pos); |
| 523 | if (ret >= 0) | 549 | if (ret >= 0) |
| 524 | file_pos_write(f.file, pos); | 550 | file_pos_write(f.file, pos); |
| 525 | fdput(f); | 551 | fdput_pos(f); |
| 526 | } | 552 | } |
| 527 | 553 | ||
| 528 | return ret; | 554 | return ret; |
| @@ -797,7 +823,7 @@ EXPORT_SYMBOL(vfs_writev); | |||
| 797 | SYSCALL_DEFINE3(readv, unsigned long, fd, const struct iovec __user *, vec, | 823 | SYSCALL_DEFINE3(readv, unsigned long, fd, const struct iovec __user *, vec, |
| 798 | unsigned long, vlen) | 824 | unsigned long, vlen) |
| 799 | { | 825 | { |
| 800 | struct fd f = fdget(fd); | 826 | struct fd f = fdget_pos(fd); |
| 801 | ssize_t ret = -EBADF; | 827 | ssize_t ret = -EBADF; |
| 802 | 828 | ||
| 803 | if (f.file) { | 829 | if (f.file) { |
| @@ -805,7 +831,7 @@ SYSCALL_DEFINE3(readv, unsigned long, fd, const struct iovec __user *, vec, | |||
| 805 | ret = vfs_readv(f.file, vec, vlen, &pos); | 831 | ret = vfs_readv(f.file, vec, vlen, &pos); |
| 806 | if (ret >= 0) | 832 | if (ret >= 0) |
| 807 | file_pos_write(f.file, pos); | 833 | file_pos_write(f.file, pos); |
| 808 | fdput(f); | 834 | fdput_pos(f); |
| 809 | } | 835 | } |
| 810 | 836 | ||
| 811 | if (ret > 0) | 837 | if (ret > 0) |
| @@ -817,7 +843,7 @@ SYSCALL_DEFINE3(readv, unsigned long, fd, const struct iovec __user *, vec, | |||
| 817 | SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec, | 843 | SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec, |
| 818 | unsigned long, vlen) | 844 | unsigned long, vlen) |
| 819 | { | 845 | { |
| 820 | struct fd f = fdget(fd); | 846 | struct fd f = fdget_pos(fd); |
| 821 | ssize_t ret = -EBADF; | 847 | ssize_t ret = -EBADF; |
| 822 | 848 | ||
| 823 | if (f.file) { | 849 | if (f.file) { |
| @@ -825,7 +851,7 @@ SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec, | |||
| 825 | ret = vfs_writev(f.file, vec, vlen, &pos); | 851 | ret = vfs_writev(f.file, vec, vlen, &pos); |
| 826 | if (ret >= 0) | 852 | if (ret >= 0) |
| 827 | file_pos_write(f.file, pos); | 853 | file_pos_write(f.file, pos); |
| 828 | fdput(f); | 854 | fdput_pos(f); |
| 829 | } | 855 | } |
| 830 | 856 | ||
| 831 | if (ret > 0) | 857 | if (ret > 0) |
| @@ -968,7 +994,7 @@ COMPAT_SYSCALL_DEFINE3(readv, compat_ulong_t, fd, | |||
| 968 | const struct compat_iovec __user *,vec, | 994 | const struct compat_iovec __user *,vec, |
| 969 | compat_ulong_t, vlen) | 995 | compat_ulong_t, vlen) |
| 970 | { | 996 | { |
| 971 | struct fd f = fdget(fd); | 997 | struct fd f = fdget_pos(fd); |
| 972 | ssize_t ret; | 998 | ssize_t ret; |
| 973 | loff_t pos; | 999 | loff_t pos; |
| 974 | 1000 | ||
| @@ -978,7 +1004,7 @@ COMPAT_SYSCALL_DEFINE3(readv, compat_ulong_t, fd, | |||
| 978 | ret = compat_readv(f.file, vec, vlen, &pos); | 1004 | ret = compat_readv(f.file, vec, vlen, &pos); |
| 979 | if (ret >= 0) | 1005 | if (ret >= 0) |
| 980 | f.file->f_pos = pos; | 1006 | f.file->f_pos = pos; |
| 981 | fdput(f); | 1007 | fdput_pos(f); |
| 982 | return ret; | 1008 | return ret; |
| 983 | } | 1009 | } |
| 984 | 1010 | ||
| @@ -1035,7 +1061,7 @@ COMPAT_SYSCALL_DEFINE3(writev, compat_ulong_t, fd, | |||
| 1035 | const struct compat_iovec __user *, vec, | 1061 | const struct compat_iovec __user *, vec, |
| 1036 | compat_ulong_t, vlen) | 1062 | compat_ulong_t, vlen) |
| 1037 | { | 1063 | { |
| 1038 | struct fd f = fdget(fd); | 1064 | struct fd f = fdget_pos(fd); |
| 1039 | ssize_t ret; | 1065 | ssize_t ret; |
| 1040 | loff_t pos; | 1066 | loff_t pos; |
| 1041 | 1067 | ||
| @@ -1045,7 +1071,7 @@ COMPAT_SYSCALL_DEFINE3(writev, compat_ulong_t, fd, | |||
| 1045 | ret = compat_writev(f.file, vec, vlen, &pos); | 1071 | ret = compat_writev(f.file, vec, vlen, &pos); |
| 1046 | if (ret >= 0) | 1072 | if (ret >= 0) |
| 1047 | f.file->f_pos = pos; | 1073 | f.file->f_pos = pos; |
| 1048 | fdput(f); | 1074 | fdput_pos(f); |
| 1049 | return ret; | 1075 | return ret; |
| 1050 | } | 1076 | } |
| 1051 | 1077 | ||
diff --git a/include/linux/file.h b/include/linux/file.h index cbacf4faf447..f2517fa2d610 100644 --- a/include/linux/file.h +++ b/include/linux/file.h | |||
| @@ -28,12 +28,14 @@ static inline void fput_light(struct file *file, int fput_needed) | |||
| 28 | 28 | ||
| 29 | struct fd { | 29 | struct fd { |
| 30 | struct file *file; | 30 | struct file *file; |
| 31 | int need_put; | 31 | unsigned int flags; |
| 32 | }; | 32 | }; |
| 33 | #define FDPUT_FPUT 1 | ||
| 34 | #define FDPUT_POS_UNLOCK 2 | ||
| 33 | 35 | ||
| 34 | static inline void fdput(struct fd fd) | 36 | static inline void fdput(struct fd fd) |
| 35 | { | 37 | { |
| 36 | if (fd.need_put) | 38 | if (fd.flags & FDPUT_FPUT) |
| 37 | fput(fd.file); | 39 | fput(fd.file); |
| 38 | } | 40 | } |
| 39 | 41 | ||
diff --git a/include/linux/fs.h b/include/linux/fs.h index 60829565e552..ebfde04bca06 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
| @@ -123,6 +123,9 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, | |||
| 123 | /* File is opened with O_PATH; almost nothing can be done with it */ | 123 | /* File is opened with O_PATH; almost nothing can be done with it */ |
| 124 | #define FMODE_PATH ((__force fmode_t)0x4000) | 124 | #define FMODE_PATH ((__force fmode_t)0x4000) |
| 125 | 125 | ||
| 126 | /* File needs atomic accesses to f_pos */ | ||
| 127 | #define FMODE_ATOMIC_POS ((__force fmode_t)0x8000) | ||
| 128 | |||
| 126 | /* File was opened by fanotify and shouldn't generate fanotify events */ | 129 | /* File was opened by fanotify and shouldn't generate fanotify events */ |
| 127 | #define FMODE_NONOTIFY ((__force fmode_t)0x1000000) | 130 | #define FMODE_NONOTIFY ((__force fmode_t)0x1000000) |
| 128 | 131 | ||
| @@ -780,13 +783,14 @@ struct file { | |||
| 780 | const struct file_operations *f_op; | 783 | const struct file_operations *f_op; |
| 781 | 784 | ||
| 782 | /* | 785 | /* |
| 783 | * Protects f_ep_links, f_flags, f_pos vs i_size in lseek SEEK_CUR. | 786 | * Protects f_ep_links, f_flags. |
| 784 | * Must not be taken from IRQ context. | 787 | * Must not be taken from IRQ context. |
| 785 | */ | 788 | */ |
| 786 | spinlock_t f_lock; | 789 | spinlock_t f_lock; |
| 787 | atomic_long_t f_count; | 790 | atomic_long_t f_count; |
| 788 | unsigned int f_flags; | 791 | unsigned int f_flags; |
| 789 | fmode_t f_mode; | 792 | fmode_t f_mode; |
| 793 | struct mutex f_pos_lock; | ||
| 790 | loff_t f_pos; | 794 | loff_t f_pos; |
| 791 | struct fown_struct f_owner; | 795 | struct fown_struct f_owner; |
| 792 | const struct cred *f_cred; | 796 | const struct cred *f_cred; |
