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 }
98 m->op->stop(m, p);
99 goto Done;
100Fill:
101
102 while (m->count < size) {
103 size_t offs = m->count;
104 loff_t next = pos;
105 p = m->op->next(m, p, &next);
106 if (!p || IS_ERR(p)) {
107 err = PTR_ERR(p);
108 break;
109 }
110 err = m->op->show(m, p);
111 if (err || m->count == m->size) {
112 m->count = offs;
113 break;
114 }
115 pos = next;
116 }
117 m->op->stop(m, p);
118 n = min(m->count, size);
119 err = copy_to_user(buf, m->buf, n);
120 if (err)
121 goto Efault;
122 copied += n;
123 m->count -= n;
124 if (m->count)
125 m->from = n;
126 else
127 pos++;
128 m->index = pos;
129Done:
130 if (!copied)
131 copied = err;
132 else
133 *ppos += copied;
134 up(&m->sem);
135 return copied;
136Enomem:
137 err = -ENOMEM;
138 goto Done;
139Efault:
140 err = -EFAULT;
141 goto Done;
142}
143
144static int traverse(struct seq_file *m, loff_t offset)
145{
146 loff_t pos = 0;
147 int error = 0;
148 void *p;
149
150 m->index = 0;
151 m->count = m->from = 0;
152 if (!offset)
153 return 0;
154 if (!m->buf) {
155 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
156 if (!m->buf)
157 return -ENOMEM;
158 }
159 p = m->op->start(m, &m->index);
160 while (p) {
161 error = PTR_ERR(p);
162 if (IS_ERR(p))
163 break;
164 error = m->op->show(m, p);
165 if (error)
166 break;
167 if (m->count == m->size)
168 goto Eoverflow;
169 if (pos + m->count > offset) {
170 m->from = offset - pos;
171 m->count -= m->from;
172 break;
173 }
174 pos += m->count;
175 m->count = 0;
176 if (pos == offset) {
177 m->index++;
178 break;
179 }
180 p = m->op->next(m, p, &m->index);
181 }
182 m->op->stop(m, p);
183 return error;
184
185Eoverflow:
186 m->op->stop(m, p);
187 kfree(m->buf);
188 m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
189 return !m->buf ? -ENOMEM : -EAGAIN;
190}
191
192
193
194
195
196
197
198loff_t seq_lseek(struct file *file, loff_t offset, int origin)
199{
200 struct seq_file *m = (struct seq_file *)file->private_data;
201 long long retval = -EINVAL;
202
203 down(&m->sem);
204 switch (origin) {
205 case 1:
206 offset += file->f_pos;
207 case 0:
208 if (offset < 0)
209 break;
210 retval = offset;
211 if (offset != file->f_pos) {
212 while ((retval=traverse(m, offset)) == -EAGAIN)
213 ;
214 if (retval) {
215
216 file->f_pos = 0;
217 m->index = 0;
218 m->count = 0;
219 } else {
220 retval = file->f_pos = offset;
221 }
222 }
223 }
224 up(&m->sem);
225 return retval;
226}
227
228
229
230
231
232
233
234
235
236int seq_release(struct inode *inode, struct file *file)
237{
238 struct seq_file *m = (struct seq_file *)file->private_data;
239 kfree(m->buf);
240 kfree(m);
241 return 0;
242}
243
244
245
246
247
248
249
250
251
252
253
254int seq_escape(struct seq_file *m, const char *s, const char *esc)
255{
256 char *end = m->buf + m->size;
257 char *p;
258 char c;
259
260 for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
261 if (!strchr(esc, c)) {
262 *p++ = c;
263 continue;
264 }
265 if (p + 3 < end) {
266 *p++ = '\\';
267 *p++ = '0' + ((c & 0300) >> 6);
268 *p++ = '0' + ((c & 070) >> 3);
269 *p++ = '0' + (c & 07);
270 continue;
271 }
272 m->count = m->size;
273 return -1;
274 }
275 m->count = p - m->buf;
276 return 0;
277}
278
279int seq_printf(struct seq_file *m, const char *f, ...)
280{
281 va_list args;
282 int len;
283
284 if (m->count < m->size) {
285 va_start(args, f);
286 len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
287 va_end(args);
288 if (m->count + len < m->size) {
289 m->count += len;
290 return 0;
291 }
292 }
293 m->count = m->size;
294 return -1;
295}
296