1
2
3
4
5
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
16
17
18
19
20
21
22
23
24
25
26
27int 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
41
42
43
44
45ssize_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
59 if (!m->buf) {
60 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
61 if (!m->buf)
62 goto Enomem;
63 }
64
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
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;
102Fill:
103
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;
131Done:
132 if (!copied)
133 copied = err;
134 else
135 *ppos += copied;
136 up(&m->sem);
137 return copied;
138Enomem:
139 err = -ENOMEM;
140 goto Done;
141Efault:
142 err = -EFAULT;
143 goto Done;
144}
145
146static 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
187Eoverflow:
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
196
197
198
199
200loff_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
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
232
233
234
235
236
237
238int 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
248
249
250
251
252
253
254
255
256int 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++ = '0' + ((c & 0300) >> 6);
270 *p++ = '0' + ((c & 070) >> 3);
271 *p++ = '0' + (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
281int 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}
298
299static void *single_start(struct seq_file *p, loff_t *pos)
300{
301 return NULL + (*pos == 0);
302}
303
304static void *single_next(struct seq_file *p, void *v, loff_t *pos)
305{
306 ++*pos;
307 return NULL;
308}
309
310static void single_stop(struct seq_file *p, void *v)
311{
312}
313
314int single_open(struct file *file, int (*show)(struct seq_file *, void*), void *data)
315{
316 struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
317 int res = -ENOMEM;
318
319 if (op) {
320 op->start = single_start;
321 op->next = single_next;
322 op->stop = single_stop;
323 op->show = show;
324 res = seq_open(file, op);
325 if (!res)
326 ((struct seq_file *)file->private_data)->private = data;
327 else
328 kfree(op);
329 }
330 return res;
331}
332
333int single_release(struct inode *inode, struct file *file)
334{
335 struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
336 int res = seq_release(inode, file);
337 kfree(op);
338 return res;
339}
340