FreeBSD/Linux Kernel Cross Reference
sys/fs/seq_file.c
1 /*
2 * linux/fs/seq_file.c
3 *
4 * helper functions for making syntetic 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
14 /**
15 * seq_open - initialize sequential file
16 * @file: file we initialize
17 * @op: method table describing the sequence
18 *
19 * seq_open() sets @file, associating it with a sequence described
20 * by @op. @op->start() sets the iterator up and returns the first
21 * element of sequence. @op->stop() shuts it down. @op->next()
22 * returns the next element of sequence. @op->show() prints element
23 * into the buffer. In case of error ->start() and ->next() return
24 * ERR_PTR(error). In the end of sequence they return %NULL. ->show()
25 * returns 0 in case of success and negative number in case of error.
26 */
27 int 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 * seq_read - ->read() method for sequential files.
41 * @file, @buf, @size, @ppos: see file_operations method
42 *
43 * Ready-made ->f_op->read()
44 */
45 ssize_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 /* grab buffer if we didn't have one */
59 if (!m->buf) {
60 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
61 if (!m->buf)
62 goto Enomem;
63 }
64 /* if not empty - flush it first */
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 /* we need at least one record in buffer */
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;
102 Fill:
103 /* they want more? let's try to get some more */
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;
131 Done:
132 if (!copied)
133 copied = err;
134 else
135 *ppos += copied;
136 up(&m->sem);
137 return copied;
138 Enomem:
139 err = -ENOMEM;
140 goto Done;
141 Efault:
142 err = -EFAULT;
143 goto Done;
144 }
145
146 static 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
187 Eoverflow:
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 * seq_lseek - ->llseek() method for sequential files.
196 * @file, @offset, @origin: see file_operations method
197 *
198 * Ready-made ->f_op->llseek()
199 */
200 loff_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 /* with extreme perjudice... */
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 * seq_release - free the structures associated with sequential file.
232 * @file: file in question
233 * @inode: file->f_dentry->d_inode
234 *
235 * Frees the structures associated with sequential file; can be used
236 * as ->f_op->release() if you don't have private data to destroy.
237 */
238 int 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 * seq_escape - print string into buffer, escaping some characters
248 * @m: target buffer
249 * @s: string
250 * @esc: set of characters that need escaping
251 *
252 * Puts string into buffer, replacing each occurence of character from
253 * @esc with usual octal escape. Returns 0 in case of success, -1 - in
254 * case of overflow.
255 */
256 int 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++ = '' + ((c & 0300) >> 6);
270 *p++ = '' + ((c & 070) >> 3);
271 *p++ = '' + (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
281 int 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 }
Cache object: fa9ae381b6d8523b862eb4e2477fd32a
|