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