4 * Copyright (C) 1995 Linus Torvalds
7 #include <linux/sched.h>
9 #include <linux/errno.h>
10 #include <linux/stat.h>
11 #include <linux/file.h>
12 #include <linux/smp_lock.h>
14 #include <asm/uaccess.h>
16 int vfs_readdir(struct file *file, filldir_t filler, void *buf)
18 struct inode *inode = file->f_dentry->d_inode;
20 if (!file->f_op || !file->f_op->readdir)
23 down(&inode->i_zombie);
25 if (!IS_DEADDIR(inode)) {
27 res = file->f_op->readdir(file, buf, filler);
36 int dcache_dir_open(struct inode *inode, struct file *file)
38 static struct qstr cursor_name = {len:1, name:"."};
40 file->private_data = d_alloc(file->f_dentry, &cursor_name);
42 return file->private_data ? 0 : -ENOMEM;
45 int dcache_dir_close(struct inode *inode, struct file *file)
47 dput(file->private_data);
51 loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
53 down(&file->f_dentry->d_inode->i_sem);
56 offset += file->f_pos;
61 up(&file->f_dentry->d_inode->i_sem);
64 if (offset != file->f_pos) {
66 if (file->f_pos >= 2) {
68 struct dentry *cursor = file->private_data;
69 loff_t n = file->f_pos - 2;
71 spin_lock(&dcache_lock);
72 list_del(&cursor->d_child);
73 p = file->f_dentry->d_subdirs.next;
74 while (n && p != &file->f_dentry->d_subdirs) {
76 next = list_entry(p, struct dentry, d_child);
77 if (!list_empty(&next->d_hash) && next->d_inode)
81 list_add_tail(&cursor->d_child, p);
82 spin_unlock(&dcache_lock);
85 up(&file->f_dentry->d_inode->i_sem);
89 int dcache_dir_fsync(struct file * file, struct dentry *dentry, int datasync)
95 * Directory is locked and all positive dentries in it are safe, since
96 * for ramfs-type trees they can't go away without unlink() or rmdir(),
97 * both impossible due to the lock on directory.
100 int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
102 struct dentry *dentry = filp->f_dentry;
103 struct dentry *cursor = filp->private_data;
104 struct list_head *p, *q = &cursor->d_child;
110 ino = dentry->d_inode->i_ino;
111 if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
117 spin_lock(&dcache_lock);
118 ino = dentry->d_parent->d_inode->i_ino;
119 spin_unlock(&dcache_lock);
120 if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
126 spin_lock(&dcache_lock);
127 if (filp->f_pos == 2) {
129 list_add(q, &dentry->d_subdirs);
131 for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
133 next = list_entry(p, struct dentry, d_child);
134 if (list_empty(&next->d_hash) || !next->d_inode)
137 spin_unlock(&dcache_lock);
138 if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, DT_UNKNOWN) < 0)
140 spin_lock(&dcache_lock);
141 /* next is still alive */
147 spin_unlock(&dcache_lock);
149 UPDATE_ATIME(dentry->d_inode);
153 struct file_operations dcache_dir_ops = {
154 open: dcache_dir_open,
155 release: dcache_dir_close,
156 llseek: dcache_dir_lseek,
157 read: generic_read_dir,
158 readdir: dcache_readdir,
159 fsync: dcache_dir_fsync,
163 * Traditional linux readdir() handling..
165 * "count=1" is a special case, meaning that the buffer is one
166 * dirent-structure in size and that the code can't handle more
167 * anyway. Thus the special "fillonedir()" function for that
168 * case (the low-level handlers don't need to care about this).
170 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
171 #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
175 struct old_linux_dirent {
177 unsigned long d_offset;
178 unsigned short d_namlen;
182 struct readdir_callback {
183 struct old_linux_dirent * dirent;
187 static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
188 ino_t ino, unsigned int d_type)
190 struct readdir_callback * buf = (struct readdir_callback *) __buf;
191 struct old_linux_dirent * dirent;
196 dirent = buf->dirent;
197 put_user(ino, &dirent->d_ino);
198 put_user(offset, &dirent->d_offset);
199 put_user(namlen, &dirent->d_namlen);
200 copy_to_user(dirent->d_name, name, namlen);
201 put_user(0, dirent->d_name + namlen);
205 asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
209 struct readdir_callback buf;
219 error = vfs_readdir(file, fillonedir, &buf);
228 #endif /* !__ia64__ */
231 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
234 struct linux_dirent {
237 unsigned short d_reclen;
241 struct getdents_callback {
242 struct linux_dirent * current_dir;
243 struct linux_dirent * previous;
248 static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
249 ino_t ino, unsigned int d_type)
251 struct linux_dirent * dirent;
252 struct getdents_callback * buf = (struct getdents_callback *) __buf;
253 int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
255 buf->error = -EINVAL; /* only used if we fail.. */
256 if (reclen > buf->count)
258 dirent = buf->previous;
260 put_user(offset, &dirent->d_off);
261 dirent = buf->current_dir;
262 buf->previous = dirent;
263 put_user(ino, &dirent->d_ino);
264 put_user(reclen, &dirent->d_reclen);
265 copy_to_user(dirent->d_name, name, namlen);
266 put_user(0, dirent->d_name + namlen);
267 dirent = (void *)dirent + reclen;
268 buf->current_dir = dirent;
269 buf->count -= reclen;
273 asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
276 struct linux_dirent * lastdirent;
277 struct getdents_callback buf;
285 buf.current_dir = (struct linux_dirent *) dirent;
290 error = vfs_readdir(file, filldir, &buf);
294 lastdirent = buf.previous;
296 put_user(file->f_pos, &lastdirent->d_off);
297 error = count - buf.count;
307 * And even better one including d_type field and 64bit d_ino and d_off.
309 struct linux_dirent64 {
312 unsigned short d_reclen;
313 unsigned char d_type;
317 #define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
319 struct getdents_callback64 {
320 struct linux_dirent64 * current_dir;
321 struct linux_dirent64 * previous;
326 static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
327 ino_t ino, unsigned int d_type)
329 struct linux_dirent64 * dirent, d;
330 struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
331 int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
333 buf->error = -EINVAL; /* only used if we fail.. */
334 if (reclen > buf->count)
336 dirent = buf->previous;
339 copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
341 dirent = buf->current_dir;
342 buf->previous = dirent;
343 memset(&d, 0, NAME_OFFSET(&d));
347 copy_to_user(dirent, &d, NAME_OFFSET(&d));
348 copy_to_user(dirent->d_name, name, namlen);
349 put_user(0, dirent->d_name + namlen);
350 dirent = (void *)dirent + reclen;
351 buf->current_dir = dirent;
352 buf->count -= reclen;
356 asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count)
359 struct linux_dirent64 * lastdirent;
360 struct getdents_callback64 buf;
368 buf.current_dir = (struct linux_dirent64 *) dirent;
373 error = vfs_readdir(file, filldir64, &buf);
377 lastdirent = buf.previous;
379 struct linux_dirent64 d;
380 d.d_off = file->f_pos;
381 copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off));
382 error = count - buf.count;