ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / fs / seq_file.c
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  */
29 int seq_open(struct file *file, struct seq_operations *op)
30 {
31         struct seq_file *p = kmalloc(sizeof(*p), GFP_KERNEL);
32         if (!p)
33                 return -ENOMEM;
34         memset(p, 0, sizeof(*p));
35         sema_init(&p->sem, 1);
36         p->op = op;
37         file->private_data = p;
38         return 0;
39 }
40 EXPORT_SYMBOL(seq_open);
41
42 /**
43  *      seq_read -      ->read() method for sequential files.
44  *      @file, @buf, @size, @ppos: see file_operations method
45  *
46  *      Ready-made ->f_op->read()
47  */
48 ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
49 {
50         struct seq_file *m = (struct seq_file *)file->private_data;
51         size_t copied = 0;
52         loff_t pos;
53         size_t n;
54         void *p;
55         int err = 0;
56
57         if (ppos != &file->f_pos)
58                 return -EPIPE;
59
60         down(&m->sem);
61         /* grab buffer if we didn't have one */
62         if (!m->buf) {
63                 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
64                 if (!m->buf)
65                         goto Enomem;
66         }
67         /* if not empty - flush it first */
68         if (m->count) {
69                 n = min(m->count, size);
70                 err = copy_to_user(buf, m->buf + m->from, n);
71                 if (err)
72                         goto Efault;
73                 m->count -= n;
74                 m->from += n;
75                 size -= n;
76                 buf += n;
77                 copied += n;
78                 if (!m->count)
79                         m->index++;
80                 if (!size)
81                         goto Done;
82         }
83         /* we need at least one record in buffer */
84         while (1) {
85                 pos = m->index;
86                 p = m->op->start(m, &pos);
87                 err = PTR_ERR(p);
88                 if (!p || IS_ERR(p))
89                         break;
90                 err = m->op->show(m, p);
91                 if (err)
92                         break;
93                 if (m->count < m->size)
94                         goto Fill;
95                 m->op->stop(m, p);
96                 kfree(m->buf);
97                 m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
98                 if (!m->buf)
99                         goto Enomem;
100                 m->count = 0;
101         }
102         m->op->stop(m, p);
103         m->count = 0;
104         goto Done;
105 Fill:
106         /* they want more? let's try to get some more */
107         while (m->count < size) {
108                 size_t offs = m->count;
109                 loff_t next = pos;
110                 p = m->op->next(m, p, &next);
111                 if (!p || IS_ERR(p)) {
112                         err = PTR_ERR(p);
113                         break;
114                 }
115                 err = m->op->show(m, p);
116                 if (err || m->count == m->size) {
117                         m->count = offs;
118                         break;
119                 }
120                 pos = next;
121         }
122         m->op->stop(m, p);
123         n = min(m->count, size);
124         err = copy_to_user(buf, m->buf, n);
125         if (err)
126                 goto Efault;
127         copied += n;
128         m->count -= n;
129         if (m->count)
130                 m->from = n;
131         else
132                 pos++;
133         m->index = pos;
134 Done:
135         if (!copied)
136                 copied = err;
137         else
138                 *ppos += copied;
139         up(&m->sem);
140         return copied;
141 Enomem:
142         err = -ENOMEM;
143         goto Done;
144 Efault:
145         err = -EFAULT;
146         goto Done;
147 }
148 EXPORT_SYMBOL(seq_read);
149
150 static int traverse(struct seq_file *m, loff_t offset)
151 {
152         loff_t pos = 0;
153         int error = 0;
154         void *p;
155
156         m->index = 0;
157         m->count = m->from = 0;
158         if (!offset)
159                 return 0;
160         if (!m->buf) {
161                 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
162                 if (!m->buf)
163                         return -ENOMEM;
164         }
165         p = m->op->start(m, &m->index);
166         while (p) {
167                 error = PTR_ERR(p);
168                 if (IS_ERR(p))
169                         break;
170                 error = m->op->show(m, p);
171                 if (error)
172                         break;
173                 if (m->count == m->size)
174                         goto Eoverflow;
175                 if (pos + m->count > offset) {
176                         m->from = offset - pos;
177                         m->count -= m->from;
178                         break;
179                 }
180                 pos += m->count;
181                 m->count = 0;
182                 if (pos == offset) {
183                         m->index++;
184                         break;
185                 }
186                 p = m->op->next(m, p, &m->index);
187         }
188         m->op->stop(m, p);
189         return error;
190
191 Eoverflow:
192         m->op->stop(m, p);
193         kfree(m->buf);
194         m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
195         return !m->buf ? -ENOMEM : -EAGAIN;
196 }
197
198 /**
199  *      seq_lseek -     ->llseek() method for sequential files.
200  *      @file, @offset, @origin: see file_operations method
201  *
202  *      Ready-made ->f_op->llseek()
203  */
204 loff_t seq_lseek(struct file *file, loff_t offset, int origin)
205 {
206         struct seq_file *m = (struct seq_file *)file->private_data;
207         long long retval = -EINVAL;
208
209         down(&m->sem);
210         switch (origin) {
211                 case 1:
212                         offset += file->f_pos;
213                 case 0:
214                         if (offset < 0)
215                                 break;
216                         retval = offset;
217                         if (offset != file->f_pos) {
218                                 while ((retval=traverse(m, offset)) == -EAGAIN)
219                                         ;
220                                 if (retval) {
221                                         /* with extreme prejudice... */
222                                         file->f_pos = 0;
223                                         m->index = 0;
224                                         m->count = 0;
225                                 } else {
226                                         retval = file->f_pos = offset;
227                                 }
228                         }
229         }
230         up(&m->sem);
231         return retval;
232 }
233 EXPORT_SYMBOL(seq_lseek);
234
235 /**
236  *      seq_release -   free the structures associated with sequential file.
237  *      @file: file in question
238  *      @inode: file->f_dentry->d_inode
239  *
240  *      Frees the structures associated with sequential file; can be used
241  *      as ->f_op->release() if you don't have private data to destroy.
242  */
243 int seq_release(struct inode *inode, struct file *file)
244 {
245         struct seq_file *m = (struct seq_file *)file->private_data;
246         kfree(m->buf);
247         kfree(m);
248         return 0;
249 }
250 EXPORT_SYMBOL(seq_release);
251
252 /**
253  *      seq_escape -    print string into buffer, escaping some characters
254  *      @m:     target buffer
255  *      @s:     string
256  *      @esc:   set of characters that need escaping
257  *
258  *      Puts string into buffer, replacing each occurrence of character from
259  *      @esc with usual octal escape.  Returns 0 in case of success, -1 - in
260  *      case of overflow.
261  */
262 int seq_escape(struct seq_file *m, const char *s, const char *esc)
263 {
264         char *end = m->buf + m->size;
265         char *p;
266         char c;
267
268         for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
269                 if (!strchr(esc, c)) {
270                         *p++ = c;
271                         continue;
272                 }
273                 if (p + 3 < end) {
274                         *p++ = '\\';
275                         *p++ = '0' + ((c & 0300) >> 6);
276                         *p++ = '0' + ((c & 070) >> 3);
277                         *p++ = '0' + (c & 07);
278                         continue;
279                 }
280                 m->count = m->size;
281                 return -1;
282         }
283         m->count = p - m->buf;
284         return 0;
285 }
286 EXPORT_SYMBOL(seq_escape);
287
288 int seq_printf(struct seq_file *m, const char *f, ...)
289 {
290         va_list args;
291         int len;
292
293         if (m->count < m->size) {
294                 va_start(args, f);
295                 len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
296                 va_end(args);
297                 if (m->count + len < m->size) {
298                         m->count += len;
299                         return 0;
300                 }
301         }
302         m->count = m->size;
303         return -1;
304 }
305 EXPORT_SYMBOL(seq_printf);
306
307 int seq_path(struct seq_file *m,
308              struct vfsmount *mnt, struct dentry *dentry,
309              char *esc)
310 {
311         if (m->count < m->size) {
312                 char *s = m->buf + m->count;
313                 char *p = d_path(dentry, mnt, s, m->size - m->count);
314                 if (!IS_ERR(p)) {
315                         while (s <= p) {
316                                 char c = *p++;
317                                 if (!c) {
318                                         p = m->buf + m->count;
319                                         m->count = s - m->buf;
320                                         return s - p;
321                                 } else if (!strchr(esc, c)) {
322                                         *s++ = c;
323                                 } else if (s + 4 > p) {
324                                         break;
325                                 } else {
326                                         *s++ = '\\';
327                                         *s++ = '0' + ((c & 0300) >> 6);
328                                         *s++ = '0' + ((c & 070) >> 3);
329                                         *s++ = '0' + (c & 07);
330                                 }
331                         }
332                 }
333         }
334         m->count = m->size;
335         return -1;
336 }
337 EXPORT_SYMBOL(seq_path);
338
339 static void *single_start(struct seq_file *p, loff_t *pos)
340 {
341         return NULL + (*pos == 0);
342 }
343
344 static void *single_next(struct seq_file *p, void *v, loff_t *pos)
345 {
346         ++*pos;
347         return NULL;
348 }
349
350 static void single_stop(struct seq_file *p, void *v)
351 {
352 }
353
354 int single_open(struct file *file, int (*show)(struct seq_file *, void *),
355                 void *data)
356 {
357         struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
358         int res = -ENOMEM;
359
360         if (op) {
361                 op->start = single_start;
362                 op->next = single_next;
363                 op->stop = single_stop;
364                 op->show = show;
365                 res = seq_open(file, op);
366                 if (!res)
367                         ((struct seq_file *)file->private_data)->private = data;
368                 else
369                         kfree(op);
370         }
371         return res;
372 }
373 EXPORT_SYMBOL(single_open);
374
375 int single_release(struct inode *inode, struct file *file)
376 {
377         struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
378         int res = seq_release(inode, file);
379         kfree(op);
380         return res;
381 }
382 EXPORT_SYMBOL(single_release);
383
384 int seq_release_private(struct inode *inode, struct file *file)
385 {
386         struct seq_file *seq = file->private_data;
387
388         kfree(seq->private);
389         seq->private = NULL;
390         return seq_release(inode, file);
391 }
392 EXPORT_SYMBOL(seq_release_private);
393
394 int seq_putc(struct seq_file *m, char c)
395 {
396         if (m->count < m->size) {
397                 m->buf[m->count++] = c;
398                 return 0;
399         }
400         return -1;
401 }
402 EXPORT_SYMBOL(seq_putc);
403
404 int seq_puts(struct seq_file *m, const char *s)
405 {
406         int len = strlen(s);
407         if (m->count + len < m->size) {
408                 memcpy(m->buf + m->count, s, len);
409                 m->count += len;
410                 return 0;
411         }
412         m->count = m->size;
413         return -1;
414 }
415 EXPORT_SYMBOL(seq_puts);