1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * linux/fs/readdir.c
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 1995 Linus Torvalds
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/stddef.h>
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/export.h>
11*4882a593Smuzhiyun #include <linux/time.h>
12*4882a593Smuzhiyun #include <linux/mm.h>
13*4882a593Smuzhiyun #include <linux/errno.h>
14*4882a593Smuzhiyun #include <linux/stat.h>
15*4882a593Smuzhiyun #include <linux/file.h>
16*4882a593Smuzhiyun #include <linux/fs.h>
17*4882a593Smuzhiyun #include <linux/fsnotify.h>
18*4882a593Smuzhiyun #include <linux/dirent.h>
19*4882a593Smuzhiyun #include <linux/security.h>
20*4882a593Smuzhiyun #include <linux/syscalls.h>
21*4882a593Smuzhiyun #include <linux/unistd.h>
22*4882a593Smuzhiyun #include <linux/compat.h>
23*4882a593Smuzhiyun #include <linux/uaccess.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <asm/unaligned.h>
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun /*
28*4882a593Smuzhiyun * Note the "unsafe_put_user() semantics: we goto a
29*4882a593Smuzhiyun * label for errors.
30*4882a593Smuzhiyun */
31*4882a593Smuzhiyun #define unsafe_copy_dirent_name(_dst, _src, _len, label) do { \
32*4882a593Smuzhiyun char __user *dst = (_dst); \
33*4882a593Smuzhiyun const char *src = (_src); \
34*4882a593Smuzhiyun size_t len = (_len); \
35*4882a593Smuzhiyun unsafe_put_user(0, dst+len, label); \
36*4882a593Smuzhiyun unsafe_copy_to_user(dst, src, len, label); \
37*4882a593Smuzhiyun } while (0)
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun
iterate_dir(struct file * file,struct dir_context * ctx)40*4882a593Smuzhiyun int iterate_dir(struct file *file, struct dir_context *ctx)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun struct inode *inode = file_inode(file);
43*4882a593Smuzhiyun bool shared = false;
44*4882a593Smuzhiyun int res = -ENOTDIR;
45*4882a593Smuzhiyun if (file->f_op->iterate_shared)
46*4882a593Smuzhiyun shared = true;
47*4882a593Smuzhiyun else if (!file->f_op->iterate)
48*4882a593Smuzhiyun goto out;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun res = security_file_permission(file, MAY_READ);
51*4882a593Smuzhiyun if (res)
52*4882a593Smuzhiyun goto out;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun if (shared)
55*4882a593Smuzhiyun res = down_read_killable(&inode->i_rwsem);
56*4882a593Smuzhiyun else
57*4882a593Smuzhiyun res = down_write_killable(&inode->i_rwsem);
58*4882a593Smuzhiyun if (res)
59*4882a593Smuzhiyun goto out;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun res = -ENOENT;
62*4882a593Smuzhiyun if (!IS_DEADDIR(inode)) {
63*4882a593Smuzhiyun ctx->pos = file->f_pos;
64*4882a593Smuzhiyun if (shared)
65*4882a593Smuzhiyun res = file->f_op->iterate_shared(file, ctx);
66*4882a593Smuzhiyun else
67*4882a593Smuzhiyun res = file->f_op->iterate(file, ctx);
68*4882a593Smuzhiyun file->f_pos = ctx->pos;
69*4882a593Smuzhiyun fsnotify_access(file);
70*4882a593Smuzhiyun file_accessed(file);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun if (shared)
73*4882a593Smuzhiyun inode_unlock_shared(inode);
74*4882a593Smuzhiyun else
75*4882a593Smuzhiyun inode_unlock(inode);
76*4882a593Smuzhiyun out:
77*4882a593Smuzhiyun return res;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun EXPORT_SYMBOL(iterate_dir);
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun /*
82*4882a593Smuzhiyun * POSIX says that a dirent name cannot contain NULL or a '/'.
83*4882a593Smuzhiyun *
84*4882a593Smuzhiyun * It's not 100% clear what we should really do in this case.
85*4882a593Smuzhiyun * The filesystem is clearly corrupted, but returning a hard
86*4882a593Smuzhiyun * error means that you now don't see any of the other names
87*4882a593Smuzhiyun * either, so that isn't a perfect alternative.
88*4882a593Smuzhiyun *
89*4882a593Smuzhiyun * And if you return an error, what error do you use? Several
90*4882a593Smuzhiyun * filesystems seem to have decided on EUCLEAN being the error
91*4882a593Smuzhiyun * code for EFSCORRUPTED, and that may be the error to use. Or
92*4882a593Smuzhiyun * just EIO, which is perhaps more obvious to users.
93*4882a593Smuzhiyun *
94*4882a593Smuzhiyun * In order to see the other file names in the directory, the
95*4882a593Smuzhiyun * caller might want to make this a "soft" error: skip the
96*4882a593Smuzhiyun * entry, and return the error at the end instead.
97*4882a593Smuzhiyun *
98*4882a593Smuzhiyun * Note that this should likely do a "memchr(name, 0, len)"
99*4882a593Smuzhiyun * check too, since that would be filesystem corruption as
100*4882a593Smuzhiyun * well. However, that case can't actually confuse user space,
101*4882a593Smuzhiyun * which has to do a strlen() on the name anyway to find the
102*4882a593Smuzhiyun * filename length, and the above "soft error" worry means
103*4882a593Smuzhiyun * that it's probably better left alone until we have that
104*4882a593Smuzhiyun * issue clarified.
105*4882a593Smuzhiyun *
106*4882a593Smuzhiyun * Note the PATH_MAX check - it's arbitrary but the real
107*4882a593Smuzhiyun * kernel limit on a possible path component, not NAME_MAX,
108*4882a593Smuzhiyun * which is the technical standard limit.
109*4882a593Smuzhiyun */
verify_dirent_name(const char * name,int len)110*4882a593Smuzhiyun static int verify_dirent_name(const char *name, int len)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun if (len <= 0 || len >= PATH_MAX)
113*4882a593Smuzhiyun return -EIO;
114*4882a593Smuzhiyun if (memchr(name, '/', len))
115*4882a593Smuzhiyun return -EIO;
116*4882a593Smuzhiyun return 0;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun /*
120*4882a593Smuzhiyun * Traditional linux readdir() handling..
121*4882a593Smuzhiyun *
122*4882a593Smuzhiyun * "count=1" is a special case, meaning that the buffer is one
123*4882a593Smuzhiyun * dirent-structure in size and that the code can't handle more
124*4882a593Smuzhiyun * anyway. Thus the special "fillonedir()" function for that
125*4882a593Smuzhiyun * case (the low-level handlers don't need to care about this).
126*4882a593Smuzhiyun */
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun #ifdef __ARCH_WANT_OLD_READDIR
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun struct old_linux_dirent {
131*4882a593Smuzhiyun unsigned long d_ino;
132*4882a593Smuzhiyun unsigned long d_offset;
133*4882a593Smuzhiyun unsigned short d_namlen;
134*4882a593Smuzhiyun char d_name[1];
135*4882a593Smuzhiyun };
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun struct readdir_callback {
138*4882a593Smuzhiyun struct dir_context ctx;
139*4882a593Smuzhiyun struct old_linux_dirent __user * dirent;
140*4882a593Smuzhiyun int result;
141*4882a593Smuzhiyun };
142*4882a593Smuzhiyun
fillonedir(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)143*4882a593Smuzhiyun static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
144*4882a593Smuzhiyun loff_t offset, u64 ino, unsigned int d_type)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun struct readdir_callback *buf =
147*4882a593Smuzhiyun container_of(ctx, struct readdir_callback, ctx);
148*4882a593Smuzhiyun struct old_linux_dirent __user * dirent;
149*4882a593Smuzhiyun unsigned long d_ino;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun if (buf->result)
152*4882a593Smuzhiyun return -EINVAL;
153*4882a593Smuzhiyun buf->result = verify_dirent_name(name, namlen);
154*4882a593Smuzhiyun if (buf->result < 0)
155*4882a593Smuzhiyun return buf->result;
156*4882a593Smuzhiyun d_ino = ino;
157*4882a593Smuzhiyun if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
158*4882a593Smuzhiyun buf->result = -EOVERFLOW;
159*4882a593Smuzhiyun return -EOVERFLOW;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun buf->result++;
162*4882a593Smuzhiyun dirent = buf->dirent;
163*4882a593Smuzhiyun if (!user_write_access_begin(dirent,
164*4882a593Smuzhiyun (unsigned long)(dirent->d_name + namlen + 1) -
165*4882a593Smuzhiyun (unsigned long)dirent))
166*4882a593Smuzhiyun goto efault;
167*4882a593Smuzhiyun unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
168*4882a593Smuzhiyun unsafe_put_user(offset, &dirent->d_offset, efault_end);
169*4882a593Smuzhiyun unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
170*4882a593Smuzhiyun unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
171*4882a593Smuzhiyun user_write_access_end();
172*4882a593Smuzhiyun return 0;
173*4882a593Smuzhiyun efault_end:
174*4882a593Smuzhiyun user_write_access_end();
175*4882a593Smuzhiyun efault:
176*4882a593Smuzhiyun buf->result = -EFAULT;
177*4882a593Smuzhiyun return -EFAULT;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun
SYSCALL_DEFINE3(old_readdir,unsigned int,fd,struct old_linux_dirent __user *,dirent,unsigned int,count)180*4882a593Smuzhiyun SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
181*4882a593Smuzhiyun struct old_linux_dirent __user *, dirent, unsigned int, count)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun int error;
184*4882a593Smuzhiyun struct fd f = fdget_pos(fd);
185*4882a593Smuzhiyun struct readdir_callback buf = {
186*4882a593Smuzhiyun .ctx.actor = fillonedir,
187*4882a593Smuzhiyun .dirent = dirent
188*4882a593Smuzhiyun };
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun if (!f.file)
191*4882a593Smuzhiyun return -EBADF;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun error = iterate_dir(f.file, &buf.ctx);
194*4882a593Smuzhiyun if (buf.result)
195*4882a593Smuzhiyun error = buf.result;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun fdput_pos(f);
198*4882a593Smuzhiyun return error;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun #endif /* __ARCH_WANT_OLD_READDIR */
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun /*
204*4882a593Smuzhiyun * New, all-improved, singing, dancing, iBCS2-compliant getdents()
205*4882a593Smuzhiyun * interface.
206*4882a593Smuzhiyun */
207*4882a593Smuzhiyun struct linux_dirent {
208*4882a593Smuzhiyun unsigned long d_ino;
209*4882a593Smuzhiyun unsigned long d_off;
210*4882a593Smuzhiyun unsigned short d_reclen;
211*4882a593Smuzhiyun char d_name[1];
212*4882a593Smuzhiyun };
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun struct getdents_callback {
215*4882a593Smuzhiyun struct dir_context ctx;
216*4882a593Smuzhiyun struct linux_dirent __user * current_dir;
217*4882a593Smuzhiyun int prev_reclen;
218*4882a593Smuzhiyun int count;
219*4882a593Smuzhiyun int error;
220*4882a593Smuzhiyun };
221*4882a593Smuzhiyun
filldir(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)222*4882a593Smuzhiyun static int filldir(struct dir_context *ctx, const char *name, int namlen,
223*4882a593Smuzhiyun loff_t offset, u64 ino, unsigned int d_type)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun struct linux_dirent __user *dirent, *prev;
226*4882a593Smuzhiyun struct getdents_callback *buf =
227*4882a593Smuzhiyun container_of(ctx, struct getdents_callback, ctx);
228*4882a593Smuzhiyun unsigned long d_ino;
229*4882a593Smuzhiyun int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
230*4882a593Smuzhiyun sizeof(long));
231*4882a593Smuzhiyun int prev_reclen;
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun buf->error = verify_dirent_name(name, namlen);
234*4882a593Smuzhiyun if (unlikely(buf->error))
235*4882a593Smuzhiyun return buf->error;
236*4882a593Smuzhiyun buf->error = -EINVAL; /* only used if we fail.. */
237*4882a593Smuzhiyun if (reclen > buf->count)
238*4882a593Smuzhiyun return -EINVAL;
239*4882a593Smuzhiyun d_ino = ino;
240*4882a593Smuzhiyun if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
241*4882a593Smuzhiyun buf->error = -EOVERFLOW;
242*4882a593Smuzhiyun return -EOVERFLOW;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun prev_reclen = buf->prev_reclen;
245*4882a593Smuzhiyun if (prev_reclen && signal_pending(current))
246*4882a593Smuzhiyun return -EINTR;
247*4882a593Smuzhiyun dirent = buf->current_dir;
248*4882a593Smuzhiyun prev = (void __user *) dirent - prev_reclen;
249*4882a593Smuzhiyun if (!user_write_access_begin(prev, reclen + prev_reclen))
250*4882a593Smuzhiyun goto efault;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun /* This might be 'dirent->d_off', but if so it will get overwritten */
253*4882a593Smuzhiyun unsafe_put_user(offset, &prev->d_off, efault_end);
254*4882a593Smuzhiyun unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
255*4882a593Smuzhiyun unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
256*4882a593Smuzhiyun unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
257*4882a593Smuzhiyun unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
258*4882a593Smuzhiyun user_write_access_end();
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun buf->current_dir = (void __user *)dirent + reclen;
261*4882a593Smuzhiyun buf->prev_reclen = reclen;
262*4882a593Smuzhiyun buf->count -= reclen;
263*4882a593Smuzhiyun return 0;
264*4882a593Smuzhiyun efault_end:
265*4882a593Smuzhiyun user_write_access_end();
266*4882a593Smuzhiyun efault:
267*4882a593Smuzhiyun buf->error = -EFAULT;
268*4882a593Smuzhiyun return -EFAULT;
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun
SYSCALL_DEFINE3(getdents,unsigned int,fd,struct linux_dirent __user *,dirent,unsigned int,count)271*4882a593Smuzhiyun SYSCALL_DEFINE3(getdents, unsigned int, fd,
272*4882a593Smuzhiyun struct linux_dirent __user *, dirent, unsigned int, count)
273*4882a593Smuzhiyun {
274*4882a593Smuzhiyun struct fd f;
275*4882a593Smuzhiyun struct getdents_callback buf = {
276*4882a593Smuzhiyun .ctx.actor = filldir,
277*4882a593Smuzhiyun .count = count,
278*4882a593Smuzhiyun .current_dir = dirent
279*4882a593Smuzhiyun };
280*4882a593Smuzhiyun int error;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun f = fdget_pos(fd);
283*4882a593Smuzhiyun if (!f.file)
284*4882a593Smuzhiyun return -EBADF;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun error = iterate_dir(f.file, &buf.ctx);
287*4882a593Smuzhiyun if (error >= 0)
288*4882a593Smuzhiyun error = buf.error;
289*4882a593Smuzhiyun if (buf.prev_reclen) {
290*4882a593Smuzhiyun struct linux_dirent __user * lastdirent;
291*4882a593Smuzhiyun lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun if (put_user(buf.ctx.pos, &lastdirent->d_off))
294*4882a593Smuzhiyun error = -EFAULT;
295*4882a593Smuzhiyun else
296*4882a593Smuzhiyun error = count - buf.count;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun fdput_pos(f);
299*4882a593Smuzhiyun return error;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun struct getdents_callback64 {
303*4882a593Smuzhiyun struct dir_context ctx;
304*4882a593Smuzhiyun struct linux_dirent64 __user * current_dir;
305*4882a593Smuzhiyun int prev_reclen;
306*4882a593Smuzhiyun int count;
307*4882a593Smuzhiyun int error;
308*4882a593Smuzhiyun };
309*4882a593Smuzhiyun
filldir64(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)310*4882a593Smuzhiyun static int filldir64(struct dir_context *ctx, const char *name, int namlen,
311*4882a593Smuzhiyun loff_t offset, u64 ino, unsigned int d_type)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun struct linux_dirent64 __user *dirent, *prev;
314*4882a593Smuzhiyun struct getdents_callback64 *buf =
315*4882a593Smuzhiyun container_of(ctx, struct getdents_callback64, ctx);
316*4882a593Smuzhiyun int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
317*4882a593Smuzhiyun sizeof(u64));
318*4882a593Smuzhiyun int prev_reclen;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun buf->error = verify_dirent_name(name, namlen);
321*4882a593Smuzhiyun if (unlikely(buf->error))
322*4882a593Smuzhiyun return buf->error;
323*4882a593Smuzhiyun buf->error = -EINVAL; /* only used if we fail.. */
324*4882a593Smuzhiyun if (reclen > buf->count)
325*4882a593Smuzhiyun return -EINVAL;
326*4882a593Smuzhiyun prev_reclen = buf->prev_reclen;
327*4882a593Smuzhiyun if (prev_reclen && signal_pending(current))
328*4882a593Smuzhiyun return -EINTR;
329*4882a593Smuzhiyun dirent = buf->current_dir;
330*4882a593Smuzhiyun prev = (void __user *)dirent - prev_reclen;
331*4882a593Smuzhiyun if (!user_write_access_begin(prev, reclen + prev_reclen))
332*4882a593Smuzhiyun goto efault;
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun /* This might be 'dirent->d_off', but if so it will get overwritten */
335*4882a593Smuzhiyun unsafe_put_user(offset, &prev->d_off, efault_end);
336*4882a593Smuzhiyun unsafe_put_user(ino, &dirent->d_ino, efault_end);
337*4882a593Smuzhiyun unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
338*4882a593Smuzhiyun unsafe_put_user(d_type, &dirent->d_type, efault_end);
339*4882a593Smuzhiyun unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
340*4882a593Smuzhiyun user_write_access_end();
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun buf->prev_reclen = reclen;
343*4882a593Smuzhiyun buf->current_dir = (void __user *)dirent + reclen;
344*4882a593Smuzhiyun buf->count -= reclen;
345*4882a593Smuzhiyun return 0;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun efault_end:
348*4882a593Smuzhiyun user_write_access_end();
349*4882a593Smuzhiyun efault:
350*4882a593Smuzhiyun buf->error = -EFAULT;
351*4882a593Smuzhiyun return -EFAULT;
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun
SYSCALL_DEFINE3(getdents64,unsigned int,fd,struct linux_dirent64 __user *,dirent,unsigned int,count)354*4882a593Smuzhiyun SYSCALL_DEFINE3(getdents64, unsigned int, fd,
355*4882a593Smuzhiyun struct linux_dirent64 __user *, dirent, unsigned int, count)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun struct fd f;
358*4882a593Smuzhiyun struct getdents_callback64 buf = {
359*4882a593Smuzhiyun .ctx.actor = filldir64,
360*4882a593Smuzhiyun .count = count,
361*4882a593Smuzhiyun .current_dir = dirent
362*4882a593Smuzhiyun };
363*4882a593Smuzhiyun int error;
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun f = fdget_pos(fd);
366*4882a593Smuzhiyun if (!f.file)
367*4882a593Smuzhiyun return -EBADF;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun error = iterate_dir(f.file, &buf.ctx);
370*4882a593Smuzhiyun if (error >= 0)
371*4882a593Smuzhiyun error = buf.error;
372*4882a593Smuzhiyun if (buf.prev_reclen) {
373*4882a593Smuzhiyun struct linux_dirent64 __user * lastdirent;
374*4882a593Smuzhiyun typeof(lastdirent->d_off) d_off = buf.ctx.pos;
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun lastdirent = (void __user *) buf.current_dir - buf.prev_reclen;
377*4882a593Smuzhiyun if (put_user(d_off, &lastdirent->d_off))
378*4882a593Smuzhiyun error = -EFAULT;
379*4882a593Smuzhiyun else
380*4882a593Smuzhiyun error = count - buf.count;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun fdput_pos(f);
383*4882a593Smuzhiyun return error;
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun #ifdef CONFIG_COMPAT
387*4882a593Smuzhiyun struct compat_old_linux_dirent {
388*4882a593Smuzhiyun compat_ulong_t d_ino;
389*4882a593Smuzhiyun compat_ulong_t d_offset;
390*4882a593Smuzhiyun unsigned short d_namlen;
391*4882a593Smuzhiyun char d_name[1];
392*4882a593Smuzhiyun };
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun struct compat_readdir_callback {
395*4882a593Smuzhiyun struct dir_context ctx;
396*4882a593Smuzhiyun struct compat_old_linux_dirent __user *dirent;
397*4882a593Smuzhiyun int result;
398*4882a593Smuzhiyun };
399*4882a593Smuzhiyun
compat_fillonedir(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)400*4882a593Smuzhiyun static int compat_fillonedir(struct dir_context *ctx, const char *name,
401*4882a593Smuzhiyun int namlen, loff_t offset, u64 ino,
402*4882a593Smuzhiyun unsigned int d_type)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun struct compat_readdir_callback *buf =
405*4882a593Smuzhiyun container_of(ctx, struct compat_readdir_callback, ctx);
406*4882a593Smuzhiyun struct compat_old_linux_dirent __user *dirent;
407*4882a593Smuzhiyun compat_ulong_t d_ino;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun if (buf->result)
410*4882a593Smuzhiyun return -EINVAL;
411*4882a593Smuzhiyun buf->result = verify_dirent_name(name, namlen);
412*4882a593Smuzhiyun if (buf->result < 0)
413*4882a593Smuzhiyun return buf->result;
414*4882a593Smuzhiyun d_ino = ino;
415*4882a593Smuzhiyun if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
416*4882a593Smuzhiyun buf->result = -EOVERFLOW;
417*4882a593Smuzhiyun return -EOVERFLOW;
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun buf->result++;
420*4882a593Smuzhiyun dirent = buf->dirent;
421*4882a593Smuzhiyun if (!user_write_access_begin(dirent,
422*4882a593Smuzhiyun (unsigned long)(dirent->d_name + namlen + 1) -
423*4882a593Smuzhiyun (unsigned long)dirent))
424*4882a593Smuzhiyun goto efault;
425*4882a593Smuzhiyun unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
426*4882a593Smuzhiyun unsafe_put_user(offset, &dirent->d_offset, efault_end);
427*4882a593Smuzhiyun unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
428*4882a593Smuzhiyun unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
429*4882a593Smuzhiyun user_write_access_end();
430*4882a593Smuzhiyun return 0;
431*4882a593Smuzhiyun efault_end:
432*4882a593Smuzhiyun user_write_access_end();
433*4882a593Smuzhiyun efault:
434*4882a593Smuzhiyun buf->result = -EFAULT;
435*4882a593Smuzhiyun return -EFAULT;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun
COMPAT_SYSCALL_DEFINE3(old_readdir,unsigned int,fd,struct compat_old_linux_dirent __user *,dirent,unsigned int,count)438*4882a593Smuzhiyun COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
439*4882a593Smuzhiyun struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun int error;
442*4882a593Smuzhiyun struct fd f = fdget_pos(fd);
443*4882a593Smuzhiyun struct compat_readdir_callback buf = {
444*4882a593Smuzhiyun .ctx.actor = compat_fillonedir,
445*4882a593Smuzhiyun .dirent = dirent
446*4882a593Smuzhiyun };
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun if (!f.file)
449*4882a593Smuzhiyun return -EBADF;
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun error = iterate_dir(f.file, &buf.ctx);
452*4882a593Smuzhiyun if (buf.result)
453*4882a593Smuzhiyun error = buf.result;
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun fdput_pos(f);
456*4882a593Smuzhiyun return error;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun struct compat_linux_dirent {
460*4882a593Smuzhiyun compat_ulong_t d_ino;
461*4882a593Smuzhiyun compat_ulong_t d_off;
462*4882a593Smuzhiyun unsigned short d_reclen;
463*4882a593Smuzhiyun char d_name[1];
464*4882a593Smuzhiyun };
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun struct compat_getdents_callback {
467*4882a593Smuzhiyun struct dir_context ctx;
468*4882a593Smuzhiyun struct compat_linux_dirent __user *current_dir;
469*4882a593Smuzhiyun int prev_reclen;
470*4882a593Smuzhiyun int count;
471*4882a593Smuzhiyun int error;
472*4882a593Smuzhiyun };
473*4882a593Smuzhiyun
compat_filldir(struct dir_context * ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)474*4882a593Smuzhiyun static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
475*4882a593Smuzhiyun loff_t offset, u64 ino, unsigned int d_type)
476*4882a593Smuzhiyun {
477*4882a593Smuzhiyun struct compat_linux_dirent __user *dirent, *prev;
478*4882a593Smuzhiyun struct compat_getdents_callback *buf =
479*4882a593Smuzhiyun container_of(ctx, struct compat_getdents_callback, ctx);
480*4882a593Smuzhiyun compat_ulong_t d_ino;
481*4882a593Smuzhiyun int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
482*4882a593Smuzhiyun namlen + 2, sizeof(compat_long_t));
483*4882a593Smuzhiyun int prev_reclen;
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun buf->error = verify_dirent_name(name, namlen);
486*4882a593Smuzhiyun if (unlikely(buf->error))
487*4882a593Smuzhiyun return buf->error;
488*4882a593Smuzhiyun buf->error = -EINVAL; /* only used if we fail.. */
489*4882a593Smuzhiyun if (reclen > buf->count)
490*4882a593Smuzhiyun return -EINVAL;
491*4882a593Smuzhiyun d_ino = ino;
492*4882a593Smuzhiyun if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
493*4882a593Smuzhiyun buf->error = -EOVERFLOW;
494*4882a593Smuzhiyun return -EOVERFLOW;
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun prev_reclen = buf->prev_reclen;
497*4882a593Smuzhiyun if (prev_reclen && signal_pending(current))
498*4882a593Smuzhiyun return -EINTR;
499*4882a593Smuzhiyun dirent = buf->current_dir;
500*4882a593Smuzhiyun prev = (void __user *) dirent - prev_reclen;
501*4882a593Smuzhiyun if (!user_write_access_begin(prev, reclen + prev_reclen))
502*4882a593Smuzhiyun goto efault;
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun unsafe_put_user(offset, &prev->d_off, efault_end);
505*4882a593Smuzhiyun unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
506*4882a593Smuzhiyun unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
507*4882a593Smuzhiyun unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
508*4882a593Smuzhiyun unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
509*4882a593Smuzhiyun user_write_access_end();
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun buf->prev_reclen = reclen;
512*4882a593Smuzhiyun buf->current_dir = (void __user *)dirent + reclen;
513*4882a593Smuzhiyun buf->count -= reclen;
514*4882a593Smuzhiyun return 0;
515*4882a593Smuzhiyun efault_end:
516*4882a593Smuzhiyun user_write_access_end();
517*4882a593Smuzhiyun efault:
518*4882a593Smuzhiyun buf->error = -EFAULT;
519*4882a593Smuzhiyun return -EFAULT;
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun
COMPAT_SYSCALL_DEFINE3(getdents,unsigned int,fd,struct compat_linux_dirent __user *,dirent,unsigned int,count)522*4882a593Smuzhiyun COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
523*4882a593Smuzhiyun struct compat_linux_dirent __user *, dirent, unsigned int, count)
524*4882a593Smuzhiyun {
525*4882a593Smuzhiyun struct fd f;
526*4882a593Smuzhiyun struct compat_getdents_callback buf = {
527*4882a593Smuzhiyun .ctx.actor = compat_filldir,
528*4882a593Smuzhiyun .current_dir = dirent,
529*4882a593Smuzhiyun .count = count
530*4882a593Smuzhiyun };
531*4882a593Smuzhiyun int error;
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun f = fdget_pos(fd);
534*4882a593Smuzhiyun if (!f.file)
535*4882a593Smuzhiyun return -EBADF;
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun error = iterate_dir(f.file, &buf.ctx);
538*4882a593Smuzhiyun if (error >= 0)
539*4882a593Smuzhiyun error = buf.error;
540*4882a593Smuzhiyun if (buf.prev_reclen) {
541*4882a593Smuzhiyun struct compat_linux_dirent __user * lastdirent;
542*4882a593Smuzhiyun lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun if (put_user(buf.ctx.pos, &lastdirent->d_off))
545*4882a593Smuzhiyun error = -EFAULT;
546*4882a593Smuzhiyun else
547*4882a593Smuzhiyun error = count - buf.count;
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun fdput_pos(f);
550*4882a593Smuzhiyun return error;
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun #endif
553