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