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