Fix prototype of SMP version of synchronize_irq.
[linux-2.6/linux-mips.git] / fs / readdir.c
blob5261c81859d64b37ccffbfff248b3006b0b42908
1 /*
2 * linux/fs/readdir.c
4 * Copyright (C) 1995 Linus Torvalds
5 */
7 #include <linux/time.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>
13 #include <linux/fs.h>
14 #include <linux/dirent.h>
15 #include <linux/security.h>
17 #include <asm/uaccess.h>
19 int vfs_readdir(struct file *file, filldir_t filler, void *buf)
21 struct inode *inode = file->f_dentry->d_inode;
22 int res = -ENOTDIR;
23 if (!file->f_op || !file->f_op->readdir)
24 goto out;
26 res = security_file_permission(file, MAY_READ);
27 if (res)
28 goto out;
30 down(&inode->i_sem);
31 res = -ENOENT;
32 if (!IS_DEADDIR(inode)) {
33 res = file->f_op->readdir(file, buf, filler);
35 up(&inode->i_sem);
36 out:
37 return res;
41 * Traditional linux readdir() handling..
43 * "count=1" is a special case, meaning that the buffer is one
44 * dirent-structure in size and that the code can't handle more
45 * anyway. Thus the special "fillonedir()" function for that
46 * case (the low-level handlers don't need to care about this).
48 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
49 #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
51 #ifndef __ia64__
53 struct old_linux_dirent {
54 unsigned long d_ino;
55 unsigned long d_offset;
56 unsigned short d_namlen;
57 char d_name[1];
60 struct readdir_callback {
61 struct old_linux_dirent __user * dirent;
62 int result;
65 static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
66 ino_t ino, unsigned int d_type)
68 struct readdir_callback * buf = (struct readdir_callback *) __buf;
69 struct old_linux_dirent __user * dirent;
71 if (buf->result)
72 return -EINVAL;
73 buf->result++;
74 dirent = buf->dirent;
75 if (!access_ok(VERIFY_WRITE, (unsigned long)dirent,
76 (unsigned long)(dirent->d_name + namlen + 1) -
77 (unsigned long)dirent))
78 goto efault;
79 if ( __put_user(ino, &dirent->d_ino) ||
80 __put_user(offset, &dirent->d_offset) ||
81 __put_user(namlen, &dirent->d_namlen) ||
82 __copy_to_user(dirent->d_name, name, namlen) ||
83 __put_user(0, dirent->d_name + namlen))
84 goto efault;
85 return 0;
86 efault:
87 buf->result = -EFAULT;
88 return -EFAULT;
91 asmlinkage long old_readdir(unsigned int fd, struct old_linux_dirent __user * dirent, unsigned int count)
93 int error;
94 struct file * file;
95 struct readdir_callback buf;
97 error = -EBADF;
98 file = fget(fd);
99 if (!file)
100 goto out;
102 buf.result = 0;
103 buf.dirent = dirent;
105 error = vfs_readdir(file, fillonedir, &buf);
106 if (error >= 0)
107 error = buf.result;
109 fput(file);
110 out:
111 return error;
114 #endif /* !__ia64__ */
117 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
118 * interface.
120 struct linux_dirent {
121 unsigned long d_ino;
122 unsigned long d_off;
123 unsigned short d_reclen;
124 char d_name[1];
127 struct getdents_callback {
128 struct linux_dirent __user * current_dir;
129 struct linux_dirent __user * previous;
130 int count;
131 int error;
134 static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
135 ino_t ino, unsigned int d_type)
137 struct linux_dirent __user * dirent;
138 struct getdents_callback * buf = (struct getdents_callback *) __buf;
139 int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
141 buf->error = -EINVAL; /* only used if we fail.. */
142 if (reclen > buf->count)
143 return -EINVAL;
144 dirent = buf->previous;
145 if (dirent) {
146 if (__put_user(offset, &dirent->d_off))
147 goto efault;
149 dirent = buf->current_dir;
150 if (__put_user(ino, &dirent->d_ino))
151 goto efault;
152 if (__put_user(reclen, &dirent->d_reclen))
153 goto efault;
154 if (copy_to_user(dirent->d_name, name, namlen))
155 goto efault;
156 if (__put_user(0, dirent->d_name + namlen))
157 goto efault;
158 buf->previous = dirent;
159 ((char *) dirent) += reclen;
160 buf->current_dir = dirent;
161 buf->count -= reclen;
162 return 0;
163 efault:
164 buf->error = -EFAULT;
165 return -EFAULT;
168 asmlinkage long sys_getdents(unsigned int fd, struct linux_dirent __user * dirent, unsigned int count)
170 struct file * file;
171 struct linux_dirent __user * lastdirent;
172 struct getdents_callback buf;
173 int error;
175 error = -EFAULT;
176 if (!access_ok(VERIFY_WRITE, dirent, count))
177 goto out;
179 error = -EBADF;
180 file = fget(fd);
181 if (!file)
182 goto out;
184 buf.current_dir = dirent;
185 buf.previous = NULL;
186 buf.count = count;
187 buf.error = 0;
189 error = vfs_readdir(file, filldir, &buf);
190 if (error < 0)
191 goto out_putf;
192 error = buf.error;
193 lastdirent = buf.previous;
194 if (lastdirent) {
195 if (put_user(file->f_pos, &lastdirent->d_off))
196 error = -EFAULT;
197 else
198 error = count - buf.count;
201 out_putf:
202 fput(file);
203 out:
204 return error;
207 #define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
209 struct getdents_callback64 {
210 struct linux_dirent64 __user * current_dir;
211 struct linux_dirent64 __user * previous;
212 int count;
213 int error;
216 static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
217 ino_t ino, unsigned int d_type)
219 struct linux_dirent64 __user *dirent;
220 struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
221 int reclen = ROUND_UP64(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 if (__put_user(offset, &dirent->d_off))
229 goto efault;
231 dirent = buf->current_dir;
232 if (__put_user(ino, &dirent->d_ino))
233 goto efault;
234 if (__put_user(0, &dirent->d_off))
235 goto efault;
236 if (__put_user(reclen, &dirent->d_reclen))
237 goto efault;
238 if (__put_user(d_type, &dirent->d_type))
239 goto efault;
240 if (copy_to_user(dirent->d_name, name, namlen))
241 goto efault;
242 if (__put_user(0, dirent->d_name + namlen))
243 goto efault;
244 buf->previous = dirent;
245 ((char *) dirent) += reclen;
246 buf->current_dir = dirent;
247 buf->count -= reclen;
248 return 0;
249 efault:
250 buf->error = -EFAULT;
251 return -EFAULT;
254 asmlinkage long sys_getdents64(unsigned int fd, struct linux_dirent64 __user * dirent, unsigned int count)
256 struct file * file;
257 struct linux_dirent64 __user * lastdirent;
258 struct getdents_callback64 buf;
259 int error;
261 error = -EFAULT;
262 if (!access_ok(VERIFY_WRITE, dirent, count))
263 goto out;
265 error = -EBADF;
266 file = fget(fd);
267 if (!file)
268 goto out;
270 buf.current_dir = dirent;
271 buf.previous = NULL;
272 buf.count = count;
273 buf.error = 0;
275 error = vfs_readdir(file, filldir64, &buf);
276 if (error < 0)
277 goto out_putf;
278 error = buf.error;
279 lastdirent = buf.previous;
280 if (lastdirent) {
281 typeof(lastdirent->d_off) d_off = file->f_pos;
282 __put_user(d_off, &lastdirent->d_off);
283 error = count - buf.count;
286 out_putf:
287 fput(file);
288 out:
289 return error;