Linux 2.2.0
[davej-history.git] / fs / readdir.c
blob305b1cc7476296391215f01cc984d5fab0103bbe
1 /*
2 * linux/fs/readdir.c
4 * Copyright (C) 1995 Linus Torvalds
5 */
7 #include <linux/sched.h>
8 #include <linux/mm.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>
17 * Traditional linux readdir() handling..
19 * "count=1" is a special case, meaning that the buffer is one
20 * dirent-structure in size and that the code can't handle more
21 * anyway. Thus the special "fillonedir()" function for that
22 * case (the low-level handlers don't need to care about this).
24 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
25 #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
27 struct old_linux_dirent {
28 unsigned long d_ino;
29 unsigned long d_offset;
30 unsigned short d_namlen;
31 char d_name[1];
34 struct readdir_callback {
35 struct old_linux_dirent * dirent;
36 int count;
39 static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
41 struct readdir_callback * buf = (struct readdir_callback *) __buf;
42 struct old_linux_dirent * dirent;
44 if (buf->count)
45 return -EINVAL;
46 buf->count++;
47 dirent = buf->dirent;
48 put_user(ino, &dirent->d_ino);
49 put_user(offset, &dirent->d_offset);
50 put_user(namlen, &dirent->d_namlen);
51 copy_to_user(dirent->d_name, name, namlen);
52 put_user(0, dirent->d_name + namlen);
53 return 0;
56 asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
58 int error;
59 struct file * file;
60 struct dentry * dentry;
61 struct inode * inode;
62 struct readdir_callback buf;
64 lock_kernel();
65 error = -EBADF;
66 file = fget(fd);
67 if (!file)
68 goto out;
70 dentry = file->f_dentry;
71 if (!dentry)
72 goto out_putf;
74 inode = dentry->d_inode;
75 if (!inode)
76 goto out_putf;
78 buf.count = 0;
79 buf.dirent = dirent;
81 error = -ENOTDIR;
82 if (!file->f_op || !file->f_op->readdir)
83 goto out_putf;
86 * Get the inode's semaphore to prevent changes
87 * to the directory while we read it.
89 down(&inode->i_sem);
90 error = file->f_op->readdir(file, &buf, fillonedir);
91 up(&inode->i_sem);
92 if (error < 0)
93 goto out_putf;
94 error = buf.count;
96 out_putf:
97 fput(file);
98 out:
99 unlock_kernel();
100 return error;
104 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
105 * interface.
107 struct linux_dirent {
108 unsigned long d_ino;
109 unsigned long d_off;
110 unsigned short d_reclen;
111 char d_name[1];
114 struct getdents_callback {
115 struct linux_dirent * current_dir;
116 struct linux_dirent * previous;
117 int count;
118 int error;
121 static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
123 struct linux_dirent * dirent;
124 struct getdents_callback * buf = (struct getdents_callback *) __buf;
125 int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
127 buf->error = -EINVAL; /* only used if we fail.. */
128 if (reclen > buf->count)
129 return -EINVAL;
130 dirent = buf->previous;
131 if (dirent)
132 put_user(offset, &dirent->d_off);
133 dirent = buf->current_dir;
134 buf->previous = dirent;
135 put_user(ino, &dirent->d_ino);
136 put_user(reclen, &dirent->d_reclen);
137 copy_to_user(dirent->d_name, name, namlen);
138 put_user(0, dirent->d_name + namlen);
139 ((char *) dirent) += reclen;
140 buf->current_dir = dirent;
141 buf->count -= reclen;
142 return 0;
145 asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count)
147 struct file * file;
148 struct dentry * dentry;
149 struct inode * inode;
150 struct linux_dirent * lastdirent;
151 struct getdents_callback buf;
152 int error;
154 lock_kernel();
155 error = -EBADF;
156 file = fget(fd);
157 if (!file)
158 goto out;
160 dentry = file->f_dentry;
161 if (!dentry)
162 goto out_putf;
164 inode = dentry->d_inode;
165 if (!inode)
166 goto out_putf;
168 buf.current_dir = (struct linux_dirent *) dirent;
169 buf.previous = NULL;
170 buf.count = count;
171 buf.error = 0;
173 error = -ENOTDIR;
174 if (!file->f_op || !file->f_op->readdir)
175 goto out_putf;
178 * Get the inode's semaphore to prevent changes
179 * to the directory while we read it.
181 down(&inode->i_sem);
182 error = file->f_op->readdir(file, &buf, filldir);
183 up(&inode->i_sem);
184 if (error < 0)
185 goto out_putf;
186 error = buf.error;
187 lastdirent = buf.previous;
188 if (lastdirent) {
189 put_user(file->f_pos, &lastdirent->d_off);
190 error = count - buf.count;
193 out_putf:
194 fput(file);
195 out:
196 unlock_kernel();
197 return error;