]> git.ipfire.org Git - people/ms/linux.git/blame - fs/readdir.c
Importing "grsecurity-3.1-3.19.2-201503201903.patch"
[people/ms/linux.git] / fs / readdir.c
CommitLineData
1da177e4
LT
1/*
2 * linux/fs/readdir.c
3 *
4 * Copyright (C) 1995 Linus Torvalds
5 */
6
85c9fe8f 7#include <linux/stddef.h>
022a1692 8#include <linux/kernel.h>
630d9c47 9#include <linux/export.h>
1da177e4
LT
10#include <linux/time.h>
11#include <linux/mm.h>
12#include <linux/errno.h>
13#include <linux/stat.h>
14#include <linux/file.h>
1da177e4 15#include <linux/fs.h>
d4c7cf6c 16#include <linux/fsnotify.h>
1da177e4
LT
17#include <linux/dirent.h>
18#include <linux/security.h>
19#include <linux/syscalls.h>
20#include <linux/unistd.h>
63d9c273 21#include <linux/namei.h>
1da177e4
LT
22
23#include <asm/uaccess.h>
24
5c0ba4e0 25int iterate_dir(struct file *file, struct dir_context *ctx)
1da177e4 26{
496ad9aa 27 struct inode *inode = file_inode(file);
1da177e4 28 int res = -ENOTDIR;
72c2d531 29 if (!file->f_op->iterate)
1da177e4
LT
30 goto out;
31
32 res = security_file_permission(file, MAY_READ);
33 if (res)
34 goto out;
35
da784511
LH
36 res = mutex_lock_killable(&inode->i_mutex);
37 if (res)
38 goto out;
39
1da177e4
LT
40 res = -ENOENT;
41 if (!IS_DEADDIR(inode)) {
2233f31a
AV
42 ctx->pos = file->f_pos;
43 res = file->f_op->iterate(file, ctx);
44 file->f_pos = ctx->pos;
d4c7cf6c 45 fsnotify_access(file);
1da177e4
LT
46 file_accessed(file);
47 }
1b1dcc1b 48 mutex_unlock(&inode->i_mutex);
1da177e4
LT
49out:
50 return res;
51}
5c0ba4e0 52EXPORT_SYMBOL(iterate_dir);
1da177e4
LT
53
54/*
55 * Traditional linux readdir() handling..
56 *
57 * "count=1" is a special case, meaning that the buffer is one
58 * dirent-structure in size and that the code can't handle more
59 * anyway. Thus the special "fillonedir()" function for that
60 * case (the low-level handlers don't need to care about this).
61 */
1da177e4
LT
62
63#ifdef __ARCH_WANT_OLD_READDIR
64
65struct old_linux_dirent {
66 unsigned long d_ino;
67 unsigned long d_offset;
68 unsigned short d_namlen;
69 char d_name[1];
70};
71
72struct readdir_callback {
5c0ba4e0 73 struct dir_context ctx;
1da177e4 74 struct old_linux_dirent __user * dirent;
63d9c273 75 struct file * file;
1da177e4
LT
76 int result;
77};
78
ac7576f4
MS
79static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
80 loff_t offset, u64 ino, unsigned int d_type)
1da177e4 81{
ac7576f4
MS
82 struct readdir_callback *buf =
83 container_of(ctx, struct readdir_callback, ctx);
1da177e4 84 struct old_linux_dirent __user * dirent;
afefdbb2 85 unsigned long d_ino;
1da177e4
LT
86
87 if (buf->result)
88 return -EINVAL;
afefdbb2 89 d_ino = ino;
8f3f655d
AV
90 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
91 buf->result = -EOVERFLOW;
afefdbb2 92 return -EOVERFLOW;
8f3f655d 93 }
63d9c273
MT
94
95 if (!gr_acl_handle_filldir(buf->file, name, namlen, ino))
96 return 0;
97
1da177e4
LT
98 buf->result++;
99 dirent = buf->dirent;
100 if (!access_ok(VERIFY_WRITE, dirent,
101 (unsigned long)(dirent->d_name + namlen + 1) -
102 (unsigned long)dirent))
103 goto efault;
afefdbb2 104 if ( __put_user(d_ino, &dirent->d_ino) ||
1da177e4
LT
105 __put_user(offset, &dirent->d_offset) ||
106 __put_user(namlen, &dirent->d_namlen) ||
107 __copy_to_user(dirent->d_name, name, namlen) ||
108 __put_user(0, dirent->d_name + namlen))
109 goto efault;
110 return 0;
111efault:
112 buf->result = -EFAULT;
113 return -EFAULT;
114}
115
d4e82042
HC
116SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
117 struct old_linux_dirent __user *, dirent, unsigned int, count)
1da177e4
LT
118{
119 int error;
2903ff01 120 struct fd f = fdget(fd);
ac6614b7
AV
121 struct readdir_callback buf = {
122 .ctx.actor = fillonedir,
123 .dirent = dirent
124 };
1da177e4 125
2903ff01 126 if (!f.file)
863ced7f 127 return -EBADF;
1da177e4 128
63d9c273 129 buf.file = f.file;
5c0ba4e0 130 error = iterate_dir(f.file, &buf.ctx);
53c9c5c0 131 if (buf.result)
1da177e4
LT
132 error = buf.result;
133
2903ff01 134 fdput(f);
1da177e4
LT
135 return error;
136}
137
138#endif /* __ARCH_WANT_OLD_READDIR */
139
140/*
141 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
142 * interface.
143 */
144struct linux_dirent {
145 unsigned long d_ino;
146 unsigned long d_off;
147 unsigned short d_reclen;
148 char d_name[1];
149};
150
151struct getdents_callback {
5c0ba4e0 152 struct dir_context ctx;
1da177e4
LT
153 struct linux_dirent __user * current_dir;
154 struct linux_dirent __user * previous;
63d9c273 155 struct file * file;
1da177e4
LT
156 int count;
157 int error;
158};
159
ac7576f4
MS
160static int filldir(struct dir_context *ctx, const char *name, int namlen,
161 loff_t offset, u64 ino, unsigned int d_type)
1da177e4
LT
162{
163 struct linux_dirent __user * dirent;
ac7576f4
MS
164 struct getdents_callback *buf =
165 container_of(ctx, struct getdents_callback, ctx);
afefdbb2 166 unsigned long d_ino;
85c9fe8f
KW
167 int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
168 sizeof(long));
1da177e4
LT
169
170 buf->error = -EINVAL; /* only used if we fail.. */
171 if (reclen > buf->count)
172 return -EINVAL;
afefdbb2 173 d_ino = ino;
8f3f655d
AV
174 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
175 buf->error = -EOVERFLOW;
afefdbb2 176 return -EOVERFLOW;
8f3f655d 177 }
63d9c273
MT
178
179 if (!gr_acl_handle_filldir(buf->file, name, namlen, ino))
180 return 0;
181
1da177e4
LT
182 dirent = buf->previous;
183 if (dirent) {
184 if (__put_user(offset, &dirent->d_off))
185 goto efault;
186 }
187 dirent = buf->current_dir;
afefdbb2 188 if (__put_user(d_ino, &dirent->d_ino))
1da177e4
LT
189 goto efault;
190 if (__put_user(reclen, &dirent->d_reclen))
191 goto efault;
192 if (copy_to_user(dirent->d_name, name, namlen))
193 goto efault;
194 if (__put_user(0, dirent->d_name + namlen))
195 goto efault;
196 if (__put_user(d_type, (char __user *) dirent + reclen - 1))
197 goto efault;
198 buf->previous = dirent;
199 dirent = (void __user *)dirent + reclen;
200 buf->current_dir = dirent;
201 buf->count -= reclen;
202 return 0;
203efault:
204 buf->error = -EFAULT;
205 return -EFAULT;
206}
207
20f37034
HC
208SYSCALL_DEFINE3(getdents, unsigned int, fd,
209 struct linux_dirent __user *, dirent, unsigned int, count)
1da177e4 210{
2903ff01 211 struct fd f;
1da177e4 212 struct linux_dirent __user * lastdirent;
ac6614b7
AV
213 struct getdents_callback buf = {
214 .ctx.actor = filldir,
215 .count = count,
216 .current_dir = dirent
217 };
1da177e4
LT
218 int error;
219
1da177e4 220 if (!access_ok(VERIFY_WRITE, dirent, count))
863ced7f 221 return -EFAULT;
1da177e4 222
2903ff01
AV
223 f = fdget(fd);
224 if (!f.file)
863ced7f 225 return -EBADF;
1da177e4 226
63d9c273 227 buf.file = f.file;
5c0ba4e0 228 error = iterate_dir(f.file, &buf.ctx);
53c9c5c0
AV
229 if (error >= 0)
230 error = buf.error;
1da177e4
LT
231 lastdirent = buf.previous;
232 if (lastdirent) {
bb6f619b 233 if (put_user(buf.ctx.pos, &lastdirent->d_off))
1da177e4
LT
234 error = -EFAULT;
235 else
236 error = count - buf.count;
237 }
2903ff01 238 fdput(f);
1da177e4
LT
239 return error;
240}
241
1da177e4 242struct getdents_callback64 {
5c0ba4e0 243 struct dir_context ctx;
1da177e4
LT
244 struct linux_dirent64 __user * current_dir;
245 struct linux_dirent64 __user * previous;
63d9c273 246 struct file *file;
1da177e4
LT
247 int count;
248 int error;
249};
250
ac7576f4
MS
251static int filldir64(struct dir_context *ctx, const char *name, int namlen,
252 loff_t offset, u64 ino, unsigned int d_type)
1da177e4
LT
253{
254 struct linux_dirent64 __user *dirent;
ac7576f4
MS
255 struct getdents_callback64 *buf =
256 container_of(ctx, struct getdents_callback64, ctx);
85c9fe8f
KW
257 int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
258 sizeof(u64));
1da177e4
LT
259
260 buf->error = -EINVAL; /* only used if we fail.. */
261 if (reclen > buf->count)
262 return -EINVAL;
63d9c273
MT
263
264 if (!gr_acl_handle_filldir(buf->file, name, namlen, ino))
265 return 0;
266
1da177e4
LT
267 dirent = buf->previous;
268 if (dirent) {
269 if (__put_user(offset, &dirent->d_off))
270 goto efault;
271 }
272 dirent = buf->current_dir;
273 if (__put_user(ino, &dirent->d_ino))
274 goto efault;
275 if (__put_user(0, &dirent->d_off))
276 goto efault;
277 if (__put_user(reclen, &dirent->d_reclen))
278 goto efault;
279 if (__put_user(d_type, &dirent->d_type))
280 goto efault;
281 if (copy_to_user(dirent->d_name, name, namlen))
282 goto efault;
283 if (__put_user(0, dirent->d_name + namlen))
284 goto efault;
285 buf->previous = dirent;
286 dirent = (void __user *)dirent + reclen;
287 buf->current_dir = dirent;
288 buf->count -= reclen;
289 return 0;
290efault:
291 buf->error = -EFAULT;
292 return -EFAULT;
293}
294
20f37034
HC
295SYSCALL_DEFINE3(getdents64, unsigned int, fd,
296 struct linux_dirent64 __user *, dirent, unsigned int, count)
1da177e4 297{
2903ff01 298 struct fd f;
1da177e4 299 struct linux_dirent64 __user * lastdirent;
ac6614b7
AV
300 struct getdents_callback64 buf = {
301 .ctx.actor = filldir64,
302 .count = count,
303 .current_dir = dirent
304 };
1da177e4
LT
305 int error;
306
1da177e4 307 if (!access_ok(VERIFY_WRITE, dirent, count))
863ced7f 308 return -EFAULT;
1da177e4 309
2903ff01
AV
310 f = fdget(fd);
311 if (!f.file)
863ced7f 312 return -EBADF;
1da177e4 313
63d9c273 314 buf.file = f.file;
5c0ba4e0 315 error = iterate_dir(f.file, &buf.ctx);
53c9c5c0
AV
316 if (error >= 0)
317 error = buf.error;
1da177e4
LT
318 lastdirent = buf.previous;
319 if (lastdirent) {
bb6f619b 320 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
1da177e4 321 if (__put_user(d_off, &lastdirent->d_off))
53c9c5c0
AV
322 error = -EFAULT;
323 else
324 error = count - buf.count;
1da177e4 325 }
2903ff01 326 fdput(f);
1da177e4
LT
327 return error;
328}