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

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/fs/readdir.c
  3.  *
  4.  *  Copyright (C) 1995  Linus Torvalds
  5.  */
  6. #include <linux/sched.h>
  7. #include <linux/mm.h>
  8. #include <linux/errno.h>
  9. #include <linux/stat.h>
  10. #include <linux/file.h>
  11. #include <linux/smp_lock.h>
  12. #include <asm/uaccess.h>
  13. int vfs_readdir(struct file *file, filldir_t filler, void *buf)
  14. {
  15. struct inode *inode = file->f_dentry->d_inode;
  16. int res = -ENOTDIR;
  17. if (!file->f_op || !file->f_op->readdir)
  18. goto out;
  19. down(&inode->i_sem);
  20. down(&inode->i_zombie);
  21. res = -ENOENT;
  22. if (!IS_DEADDIR(inode)) {
  23. lock_kernel();
  24. res = file->f_op->readdir(file, buf, filler);
  25. unlock_kernel();
  26. }
  27. up(&inode->i_zombie);
  28. up(&inode->i_sem);
  29. out:
  30. return res;
  31. }
  32. /*
  33.  * Directory is locked and all positive dentries in it are safe, since
  34.  * for ramfs-type trees they can't go away without unlink() or rmdir(),
  35.  * both impossible due to the lock on directory.
  36.  */
  37. int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
  38. {
  39. int i;
  40. struct dentry *dentry = filp->f_dentry;
  41. i = filp->f_pos;
  42. switch (i) {
  43. case 0:
  44. if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0)
  45. break;
  46. i++;
  47. filp->f_pos++;
  48. /* fallthrough */
  49. case 1:
  50. if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
  51. break;
  52. i++;
  53. filp->f_pos++;
  54. /* fallthrough */
  55. default: {
  56. struct list_head *list;
  57. int j = i-2;
  58. spin_lock(&dcache_lock);
  59. list = dentry->d_subdirs.next;
  60. for (;;) {
  61. if (list == &dentry->d_subdirs) {
  62. spin_unlock(&dcache_lock);
  63. return 0;
  64. }
  65. if (!j)
  66. break;
  67. j--;
  68. list = list->next;
  69. }
  70. while(1) {
  71. struct dentry *de = list_entry(list, struct dentry, d_child);
  72. if (!list_empty(&de->d_hash) && de->d_inode) {
  73. spin_unlock(&dcache_lock);
  74. if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0)
  75. break;
  76. spin_lock(&dcache_lock);
  77. }
  78. filp->f_pos++;
  79. list = list->next;
  80. if (list != &dentry->d_subdirs)
  81. continue;
  82. spin_unlock(&dcache_lock);
  83. break;
  84. }
  85. }
  86. }
  87. return 0;
  88. }
  89. /*
  90.  * Traditional linux readdir() handling..
  91.  *
  92.  * "count=1" is a special case, meaning that the buffer is one
  93.  * dirent-structure in size and that the code can't handle more
  94.  * anyway. Thus the special "fillonedir()" function for that
  95.  * case (the low-level handlers don't need to care about this).
  96.  */
  97. #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
  98. #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
  99. #ifndef __ia64__
  100. struct old_linux_dirent {
  101. unsigned long d_ino;
  102. unsigned long d_offset;
  103. unsigned short d_namlen;
  104. char d_name[1];
  105. };
  106. struct readdir_callback {
  107. struct old_linux_dirent * dirent;
  108. int count;
  109. };
  110. static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
  111.       ino_t ino, unsigned int d_type)
  112. {
  113. struct readdir_callback * buf = (struct readdir_callback *) __buf;
  114. struct old_linux_dirent * dirent;
  115. if (buf->count)
  116. return -EINVAL;
  117. buf->count++;
  118. dirent = buf->dirent;
  119. put_user(ino, &dirent->d_ino);
  120. put_user(offset, &dirent->d_offset);
  121. put_user(namlen, &dirent->d_namlen);
  122. copy_to_user(dirent->d_name, name, namlen);
  123. put_user(0, dirent->d_name + namlen);
  124. return 0;
  125. }
  126. asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
  127. {
  128. int error;
  129. struct file * file;
  130. struct readdir_callback buf;
  131. error = -EBADF;
  132. file = fget(fd);
  133. if (!file)
  134. goto out;
  135. buf.count = 0;
  136. buf.dirent = dirent;
  137. error = vfs_readdir(file, fillonedir, &buf);
  138. if (error >= 0)
  139. error = buf.count;
  140. fput(file);
  141. out:
  142. return error;
  143. }
  144. #endif /* !__ia64__ */
  145. /*
  146.  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
  147.  * interface. 
  148.  */
  149. struct linux_dirent {
  150. unsigned long d_ino;
  151. unsigned long d_off;
  152. unsigned short d_reclen;
  153. char d_name[1];
  154. };
  155. struct getdents_callback {
  156. struct linux_dirent * current_dir;
  157. struct linux_dirent * previous;
  158. int count;
  159. int error;
  160. };
  161. static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
  162.    ino_t ino, unsigned int d_type)
  163. {
  164. struct linux_dirent * dirent;
  165. struct getdents_callback * buf = (struct getdents_callback *) __buf;
  166. int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
  167. buf->error = -EINVAL; /* only used if we fail.. */
  168. if (reclen > buf->count)
  169. return -EINVAL;
  170. dirent = buf->previous;
  171. if (dirent)
  172. put_user(offset, &dirent->d_off);
  173. dirent = buf->current_dir;
  174. buf->previous = dirent;
  175. put_user(ino, &dirent->d_ino);
  176. put_user(reclen, &dirent->d_reclen);
  177. copy_to_user(dirent->d_name, name, namlen);
  178. put_user(0, dirent->d_name + namlen);
  179. ((char *) dirent) += reclen;
  180. buf->current_dir = dirent;
  181. buf->count -= reclen;
  182. return 0;
  183. }
  184. asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
  185. {
  186. struct file * file;
  187. struct linux_dirent * lastdirent;
  188. struct getdents_callback buf;
  189. int error;
  190. error = -EBADF;
  191. file = fget(fd);
  192. if (!file)
  193. goto out;
  194. buf.current_dir = (struct linux_dirent *) dirent;
  195. buf.previous = NULL;
  196. buf.count = count;
  197. buf.error = 0;
  198. error = vfs_readdir(file, filldir, &buf);
  199. if (error < 0)
  200. goto out_putf;
  201. error = buf.error;
  202. lastdirent = buf.previous;
  203. if (lastdirent) {
  204. put_user(file->f_pos, &lastdirent->d_off);
  205. error = count - buf.count;
  206. }
  207. out_putf:
  208. fput(file);
  209. out:
  210. return error;
  211. }
  212. /*
  213.  * And even better one including d_type field and 64bit d_ino and d_off.
  214.  */
  215. struct linux_dirent64 {
  216. u64 d_ino;
  217. s64 d_off;
  218. unsigned short d_reclen;
  219. unsigned char d_type;
  220. char d_name[0];
  221. };
  222. #define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
  223. struct getdents_callback64 {
  224. struct linux_dirent64 * current_dir;
  225. struct linux_dirent64 * previous;
  226. int count;
  227. int error;
  228. };
  229. static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
  230.      ino_t ino, unsigned int d_type)
  231. {
  232. struct linux_dirent64 * dirent, d;
  233. struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
  234. int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
  235. buf->error = -EINVAL; /* only used if we fail.. */
  236. if (reclen > buf->count)
  237. return -EINVAL;
  238. dirent = buf->previous;
  239. if (dirent) {
  240. d.d_off = offset;
  241. copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
  242. }
  243. dirent = buf->current_dir;
  244. buf->previous = dirent;
  245. memset(&d, 0, NAME_OFFSET(&d));
  246. d.d_ino = ino;
  247. d.d_reclen = reclen;
  248. d.d_type = d_type;
  249. copy_to_user(dirent, &d, NAME_OFFSET(&d));
  250. copy_to_user(dirent->d_name, name, namlen);
  251. put_user(0, dirent->d_name + namlen);
  252. ((char *) dirent) += reclen;
  253. buf->current_dir = dirent;
  254. buf->count -= reclen;
  255. return 0;
  256. }
  257. asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count)
  258. {
  259. struct file * file;
  260. struct linux_dirent64 * lastdirent;
  261. struct getdents_callback64 buf;
  262. int error;
  263. error = -EBADF;
  264. file = fget(fd);
  265. if (!file)
  266. goto out;
  267. buf.current_dir = (struct linux_dirent64 *) dirent;
  268. buf.previous = NULL;
  269. buf.count = count;
  270. buf.error = 0;
  271. error = vfs_readdir(file, filldir64, &buf);
  272. if (error < 0)
  273. goto out_putf;
  274. error = buf.error;
  275. lastdirent = buf.previous;
  276. if (lastdirent) {
  277. struct linux_dirent64 d;
  278. d.d_off = file->f_pos;
  279. copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off));
  280. error = count - buf.count;
  281. }
  282. out_putf:
  283. fput(file);
  284. out:
  285. return error;
  286. }