linux-bk/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                m->count = 0;
  98        }
  99        m->op->stop(m, p);
 100        m->count = 0;
 101        goto Done;
 102Fill:
 103        /* they want more? let's try to get some more */
 104        while (m->count < size) {
 105                size_t offs = m->count;
 106                loff_t next = pos;
 107                p = m->op->next(m, p, &next);
 108                if (!p || IS_ERR(p)) {
 109                        err = PTR_ERR(p);
 110                        break;
 111                }
 112                err = m->op->show(m, p);
 113                if (err || m->count == m->size) {
 114                        m->count = offs;
 115                        break;
 116                }
 117                pos = next;
 118        }
 119        m->op->stop(m, p);
 120        n = min(m->count, size);
 121        err = copy_to_user(buf, m->buf, n);
 122        if (err)
 123                goto Efault;
 124        copied += n;
 125        m->count -= n;
 126        if (m->count)
 127                m->from = n;
 128        else
 129                pos++;
 130        m->index = pos;
 131Done:
 132        if (!copied)
 133                copied = err;
 134        else
 135                *ppos += copied;
 136        up(&m->sem);
 137        return copied;
 138Enomem:
 139        err = -ENOMEM;
 140        goto Done;
 141Efault:
 142        err = -EFAULT;
 143        goto Done;
 144}
 145
 146static int traverse(struct seq_file *m, loff_t offset)
 147{
 148        loff_t pos = 0;
 149        int error = 0;
 150        void *p;
 151
 152        m->index = 0;
 153        m->count = m->from = 0;
 154        if (!offset)
 155                return 0;
 156        if (!m->buf) {
 157                m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
 158                if (!m->buf)
 159                        return -ENOMEM;
 160        }
 161        p = m->op->start(m, &m->index);
 162        while (p) {
 163                error = PTR_ERR(p);
 164                if (IS_ERR(p))
 165                        break;
 166                error = m->op->show(m, p);
 167                if (error)
 168                        break;
 169                if (m->count == m->size)
 170                        goto Eoverflow;
 171                if (pos + m->count > offset) {
 172                        m->from = offset - pos;
 173                        m->count -= m->from;
 174                        break;
 175                }
 176                pos += m->count;
 177                m->count = 0;
 178                if (pos == offset) {
 179                        m->index++;
 180                        break;
 181                }
 182                p = m->op->next(m, p, &m->index);
 183        }
 184        m->op->stop(m, p);
 185        return error;
 186
 187Eoverflow:
 188        m->op->stop(m, p);
 189        kfree(m->buf);
 190        m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
 191        return !m->buf ? -ENOMEM : -EAGAIN;
 192}
 193
 194/**
 195 *      seq_lseek -     ->llseek() method for sequential files.
 196 *      @file, @offset, @origin: see file_operations method
 197 *
 198 *      Ready-made ->f_op->llseek()
 199 */
 200loff_t seq_lseek(struct file *file, loff_t offset, int origin)
 201{
 202        struct seq_file *m = (struct seq_file *)file->private_data;
 203        long long retval = -EINVAL;
 204
 205        down(&m->sem);
 206        switch (origin) {
 207                case 1:
 208                        offset += file->f_pos;
 209                case 0:
 210                        if (offset < 0)
 211                                break;
 212                        retval = offset;
 213                        if (offset != file->f_pos) {
 214                                while ((retval=traverse(m, offset)) == -EAGAIN)
 215                                        ;
 216                                if (retval) {
 217                                        /* with extreme perjudice... */
 218                                        file->f_pos = 0;
 219                                        m->index = 0;
 220                                        m->count = 0;
 221                                } else {
 222                                        retval = file->f_pos = offset;
 223                                }
 224                        }
 225        }
 226        up(&m->sem);
 227        return retval;
 228}
 229
 230/**
 231 *      seq_release -   free the structures associated with sequential file.
 232 *      @file: file in question
 233 *      @inode: file->f_dentry->d_inode
 234 *
 235 *      Frees the structures associated with sequential file; can be used
 236 *      as ->f_op->release() if you don't have private data to destroy.
 237 */
 238int seq_release(struct inode *inode, struct file *file)
 239{
 240        struct seq_file *m = (struct seq_file *)file->private_data;
 241        kfree(m->buf);
 242        kfree(m);
 243        return 0;
 244}
 245
 246/**
 247 *      seq_escape -    print string into buffer, escaping some characters
 248 *      @m:     target buffer
 249 *      @s:     string
 250 *      @esc:   set of characters that need escaping
 251 *
 252 *      Puts string into buffer, replacing each occurence of character from
 253 *      @esc with usual octal escape.  Returns 0 in case of success, -1 - in
 254 *      case of overflow.
 255 */
 256int seq_escape(struct seq_file *m, const char *s, const char *esc)
 257{
 258        char *end = m->buf + m->size;
 259        char *p;
 260        char c;
 261
 262        for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
 263                if (!strchr(esc, c)) {
 264                        *p++ = c;
 265                        continue;
 266                }
 267                if (p + 3 < end) {
 268                        *p++ = '\\';
 269                        *p++ = '0' + ((c & 0300) >> 6);
 270                        *p++ = '0' + ((c & 070) >> 3);
 271                        *p++ = '0' + (c & 07);
 272                        continue;
 273                }
 274                m->count = m->size;
 275                return -1;
 276        }
 277        m->count = p - m->buf;
 278        return 0;
 279}
 280
 281int seq_printf(struct seq_file *m, const char *f, ...)
 282{
 283        va_list args;
 284        int len;
 285
 286        if (m->count < m->size) {
 287                va_start(args, f);
 288                len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
 289                va_end(args);
 290                if (m->count + len < m->size) {
 291                        m->count += len;
 292                        return 0;
 293                }
 294        }
 295        m->count = m->size;
 296        return -1;
 297}
 298
 299static void *single_start(struct seq_file *p, loff_t *pos)
 300{
 301        return NULL + (*pos == 0);
 302}
 303
 304static void *single_next(struct seq_file *p, void *v, loff_t *pos)
 305{
 306        ++*pos;
 307        return NULL;
 308}
 309
 310static void single_stop(struct seq_file *p, void *v)
 311{
 312}
 313
 314int single_open(struct file *file, int (*show)(struct seq_file *, void*), void *data)
 315{
 316        struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
 317        int res = -ENOMEM;
 318
 319        if (op) {
 320                op->start = single_start;
 321                op->next = single_next;
 322                op->stop = single_stop;
 323                op->show = show;
 324                res = seq_open(file, op);
 325                if (!res)
 326                        ((struct seq_file *)file->private_data)->private = data;
 327                else
 328                        kfree(op);
 329        }
 330        return res;
 331}
 332
 333int single_release(struct inode *inode, struct file *file)
 334{
 335        struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
 336        int res = seq_release(inode, file);
 337        kfree(op);
 338        return res;
 339}
 340
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.