diff options
Diffstat (limited to 'fs/seq_file.c')
-rw-r--r-- | fs/seq_file.c | 151 |
1 files changed, 90 insertions, 61 deletions
diff --git a/fs/seq_file.c b/fs/seq_file.c index b569ff1c4dc8..a1a4cfe19210 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c | |||
@@ -48,12 +48,78 @@ int seq_open(struct file *file, const struct seq_operations *op) | |||
48 | */ | 48 | */ |
49 | file->f_version = 0; | 49 | file->f_version = 0; |
50 | 50 | ||
51 | /* SEQ files support lseek, but not pread/pwrite */ | 51 | /* |
52 | file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE); | 52 | * seq_files support lseek() and pread(). They do not implement |
53 | * write() at all, but we clear FMODE_PWRITE here for historical | ||
54 | * reasons. | ||
55 | * | ||
56 | * If a client of seq_files a) implements file.write() and b) wishes to | ||
57 | * support pwrite() then that client will need to implement its own | ||
58 | * file.open() which calls seq_open() and then sets FMODE_PWRITE. | ||
59 | */ | ||
60 | file->f_mode &= ~FMODE_PWRITE; | ||
53 | return 0; | 61 | return 0; |
54 | } | 62 | } |
55 | EXPORT_SYMBOL(seq_open); | 63 | EXPORT_SYMBOL(seq_open); |
56 | 64 | ||
65 | static int traverse(struct seq_file *m, loff_t offset) | ||
66 | { | ||
67 | loff_t pos = 0, index; | ||
68 | int error = 0; | ||
69 | void *p; | ||
70 | |||
71 | m->version = 0; | ||
72 | index = 0; | ||
73 | m->count = m->from = 0; | ||
74 | if (!offset) { | ||
75 | m->index = index; | ||
76 | return 0; | ||
77 | } | ||
78 | if (!m->buf) { | ||
79 | m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); | ||
80 | if (!m->buf) | ||
81 | return -ENOMEM; | ||
82 | } | ||
83 | p = m->op->start(m, &index); | ||
84 | while (p) { | ||
85 | error = PTR_ERR(p); | ||
86 | if (IS_ERR(p)) | ||
87 | break; | ||
88 | error = m->op->show(m, p); | ||
89 | if (error < 0) | ||
90 | break; | ||
91 | if (unlikely(error)) { | ||
92 | error = 0; | ||
93 | m->count = 0; | ||
94 | } | ||
95 | if (m->count == m->size) | ||
96 | goto Eoverflow; | ||
97 | if (pos + m->count > offset) { | ||
98 | m->from = offset - pos; | ||
99 | m->count -= m->from; | ||
100 | m->index = index; | ||
101 | break; | ||
102 | } | ||
103 | pos += m->count; | ||
104 | m->count = 0; | ||
105 | if (pos == offset) { | ||
106 | index++; | ||
107 | m->index = index; | ||
108 | break; | ||
109 | } | ||
110 | p = m->op->next(m, p, &index); | ||
111 | } | ||
112 | m->op->stop(m, p); | ||
113 | m->index = index; | ||
114 | return error; | ||
115 | |||
116 | Eoverflow: | ||
117 | m->op->stop(m, p); | ||
118 | kfree(m->buf); | ||
119 | m->buf = kmalloc(m->size <<= 1, GFP_KERNEL); | ||
120 | return !m->buf ? -ENOMEM : -EAGAIN; | ||
121 | } | ||
122 | |||
57 | /** | 123 | /** |
58 | * seq_read - ->read() method for sequential files. | 124 | * seq_read - ->read() method for sequential files. |
59 | * @file: the file to read from | 125 | * @file: the file to read from |
@@ -73,6 +139,22 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) | |||
73 | int err = 0; | 139 | int err = 0; |
74 | 140 | ||
75 | mutex_lock(&m->lock); | 141 | mutex_lock(&m->lock); |
142 | |||
143 | /* Don't assume *ppos is where we left it */ | ||
144 | if (unlikely(*ppos != m->read_pos)) { | ||
145 | m->read_pos = *ppos; | ||
146 | while ((err = traverse(m, *ppos)) == -EAGAIN) | ||
147 | ; | ||
148 | if (err) { | ||
149 | /* With prejudice... */ | ||
150 | m->read_pos = 0; | ||
151 | m->version = 0; | ||
152 | m->index = 0; | ||
153 | m->count = 0; | ||
154 | goto Done; | ||
155 | } | ||
156 | } | ||
157 | |||
76 | /* | 158 | /* |
77 | * seq_file->op->..m_start/m_stop/m_next may do special actions | 159 | * seq_file->op->..m_start/m_stop/m_next may do special actions |
78 | * or optimisations based on the file->f_version, so we want to | 160 | * or optimisations based on the file->f_version, so we want to |
@@ -172,8 +254,10 @@ Fill: | |||
172 | Done: | 254 | Done: |
173 | if (!copied) | 255 | if (!copied) |
174 | copied = err; | 256 | copied = err; |
175 | else | 257 | else { |
176 | *ppos += copied; | 258 | *ppos += copied; |
259 | m->read_pos += copied; | ||
260 | } | ||
177 | file->f_version = m->version; | 261 | file->f_version = m->version; |
178 | mutex_unlock(&m->lock); | 262 | mutex_unlock(&m->lock); |
179 | return copied; | 263 | return copied; |
@@ -186,63 +270,6 @@ Efault: | |||
186 | } | 270 | } |
187 | EXPORT_SYMBOL(seq_read); | 271 | EXPORT_SYMBOL(seq_read); |
188 | 272 | ||
189 | static int traverse(struct seq_file *m, loff_t offset) | ||
190 | { | ||
191 | loff_t pos = 0, index; | ||
192 | int error = 0; | ||
193 | void *p; | ||
194 | |||
195 | m->version = 0; | ||
196 | index = 0; | ||
197 | m->count = m->from = 0; | ||
198 | if (!offset) { | ||
199 | m->index = index; | ||
200 | return 0; | ||
201 | } | ||
202 | if (!m->buf) { | ||
203 | m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); | ||
204 | if (!m->buf) | ||
205 | return -ENOMEM; | ||
206 | } | ||
207 | p = m->op->start(m, &index); | ||
208 | while (p) { | ||
209 | error = PTR_ERR(p); | ||
210 | if (IS_ERR(p)) | ||
211 | break; | ||
212 | error = m->op->show(m, p); | ||
213 | if (error < 0) | ||
214 | break; | ||
215 | if (unlikely(error)) { | ||
216 | error = 0; | ||
217 | m->count = 0; | ||
218 | } | ||
219 | if (m->count == m->size) | ||
220 | goto Eoverflow; | ||
221 | if (pos + m->count > offset) { | ||
222 | m->from = offset - pos; | ||
223 | m->count -= m->from; | ||
224 | m->index = index; | ||
225 | break; | ||
226 | } | ||
227 | pos += m->count; | ||
228 | m->count = 0; | ||
229 | if (pos == offset) { | ||
230 | index++; | ||
231 | m->index = index; | ||
232 | break; | ||
233 | } | ||
234 | p = m->op->next(m, p, &index); | ||
235 | } | ||
236 | m->op->stop(m, p); | ||
237 | return error; | ||
238 | |||
239 | Eoverflow: | ||
240 | m->op->stop(m, p); | ||
241 | kfree(m->buf); | ||
242 | m->buf = kmalloc(m->size <<= 1, GFP_KERNEL); | ||
243 | return !m->buf ? -ENOMEM : -EAGAIN; | ||
244 | } | ||
245 | |||
246 | /** | 273 | /** |
247 | * seq_lseek - ->llseek() method for sequential files. | 274 | * seq_lseek - ->llseek() method for sequential files. |
248 | * @file: the file in question | 275 | * @file: the file in question |
@@ -265,16 +292,18 @@ loff_t seq_lseek(struct file *file, loff_t offset, int origin) | |||
265 | if (offset < 0) | 292 | if (offset < 0) |
266 | break; | 293 | break; |
267 | retval = offset; | 294 | retval = offset; |
268 | if (offset != file->f_pos) { | 295 | if (offset != m->read_pos) { |
269 | while ((retval=traverse(m, offset)) == -EAGAIN) | 296 | while ((retval=traverse(m, offset)) == -EAGAIN) |
270 | ; | 297 | ; |
271 | if (retval) { | 298 | if (retval) { |
272 | /* with extreme prejudice... */ | 299 | /* with extreme prejudice... */ |
273 | file->f_pos = 0; | 300 | file->f_pos = 0; |
301 | m->read_pos = 0; | ||
274 | m->version = 0; | 302 | m->version = 0; |
275 | m->index = 0; | 303 | m->index = 0; |
276 | m->count = 0; | 304 | m->count = 0; |
277 | } else { | 305 | } else { |
306 | m->read_pos = offset; | ||
278 | retval = file->f_pos = offset; | 307 | retval = file->f_pos = offset; |
279 | } | 308 | } |
280 | } | 309 | } |