source: GPL/trunk/lib32/seq_file.c@ 679

Last change on this file since 679 was 679, checked in by David Azarewicz, 4 years ago

Merge changes from Paul's uniaud32next branch.

File size: 10.0 KB
Line 
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/export.h>
10#include <linux/seq_file.h>
11#include <linux/vmalloc.h>
12#include <linux/slab.h>
13#include <linux/mm.h>
14#include <linux/printk.h>
15#include <linux/mutex.h>
16#include <linux/gfp.h>
17#include <linux/err.h>
18#include <linux/kernel.h>
19
20#include <asm/uaccess.h>
21#include <asm/page.h>
22
23#define SEEK_SET 0 /* seek relative to beginning of file */
24#define SEEK_CUR 1 /* seek relative to current file position */
25#define SEEK_END 2 /* seek relative to end of file */
26#define SEEK_DATA 3 /* seek to the next data */
27#define SEEK_HOLE 4 /* seek to the next hole */
28#define SEEK_MAX SEEK_HOLE
29
30static void *seq_buf_alloc(unsigned long size)
31{
32 void *buf;
33 gfp_t gfp = GFP_KERNEL;
34
35 /*
36 * For high order allocations, use __GFP_NORETRY to avoid oom-killing -
37 * it's better to fall back to vmalloc() than to kill things. For small
38 * allocations, just use GFP_KERNEL which will oom kill, thus no need
39 * for vmalloc fallback.
40 */
41 if (size > PAGE_SIZE)
42 gfp |= __GFP_NORETRY | __GFP_NOWARN;
43 buf = kmalloc(size, gfp);
44 if (!buf && size > PAGE_SIZE)
45 buf = vmalloc(size);
46 return buf;
47}
48
49/**
50 * seq_open - initialize sequential file
51 * @file: file we initialize
52 * @op: method table describing the sequence
53 *
54 * seq_open() sets @file, associating it with a sequence described
55 * by @op. @op->start() sets the iterator up and returns the first
56 * element of sequence. @op->stop() shuts it down. @op->next()
57 * returns the next element of sequence. @op->show() prints element
58 * into the buffer. In case of error ->start() and ->next() return
59 * ERR_PTR(error). In the end of sequence they return %NULL. ->show()
60 * returns 0 in case of success and negative number in case of error.
61 * Returning SEQ_SKIP means "discard this element and move on".
62 * Note: seq_open() will allocate a struct seq_file and store its
63 * pointer in @file->private_data. This pointer should not be modified.
64 */
65int seq_open(struct file *file, const struct seq_operations *op)
66{
67 struct seq_file *p;
68
69 WARN_ON(file->private_data);
70
71 p = kzalloc(sizeof(*p), GFP_KERNEL);
72 if (!p)
73 return -ENOMEM;
74
75 file->private_data = p;
76
77 mutex_init(&p->lock);
78 p->op = op;
79
80 // No refcounting: the lifetime of 'p' is constrained
81 // to the lifetime of the file.
82 p->file = file;
83
84 /*
85 * Wrappers around seq_open(e.g. swaps_open) need to be
86 * aware of this. If they set f_version themselves, they
87 * should call seq_open first and then set f_version.
88 */
89 file->f_version = 0;
90
91 /*
92 * seq_files support lseek() and pread(). They do not implement
93 * write() at all, but we clear FMODE_PWRITE here for historical
94 * reasons.
95 *
96 * If a client of seq_files a) implements file.write() and b) wishes to
97 * support pwrite() then that client will need to implement its own
98 * file.open() which calls seq_open() and then sets FMODE_PWRITE.
99 */
100 file->f_mode &= ~FMODE_PWRITE;
101 return 0;
102}
103
104static int traverse(struct seq_file *m, loff_t offset)
105{
106 loff_t pos = 0, index;
107 int error = 0;
108 void *p;
109
110 m->version = 0;
111 index = 0;
112 m->count = m->from = 0;
113 if (!offset) {
114 m->index = index;
115 return 0;
116 }
117 if (!m->buf) {
118 m->buf = seq_buf_alloc(m->size = PAGE_SIZE);
119 if (!m->buf)
120 return -ENOMEM;
121 }
122 p = m->op->start(m, &index);
123 while (p) {
124 error = PTR_ERR(p);
125 if (IS_ERR(p))
126 break;
127 error = m->op->show(m, p);
128 if (error < 0)
129 break;
130 if (error) {
131 error = 0;
132 m->count = 0;
133 }
134 if (seq_has_overflowed(m))
135 goto Eoverflow;
136 if (pos + m->count > offset) {
137 m->from = offset - pos;
138 m->count -= m->from;
139 m->index = index;
140 break;
141 }
142 pos += m->count;
143 m->count = 0;
144 if (pos == offset) {
145 index++;
146 m->index = index;
147 break;
148 }
149 p = m->op->next(m, p, &index);
150 }
151 m->op->stop(m, p);
152 m->index = index;
153 return error;
154
155Eoverflow:
156 m->op->stop(m, p);
157 kvfree(m->buf);
158 m->count = 0;
159 m->buf = seq_buf_alloc(m->size <<= 1);
160 return !m->buf ? -ENOMEM : -EAGAIN;
161}
162
163/**
164 * seq_read - ->read() method for sequential files.
165 * @file: the file to read from
166 * @buf: the buffer to read to
167 * @size: the maximum number of bytes to read
168 * @ppos: the current position in the file
169 *
170 * Ready-made ->f_op->read()
171 */
172ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
173{
174 struct seq_file *m = file->private_data;
175 size_t copied = 0;
176 loff_t pos;
177 size_t n;
178 void *p;
179 int err = 0;
180
181 mutex_lock(&m->lock);
182
183 /*
184 * seq_file->op->..m_start/m_stop/m_next may do special actions
185 * or optimisations based on the file->f_version, so we want to
186 * pass the file->f_version to those methods.
187 *
188 * seq_file->version is just copy of f_version, and seq_file
189 * methods can treat it simply as file version.
190 * It is copied in first and copied out after all operations.
191 * It is convenient to have it as part of structure to avoid the
192 * need of passing another argument to all the seq_file methods.
193 */
194 m->version = file->f_version;
195
196 /* Don't assume *ppos is where we left it */
197 if (*ppos != m->read_pos) {
198 while ((err = traverse(m, *ppos)) == -EAGAIN)
199 ;
200 if (err) {
201 /* With prejudice... */
202 m->read_pos = 0;
203 m->version = 0;
204 m->index = 0;
205 m->count = 0;
206 goto Done;
207 } else {
208 m->read_pos = *ppos;
209 }
210 }
211
212 /* grab buffer if we didn't have one */
213 if (!m->buf) {
214 m->buf = seq_buf_alloc(m->size = PAGE_SIZE);
215 if (!m->buf)
216 goto Enomem;
217 }
218 /* if not empty - flush it first */
219 if (m->count) {
220 n = min(m->count, size);
221 err = copy_to_user(buf, m->buf + m->from, n);
222 if (err)
223 goto Efault;
224 m->count -= n;
225 m->from += n;
226 size -= n;
227 buf += n;
228 copied += n;
229 if (!m->count) {
230 m->from = 0;
231 m->index++;
232 }
233 if (!size)
234 goto Done;
235 }
236 /* we need at least one record in buffer */
237 pos = m->index;
238 p = m->op->start(m, &pos);
239 while (1) {
240 err = PTR_ERR(p);
241 if (!p || IS_ERR(p))
242 break;
243 err = m->op->show(m, p);
244 if (err < 0)
245 break;
246 if (err)
247 m->count = 0;
248 if (!m->count) {
249 p = m->op->next(m, p, &pos);
250 m->index = pos;
251 continue;
252 }
253 if (m->count < m->size)
254 goto Fill;
255 m->op->stop(m, p);
256 kvfree(m->buf);
257 m->count = 0;
258 m->buf = seq_buf_alloc(m->size <<= 1);
259 if (!m->buf)
260 goto Enomem;
261 m->version = 0;
262 pos = m->index;
263 p = m->op->start(m, &pos);
264 }
265 m->op->stop(m, p);
266 m->count = 0;
267 goto Done;
268Fill:
269 /* they want more? let's try to get some more */
270 while (m->count < size) {
271 size_t offs = m->count;
272 loff_t next = pos;
273 p = m->op->next(m, p, &next);
274 if (!p || IS_ERR(p)) {
275 err = PTR_ERR(p);
276 break;
277 }
278 err = m->op->show(m, p);
279 if (seq_has_overflowed(m) || err) {
280 m->count = offs;
281 if (err <= 0)
282 break;
283 }
284 pos = next;
285 }
286 m->op->stop(m, p);
287 n = min(m->count, size);
288 err = copy_to_user(buf, m->buf, n);
289 if (err)
290 goto Efault;
291 copied += n;
292 m->count -= n;
293 if (m->count)
294 m->from = n;
295 else
296 pos++;
297 m->index = pos;
298Done:
299 if (!copied)
300 copied = err;
301 else {
302 *ppos += copied;
303 m->read_pos += copied;
304 }
305 file->f_version = m->version;
306 mutex_unlock(&m->lock);
307 return copied;
308Enomem:
309 err = -ENOMEM;
310 goto Done;
311Efault:
312 err = -EFAULT;
313 goto Done;
314}
315
316/**
317 * seq_lseek - ->llseek() method for sequential files.
318 * @file: the file in question
319 * @offset: new position
320 * @whence: 0 for absolute, 1 for relative position
321 *
322 * Ready-made ->f_op->llseek()
323 */
324loff_t seq_lseek(struct file *file, loff_t offset, int whence)
325{
326 struct seq_file *m = file->private_data;
327 loff_t retval = -EINVAL;
328
329 mutex_lock(&m->lock);
330 m->version = file->f_version;
331 switch (whence) {
332 case SEEK_CUR:
333 offset += file->f_pos;
334 case SEEK_SET:
335 if (offset < 0)
336 break;
337 retval = offset;
338 if (offset != m->read_pos) {
339 while ((retval = traverse(m, offset)) == -EAGAIN)
340 ;
341 if (retval) {
342 /* with extreme prejudice... */
343 file->f_pos = 0;
344 m->read_pos = 0;
345 m->version = 0;
346 m->index = 0;
347 m->count = 0;
348 } else {
349 m->read_pos = offset;
350 retval = file->f_pos = offset;
351 }
352 } else {
353 file->f_pos = offset;
354 }
355 }
356 file->f_version = m->version;
357 mutex_unlock(&m->lock);
358 return retval;
359}
360
361/**
362 * seq_release - free the structures associated with sequential file.
363 * @file: file in question
364 * @inode: its inode
365 *
366 * Frees the structures associated with sequential file; can be used
367 * as ->f_op->release() if you don't have private data to destroy.
368 */
369int seq_release(struct inode *inode, struct file *file)
370{
371 struct seq_file *m = file->private_data;
372 kvfree(m->buf);
373 kfree(m);
374 return 0;
375}
376
377static void *single_start(struct seq_file *p, loff_t *pos)
378{
379 return NULL + (*pos == 0);
380}
381
382static void *single_next(struct seq_file *p, void *v, loff_t *pos)
383{
384 ++*pos;
385 return NULL;
386}
387
388static void single_stop(struct seq_file *p, void *v)
389{
390}
391
392int single_open(struct file *file, int (*show)(struct seq_file *, void *),
393 void *data)
394{
395 struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
396 int res = -ENOMEM;
397
398 if (op) {
399 op->start = single_start;
400 op->next = single_next;
401 op->stop = single_stop;
402 op->show = show;
403 res = seq_open(file, op);
404 if (!res)
405 ((struct seq_file *)file->private_data)->private = data;
406 else
407 kfree(op);
408 }
409 return res;
410}
411
412int single_open_size(struct file *file, int (*show)(struct seq_file *, void *),
413 void *data, size_t size)
414{
415 char *buf = seq_buf_alloc(size);
416 int ret;
417 if (!buf)
418 return -ENOMEM;
419 ret = single_open(file, show, data);
420 if (ret) {
421 kvfree(buf);
422 return ret;
423 }
424 ((struct seq_file *)file->private_data)->buf = buf;
425 ((struct seq_file *)file->private_data)->size = size;
426 return 0;
427}
428
429int single_release(struct inode *inode, struct file *file)
430{
431 const struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
432 int res = seq_release(inode, file);
433 kfree(op);
434 return res;
435}
Note: See TracBrowser for help on using the repository browser.