readdir.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:9k
源码类别:

Linux/Unix编程

开发平台:

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. int dcache_dir_open(struct inode *inode, struct file *file)
  33. {
  34. static struct qstr cursor_name = {len:1, name:"."};
  35. file->private_data = d_alloc(file->f_dentry, &cursor_name);
  36. return file->private_data ? 0 : -ENOMEM;
  37. }
  38. int dcache_dir_close(struct inode *inode, struct file *file)
  39. {
  40. dput(file->private_data);
  41. return 0;
  42. }
  43. loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
  44. {
  45. down(&file->f_dentry->d_inode->i_sem);
  46. switch (origin) {
  47. case 1:
  48. offset += file->f_pos;
  49. case 0:
  50. if (offset >= 0)
  51. break;
  52. default:
  53. up(&file->f_dentry->d_inode->i_sem);
  54. return -EINVAL;
  55. }
  56. if (offset != file->f_pos) {
  57. file->f_pos = offset;
  58. if (file->f_pos >= 2) {
  59. struct list_head *p;
  60. struct dentry *cursor = file->private_data;
  61. loff_t n = file->f_pos - 2;
  62. spin_lock(&dcache_lock);
  63. p = file->f_dentry->d_subdirs.next;
  64. while (n && p != &file->f_dentry->d_subdirs) {
  65. struct dentry *next;
  66. next = list_entry(p, struct dentry, d_child);
  67. if (!list_empty(&next->d_hash) && next->d_inode)
  68. n--;
  69. p = p->next;
  70. }
  71. list_del(&cursor->d_child);
  72. list_add_tail(&cursor->d_child, p);
  73. spin_unlock(&dcache_lock);
  74. }
  75. }
  76. up(&file->f_dentry->d_inode->i_sem);
  77. return offset;
  78. }
  79. int dcache_dir_fsync(struct file * file, struct dentry *dentry, int datasync)
  80. {
  81. return 0;
  82. }
  83. /*
  84.  * Directory is locked and all positive dentries in it are safe, since
  85.  * for ramfs-type trees they can't go away without unlink() or rmdir(),
  86.  * both impossible due to the lock on directory.
  87.  */
  88. int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
  89. {
  90. struct dentry *dentry = filp->f_dentry;
  91. struct dentry *cursor = filp->private_data;
  92. struct list_head *p, *q = &cursor->d_child;
  93. ino_t ino;
  94. int i = filp->f_pos;
  95. switch (i) {
  96. case 0:
  97. ino = dentry->d_inode->i_ino;
  98. if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
  99. break;
  100. filp->f_pos++;
  101. i++;
  102. /* fallthrough */
  103. case 1:
  104. spin_lock(&dcache_lock);
  105. ino = dentry->d_parent->d_inode->i_ino;
  106. spin_unlock(&dcache_lock);
  107. if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
  108. break;
  109. filp->f_pos++;
  110. i++;
  111. /* fallthrough */
  112. default:
  113. spin_lock(&dcache_lock);
  114. if (filp->f_pos == 2) {
  115. list_del(q);
  116. list_add(q, &dentry->d_subdirs);
  117. }
  118. for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
  119. struct dentry *next;
  120. next = list_entry(p, struct dentry, d_child);
  121. if (list_empty(&next->d_hash) || !next->d_inode)
  122. continue;
  123. spin_unlock(&dcache_lock);
  124. if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, DT_UNKNOWN) < 0)
  125. return 0;
  126. spin_lock(&dcache_lock);
  127. /* next is still alive */
  128. list_del(q);
  129. list_add(q, p);
  130. p = q;
  131. filp->f_pos++;
  132. }
  133. spin_unlock(&dcache_lock);
  134. }
  135. return 0;
  136. }
  137. struct file_operations dcache_dir_ops = {
  138. open: dcache_dir_open,
  139. release: dcache_dir_close,
  140. llseek: dcache_dir_lseek,
  141. read: generic_read_dir,
  142. readdir: dcache_readdir,
  143. fsync: dcache_dir_fsync,
  144. };
  145. /*
  146.  * Traditional linux readdir() handling..
  147.  *
  148.  * "count=1" is a special case, meaning that the buffer is one
  149.  * dirent-structure in size and that the code can't handle more
  150.  * anyway. Thus the special "fillonedir()" function for that
  151.  * case (the low-level handlers don't need to care about this).
  152.  */
  153. #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
  154. #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
  155. #ifndef __ia64__
  156. struct old_linux_dirent {
  157. unsigned long d_ino;
  158. unsigned long d_offset;
  159. unsigned short d_namlen;
  160. char d_name[1];
  161. };
  162. struct readdir_callback {
  163. struct old_linux_dirent * dirent;
  164. int count;
  165. };
  166. static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
  167.       ino_t ino, unsigned int d_type)
  168. {
  169. struct readdir_callback * buf = (struct readdir_callback *) __buf;
  170. struct old_linux_dirent * dirent;
  171. if (buf->count)
  172. return -EINVAL;
  173. buf->count++;
  174. dirent = buf->dirent;
  175. put_user(ino, &dirent->d_ino);
  176. put_user(offset, &dirent->d_offset);
  177. put_user(namlen, &dirent->d_namlen);
  178. copy_to_user(dirent->d_name, name, namlen);
  179. put_user(0, dirent->d_name + namlen);
  180. return 0;
  181. }
  182. asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
  183. {
  184. int error;
  185. struct file * file;
  186. struct readdir_callback buf;
  187. error = -EBADF;
  188. file = fget(fd);
  189. if (!file)
  190. goto out;
  191. buf.count = 0;
  192. buf.dirent = dirent;
  193. error = vfs_readdir(file, fillonedir, &buf);
  194. if (error >= 0)
  195. error = buf.count;
  196. fput(file);
  197. out:
  198. return error;
  199. }
  200. #endif /* !__ia64__ */
  201. /*
  202.  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
  203.  * interface. 
  204.  */
  205. struct linux_dirent {
  206. unsigned long d_ino;
  207. unsigned long d_off;
  208. unsigned short d_reclen;
  209. char d_name[1];
  210. };
  211. struct getdents_callback {
  212. struct linux_dirent * current_dir;
  213. struct linux_dirent * previous;
  214. int count;
  215. int error;
  216. };
  217. static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
  218.    ino_t ino, unsigned int d_type)
  219. {
  220. struct linux_dirent * dirent;
  221. struct getdents_callback * buf = (struct getdents_callback *) __buf;
  222. int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
  223. buf->error = -EINVAL; /* only used if we fail.. */
  224. if (reclen > buf->count)
  225. return -EINVAL;
  226. dirent = buf->previous;
  227. if (dirent)
  228. put_user(offset, &dirent->d_off);
  229. dirent = buf->current_dir;
  230. buf->previous = dirent;
  231. put_user(ino, &dirent->d_ino);
  232. put_user(reclen, &dirent->d_reclen);
  233. copy_to_user(dirent->d_name, name, namlen);
  234. put_user(0, dirent->d_name + namlen);
  235. ((char *) dirent) += reclen;
  236. buf->current_dir = dirent;
  237. buf->count -= reclen;
  238. return 0;
  239. }
  240. asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
  241. {
  242. struct file * file;
  243. struct linux_dirent * lastdirent;
  244. struct getdents_callback buf;
  245. int error;
  246. error = -EBADF;
  247. file = fget(fd);
  248. if (!file)
  249. goto out;
  250. buf.current_dir = (struct linux_dirent *) dirent;
  251. buf.previous = NULL;
  252. buf.count = count;
  253. buf.error = 0;
  254. error = vfs_readdir(file, filldir, &buf);
  255. if (error < 0)
  256. goto out_putf;
  257. error = buf.error;
  258. lastdirent = buf.previous;
  259. if (lastdirent) {
  260. put_user(file->f_pos, &lastdirent->d_off);
  261. error = count - buf.count;
  262. }
  263. out_putf:
  264. fput(file);
  265. out:
  266. return error;
  267. }
  268. /*
  269.  * And even better one including d_type field and 64bit d_ino and d_off.
  270.  */
  271. struct linux_dirent64 {
  272. u64 d_ino;
  273. s64 d_off;
  274. unsigned short d_reclen;
  275. unsigned char d_type;
  276. char d_name[0];
  277. };
  278. #define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
  279. struct getdents_callback64 {
  280. struct linux_dirent64 * current_dir;
  281. struct linux_dirent64 * previous;
  282. int count;
  283. int error;
  284. };
  285. static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
  286.      ino_t ino, unsigned int d_type)
  287. {
  288. struct linux_dirent64 * dirent, d;
  289. struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
  290. int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
  291. buf->error = -EINVAL; /* only used if we fail.. */
  292. if (reclen > buf->count)
  293. return -EINVAL;
  294. dirent = buf->previous;
  295. if (dirent) {
  296. d.d_off = offset;
  297. copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
  298. }
  299. dirent = buf->current_dir;
  300. buf->previous = dirent;
  301. memset(&d, 0, NAME_OFFSET(&d));
  302. d.d_ino = ino;
  303. d.d_reclen = reclen;
  304. d.d_type = d_type;
  305. copy_to_user(dirent, &d, NAME_OFFSET(&d));
  306. copy_to_user(dirent->d_name, name, namlen);
  307. put_user(0, dirent->d_name + namlen);
  308. ((char *) dirent) += reclen;
  309. buf->current_dir = dirent;
  310. buf->count -= reclen;
  311. return 0;
  312. }
  313. asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count)
  314. {
  315. struct file * file;
  316. struct linux_dirent64 * lastdirent;
  317. struct getdents_callback64 buf;
  318. int error;
  319. error = -EBADF;
  320. file = fget(fd);
  321. if (!file)
  322. goto out;
  323. buf.current_dir = (struct linux_dirent64 *) dirent;
  324. buf.previous = NULL;
  325. buf.count = count;
  326. buf.error = 0;
  327. error = vfs_readdir(file, filldir64, &buf);
  328. if (error < 0)
  329. goto out_putf;
  330. error = buf.error;
  331. lastdirent = buf.previous;
  332. if (lastdirent) {
  333. struct linux_dirent64 d;
  334. d.d_off = file->f_pos;
  335. copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off));
  336. error = count - buf.count;
  337. }
  338. out_putf:
  339. fput(file);
  340. out:
  341. return error;
  342. }