aboutsummaryrefslogtreecommitdiffstats
path: root/fs/read_write.c
diff options
context:
space:
mode:
authorAlain Knaff <alain@knaff.lu>2008-11-10 20:08:08 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2009-01-05 11:53:07 -0500
commit5b6f1eb97d462a45be3b30759758b5fdbb562c8c (patch)
treee8f664c34dde71b95955f57caad3093581be87ef /fs/read_write.c
parent7d3b56ba37a95f1f370f50258ed3954c304c524b (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.c13
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 }
130out:
118 unlock_kernel(); 131 unlock_kernel();
119 return retval; 132 return retval;
120} 133}