linux/fs/seq_file.c
<<
>>
Prefs
   1/*
   2 * linux/fs/seq_file.c
   3 *
   4 * helper functions for making synthetic files from sequences of records.
   5 * initial implementation -- AV, Oct 2001.
   6 */
   7
   8#include <linux/fs.h>
   9#include <linux/module.h>
  10#include <linux/seq_file.h>
  11#include <linux/slab.h>
  12
  13#include <asm/uaccess.h>
  14#include <asm/page.h>
  15
  16/**
  17 *      seq_open -      initialize sequential file
  18 *      @file: file we initialize
  19 *      @op: method table describing the sequence
  20 *
  21 *      seq_open() sets @file, associating it with a sequence described
  22 *      by @op.  @op->start() sets the iterator up and returns the first
  23 *      element of sequence. @op->stop() shuts it down.  @op->next()
  24 *      returns the next element of sequence.  @op->show() prints element
  25 *      into the buffer.  In case of error ->start() and ->next() return
  26 *      ERR_PTR(error).  In the end of sequence they return %NULL. ->show()
  27 *      returns 0 in case of success and negative number in case of error.
  28 *      Returning SEQ_SKIP means "discard this element and move on".
  29 */
  30int seq_open(struct file *file, const struct seq_operations *op)
  31{
  32        struct seq_file *p = file->private_data;
  33
  34        if (!p) {
  35                p = kmalloc(sizeof(*p), GFP_KERNEL);
  36                if (!p)
  37                        return -ENOMEM;
  38                file->private_data = p;
  39        }
  40        memset(p, 0, sizeof(*p));
  41        mutex_init(&p->lock);
  42        p->op = op;
  43
  44        /*
  45         * Wrappers around seq_open(e.g. swaps_open) need to be
  46         * aware of this. If they set f_version themselves, they
  47         * should call seq_open first and then set f_version.
  48         */
  49        file->f_version = 0;
  50
  51        /* SEQ files support lseek, but not pread/pwrite */
  52        file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
  53        return 0;
  54}
  55EXPORT_SYMBOL(seq_open);
  56
  57/**
  58 *      seq_read -      ->read() method for sequential files.
  59 *      @file: the file to read from
  60 *      @buf: the buffer to read to
  61 *      @size: the maximum number of bytes to read
  62 *      @ppos: the current position in the file
  63 *
  64 *      Ready-made ->f_op->read()
  65 */
  66ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
  67{
  68        struct seq_file *m = (struct seq_file *)file->private_data;
  69        size_t copied = 0;
  70        loff_t pos;
  71        size_t n;
  72        void *p;
  73        int err = 0;
  74
  75        mutex_lock(&m->lock);
  76        /*
  77         * seq_file->op->..m_start/m_stop/m_next may do special actions
  78         * or optimisations based on the file->f_version, so we want to
  79         * pass the file->f_version to those methods.
  80         *
  81         * seq_file->version is just copy of f_version, and seq_file
  82         * methods can treat it simply as file version.
  83         * It is copied in first and copied out after all operations.
  84         * It is convenient to have it as  part of structure to avoid the
  85         * need of passing another argument to all the seq_file methods.
  86         */
  87        m->version = file->f_version;
  88        /* grab buffer if we didn't have one */
  89        if (!m->buf) {
  90                m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
  91                if (!m->buf)
  92                        goto Enomem;
  93        }
  94        /* if not empty - flush it first */
  95        if (m->count) {
  96                n = min(m->count, size);
  97                err = copy_to_user(buf, m->buf + m->from, n);
  98                if (err)
  99                        goto Efault;
 100                m->count -= n;
 101                m->from += n;
 102                size -= n;
 103                buf += n;
 104                copied += n;
 105                if (!m->count)
 106                        m->index++;
 107                if (!size)
 108                        goto Done;
 109        }
 110        /* we need at least one record in buffer */
 111        pos = m->index;
 112        p = m->op->start(m, &pos);
 113        while (1) {
 114                err = PTR_ERR(p);
 115                if (!p || IS_ERR(p))
 116                        break;
 117                err = m->op->show(m, p);
 118                if (err < 0)
 119                        break;
 120                if (unlikely(err))
 121                        m->count = 0;
 122                if (unlikely(!m->count)) {
 123                        p = m->op->next(m, p, &pos);
 124                        m->index = pos;
 125                        continue;
 126                }
 127                if (m->count < m->size)
 128                        goto Fill;
 129                m->op->stop(m, p);
 130                kfree(m->buf);
 131                m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
 132                if (!m->buf)
 133                        goto Enomem;
 134                m->count = 0;
 135                m->version = 0;
 136                pos = m->index;
 137                p = m->op->start(m, &pos);
 138        }
 139        m->op->stop(m, p);
 140        m->count = 0;
 141        goto Done;
 142Fill:
 143        /* they want more? let's try to get some more */
 144        while (m->count < size) {
 145                size_t offs = m->count;
 146                loff_t next = pos;
 147                p = m->op->next(m, p, &next);
 148                if (!p || IS_ERR(p)) {
 149                        err = PTR_ERR(p);
 150                        break;
 151                }
 152                err = m->op->show(m, p);
 153                if (m->count == m->size || err) {
 154                        m->count = offs;
 155                        if (likely(err <= 0))
 156                                break;
 157                }
 158                pos = next;
 159        }
 160        m->op->stop(m, p);
 161        n = min(m->count, size);
 162        err = copy_to_user(buf, m->buf, n);
 163        if (err)
 164                goto Efault;
 165        copied += n;
 166        m->count -= n;
 167        if (m->count)
 168                m->from = n;
 169        else
 170                pos++;
 171        m->index = pos;
 172Done:
 173        if (!copied)
 174                copied = err;
 175        else
 176                *ppos += copied;
 177        file->f_version = m->version;
 178        mutex_unlock(&m->lock);
 179        return copied;
 180Enomem:
 181        err = -ENOMEM;
 182        goto Done;
 183Efault:
 184        err = -EFAULT;
 185        goto Done;
 186}
 187EXPORT_SYMBOL(seq_read);
 188
 189static int traverse(struct seq_file *m, loff_t offset)
 190{
 191        loff_t pos = 0, index;
 192        int error = 0;
 193        void *p;
 194
 195        m->version = 0;
 196        index = 0;
 197        m->count = m->from = 0;
 198        if (!offset) {
 199                m->index = index;
 200                return 0;
 201        }
 202        if (!m->buf) {
 203                m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
 204                if (!m->buf)
 205                        return -ENOMEM;
 206        }
 207        p = m->op->start(m, &index);
 208        while (p) {
 209                error = PTR_ERR(p);
 210                if (IS_ERR(p))
 211                        break;
 212                error = m->op->show(m, p);
 213                if (error < 0)
 214                        break;
 215                if (unlikely(error)) {
 216                        error = 0;
 217                        m->count = 0;
 218                }
 219                if (m->count == m->size)
 220                        goto Eoverflow;
 221                if (pos + m->count > offset) {
 222                        m->from = offset - pos;
 223                        m->count -= m->from;
 224                        m->index = index;
 225                        break;
 226                }
 227                pos += m->count;
 228                m->count = 0;
 229                if (pos == offset) {
 230                        index++;
 231                        m->index = index;
 232                        break;
 233                }
 234                p = m->op->next(m, p, &index);
 235        }
 236        m->op->stop(m, p);
 237        return error;
 238
 239Eoverflow:
 240        m->op->stop(m, p);
 241        kfree(m->buf);
 242        m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
 243        return !m->buf ? -ENOMEM : -EAGAIN;
 244}
 245
 246/**
 247 *      seq_lseek -     ->llseek() method for sequential files.
 248 *      @file: the file in question
 249 *      @offset: new position
 250 *      @origin: 0 for absolute, 1 for relative position
 251 *
 252 *      Ready-made ->f_op->llseek()
 253 */
 254loff_t seq_lseek(struct file *file, loff_t offset, int origin)
 255{
 256        struct seq_file *m = (struct seq_file *)file->private_data;
 257        loff_t retval = -EINVAL;
 258
 259        mutex_lock(&m->lock);
 260        m->version = file->f_version;
 261        switch (origin) {
 262                case 1:
 263                        offset += file->f_pos;
 264                case 0:
 265                        if (offset < 0)
 266                                break;
 267                        retval = offset;
 268                        if (offset != file->f_pos) {
 269                                while ((retval=traverse(m, offset)) == -EAGAIN)
 270                                        ;
 271                                if (retval) {
 272                                        /* with extreme prejudice... */
 273                                        file->f_pos = 0;
 274                                        m->version = 0;
 275                                        m->index = 0;
 276                                        m->count = 0;
 277                                } else {
 278                                        retval = file->f_pos = offset;
 279                                }
 280                        }
 281        }
 282        file->f_version = m->version;
 283        mutex_unlock(&m->lock);
 284        return retval;
 285}
 286EXPORT_SYMBOL(seq_lseek);
 287
 288/**
 289 *      seq_release -   free the structures associated with sequential file.
 290 *      @file: file in question
 291 *      @inode: file->f_path.dentry->d_inode
 292 *
 293 *      Frees the structures associated with sequential file; can be used
 294 *      as ->f_op->release() if you don't have private data to destroy.
 295 */
 296int seq_release(struct inode *inode, struct file *file)
 297{
 298        struct seq_file *m = (struct seq_file *)file->private_data;
 299        kfree(m->buf);
 300        kfree(m);
 301        return 0;
 302}
 303EXPORT_SYMBOL(seq_release);
 304
 305/**
 306 *      seq_escape -    print string into buffer, escaping some characters
 307 *      @m:     target buffer
 308 *      @s:     string
 309 *      @esc:   set of characters that need escaping
 310 *
 311 *      Puts string into buffer, replacing each occurrence of character from
 312 *      @esc with usual octal escape.  Returns 0 in case of success, -1 - in
 313 *      case of overflow.
 314 */
 315int seq_escape(struct seq_file *m, const char *s, const char *esc)
 316{
 317        char *end = m->buf + m->size;
 318        char *p;
 319        char c;
 320
 321        for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
 322                if (!strchr(esc, c)) {
 323                        *p++ = c;
 324                        continue;
 325                }
 326                if (p + 3 < end) {
 327                        *p++ = '\\';
 328                        *p++ = '0' + ((c & 0300) >> 6);
 329                        *p++ = '0' + ((c & 070) >> 3);
 330                        *p++ = '0' + (c & 07);
 331                        continue;
 332                }
 333                m->count = m->size;
 334                return -1;
 335        }
 336        m->count = p - m->buf;
 337        return 0;
 338}
 339EXPORT_SYMBOL(seq_escape);
 340
 341int seq_printf(struct seq_file *m, const char *f, ...)
 342{
 343        va_list args;
 344        int len;
 345
 346        if (m->count < m->size) {
 347                va_start(args, f);
 348                len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
 349                va_end(args);
 350                if (m->count + len < m->size) {
 351                        m->count += len;
 352                        return 0;
 353                }
 354        }
 355        m->count = m->size;
 356        return -1;
 357}
 358EXPORT_SYMBOL(seq_printf);
 359
 360static char *mangle_path(char *s, char *p, char *esc)
 361{
 362        while (s <= p) {
 363                char c = *p++;
 364                if (!c) {
 365                        return s;
 366                } else if (!strchr(esc, c)) {
 367                        *s++ = c;
 368                } else if (s + 4 > p) {
 369                        break;
 370                } else {
 371                        *s++ = '\\';
 372                        *s++ = '0' + ((c & 0300) >> 6);
 373                        *s++ = '0' + ((c & 070) >> 3);
 374                        *s++ = '0' + (c & 07);
 375                }
 376        }
 377        return NULL;
 378}
 379
 380/*
 381 * return the absolute path of 'dentry' residing in mount 'mnt'.
 382 */
 383int seq_path(struct seq_file *m, struct path *path, char *esc)
 384{
 385        if (m->count < m->size) {
 386                char *s = m->buf + m->count;
 387                char *p = d_path(path, s, m->size - m->count);
 388                if (!IS_ERR(p)) {
 389                        s = mangle_path(s, p, esc);
 390                        if (s) {
 391                                p = m->buf + m->count;
 392                                m->count = s - m->buf;
 393                                return s - p;
 394                        }
 395                }
 396        }
 397        m->count = m->size;
 398        return -1;
 399}
 400EXPORT_SYMBOL(seq_path);
 401
 402/*
 403 * Same as seq_path, but relative to supplied root.
 404 *
 405 * root may be changed, see __d_path().
 406 */
 407int seq_path_root(struct seq_file *m, struct path *path, struct path *root,
 408                  char *esc)
 409{
 410        int err = -ENAMETOOLONG;
 411        if (m->count < m->size) {
 412                char *s = m->buf + m->count;
 413                char *p;
 414
 415                spin_lock(&dcache_lock);
 416                p = __d_path(path, root, s, m->size - m->count);
 417                spin_unlock(&dcache_lock);
 418                err = PTR_ERR(p);
 419                if (!IS_ERR(p)) {
 420                        s = mangle_path(s, p, esc);
 421                        if (s) {
 422                                p = m->buf + m->count;
 423                                m->count = s - m->buf;
 424                                return 0;
 425                        }
 426                }
 427        }
 428        m->count = m->size;
 429        return err;
 430}
 431
 432/*
 433 * returns the path of the 'dentry' from the root of its filesystem.
 434 */
 435int seq_dentry(struct seq_file *m, struct dentry *dentry, char *esc)
 436{
 437        if (m->count < m->size) {
 438                char *s = m->buf + m->count;
 439                char *p = dentry_path(dentry, s, m->size - m->count);
 440                if (!IS_ERR(p)) {
 441                        s = mangle_path(s, p, esc);
 442                        if (s) {
 443                                p = m->buf + m->count;
 444                                m->count = s - m->buf;
 445                                return s - p;
 446                        }
 447                }
 448        }
 449        m->count = m->size;
 450        return -1;
 451}
 452
 453int seq_bitmap(struct seq_file *m, unsigned long *bits, unsigned int nr_bits)
 454{
 455        if (m->count < m->size) {
 456                int len = bitmap_scnprintf(m->buf + m->count,
 457                                m->size - m->count, bits, nr_bits);
 458                if (m->count + len < m->size) {
 459                        m->count += len;
 460                        return 0;
 461                }
 462        }
 463        m->count = m->size;
 464        return -1;
 465}
 466EXPORT_SYMBOL(seq_bitmap);
 467
 468int seq_bitmap_list(struct seq_file *m, unsigned long *bits,
 469                unsigned int nr_bits)
 470{
 471        if (m->count < m->size) {
 472                int len = bitmap_scnlistprintf(m->buf + m->count,
 473                                m->size - m->count, bits, nr_bits);
 474                if (m->count + len < m->size) {
 475                        m->count += len;
 476                        return 0;
 477                }
 478        }
 479        m->count = m->size;
 480        return -1;
 481}
 482EXPORT_SYMBOL(seq_bitmap_list);
 483
 484static void *single_start(struct seq_file *p, loff_t *pos)
 485{
 486        return NULL + (*pos == 0);
 487}
 488
 489static void *single_next(struct seq_file *p, void *v, loff_t *pos)
 490{
 491        ++*pos;
 492        return NULL;
 493}
 494
 495static void single_stop(struct seq_file *p, void *v)
 496{
 497}
 498
 499int single_open(struct file *file, int (*show)(struct seq_file *, void *),
 500                void *data)
 501{
 502        struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
 503        int res = -ENOMEM;
 504
 505        if (op) {
 506                op->start = single_start;
 507                op->next = single_next;
 508                op->stop = single_stop;
 509                op->show = show;
 510                res = seq_open(file, op);
 511                if (!res)
 512                        ((struct seq_file *)file->private_data)->private = data;
 513                else
 514                        kfree(op);
 515        }
 516        return res;
 517}
 518EXPORT_SYMBOL(single_open);
 519
 520int single_release(struct inode *inode, struct file *file)
 521{
 522        const struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
 523        int res = seq_release(inode, file);
 524        kfree(op);
 525        return res;
 526}
 527EXPORT_SYMBOL(single_release);
 528
 529int seq_release_private(struct inode *inode, struct file *file)
 530{
 531        struct seq_file *seq = file->private_data;
 532
 533        kfree(seq->private);
 534        seq->private = NULL;
 535        return seq_release(inode, file);
 536}
 537EXPORT_SYMBOL(seq_release_private);
 538
 539void *__seq_open_private(struct file *f, const struct seq_operations *ops,
 540                int psize)
 541{
 542        int rc;
 543        void *private;
 544        struct seq_file *seq;
 545
 546        private = kzalloc(psize, GFP_KERNEL);
 547        if (private == NULL)
 548                goto out;
 549
 550        rc = seq_open(f, ops);
 551        if (rc < 0)
 552                goto out_free;
 553
 554        seq = f->private_data;
 555        seq->private = private;
 556        return private;
 557
 558out_free:
 559        kfree(private);
 560out:
 561        return NULL;
 562}
 563EXPORT_SYMBOL(__seq_open_private);
 564
 565int seq_open_private(struct file *filp, const struct seq_operations *ops,
 566                int psize)
 567{
 568        return __seq_open_private(filp, ops, psize) ? 0 : -ENOMEM;
 569}
 570EXPORT_SYMBOL(seq_open_private);
 571
 572int seq_putc(struct seq_file *m, char c)
 573{
 574        if (m->count < m->size) {
 575                m->buf[m->count++] = c;
 576                return 0;
 577        }
 578        return -1;
 579}
 580EXPORT_SYMBOL(seq_putc);
 581
 582int seq_puts(struct seq_file *m, const char *s)
 583{
 584        int len = strlen(s);
 585        if (m->count + len < m->size) {
 586                memcpy(m->buf + m->count, s, len);
 587                m->count += len;
 588                return 0;
 589        }
 590        m->count = m->size;
 591        return -1;
 592}
 593EXPORT_SYMBOL(seq_puts);
 594
 595struct list_head *seq_list_start(struct list_head *head, loff_t pos)
 596{
 597        struct list_head *lh;
 598
 599        list_for_each(lh, head)
 600                if (pos-- == 0)
 601                        return lh;
 602
 603        return NULL;
 604}
 605
 606EXPORT_SYMBOL(seq_list_start);
 607
 608struct list_head *seq_list_start_head(struct list_head *head, loff_t pos)
 609{
 610        if (!pos)
 611                return head;
 612
 613        return seq_list_start(head, pos - 1);
 614}
 615
 616EXPORT_SYMBOL(seq_list_start_head);
 617
 618struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos)
 619{
 620        struct list_head *lh;
 621
 622        lh = ((struct list_head *)v)->next;
 623        ++*ppos;
 624        return lh == head ? NULL : lh;
 625}
 626
 627EXPORT_SYMBOL(seq_list_next);
 628
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.