Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

seq_file: properly cope with pread

Currently seq_read assumes that the offset passed to it is always the
offset it passed to user space. In the case pread this assumption is
broken and we do the wrong thing when presented with pread.

To solve this I introduce an offset cache inside of struct seq_file so we
know where our logical file position is. Then in seq_read if we try to
read from another offset we reset our data structures and attempt to go to
the offset user space wanted.

[akpm@linux-foundation.org: restore FMODE_PWRITE]
[pjt@google.com: seq_open needs its fmode opened up to take advantage of this]
Signed-off-by: Eric Biederman <ebiederm@xmission.com>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Paul Turner <pjt@google.com>
Cc: <stable@kernel.org> [2.6.25.x, 2.6.26.x, 2.6.27.x, 2.6.28.x]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Eric Biederman and committed by
Linus Torvalds
8f19d472 55ec8217

+33 -4
+32 -4
fs/seq_file.c
··· 48 48 */ 49 49 file->f_version = 0; 50 50 51 - /* SEQ files support lseek, but not pread/pwrite */ 52 - file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE); 51 + /* 52 + * seq_files support lseek() and pread(). They do not implement 53 + * write() at all, but we clear FMODE_PWRITE here for historical 54 + * reasons. 55 + * 56 + * If a client of seq_files a) implements file.write() and b) wishes to 57 + * support pwrite() then that client will need to implement its own 58 + * file.open() which calls seq_open() and then sets FMODE_PWRITE. 59 + */ 60 + file->f_mode &= ~FMODE_PWRITE; 53 61 return 0; 54 62 } 55 63 EXPORT_SYMBOL(seq_open); ··· 139 131 int err = 0; 140 132 141 133 mutex_lock(&m->lock); 134 + 135 + /* Don't assume *ppos is where we left it */ 136 + if (unlikely(*ppos != m->read_pos)) { 137 + m->read_pos = *ppos; 138 + while ((err = traverse(m, *ppos)) == -EAGAIN) 139 + ; 140 + if (err) { 141 + /* With prejudice... */ 142 + m->read_pos = 0; 143 + m->version = 0; 144 + m->index = 0; 145 + m->count = 0; 146 + goto Done; 147 + } 148 + } 149 + 142 150 /* 143 151 * seq_file->op->..m_start/m_stop/m_next may do special actions 144 152 * or optimisations based on the file->f_version, so we want to ··· 254 230 Done: 255 231 if (!copied) 256 232 copied = err; 257 - else 233 + else { 258 234 *ppos += copied; 235 + m->read_pos += copied; 236 + } 259 237 file->f_version = m->version; 260 238 mutex_unlock(&m->lock); 261 239 return copied; ··· 292 266 if (offset < 0) 293 267 break; 294 268 retval = offset; 295 - if (offset != file->f_pos) { 269 + if (offset != m->read_pos) { 296 270 while ((retval=traverse(m, offset)) == -EAGAIN) 297 271 ; 298 272 if (retval) { 299 273 /* with extreme prejudice... */ 300 274 file->f_pos = 0; 275 + m->read_pos = 0; 301 276 m->version = 0; 302 277 m->index = 0; 303 278 m->count = 0; 304 279 } else { 280 + m->read_pos = offset; 305 281 retval = file->f_pos = offset; 306 282 } 307 283 }
+1
include/linux/seq_file.h
··· 19 19 size_t from; 20 20 size_t count; 21 21 loff_t index; 22 + loff_t read_pos; 22 23 u64 version; 23 24 struct mutex lock; 24 25 const struct seq_operations *op;