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>
This commit is contained in:
Родитель
55ec82176e
Коммит
8f19d47293
|
@ -48,8 +48,16 @@ int seq_open(struct file *file, const struct seq_operations *op)
|
|||
*/
|
||||
file->f_version = 0;
|
||||
|
||||
/* SEQ files support lseek, but not pread/pwrite */
|
||||
file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
|
||||
/*
|
||||
* seq_files support lseek() and pread(). They do not implement
|
||||
* write() at all, but we clear FMODE_PWRITE here for historical
|
||||
* reasons.
|
||||
*
|
||||
* If a client of seq_files a) implements file.write() and b) wishes to
|
||||
* support pwrite() then that client will need to implement its own
|
||||
* file.open() which calls seq_open() and then sets FMODE_PWRITE.
|
||||
*/
|
||||
file->f_mode &= ~FMODE_PWRITE;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(seq_open);
|
||||
|
@ -131,6 +139,22 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
|
|||
int err = 0;
|
||||
|
||||
mutex_lock(&m->lock);
|
||||
|
||||
/* Don't assume *ppos is where we left it */
|
||||
if (unlikely(*ppos != m->read_pos)) {
|
||||
m->read_pos = *ppos;
|
||||
while ((err = traverse(m, *ppos)) == -EAGAIN)
|
||||
;
|
||||
if (err) {
|
||||
/* With prejudice... */
|
||||
m->read_pos = 0;
|
||||
m->version = 0;
|
||||
m->index = 0;
|
||||
m->count = 0;
|
||||
goto Done;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* seq_file->op->..m_start/m_stop/m_next may do special actions
|
||||
* or optimisations based on the file->f_version, so we want to
|
||||
|
@ -230,8 +254,10 @@ Fill:
|
|||
Done:
|
||||
if (!copied)
|
||||
copied = err;
|
||||
else
|
||||
else {
|
||||
*ppos += copied;
|
||||
m->read_pos += copied;
|
||||
}
|
||||
file->f_version = m->version;
|
||||
mutex_unlock(&m->lock);
|
||||
return copied;
|
||||
|
@ -266,16 +292,18 @@ loff_t seq_lseek(struct file *file, loff_t offset, int origin)
|
|||
if (offset < 0)
|
||||
break;
|
||||
retval = offset;
|
||||
if (offset != file->f_pos) {
|
||||
if (offset != m->read_pos) {
|
||||
while ((retval=traverse(m, offset)) == -EAGAIN)
|
||||
;
|
||||
if (retval) {
|
||||
/* with extreme prejudice... */
|
||||
file->f_pos = 0;
|
||||
m->read_pos = 0;
|
||||
m->version = 0;
|
||||
m->index = 0;
|
||||
m->count = 0;
|
||||
} else {
|
||||
m->read_pos = offset;
|
||||
retval = file->f_pos = offset;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ struct seq_file {
|
|||
size_t from;
|
||||
size_t count;
|
||||
loff_t index;
|
||||
loff_t read_pos;
|
||||
u64 version;
|
||||
struct mutex lock;
|
||||
const struct seq_operations *op;
|
||||
|
|
Загрузка…
Ссылка в новой задаче