The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/seq_file.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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  */
   27 int 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  */
   45 ssize_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;
  102 Fill:
  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;
  131 Done:
  132         if (!copied)
  133                 copied = err;
  134         else
  135                 *ppos += copied;
  136         up(&m->sem);
  137         return copied;
  138 Enomem:
  139         err = -ENOMEM;
  140         goto Done;
  141 Efault:
  142         err = -EFAULT;
  143         goto Done;
  144 }
  145 
  146 static 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 
  187 Eoverflow:
  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  */
  200 loff_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  */
  238 int 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  */
  256 int 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++ = '' + ((c & 0300) >> 6);
  270                         *p++ = '' + ((c & 070) >> 3);
  271                         *p++ = '' + (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 
  281 int 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 }

Cache object: fa9ae381b6d8523b862eb4e2477fd32a


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.