diff options
author | Alain Knaff <alain@knaff.lu> | 2008-11-10 20:08:08 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-01-05 11:53:07 -0500 |
commit | 5b6f1eb97d462a45be3b30759758b5fdbb562c8c (patch) | |
tree | e8f664c34dde71b95955f57caad3093581be87ef /fs/read_write.c | |
parent | 7d3b56ba37a95f1f370f50258ed3954c304c524b (diff) |
vfs: lseek(fd, 0, SEEK_CUR) race condition
This patch fixes a race condition in lseek. While it is expected that
unpredictable behaviour may result while repositioning the offset of a
file descriptor concurrently with reading/writing to the same file
descriptor, this should not happen when merely *reading* the file
descriptor's offset.
Unfortunately, the only portable way in Unix to read a file
descriptor's offset is lseek(fd, 0, SEEK_CUR); however executing this
concurrently with read/write may mess up the position.
[with fixes from akpm]
Signed-off-by: Alain Knaff <alain@knaff.lu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/read_write.c')
-rw-r--r-- | fs/read_write.c | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/fs/read_write.c b/fs/read_write.c index 969a6d9c020b..5cc6924eb158 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
@@ -50,6 +50,14 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) | |||
50 | offset += inode->i_size; | 50 | offset += inode->i_size; |
51 | break; | 51 | break; |
52 | case SEEK_CUR: | 52 | case SEEK_CUR: |
53 | /* | ||
54 | * Here we special-case the lseek(fd, 0, SEEK_CUR) | ||
55 | * position-querying operation. Avoid rewriting the "same" | ||
56 | * f_pos value back to the file because a concurrent read(), | ||
57 | * write() or lseek() might have altered it | ||
58 | */ | ||
59 | if (offset == 0) | ||
60 | return file->f_pos; | ||
53 | offset += file->f_pos; | 61 | offset += file->f_pos; |
54 | break; | 62 | break; |
55 | } | 63 | } |
@@ -105,6 +113,10 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin) | |||
105 | offset += i_size_read(file->f_path.dentry->d_inode); | 113 | offset += i_size_read(file->f_path.dentry->d_inode); |
106 | break; | 114 | break; |
107 | case SEEK_CUR: | 115 | case SEEK_CUR: |
116 | if (offset == 0) { | ||
117 | retval = file->f_pos; | ||
118 | goto out; | ||
119 | } | ||
108 | offset += file->f_pos; | 120 | offset += file->f_pos; |
109 | } | 121 | } |
110 | retval = -EINVAL; | 122 | retval = -EINVAL; |
@@ -115,6 +127,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin) | |||
115 | } | 127 | } |
116 | retval = offset; | 128 | retval = offset; |
117 | } | 129 | } |
130 | out: | ||
118 | unlock_kernel(); | 131 | unlock_kernel(); |
119 | return retval; | 132 | return retval; |
120 | } | 133 | } |