X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fseq_file.c;h=a1a4cfe1921040d6044657e6c235e2f5d55f8292;hb=a1c8c4d1ff54c6c86930ee3c4c73c69eeb9ede61;hp=bf2bcfd4bcfb8a9f8e0ce12afd0487c4a47472ad;hpb=521b5d0c40386f4a9805cdec7bd979fc96a86aeb;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/seq_file.c b/fs/seq_file.c index bf2bcfd..a1a4cfe 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -48,12 +48,78 @@ 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); +static int traverse(struct seq_file *m, loff_t offset) +{ + loff_t pos = 0, index; + int error = 0; + void *p; + + m->version = 0; + index = 0; + m->count = m->from = 0; + if (!offset) { + m->index = index; + return 0; + } + if (!m->buf) { + m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); + if (!m->buf) + return -ENOMEM; + } + p = m->op->start(m, &index); + while (p) { + error = PTR_ERR(p); + if (IS_ERR(p)) + break; + error = m->op->show(m, p); + if (error < 0) + break; + if (unlikely(error)) { + error = 0; + m->count = 0; + } + if (m->count == m->size) + goto Eoverflow; + if (pos + m->count > offset) { + m->from = offset - pos; + m->count -= m->from; + m->index = index; + break; + } + pos += m->count; + m->count = 0; + if (pos == offset) { + index++; + m->index = index; + break; + } + p = m->op->next(m, p, &index); + } + m->op->stop(m, p); + m->index = index; + return error; + +Eoverflow: + m->op->stop(m, p); + kfree(m->buf); + m->buf = kmalloc(m->size <<= 1, GFP_KERNEL); + return !m->buf ? -ENOMEM : -EAGAIN; +} + /** * seq_read - ->read() method for sequential files. * @file: the file to read from @@ -73,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 @@ -108,9 +190,9 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) goto Done; } /* we need at least one record in buffer */ + pos = m->index; + p = m->op->start(m, &pos); while (1) { - pos = m->index; - p = m->op->start(m, &pos); err = PTR_ERR(p); if (!p || IS_ERR(p)) break; @@ -119,6 +201,11 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) break; if (unlikely(err)) m->count = 0; + if (unlikely(!m->count)) { + p = m->op->next(m, p, &pos); + m->index = pos; + continue; + } if (m->count < m->size) goto Fill; m->op->stop(m, p); @@ -128,6 +215,8 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) goto Enomem; m->count = 0; m->version = 0; + pos = m->index; + p = m->op->start(m, &pos); } m->op->stop(m, p); m->count = 0; @@ -165,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; @@ -179,63 +270,6 @@ Efault: } EXPORT_SYMBOL(seq_read); -static int traverse(struct seq_file *m, loff_t offset) -{ - loff_t pos = 0, index; - int error = 0; - void *p; - - m->version = 0; - index = 0; - m->count = m->from = 0; - if (!offset) { - m->index = index; - return 0; - } - if (!m->buf) { - m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); - if (!m->buf) - return -ENOMEM; - } - p = m->op->start(m, &index); - while (p) { - error = PTR_ERR(p); - if (IS_ERR(p)) - break; - error = m->op->show(m, p); - if (error < 0) - break; - if (unlikely(error)) { - error = 0; - m->count = 0; - } - if (m->count == m->size) - goto Eoverflow; - if (pos + m->count > offset) { - m->from = offset - pos; - m->count -= m->from; - m->index = index; - break; - } - pos += m->count; - m->count = 0; - if (pos == offset) { - index++; - m->index = index; - break; - } - p = m->op->next(m, p, &index); - } - m->op->stop(m, p); - return error; - -Eoverflow: - m->op->stop(m, p); - kfree(m->buf); - m->buf = kmalloc(m->size <<= 1, GFP_KERNEL); - return !m->buf ? -ENOMEM : -EAGAIN; -} - /** * seq_lseek - ->llseek() method for sequential files. * @file: the file in question @@ -247,7 +281,7 @@ Eoverflow: loff_t seq_lseek(struct file *file, loff_t offset, int origin) { struct seq_file *m = (struct seq_file *)file->private_data; - long long retval = -EINVAL; + loff_t retval = -EINVAL; mutex_lock(&m->lock); m->version = file->f_version; @@ -258,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; } } @@ -350,28 +386,58 @@ int seq_printf(struct seq_file *m, const char *f, ...) } EXPORT_SYMBOL(seq_printf); +/** + * mangle_path - mangle and copy path to buffer beginning + * @s: buffer start + * @p: beginning of path in above buffer + * @esc: set of characters that need escaping + * + * Copy the path from @p to @s, replacing each occurrence of character from + * @esc with usual octal escape. + * Returns pointer past last written character in @s, or NULL in case of + * failure. + */ +char *mangle_path(char *s, char *p, char *esc) +{ + while (s <= p) { + char c = *p++; + if (!c) { + return s; + } else if (!strchr(esc, c)) { + *s++ = c; + } else if (s + 4 > p) { + break; + } else { + *s++ = '\\'; + *s++ = '0' + ((c & 0300) >> 6); + *s++ = '0' + ((c & 070) >> 3); + *s++ = '0' + (c & 07); + } + } + return NULL; +} +EXPORT_SYMBOL(mangle_path); + +/** + * seq_path - seq_file interface to print a pathname + * @m: the seq_file handle + * @path: the struct path to print + * @esc: set of characters to escape in the output + * + * return the absolute path of 'path', as represented by the + * dentry / mnt pair in the path parameter. + */ int seq_path(struct seq_file *m, struct path *path, char *esc) { if (m->count < m->size) { char *s = m->buf + m->count; char *p = d_path(path, s, m->size - m->count); if (!IS_ERR(p)) { - while (s <= p) { - char c = *p++; - if (!c) { - p = m->buf + m->count; - m->count = s - m->buf; - return s - p; - } else if (!strchr(esc, c)) { - *s++ = c; - } else if (s + 4 > p) { - break; - } else { - *s++ = '\\'; - *s++ = '0' + ((c & 0300) >> 6); - *s++ = '0' + ((c & 070) >> 3); - *s++ = '0' + (c & 07); - } + s = mangle_path(s, p, esc); + if (s) { + p = m->buf + m->count; + m->count = s - m->buf; + return s - p; } } } @@ -380,6 +446,89 @@ int seq_path(struct seq_file *m, struct path *path, char *esc) } EXPORT_SYMBOL(seq_path); +/* + * Same as seq_path, but relative to supplied root. + * + * root may be changed, see __d_path(). + */ +int seq_path_root(struct seq_file *m, struct path *path, struct path *root, + char *esc) +{ + int err = -ENAMETOOLONG; + if (m->count < m->size) { + char *s = m->buf + m->count; + char *p; + + spin_lock(&dcache_lock); + p = __d_path(path, root, s, m->size - m->count); + spin_unlock(&dcache_lock); + err = PTR_ERR(p); + if (!IS_ERR(p)) { + s = mangle_path(s, p, esc); + if (s) { + p = m->buf + m->count; + m->count = s - m->buf; + return 0; + } + } + } + m->count = m->size; + return err; +} + +/* + * returns the path of the 'dentry' from the root of its filesystem. + */ +int seq_dentry(struct seq_file *m, struct dentry *dentry, char *esc) +{ + if (m->count < m->size) { + char *s = m->buf + m->count; + char *p = dentry_path(dentry, s, m->size - m->count); + if (!IS_ERR(p)) { + s = mangle_path(s, p, esc); + if (s) { + p = m->buf + m->count; + m->count = s - m->buf; + return s - p; + } + } + } + m->count = m->size; + return -1; +} + +int seq_bitmap(struct seq_file *m, const unsigned long *bits, + unsigned int nr_bits) +{ + if (m->count < m->size) { + int len = bitmap_scnprintf(m->buf + m->count, + m->size - m->count, bits, nr_bits); + if (m->count + len < m->size) { + m->count += len; + return 0; + } + } + m->count = m->size; + return -1; +} +EXPORT_SYMBOL(seq_bitmap); + +int seq_bitmap_list(struct seq_file *m, unsigned long *bits, + unsigned int nr_bits) +{ + if (m->count < m->size) { + int len = bitmap_scnlistprintf(m->buf + m->count, + m->size - m->count, bits, nr_bits); + if (m->count + len < m->size) { + m->count += len; + return 0; + } + } + m->count = m->size; + return -1; +} +EXPORT_SYMBOL(seq_bitmap_list); + static void *single_start(struct seq_file *p, loff_t *pos) { return NULL + (*pos == 0);