blob: d7308b8f6cf7625fde9dba6ee6df22f7b2c33682 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/readdir.c
3 *
4 * Copyright (C) 1995 Linus Torvalds
5 */
6
Kevin Winchester85c9fe82010-08-09 17:20:22 -07007#include <linux/stddef.h>
Milind Arun Choudhary022a1692007-05-08 00:29:02 -07008#include <linux/kernel.h>
Paul Gortmaker630d9c42011-11-16 23:57:37 -05009#include <linux/export.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <linux/time.h>
11#include <linux/mm.h>
12#include <linux/errno.h>
13#include <linux/stat.h>
14#include <linux/file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/fs.h>
Heinrich Schuchardtd4c7cf62014-06-04 16:05:41 -070016#include <linux/fsnotify.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/dirent.h>
18#include <linux/security.h>
19#include <linux/syscalls.h>
20#include <linux/unistd.h>
21
22#include <asm/uaccess.h>
23
Al Viro5c0ba4e2013-05-15 13:52:59 -040024int iterate_dir(struct file *file, struct dir_context *ctx)
Linus Torvalds1da177e2005-04-16 15:20:36 -070025{
Al Viro496ad9a2013-01-23 17:07:38 -050026 struct inode *inode = file_inode(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -070027 int res = -ENOTDIR;
Al Viro72c2d532013-09-22 16:27:52 -040028 if (!file->f_op->iterate)
Linus Torvalds1da177e2005-04-16 15:20:36 -070029 goto out;
30
31 res = security_file_permission(file, MAY_READ);
32 if (res)
33 goto out;
34
Al Viro9902af72016-04-15 15:08:36 -040035 inode_lock(inode);
36 // res = mutex_lock_killable(&inode->i_mutex);
37 // if (res)
38 // goto out;
Liam R. Howlettda784512007-12-06 17:39:54 -050039
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 res = -ENOENT;
41 if (!IS_DEADDIR(inode)) {
Al Viro2233f312013-05-22 21:44:23 -040042 ctx->pos = file->f_pos;
43 res = file->f_op->iterate(file, ctx);
44 file->f_pos = ctx->pos;
Heinrich Schuchardtd4c7cf62014-06-04 16:05:41 -070045 fsnotify_access(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 file_accessed(file);
47 }
Al Viro59551022016-01-22 15:40:57 -050048 inode_unlock(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070049out:
50 return res;
51}
Al Viro5c0ba4e2013-05-15 13:52:59 -040052EXPORT_SYMBOL(iterate_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
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 {
Al Viro5c0ba4e2013-05-15 13:52:59 -040073 struct dir_context ctx;
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 struct old_linux_dirent __user * dirent;
75 int result;
76};
77
Miklos Szerediac7576f2014-10-30 17:37:34 +010078static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
79 loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080{
Miklos Szerediac7576f2014-10-30 17:37:34 +010081 struct readdir_callback *buf =
82 container_of(ctx, struct readdir_callback, ctx);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 struct old_linux_dirent __user * dirent;
David Howellsafefdbb2006-10-03 01:13:46 -070084 unsigned long d_ino;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
86 if (buf->result)
87 return -EINVAL;
David Howellsafefdbb2006-10-03 01:13:46 -070088 d_ino = ino;
Al Viro8f3f6552008-08-12 00:28:24 -040089 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
90 buf->result = -EOVERFLOW;
David Howellsafefdbb2006-10-03 01:13:46 -070091 return -EOVERFLOW;
Al Viro8f3f6552008-08-12 00:28:24 -040092 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 buf->result++;
94 dirent = buf->dirent;
95 if (!access_ok(VERIFY_WRITE, dirent,
96 (unsigned long)(dirent->d_name + namlen + 1) -
97 (unsigned long)dirent))
98 goto efault;
David Howellsafefdbb2006-10-03 01:13:46 -070099 if ( __put_user(d_ino, &dirent->d_ino) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 __put_user(offset, &dirent->d_offset) ||
101 __put_user(namlen, &dirent->d_namlen) ||
102 __copy_to_user(dirent->d_name, name, namlen) ||
103 __put_user(0, dirent->d_name + namlen))
104 goto efault;
105 return 0;
106efault:
107 buf->result = -EFAULT;
108 return -EFAULT;
109}
110
Heiko Carstensd4e82042009-01-14 14:14:34 +0100111SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
112 struct old_linux_dirent __user *, dirent, unsigned int, count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113{
114 int error;
Al Viro63b6df12016-04-20 17:08:21 -0400115 struct fd f = fdget_pos(fd);
Al Viroac6614b2013-05-22 22:22:04 -0400116 struct readdir_callback buf = {
117 .ctx.actor = fillonedir,
118 .dirent = dirent
119 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Al Viro2903ff02012-08-28 12:52:22 -0400121 if (!f.file)
Al Viro863ced72012-04-21 18:40:32 -0400122 return -EBADF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
Al Viro5c0ba4e2013-05-15 13:52:59 -0400124 error = iterate_dir(f.file, &buf.ctx);
Al Viro53c9c5c2008-08-24 07:29:52 -0400125 if (buf.result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 error = buf.result;
127
Al Viro63b6df12016-04-20 17:08:21 -0400128 fdput_pos(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 return error;
130}
131
132#endif /* __ARCH_WANT_OLD_READDIR */
133
134/*
135 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
136 * interface.
137 */
138struct linux_dirent {
139 unsigned long d_ino;
140 unsigned long d_off;
141 unsigned short d_reclen;
142 char d_name[1];
143};
144
145struct getdents_callback {
Al Viro5c0ba4e2013-05-15 13:52:59 -0400146 struct dir_context ctx;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 struct linux_dirent __user * current_dir;
148 struct linux_dirent __user * previous;
149 int count;
150 int error;
151};
152
Miklos Szerediac7576f2014-10-30 17:37:34 +0100153static int filldir(struct dir_context *ctx, const char *name, int namlen,
154 loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155{
156 struct linux_dirent __user * dirent;
Miklos Szerediac7576f2014-10-30 17:37:34 +0100157 struct getdents_callback *buf =
158 container_of(ctx, struct getdents_callback, ctx);
David Howellsafefdbb2006-10-03 01:13:46 -0700159 unsigned long d_ino;
Kevin Winchester85c9fe82010-08-09 17:20:22 -0700160 int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
161 sizeof(long));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162
163 buf->error = -EINVAL; /* only used if we fail.. */
164 if (reclen > buf->count)
165 return -EINVAL;
David Howellsafefdbb2006-10-03 01:13:46 -0700166 d_ino = ino;
Al Viro8f3f6552008-08-12 00:28:24 -0400167 if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
168 buf->error = -EOVERFLOW;
David Howellsafefdbb2006-10-03 01:13:46 -0700169 return -EOVERFLOW;
Al Viro8f3f6552008-08-12 00:28:24 -0400170 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 dirent = buf->previous;
172 if (dirent) {
173 if (__put_user(offset, &dirent->d_off))
174 goto efault;
175 }
176 dirent = buf->current_dir;
David Howellsafefdbb2006-10-03 01:13:46 -0700177 if (__put_user(d_ino, &dirent->d_ino))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 goto efault;
179 if (__put_user(reclen, &dirent->d_reclen))
180 goto efault;
181 if (copy_to_user(dirent->d_name, name, namlen))
182 goto efault;
183 if (__put_user(0, dirent->d_name + namlen))
184 goto efault;
185 if (__put_user(d_type, (char __user *) dirent + reclen - 1))
186 goto efault;
187 buf->previous = dirent;
188 dirent = (void __user *)dirent + reclen;
189 buf->current_dir = dirent;
190 buf->count -= reclen;
191 return 0;
192efault:
193 buf->error = -EFAULT;
194 return -EFAULT;
195}
196
Heiko Carstens20f37032009-01-14 14:14:23 +0100197SYSCALL_DEFINE3(getdents, unsigned int, fd,
198 struct linux_dirent __user *, dirent, unsigned int, count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
Al Viro2903ff02012-08-28 12:52:22 -0400200 struct fd f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 struct linux_dirent __user * lastdirent;
Al Viroac6614b2013-05-22 22:22:04 -0400202 struct getdents_callback buf = {
203 .ctx.actor = filldir,
204 .count = count,
205 .current_dir = dirent
206 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 int error;
208
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 if (!access_ok(VERIFY_WRITE, dirent, count))
Al Viro863ced72012-04-21 18:40:32 -0400210 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211
Al Viro63b6df12016-04-20 17:08:21 -0400212 f = fdget_pos(fd);
Al Viro2903ff02012-08-28 12:52:22 -0400213 if (!f.file)
Al Viro863ced72012-04-21 18:40:32 -0400214 return -EBADF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
Al Viro5c0ba4e2013-05-15 13:52:59 -0400216 error = iterate_dir(f.file, &buf.ctx);
Al Viro53c9c5c2008-08-24 07:29:52 -0400217 if (error >= 0)
218 error = buf.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 lastdirent = buf.previous;
220 if (lastdirent) {
Al Virobb6f6192013-05-15 18:49:12 -0400221 if (put_user(buf.ctx.pos, &lastdirent->d_off))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 error = -EFAULT;
223 else
224 error = count - buf.count;
225 }
Al Viro63b6df12016-04-20 17:08:21 -0400226 fdput_pos(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 return error;
228}
229
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230struct getdents_callback64 {
Al Viro5c0ba4e2013-05-15 13:52:59 -0400231 struct dir_context ctx;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 struct linux_dirent64 __user * current_dir;
233 struct linux_dirent64 __user * previous;
234 int count;
235 int error;
236};
237
Miklos Szerediac7576f2014-10-30 17:37:34 +0100238static int filldir64(struct dir_context *ctx, const char *name, int namlen,
239 loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240{
241 struct linux_dirent64 __user *dirent;
Miklos Szerediac7576f2014-10-30 17:37:34 +0100242 struct getdents_callback64 *buf =
243 container_of(ctx, struct getdents_callback64, ctx);
Kevin Winchester85c9fe82010-08-09 17:20:22 -0700244 int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
245 sizeof(u64));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247 buf->error = -EINVAL; /* only used if we fail.. */
248 if (reclen > buf->count)
249 return -EINVAL;
250 dirent = buf->previous;
251 if (dirent) {
252 if (__put_user(offset, &dirent->d_off))
253 goto efault;
254 }
255 dirent = buf->current_dir;
256 if (__put_user(ino, &dirent->d_ino))
257 goto efault;
258 if (__put_user(0, &dirent->d_off))
259 goto efault;
260 if (__put_user(reclen, &dirent->d_reclen))
261 goto efault;
262 if (__put_user(d_type, &dirent->d_type))
263 goto efault;
264 if (copy_to_user(dirent->d_name, name, namlen))
265 goto efault;
266 if (__put_user(0, dirent->d_name + namlen))
267 goto efault;
268 buf->previous = dirent;
269 dirent = (void __user *)dirent + reclen;
270 buf->current_dir = dirent;
271 buf->count -= reclen;
272 return 0;
273efault:
274 buf->error = -EFAULT;
275 return -EFAULT;
276}
277
Heiko Carstens20f37032009-01-14 14:14:23 +0100278SYSCALL_DEFINE3(getdents64, unsigned int, fd,
279 struct linux_dirent64 __user *, dirent, unsigned int, count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280{
Al Viro2903ff02012-08-28 12:52:22 -0400281 struct fd f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 struct linux_dirent64 __user * lastdirent;
Al Viroac6614b2013-05-22 22:22:04 -0400283 struct getdents_callback64 buf = {
284 .ctx.actor = filldir64,
285 .count = count,
286 .current_dir = dirent
287 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 int error;
289
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 if (!access_ok(VERIFY_WRITE, dirent, count))
Al Viro863ced72012-04-21 18:40:32 -0400291 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
Al Viro63b6df12016-04-20 17:08:21 -0400293 f = fdget_pos(fd);
Al Viro2903ff02012-08-28 12:52:22 -0400294 if (!f.file)
Al Viro863ced72012-04-21 18:40:32 -0400295 return -EBADF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
Al Viro5c0ba4e2013-05-15 13:52:59 -0400297 error = iterate_dir(f.file, &buf.ctx);
Al Viro53c9c5c2008-08-24 07:29:52 -0400298 if (error >= 0)
299 error = buf.error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 lastdirent = buf.previous;
301 if (lastdirent) {
Al Virobb6f6192013-05-15 18:49:12 -0400302 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 if (__put_user(d_off, &lastdirent->d_off))
Al Viro53c9c5c2008-08-24 07:29:52 -0400304 error = -EFAULT;
305 else
306 error = count - buf.count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 }
Al Viro63b6df12016-04-20 17:08:21 -0400308 fdput_pos(f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 return error;
310}