seq_file.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:6k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  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. #include <linux/fs.h>
  8. #include <linux/seq_file.h>
  9. #include <linux/slab.h>
  10. #include <asm/uaccess.h>
  11. /**
  12.  * seq_open - initialize sequential file
  13.  * @file: file we initialize
  14.  * @op: method table describing the sequence
  15.  *
  16.  * seq_open() sets @file, associating it with a sequence described
  17.  * by @op.  @op->start() sets the iterator up and returns the first
  18.  * element of sequence. @op->stop() shuts it down.  @op->next()
  19.  * returns the next element of sequence.  @op->show() prints element
  20.  * into the buffer.  In case of error ->start() and ->next() return
  21.  * ERR_PTR(error).  In the end of sequence they return %NULL. ->show()
  22.  * returns 0 in case of success and negative number in case of error.
  23.  */
  24. int seq_open(struct file *file, struct seq_operations *op)
  25. {
  26. struct seq_file *p = kmalloc(sizeof(*p), GFP_KERNEL);
  27. if (!p)
  28. return -ENOMEM;
  29. memset(p, 0, sizeof(*p));
  30. sema_init(&p->sem, 1);
  31. p->op = op;
  32. file->private_data = p;
  33. return 0;
  34. }
  35. /**
  36.  * seq_read - ->read() method for sequential files.
  37.  * @file, @buf, @size, @ppos: see file_operations method
  38.  *
  39.  * Ready-made ->f_op->read()
  40.  */
  41. ssize_t seq_read(struct file *file, char *buf, size_t size, loff_t *ppos)
  42. {
  43. struct seq_file *m = (struct seq_file *)file->private_data;
  44. size_t copied = 0;
  45. loff_t pos;
  46. size_t n;
  47. void *p;
  48. int err = 0;
  49. if (ppos != &file->f_pos)
  50. return -EPIPE;
  51. down(&m->sem);
  52. /* grab buffer if we didn't have one */
  53. if (!m->buf) {
  54. m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
  55. if (!m->buf)
  56. goto Enomem;
  57. }
  58. /* if not empty - flush it first */
  59. if (m->count) {
  60. n = min(m->count, size);
  61. err = copy_to_user(buf, m->buf + m->from, n);
  62. if (err)
  63. goto Efault;
  64. m->count -= n;
  65. m->from += n;
  66. size -= n;
  67. buf += n;
  68. copied += n;
  69. if (!m->count)
  70. m->index++;
  71. if (!size)
  72. goto Done;
  73. }
  74. /* we need at least one record in buffer */
  75. while (1) {
  76. pos = m->index;
  77. p = m->op->start(m, &pos);
  78. err = PTR_ERR(p);
  79. if (!p || IS_ERR(p))
  80. break;
  81. err = m->op->show(m, p);
  82. if (err)
  83. break;
  84. if (m->count < m->size)
  85. goto Fill;
  86. m->op->stop(m, p);
  87. kfree(m->buf);
  88. m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
  89. if (!m->buf)
  90. goto Enomem;
  91. }
  92. m->op->stop(m, p);
  93. goto Done;
  94. Fill:
  95. /* they want more? let's try to get some more */
  96. while (m->count < size) {
  97. size_t offs = m->count;
  98. loff_t next = pos;
  99. p = m->op->next(m, p, &next);
  100. if (!p || IS_ERR(p)) {
  101. err = PTR_ERR(p);
  102. break;
  103. }
  104. err = m->op->show(m, p);
  105. if (err || m->count == m->size) {
  106. m->count = offs;
  107. break;
  108. }
  109. pos = next;
  110. }
  111. m->op->stop(m, p);
  112. n = min(m->count, size);
  113. err = copy_to_user(buf, m->buf, n);
  114. if (err)
  115. goto Efault;
  116. copied += n;
  117. m->count -= n;
  118. if (m->count)
  119. m->from = n;
  120. else
  121. pos++;
  122. m->index = pos;
  123. Done:
  124. if (!copied)
  125. copied = err;
  126. else
  127. *ppos += copied;
  128. up(&m->sem);
  129. return copied;
  130. Enomem:
  131. err = -ENOMEM;
  132. goto Done;
  133. Efault:
  134. err = -EFAULT;
  135. goto Done;
  136. }
  137. static int traverse(struct seq_file *m, loff_t offset)
  138. {
  139. loff_t pos = 0;
  140. int error = 0;
  141. void *p;
  142. m->index = 0;
  143. m->count = m->from = 0;
  144. if (!offset)
  145. return 0;
  146. if (!m->buf) {
  147. m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
  148. if (!m->buf)
  149. return -ENOMEM;
  150. }
  151. p = m->op->start(m, &m->index);
  152. while (p) {
  153. error = PTR_ERR(p);
  154. if (IS_ERR(p))
  155. break;
  156. error = m->op->show(m, p);
  157. if (error)
  158. break;
  159. if (m->count == m->size)
  160. goto Eoverflow;
  161. if (pos + m->count > offset) {
  162. m->from = offset - pos;
  163. m->count -= m->from;
  164. break;
  165. }
  166. pos += m->count;
  167. m->count = 0;
  168. if (pos == offset) {
  169. m->index++;
  170. break;
  171. }
  172. p = m->op->next(m, p, &m->index);
  173. }
  174. m->op->stop(m, p);
  175. return error;
  176. Eoverflow:
  177. m->op->stop(m, p);
  178. kfree(m->buf);
  179. m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
  180. return !m->buf ? -ENOMEM : -EAGAIN;
  181. }
  182. /**
  183.  * seq_lseek - ->llseek() method for sequential files.
  184.  * @file, @offset, @origin: see file_operations method
  185.  *
  186.  * Ready-made ->f_op->llseek()
  187.  */
  188. loff_t seq_lseek(struct file *file, loff_t offset, int origin)
  189. {
  190. struct seq_file *m = (struct seq_file *)file->private_data;
  191. long long retval = -EINVAL;
  192. down(&m->sem);
  193. switch (origin) {
  194. case 1:
  195. offset += file->f_pos;
  196. case 0:
  197. if (offset < 0)
  198. break;
  199. retval = offset;
  200. if (offset != file->f_pos) {
  201. while ((retval=traverse(m, offset)) == -EAGAIN)
  202. ;
  203. if (retval) {
  204. /* with extreme perjudice... */
  205. file->f_pos = 0;
  206. m->index = 0;
  207. m->count = 0;
  208. } else {
  209. retval = file->f_pos = offset;
  210. }
  211. }
  212. }
  213. up(&m->sem);
  214. return retval;
  215. }
  216. /**
  217.  * seq_release - free the structures associated with sequential file.
  218.  * @file: file in question
  219.  * @inode: file->f_dentry->d_inode
  220.  *
  221.  * Frees the structures associated with sequential file; can be used
  222.  * as ->f_op->release() if you don't have private data to destroy.
  223.  */
  224. int seq_release(struct inode *inode, struct file *file)
  225. {
  226. struct seq_file *m = (struct seq_file *)file->private_data;
  227. kfree(m->buf);
  228. kfree(m);
  229. return 0;
  230. }
  231. /**
  232.  * seq_escape - print string into buffer, escaping some characters
  233.  * @m: target buffer
  234.  * @s: string
  235.  * @esc: set of characters that need escaping
  236.  *
  237.  * Puts string into buffer, replacing each occurence of character from
  238.  * @esc with usual octal escape.  Returns 0 in case of success, -1 - in
  239.  * case of overflow.
  240.  */
  241. int seq_escape(struct seq_file *m, const char *s, const char *esc)
  242. {
  243. char *end = m->buf + m->size;
  244.         char *p;
  245. char c;
  246.         for (p = m->buf + m->count; (c = *s) != '' && p < end; s++) {
  247. if (!strchr(esc, c)) {
  248. *p++ = c;
  249. continue;
  250. }
  251. if (p + 3 < end) {
  252. *p++ = '\';
  253. *p++ = '0' + ((c & 0300) >> 6);
  254. *p++ = '0' + ((c & 070) >> 3);
  255. *p++ = '0' + (c & 07);
  256. continue;
  257. }
  258. m->count = m->size;
  259. return -1;
  260.         }
  261. m->count = p - m->buf;
  262.         return 0;
  263. }
  264. int seq_printf(struct seq_file *m, const char *f, ...)
  265. {
  266. va_list args;
  267. int len;
  268. if (m->count < m->size) {
  269. va_start(args, f);
  270. len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
  271. va_end(args);
  272. if (m->count + len < m->size) {
  273. m->count += len;
  274. return 0;
  275. }
  276. }
  277. m->count = m->size;
  278. return -1;
  279. }