xref: /openbmc/linux/fs/readdir.c (revision 6f52b16c5b29b89d92c0e7236f4655dc8491ad70)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  linux/fs/readdir.c
4  *
5  *  Copyright (C) 1995  Linus Torvalds
6  */
7 
8 #include <linux/stddef.h>
9 #include <linux/kernel.h>
10 #include <linux/export.h>
11 #include <linux/time.h>
12 #include <linux/mm.h>
13 #include <linux/errno.h>
14 #include <linux/stat.h>
15 #include <linux/file.h>
16 #include <linux/fs.h>
17 #include <linux/fsnotify.h>
18 #include <linux/dirent.h>
19 #include <linux/security.h>
20 #include <linux/syscalls.h>
21 #include <linux/unistd.h>
22 #include <linux/compat.h>
23 
24 #include <linux/uaccess.h>
25 
26 int iterate_dir(struct file *file, struct dir_context *ctx)
27 {
28 	struct inode *inode = file_inode(file);
29 	bool shared = false;
30 	int res = -ENOTDIR;
31 	if (file->f_op->iterate_shared)
32 		shared = true;
33 	else if (!file->f_op->iterate)
34 		goto out;
35 
36 	res = security_file_permission(file, MAY_READ);
37 	if (res)
38 		goto out;
39 
40 	if (shared) {
41 		inode_lock_shared(inode);
42 	} else {
43 		res = down_write_killable(&inode->i_rwsem);
44 		if (res)
45 			goto out;
46 	}
47 
48 	res = -ENOENT;
49 	if (!IS_DEADDIR(inode)) {
50 		ctx->pos = file->f_pos;
51 		if (shared)
52 			res = file->f_op->iterate_shared(file, ctx);
53 		else
54 			res = file->f_op->iterate(file, ctx);
55 		file->f_pos = ctx->pos;
56 		fsnotify_access(file);
57 		file_accessed(file);
58 	}
59 	if (shared)
60 		inode_unlock_shared(inode);
61 	else
62 		inode_unlock(inode);
63 out:
64 	return res;
65 }
66 EXPORT_SYMBOL(iterate_dir);
67 
68 /*
69  * Traditional linux readdir() handling..
70  *
71  * "count=1" is a special case, meaning that the buffer is one
72  * dirent-structure in size and that the code can't handle more
73  * anyway. Thus the special "fillonedir()" function for that
74  * case (the low-level handlers don't need to care about this).
75  */
76 
77 #ifdef __ARCH_WANT_OLD_READDIR
78 
79 struct old_linux_dirent {
80 	unsigned long	d_ino;
81 	unsigned long	d_offset;
82 	unsigned short	d_namlen;
83 	char		d_name[1];
84 };
85 
86 struct readdir_callback {
87 	struct dir_context ctx;
88 	struct old_linux_dirent __user * dirent;
89 	int result;
90 };
91 
92 static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
93 		      loff_t offset, u64 ino, unsigned int d_type)
94 {
95 	struct readdir_callback *buf =
96 		container_of(ctx, struct readdir_callback, ctx);
97 	struct old_linux_dirent __user * dirent;
98 	unsigned long d_ino;
99 
100 	if (buf->result)
101 		return -EINVAL;
102 	d_ino = ino;
103 	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
104 		buf->result = -EOVERFLOW;
105 		return -EOVERFLOW;
106 	}
107 	buf->result++;
108 	dirent = buf->dirent;
109 	if (!access_ok(VERIFY_WRITE, dirent,
110 			(unsigned long)(dirent->d_name + namlen + 1) -
111 				(unsigned long)dirent))
112 		goto efault;
113 	if (	__put_user(d_ino, &dirent->d_ino) ||
114 		__put_user(offset, &dirent->d_offset) ||
115 		__put_user(namlen, &dirent->d_namlen) ||
116 		__copy_to_user(dirent->d_name, name, namlen) ||
117 		__put_user(0, dirent->d_name + namlen))
118 		goto efault;
119 	return 0;
120 efault:
121 	buf->result = -EFAULT;
122 	return -EFAULT;
123 }
124 
125 SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
126 		struct old_linux_dirent __user *, dirent, unsigned int, count)
127 {
128 	int error;
129 	struct fd f = fdget_pos(fd);
130 	struct readdir_callback buf = {
131 		.ctx.actor = fillonedir,
132 		.dirent = dirent
133 	};
134 
135 	if (!f.file)
136 		return -EBADF;
137 
138 	error = iterate_dir(f.file, &buf.ctx);
139 	if (buf.result)
140 		error = buf.result;
141 
142 	fdput_pos(f);
143 	return error;
144 }
145 
146 #endif /* __ARCH_WANT_OLD_READDIR */
147 
148 /*
149  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
150  * interface.
151  */
152 struct linux_dirent {
153 	unsigned long	d_ino;
154 	unsigned long	d_off;
155 	unsigned short	d_reclen;
156 	char		d_name[1];
157 };
158 
159 struct getdents_callback {
160 	struct dir_context ctx;
161 	struct linux_dirent __user * current_dir;
162 	struct linux_dirent __user * previous;
163 	int count;
164 	int error;
165 };
166 
167 static int filldir(struct dir_context *ctx, const char *name, int namlen,
168 		   loff_t offset, u64 ino, unsigned int d_type)
169 {
170 	struct linux_dirent __user * dirent;
171 	struct getdents_callback *buf =
172 		container_of(ctx, struct getdents_callback, ctx);
173 	unsigned long d_ino;
174 	int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
175 		sizeof(long));
176 
177 	buf->error = -EINVAL;	/* only used if we fail.. */
178 	if (reclen > buf->count)
179 		return -EINVAL;
180 	d_ino = ino;
181 	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
182 		buf->error = -EOVERFLOW;
183 		return -EOVERFLOW;
184 	}
185 	dirent = buf->previous;
186 	if (dirent) {
187 		if (signal_pending(current))
188 			return -EINTR;
189 		if (__put_user(offset, &dirent->d_off))
190 			goto efault;
191 	}
192 	dirent = buf->current_dir;
193 	if (__put_user(d_ino, &dirent->d_ino))
194 		goto efault;
195 	if (__put_user(reclen, &dirent->d_reclen))
196 		goto efault;
197 	if (copy_to_user(dirent->d_name, name, namlen))
198 		goto efault;
199 	if (__put_user(0, dirent->d_name + namlen))
200 		goto efault;
201 	if (__put_user(d_type, (char __user *) dirent + reclen - 1))
202 		goto efault;
203 	buf->previous = dirent;
204 	dirent = (void __user *)dirent + reclen;
205 	buf->current_dir = dirent;
206 	buf->count -= reclen;
207 	return 0;
208 efault:
209 	buf->error = -EFAULT;
210 	return -EFAULT;
211 }
212 
213 SYSCALL_DEFINE3(getdents, unsigned int, fd,
214 		struct linux_dirent __user *, dirent, unsigned int, count)
215 {
216 	struct fd f;
217 	struct linux_dirent __user * lastdirent;
218 	struct getdents_callback buf = {
219 		.ctx.actor = filldir,
220 		.count = count,
221 		.current_dir = dirent
222 	};
223 	int error;
224 
225 	if (!access_ok(VERIFY_WRITE, dirent, count))
226 		return -EFAULT;
227 
228 	f = fdget_pos(fd);
229 	if (!f.file)
230 		return -EBADF;
231 
232 	error = iterate_dir(f.file, &buf.ctx);
233 	if (error >= 0)
234 		error = buf.error;
235 	lastdirent = buf.previous;
236 	if (lastdirent) {
237 		if (put_user(buf.ctx.pos, &lastdirent->d_off))
238 			error = -EFAULT;
239 		else
240 			error = count - buf.count;
241 	}
242 	fdput_pos(f);
243 	return error;
244 }
245 
246 struct getdents_callback64 {
247 	struct dir_context ctx;
248 	struct linux_dirent64 __user * current_dir;
249 	struct linux_dirent64 __user * previous;
250 	int count;
251 	int error;
252 };
253 
254 static int filldir64(struct dir_context *ctx, const char *name, int namlen,
255 		     loff_t offset, u64 ino, unsigned int d_type)
256 {
257 	struct linux_dirent64 __user *dirent;
258 	struct getdents_callback64 *buf =
259 		container_of(ctx, struct getdents_callback64, ctx);
260 	int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
261 		sizeof(u64));
262 
263 	buf->error = -EINVAL;	/* only used if we fail.. */
264 	if (reclen > buf->count)
265 		return -EINVAL;
266 	dirent = buf->previous;
267 	if (dirent) {
268 		if (signal_pending(current))
269 			return -EINTR;
270 		if (__put_user(offset, &dirent->d_off))
271 			goto efault;
272 	}
273 	dirent = buf->current_dir;
274 	if (__put_user(ino, &dirent->d_ino))
275 		goto efault;
276 	if (__put_user(0, &dirent->d_off))
277 		goto efault;
278 	if (__put_user(reclen, &dirent->d_reclen))
279 		goto efault;
280 	if (__put_user(d_type, &dirent->d_type))
281 		goto efault;
282 	if (copy_to_user(dirent->d_name, name, namlen))
283 		goto efault;
284 	if (__put_user(0, dirent->d_name + namlen))
285 		goto efault;
286 	buf->previous = dirent;
287 	dirent = (void __user *)dirent + reclen;
288 	buf->current_dir = dirent;
289 	buf->count -= reclen;
290 	return 0;
291 efault:
292 	buf->error = -EFAULT;
293 	return -EFAULT;
294 }
295 
296 SYSCALL_DEFINE3(getdents64, unsigned int, fd,
297 		struct linux_dirent64 __user *, dirent, unsigned int, count)
298 {
299 	struct fd f;
300 	struct linux_dirent64 __user * lastdirent;
301 	struct getdents_callback64 buf = {
302 		.ctx.actor = filldir64,
303 		.count = count,
304 		.current_dir = dirent
305 	};
306 	int error;
307 
308 	if (!access_ok(VERIFY_WRITE, dirent, count))
309 		return -EFAULT;
310 
311 	f = fdget_pos(fd);
312 	if (!f.file)
313 		return -EBADF;
314 
315 	error = iterate_dir(f.file, &buf.ctx);
316 	if (error >= 0)
317 		error = buf.error;
318 	lastdirent = buf.previous;
319 	if (lastdirent) {
320 		typeof(lastdirent->d_off) d_off = buf.ctx.pos;
321 		if (__put_user(d_off, &lastdirent->d_off))
322 			error = -EFAULT;
323 		else
324 			error = count - buf.count;
325 	}
326 	fdput_pos(f);
327 	return error;
328 }
329 
330 #ifdef CONFIG_COMPAT
331 struct compat_old_linux_dirent {
332 	compat_ulong_t	d_ino;
333 	compat_ulong_t	d_offset;
334 	unsigned short	d_namlen;
335 	char		d_name[1];
336 };
337 
338 struct compat_readdir_callback {
339 	struct dir_context ctx;
340 	struct compat_old_linux_dirent __user *dirent;
341 	int result;
342 };
343 
344 static int compat_fillonedir(struct dir_context *ctx, const char *name,
345 			     int namlen, loff_t offset, u64 ino,
346 			     unsigned int d_type)
347 {
348 	struct compat_readdir_callback *buf =
349 		container_of(ctx, struct compat_readdir_callback, ctx);
350 	struct compat_old_linux_dirent __user *dirent;
351 	compat_ulong_t d_ino;
352 
353 	if (buf->result)
354 		return -EINVAL;
355 	d_ino = ino;
356 	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
357 		buf->result = -EOVERFLOW;
358 		return -EOVERFLOW;
359 	}
360 	buf->result++;
361 	dirent = buf->dirent;
362 	if (!access_ok(VERIFY_WRITE, dirent,
363 			(unsigned long)(dirent->d_name + namlen + 1) -
364 				(unsigned long)dirent))
365 		goto efault;
366 	if (	__put_user(d_ino, &dirent->d_ino) ||
367 		__put_user(offset, &dirent->d_offset) ||
368 		__put_user(namlen, &dirent->d_namlen) ||
369 		__copy_to_user(dirent->d_name, name, namlen) ||
370 		__put_user(0, dirent->d_name + namlen))
371 		goto efault;
372 	return 0;
373 efault:
374 	buf->result = -EFAULT;
375 	return -EFAULT;
376 }
377 
378 COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
379 		struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
380 {
381 	int error;
382 	struct fd f = fdget_pos(fd);
383 	struct compat_readdir_callback buf = {
384 		.ctx.actor = compat_fillonedir,
385 		.dirent = dirent
386 	};
387 
388 	if (!f.file)
389 		return -EBADF;
390 
391 	error = iterate_dir(f.file, &buf.ctx);
392 	if (buf.result)
393 		error = buf.result;
394 
395 	fdput_pos(f);
396 	return error;
397 }
398 
399 struct compat_linux_dirent {
400 	compat_ulong_t	d_ino;
401 	compat_ulong_t	d_off;
402 	unsigned short	d_reclen;
403 	char		d_name[1];
404 };
405 
406 struct compat_getdents_callback {
407 	struct dir_context ctx;
408 	struct compat_linux_dirent __user *current_dir;
409 	struct compat_linux_dirent __user *previous;
410 	int count;
411 	int error;
412 };
413 
414 static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
415 		loff_t offset, u64 ino, unsigned int d_type)
416 {
417 	struct compat_linux_dirent __user * dirent;
418 	struct compat_getdents_callback *buf =
419 		container_of(ctx, struct compat_getdents_callback, ctx);
420 	compat_ulong_t d_ino;
421 	int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
422 		namlen + 2, sizeof(compat_long_t));
423 
424 	buf->error = -EINVAL;	/* only used if we fail.. */
425 	if (reclen > buf->count)
426 		return -EINVAL;
427 	d_ino = ino;
428 	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
429 		buf->error = -EOVERFLOW;
430 		return -EOVERFLOW;
431 	}
432 	dirent = buf->previous;
433 	if (dirent) {
434 		if (signal_pending(current))
435 			return -EINTR;
436 		if (__put_user(offset, &dirent->d_off))
437 			goto efault;
438 	}
439 	dirent = buf->current_dir;
440 	if (__put_user(d_ino, &dirent->d_ino))
441 		goto efault;
442 	if (__put_user(reclen, &dirent->d_reclen))
443 		goto efault;
444 	if (copy_to_user(dirent->d_name, name, namlen))
445 		goto efault;
446 	if (__put_user(0, dirent->d_name + namlen))
447 		goto efault;
448 	if (__put_user(d_type, (char  __user *) dirent + reclen - 1))
449 		goto efault;
450 	buf->previous = dirent;
451 	dirent = (void __user *)dirent + reclen;
452 	buf->current_dir = dirent;
453 	buf->count -= reclen;
454 	return 0;
455 efault:
456 	buf->error = -EFAULT;
457 	return -EFAULT;
458 }
459 
460 COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
461 		struct compat_linux_dirent __user *, dirent, unsigned int, count)
462 {
463 	struct fd f;
464 	struct compat_linux_dirent __user * lastdirent;
465 	struct compat_getdents_callback buf = {
466 		.ctx.actor = compat_filldir,
467 		.current_dir = dirent,
468 		.count = count
469 	};
470 	int error;
471 
472 	if (!access_ok(VERIFY_WRITE, dirent, count))
473 		return -EFAULT;
474 
475 	f = fdget_pos(fd);
476 	if (!f.file)
477 		return -EBADF;
478 
479 	error = iterate_dir(f.file, &buf.ctx);
480 	if (error >= 0)
481 		error = buf.error;
482 	lastdirent = buf.previous;
483 	if (lastdirent) {
484 		if (put_user(buf.ctx.pos, &lastdirent->d_off))
485 			error = -EFAULT;
486 		else
487 			error = count - buf.count;
488 	}
489 	fdput_pos(f);
490 	return error;
491 }
492 #endif
493