linux-old/fs/seq_file.c
<<
>>
Prefs
   1/*
   2 * linux/fs/seq_file.c
   3 *
   4 * helper functions for making syntetic files from sequences of records.
   5 * initial implementation -- AV, Oct 2001.
   6 */
   7
   8#include <linux/fs.h>
   9#include <linux/seq_file.h>
  10#include <linux/slab.h>
  11
  12#include <asm/uaccess.h>
  13
  14/**
  15 *      seq_open -      initialize sequential file
  16 *      @file: file we initialize
  17 *      @op: method table describing the sequence
  18 *
  19 *      seq_open() sets @file, associating it with a sequence described
  20 *      by @op.  @op->start() sets the iterator up and returns the first
  21 *      element of sequence. @op->stop() shuts it down.  @op->next()
  22 *      returns the next element of sequence.  @op->show() prints element
  23 *      into the buffer.  In case of error ->start() and ->next() return
  24 *      ERR_PTR(error).  In the end of sequence they return %NULL. ->show()
  25 *      returns 0 in case of success and negative number in case of error.
  26 */
  27int seq_open(struct file *file, struct seq_operations *op)
  28{
  29        struct seq_file *p = kmalloc(sizeof(*p), GFP_KERNEL);
  30        if (!p)
  31                return -ENOMEM;
  32        memset(p, 0, sizeof(*p));
  33        sema_init(&p->sem, 1);
  34        p->op = op;
  35        file->private_data = p;
  36        return 0;
  37}
  38
  39/**
  40 *      seq_read -      ->read() method for sequential files.
  41 *      @file, @buf, @size, @ppos: see file_operations method
  42 *
  43 *      Ready-made ->f_op->read()
  44 */
  45ssize_t seq_read(struct file *file, char *buf, size_t size, loff_t *ppos)
  46{
  47        struct seq_file *m = (struct seq_file *)file->private_data;
  48        size_t copied = 0;
  49        loff_t pos;
  50        size_t n;
  51        void *p;
  52        int err = 0;
  53
  54        if (ppos != &file->f_pos)
  55                return -EPIPE;
  56
  57        down(&m->sem);
  58        /* grab buffer if we didn't have one */
  59        if (!m->buf) {
  60                m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
  61                if (!m->buf)
  62                        goto Enomem;
  63        }
  64        /* if not empty - flush it first */
  65        if (m->count) {
  66                n = min(m->count, size);
  67                err = copy_to_user(buf, m->buf + m->from, n);
  68                if (err)
  69                        goto Efault;
  70                m->count -= n;
  71                m->from += n;
  72                size -= n;
  73                buf += n;
  74                copied += n;
  75                if (!m->count)
  76                        m->index++;
  77                if (!size)
  78                        goto Done;
  79        }
  80        /* we need at least one record in buffer */
  81        while (1) {
  82                pos = m->index;
  83                p = m->op->start(m, &pos);
  84                err = PTR_ERR(p);
  85                if (!p || IS_ERR(p))
  86                        break;
  87                err = m->op->show(m, p);
  88                if (err)
  89                        break;
  90                if (m->count < m->size)
  91                        goto Fill;
  92                m->op->stop(m, p);
  93                kfree(m->buf);
  94                m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
  95                if (!m->buf)
  96                        goto Enomem;
  97        }
  98        m->op->stop(m, p);
  99        goto Done;
 100Fill:
 101        /* they want more? let's try to get some more */
 102        while (m->count < size) {
 103                size_t offs = m->count;
 104                loff_t next = pos;
 105                p = m->op->next(m, p, &next);
 106                if (!p || IS_ERR(p)) {
 107                        err = PTR_ERR(p);
 108                        break;
 109                }
 110                err = m->op->show(m, p);
 111                if (err || m->count == m->size) {
 112                        m->count = offs;
 113                        break;
 114                }
 115                pos = next;
 116        }
 117        m->op->stop(m, p);
 118        n = min(m->count, size);
 119        err = copy_to_user(buf, m->buf, n);
 120        if (err)
 121                goto Efault;
 122        copied += n;
 123        m->count -= n;
 124        if (m->count)
 125                m->from = n;
 126        else
 127                pos++;
 128        m->index = pos;
 129Done:
 130        if (!copied)
 131                copied = err;
 132        else
 133                *ppos += copied;
 134        up(&m->sem);
 135        return copied;
 136Enomem:
 137        err = -ENOMEM;
 138        goto Done;
 139Efault:
 140        err = -EFAULT;
 141        goto Done;
 142}
 143
 144static int traverse(struct seq_file *m, loff_t offset)
 145{
 146        loff_t pos = 0;
 147        int error = 0;
 148        void *p;
 149
 150        m->index = 0;
 151        m->count = m->from = 0;
 152        if (!offset)
 153                return 0;
 154        if (!m->buf) {
 155                m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
 156                if (!m->buf)
 157                        return -ENOMEM;
 158        }
 159        p = m->op->start(m, &m->index);
 160        while (p) {
 161                error = PTR_ERR(p);
 162                if (IS_ERR(p))
 163                        break;
 164                error = m->op->show(m, p);
 165                if (error)
 166                        break;
 167                if (m->count == m->size)
 168                        goto Eoverflow;
 169                if (pos + m->count > offset) {
 170                        m->from = offset - pos;
 171                        m->count -= m->from;
 172                        break;
 173                }
 174                pos += m->count;
 175                m->count = 0;
 176                if (pos == offset) {
 177                        m->index++;
 178                        break;
 179                }
 180                p = m->op->next(m, p, &m->index);
 181        }
 182        m->op->stop(m, p);
 183        return error;
 184
 185Eoverflow:
 186        m->op->stop(m, p);
 187        kfree(m->buf);
 188        m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
 189        return !m->buf ? -ENOMEM : -EAGAIN;
 190}
 191
 192/**
 193 *      seq_lseek -     ->llseek() method for sequential files.
 194 *      @file, @offset, @origin: see file_operations method
 195 *
 196 *      Ready-made ->f_op->llseek()
 197 */
 198loff_t seq_lseek(struct file *file, loff_t offset, int origin)
 199{
 200        struct seq_file *m = (struct seq_file *)file->private_data;
 201        long long retval = -EINVAL;
 202
 203        down(&m->sem);
 204        switch (origin) {
 205                case 1:
 206                        offset += file->f_pos;
 207                case 0:
 208                        if (offset < 0)
 209                                break;
 210                        retval = offset;
 211                        if (offset != file->f_pos) {
 212                                while ((retval=traverse(m, offset)) == -EAGAIN)
 213                                        ;
 214                                if (retval) {
 215                                        /* with extreme perjudice... */
 216                                        file->f_pos = 0;
 217                                        m->index = 0;
 218                                        m->count = 0;
 219                                } else {
 220                                        retval = file->f_pos = offset;
 221                                }
 222                        }
 223        }
 224        up(&m->sem);
 225        return retval;
 226}
 227
 228/**
 229 *      seq_release -   free the structures associated with sequential file.
 230 *      @file: file in question
 231 *      @inode: file->f_dentry->d_inode
 232 *
 233 *      Frees the structures associated with sequential file; can be used
 234 *      as ->f_op->release() if you don't have private data to destroy.
 235 */
 236int seq_release(struct inode *inode, struct file *file)
 237{
 238        struct seq_file *m = (struct seq_file *)file->private_data;
 239        kfree(m->buf);
 240        kfree(m);
 241        return 0;
 242}
 243
 244/**
 245 *      seq_escape -    print string into buffer, escaping some characters
 246 *      @m:     target buffer
 247 *      @s:     string
 248 *      @esc:   set of characters that need escaping
 249 *
 250 *      Puts string into buffer, replacing each occurence of character from
 251 *      @esc with usual octal escape.  Returns 0 in case of success, -1 - in
 252 *      case of overflow.
 253 */
 254int seq_escape(struct seq_file *m, const char *s, const char *esc)
 255{
 256        char *end = m->buf + m->size;
 257        char *p;
 258        char c;
 259
 260        for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
 261                if (!strchr(esc, c)) {
 262                        *p++ = c;
 263                        continue;
 264                }
 265                if (p + 3 < end) {
 266                        *p++ = '\\';
 267                        *p++ = '0' + ((c & 0300) >> 6);
 268                        *p++ = '0' + ((c & 070) >> 3);
 269                        *p++ = '0' + (c & 07);
 270                        continue;
 271                }
 272                m->count = m->size;
 273                return -1;
 274        }
 275        m->count = p - m->buf;
 276        return 0;
 277}
 278
 279int seq_printf(struct seq_file *m, const char *f, ...)
 280{
 281        va_list args;
 282        int len;
 283
 284        if (m->count < m->size) {
 285                va_start(args, f);
 286                len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
 287                va_end(args);
 288                if (m->count + len < m->size) {
 289                        m->count += len;
 290                        return 0;
 291                }
 292        }
 293        m->count = m->size;
 294        return -1;
 295}
 296
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.