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/module.h> 10 #include <linux/seq_file.h> 11 #include <linux/slab.h> 12 13 #include <asm/uaccess.h> 14 #include <asm/page.h> 15 16 /** 17 * seq_open - initialize sequential file 18 * @file: file we initialize 19 * @op: method table describing the sequence 20 * 21 * seq_open() sets @file, associating it with a sequence described 22 * by @op. @op->start() sets the iterator up and returns the first 23 * element of sequence. @op->stop() shuts it down. @op->next() 24 * returns the next element of sequence. @op->show() prints element 25 * into the buffer. In case of error ->start() and ->next() return 26 * ERR_PTR(error). In the end of sequence they return %NULL. ->show() 27 * returns 0 in case of success and negative number in case of error. 28 */ 29 int seq_open(struct file *file, struct seq_operations *op) 30 { 31 struct seq_file *p = kmalloc(sizeof(*p), GFP_KERNEL); 32 if (!p) 33 return -ENOMEM; 34 memset(p, 0, sizeof(*p)); 35 sema_init(&p->sem, 1); 36 p->op = op; 37 file->private_data = p; 38 39 /* 40 * Wrappers around seq_open(e.g. swaps_open) need to be 41 * aware of this. If they set f_version themselves, they 42 * should call seq_open first and then set f_version. 43 */ 44 file->f_version = 0; 45 46 /* SEQ files support lseek, but not pread/pwrite */ 47 file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE); 48 return 0; 49 } 50 EXPORT_SYMBOL(seq_open); 51 52 /** 53 * seq_read - ->read() method for sequential files. 54 * @file, @buf, @size, @ppos: see file_operations method 55 * 56 * Ready-made ->f_op->read() 57 */ 58 ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 59 { 60 struct seq_file *m = (struct seq_file *)file->private_data; 61 size_t copied = 0; 62 loff_t pos; 63 size_t n; 64 void *p; 65 int err = 0; 66 67 down(&m->sem); 68 /* 69 * seq_file->op->..m_start/m_stop/m_next may do special actions 70 * or optimisations based on the file->f_version, so we want to 71 * pass the file->f_version to those methods. 72 * 73 * seq_file->version is just copy of f_version, and seq_file 74 * methods can treat it simply as file version. 75 * It is copied in first and copied out after all operations. 76 * It is convenient to have it as part of structure to avoid the 77 * need of passing another argument to all the seq_file methods. 78 */ 79 m->version = file->f_version; 80 /* grab buffer if we didn't have one */ 81 if (!m->buf) { 82 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); 83 if (!m->buf) 84 goto Enomem; 85 } 86 /* if not empty - flush it first */ 87 if (m->count) { 88 n = min(m->count, size); 89 err = copy_to_user(buf, m->buf + m->from, n); 90 if (err) 91 goto Efault; 92 m->count -= n; 93 m->from += n; 94 size -= n; 95 buf += n; 96 copied += n; 97 if (!m->count) 98 m->index++; 99 if (!size) 100 goto Done; 101 } 102 /* we need at least one record in buffer */ 103 while (1) { 104 pos = m->index; 105 p = m->op->start(m, &pos); 106 err = PTR_ERR(p); 107 if (!p || IS_ERR(p)) 108 break; 109 err = m->op->show(m, p); 110 if (err) 111 break; 112 if (m->count < m->size) 113 goto Fill; 114 m->op->stop(m, p); 115 kfree(m->buf); 116 m->buf = kmalloc(m->size <<= 1, GFP_KERNEL); 117 if (!m->buf) 118 goto Enomem; 119 m->count = 0; 120 m->version = 0; 121 } 122 m->op->stop(m, p); 123 m->count = 0; 124 goto Done; 125 Fill: 126 /* they want more? let's try to get some more */ 127 while (m->count < size) { 128 size_t offs = m->count; 129 loff_t next = pos; 130 p = m->op->next(m, p, &next); 131 if (!p || IS_ERR(p)) { 132 err = PTR_ERR(p); 133 break; 134 } 135 err = m->op->show(m, p); 136 if (err || m->count == m->size) { 137 m->count = offs; 138 break; 139 } 140 pos = next; 141 } 142 m->op->stop(m, p); 143 n = min(m->count, size); 144 err = copy_to_user(buf, m->buf, n); 145 if (err) 146 goto Efault; 147 copied += n; 148 m->count -= n; 149 if (m->count) 150 m->from = n; 151 else 152 pos++; 153 m->index = pos; 154 Done: 155 if (!copied) 156 copied = err; 157 else 158 *ppos += copied; 159 file->f_version = m->version; 160 up(&m->sem); 161 return copied; 162 Enomem: 163 err = -ENOMEM; 164 goto Done; 165 Efault: 166 err = -EFAULT; 167 goto Done; 168 } 169 EXPORT_SYMBOL(seq_read); 170 171 static int traverse(struct seq_file *m, loff_t offset) 172 { 173 loff_t pos = 0; 174 int error = 0; 175 void *p; 176 177 m->version = 0; 178 m->index = 0; 179 m->count = m->from = 0; 180 if (!offset) 181 return 0; 182 if (!m->buf) { 183 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL); 184 if (!m->buf) 185 return -ENOMEM; 186 } 187 p = m->op->start(m, &m->index); 188 while (p) { 189 error = PTR_ERR(p); 190 if (IS_ERR(p)) 191 break; 192 error = m->op->show(m, p); 193 if (error) 194 break; 195 if (m->count == m->size) 196 goto Eoverflow; 197 if (pos + m->count > offset) { 198 m->from = offset - pos; 199 m->count -= m->from; 200 break; 201 } 202 pos += m->count; 203 m->count = 0; 204 if (pos == offset) { 205 m->index++; 206 break; 207 } 208 p = m->op->next(m, p, &m->index); 209 } 210 m->op->stop(m, p); 211 return error; 212 213 Eoverflow: 214 m->op->stop(m, p); 215 kfree(m->buf); 216 m->buf = kmalloc(m->size <<= 1, GFP_KERNEL); 217 return !m->buf ? -ENOMEM : -EAGAIN; 218 } 219 220 /** 221 * seq_lseek - ->llseek() method for sequential files. 222 * @file, @offset, @origin: see file_operations method 223 * 224 * Ready-made ->f_op->llseek() 225 */ 226 loff_t seq_lseek(struct file *file, loff_t offset, int origin) 227 { 228 struct seq_file *m = (struct seq_file *)file->private_data; 229 long long retval = -EINVAL; 230 231 down(&m->sem); 232 m->version = file->f_version; 233 switch (origin) { 234 case 1: 235 offset += file->f_pos; 236 case 0: 237 if (offset < 0) 238 break; 239 retval = offset; 240 if (offset != file->f_pos) { 241 while ((retval=traverse(m, offset)) == -EAGAIN) 242 ; 243 if (retval) { 244 /* with extreme prejudice... */ 245 file->f_pos = 0; 246 m->version = 0; 247 m->index = 0; 248 m->count = 0; 249 } else { 250 retval = file->f_pos = offset; 251 } 252 } 253 } 254 up(&m->sem); 255 file->f_version = m->version; 256 return retval; 257 } 258 EXPORT_SYMBOL(seq_lseek); 259 260 /** 261 * seq_release - free the structures associated with sequential file. 262 * @file: file in question 263 * @inode: file->f_dentry->d_inode 264 * 265 * Frees the structures associated with sequential file; can be used 266 * as ->f_op->release() if you don't have private data to destroy. 267 */ 268 int seq_release(struct inode *inode, struct file *file) 269 { 270 struct seq_file *m = (struct seq_file *)file->private_data; 271 kfree(m->buf); 272 kfree(m); 273 return 0; 274 } 275 EXPORT_SYMBOL(seq_release); 276 277 /** 278 * seq_escape - print string into buffer, escaping some characters 279 * @m: target buffer 280 * @s: string 281 * @esc: set of characters that need escaping 282 * 283 * Puts string into buffer, replacing each occurrence of character from 284 * @esc with usual octal escape. Returns 0 in case of success, -1 - in 285 * case of overflow. 286 */ 287 int seq_escape(struct seq_file *m, const char *s, const char *esc) 288 { 289 char *end = m->buf + m->size; 290 char *p; 291 char c; 292 293 for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) { 294 if (!strchr(esc, c)) { 295 *p++ = c; 296 continue; 297 } 298 if (p + 3 < end) { 299 *p++ = '\\'; 300 *p++ = '0' + ((c & 0300) >> 6); 301 *p++ = '0' + ((c & 070) >> 3); 302 *p++ = '0' + (c & 07); 303 continue; 304 } 305 m->count = m->size; 306 return -1; 307 } 308 m->count = p - m->buf; 309 return 0; 310 } 311 EXPORT_SYMBOL(seq_escape); 312 313 int seq_printf(struct seq_file *m, const char *f, ...) 314 { 315 va_list args; 316 int len; 317 318 if (m->count < m->size) { 319 va_start(args, f); 320 len = vsnprintf(m->buf + m->count, m->size - m->count, f, args); 321 va_end(args); 322 if (m->count + len < m->size) { 323 m->count += len; 324 return 0; 325 } 326 } 327 m->count = m->size; 328 return -1; 329 } 330 EXPORT_SYMBOL(seq_printf); 331 332 int seq_path(struct seq_file *m, 333 struct vfsmount *mnt, struct dentry *dentry, 334 char *esc) 335 { 336 if (m->count < m->size) { 337 char *s = m->buf + m->count; 338 char *p = d_path(dentry, mnt, s, m->size - m->count); 339 if (!IS_ERR(p)) { 340 while (s <= p) { 341 char c = *p++; 342 if (!c) { 343 p = m->buf + m->count; 344 m->count = s - m->buf; 345 return s - p; 346 } else if (!strchr(esc, c)) { 347 *s++ = c; 348 } else if (s + 4 > p) { 349 break; 350 } else { 351 *s++ = '\\'; 352 *s++ = '0' + ((c & 0300) >> 6); 353 *s++ = '0' + ((c & 070) >> 3); 354 *s++ = '0' + (c & 07); 355 } 356 } 357 } 358 } 359 m->count = m->size; 360 return -1; 361 } 362 EXPORT_SYMBOL(seq_path); 363 364 static void *single_start(struct seq_file *p, loff_t *pos) 365 { 366 return NULL + (*pos == 0); 367 } 368 369 static void *single_next(struct seq_file *p, void *v, loff_t *pos) 370 { 371 ++*pos; 372 return NULL; 373 } 374 375 static void single_stop(struct seq_file *p, void *v) 376 { 377 } 378 379 int single_open(struct file *file, int (*show)(struct seq_file *, void *), 380 void *data) 381 { 382 struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL); 383 int res = -ENOMEM; 384 385 if (op) { 386 op->start = single_start; 387 op->next = single_next; 388 op->stop = single_stop; 389 op->show = show; 390 res = seq_open(file, op); 391 if (!res) 392 ((struct seq_file *)file->private_data)->private = data; 393 else 394 kfree(op); 395 } 396 return res; 397 } 398 EXPORT_SYMBOL(single_open); 399 400 int single_release(struct inode *inode, struct file *file) 401 { 402 struct seq_operations *op = ((struct seq_file *)file->private_data)->op; 403 int res = seq_release(inode, file); 404 kfree(op); 405 return res; 406 } 407 EXPORT_SYMBOL(single_release); 408 409 int seq_release_private(struct inode *inode, struct file *file) 410 { 411 struct seq_file *seq = file->private_data; 412 413 kfree(seq->private); 414 seq->private = NULL; 415 return seq_release(inode, file); 416 } 417 EXPORT_SYMBOL(seq_release_private); 418 419 int seq_putc(struct seq_file *m, char c) 420 { 421 if (m->count < m->size) { 422 m->buf[m->count++] = c; 423 return 0; 424 } 425 return -1; 426 } 427 EXPORT_SYMBOL(seq_putc); 428 429 int seq_puts(struct seq_file *m, const char *s) 430 { 431 int len = strlen(s); 432 if (m->count + len < m->size) { 433 memcpy(m->buf + m->count, s, len); 434 m->count += len; 435 return 0; 436 } 437 m->count = m->size; 438 return -1; 439 } 440 EXPORT_SYMBOL(seq_puts); 441