xref: /openbmc/linux/fs/readdir.c (revision 6aa7de05)
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 #include <linux/compat.h>
22 
23 #include <linux/uaccess.h>
24 
25 int iterate_dir(struct file *file, struct dir_context *ctx)
26 {
27 	struct inode *inode = file_inode(file);
28 	bool shared = false;
29 	int res = -ENOTDIR;
30 	if (file->f_op->iterate_shared)
31 		shared = true;
32 	else if (!file->f_op->iterate)
33 		goto out;
34 
35 	res = security_file_permission(file, MAY_READ);
36 	if (res)
37 		goto out;
38 
39 	if (shared)
40 		res = down_read_killable(&inode->i_rwsem);
41 	else
42 		res = down_write_killable(&inode->i_rwsem);
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 (signal_pending(current))
186 			return -EINTR;
187 		if (__put_user(offset, &dirent->d_off))
188 			goto efault;
189 	}
190 	dirent = buf->current_dir;
191 	if (__put_user(d_ino, &dirent->d_ino))
192 		goto efault;
193 	if (__put_user(reclen, &dirent->d_reclen))
194 		goto efault;
195 	if (copy_to_user(dirent->d_name, name, namlen))
196 		goto efault;
197 	if (__put_user(0, dirent->d_name + namlen))
198 		goto efault;
199 	if (__put_user(d_type, (char __user *) dirent + reclen - 1))
200 		goto efault;
201 	buf->previous = dirent;
202 	dirent = (void __user *)dirent + reclen;
203 	buf->current_dir = dirent;
204 	buf->count -= reclen;
205 	return 0;
206 efault:
207 	buf->error = -EFAULT;
208 	return -EFAULT;
209 }
210 
211 SYSCALL_DEFINE3(getdents, unsigned int, fd,
212 		struct linux_dirent __user *, dirent, unsigned int, count)
213 {
214 	struct fd f;
215 	struct linux_dirent __user * lastdirent;
216 	struct getdents_callback buf = {
217 		.ctx.actor = filldir,
218 		.count = count,
219 		.current_dir = dirent
220 	};
221 	int error;
222 
223 	if (!access_ok(VERIFY_WRITE, dirent, count))
224 		return -EFAULT;
225 
226 	f = fdget_pos(fd);
227 	if (!f.file)
228 		return -EBADF;
229 
230 	error = iterate_dir(f.file, &buf.ctx);
231 	if (error >= 0)
232 		error = buf.error;
233 	lastdirent = buf.previous;
234 	if (lastdirent) {
235 		if (put_user(buf.ctx.pos, &lastdirent->d_off))
236 			error = -EFAULT;
237 		else
238 			error = count - buf.count;
239 	}
240 	fdput_pos(f);
241 	return error;
242 }
243 
244 struct getdents_callback64 {
245 	struct dir_context ctx;
246 	struct linux_dirent64 __user * current_dir;
247 	struct linux_dirent64 __user * previous;
248 	int count;
249 	int error;
250 };
251 
252 static int filldir64(struct dir_context *ctx, const char *name, int namlen,
253 		     loff_t offset, u64 ino, unsigned int d_type)
254 {
255 	struct linux_dirent64 __user *dirent;
256 	struct getdents_callback64 *buf =
257 		container_of(ctx, struct getdents_callback64, ctx);
258 	int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
259 		sizeof(u64));
260 
261 	buf->error = -EINVAL;	/* only used if we fail.. */
262 	if (reclen > buf->count)
263 		return -EINVAL;
264 	dirent = buf->previous;
265 	if (dirent) {
266 		if (signal_pending(current))
267 			return -EINTR;
268 		if (__put_user(offset, &dirent->d_off))
269 			goto efault;
270 	}
271 	dirent = buf->current_dir;
272 	if (__put_user(ino, &dirent->d_ino))
273 		goto efault;
274 	if (__put_user(0, &dirent->d_off))
275 		goto efault;
276 	if (__put_user(reclen, &dirent->d_reclen))
277 		goto efault;
278 	if (__put_user(d_type, &dirent->d_type))
279 		goto efault;
280 	if (copy_to_user(dirent->d_name, name, namlen))
281 		goto efault;
282 	if (__put_user(0, dirent->d_name + namlen))
283 		goto efault;
284 	buf->previous = dirent;
285 	dirent = (void __user *)dirent + reclen;
286 	buf->current_dir = dirent;
287 	buf->count -= reclen;
288 	return 0;
289 efault:
290 	buf->error = -EFAULT;
291 	return -EFAULT;
292 }
293 
294 SYSCALL_DEFINE3(getdents64, unsigned int, fd,
295 		struct linux_dirent64 __user *, dirent, unsigned int, count)
296 {
297 	struct fd f;
298 	struct linux_dirent64 __user * lastdirent;
299 	struct getdents_callback64 buf = {
300 		.ctx.actor = filldir64,
301 		.count = count,
302 		.current_dir = dirent
303 	};
304 	int error;
305 
306 	if (!access_ok(VERIFY_WRITE, dirent, count))
307 		return -EFAULT;
308 
309 	f = fdget_pos(fd);
310 	if (!f.file)
311 		return -EBADF;
312 
313 	error = iterate_dir(f.file, &buf.ctx);
314 	if (error >= 0)
315 		error = buf.error;
316 	lastdirent = buf.previous;
317 	if (lastdirent) {
318 		typeof(lastdirent->d_off) d_off = buf.ctx.pos;
319 		if (__put_user(d_off, &lastdirent->d_off))
320 			error = -EFAULT;
321 		else
322 			error = count - buf.count;
323 	}
324 	fdput_pos(f);
325 	return error;
326 }
327 
328 #ifdef CONFIG_COMPAT
329 struct compat_old_linux_dirent {
330 	compat_ulong_t	d_ino;
331 	compat_ulong_t	d_offset;
332 	unsigned short	d_namlen;
333 	char		d_name[1];
334 };
335 
336 struct compat_readdir_callback {
337 	struct dir_context ctx;
338 	struct compat_old_linux_dirent __user *dirent;
339 	int result;
340 };
341 
342 static int compat_fillonedir(struct dir_context *ctx, const char *name,
343 			     int namlen, loff_t offset, u64 ino,
344 			     unsigned int d_type)
345 {
346 	struct compat_readdir_callback *buf =
347 		container_of(ctx, struct compat_readdir_callback, ctx);
348 	struct compat_old_linux_dirent __user *dirent;
349 	compat_ulong_t d_ino;
350 
351 	if (buf->result)
352 		return -EINVAL;
353 	d_ino = ino;
354 	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
355 		buf->result = -EOVERFLOW;
356 		return -EOVERFLOW;
357 	}
358 	buf->result++;
359 	dirent = buf->dirent;
360 	if (!access_ok(VERIFY_WRITE, dirent,
361 			(unsigned long)(dirent->d_name + namlen + 1) -
362 				(unsigned long)dirent))
363 		goto efault;
364 	if (	__put_user(d_ino, &dirent->d_ino) ||
365 		__put_user(offset, &dirent->d_offset) ||
366 		__put_user(namlen, &dirent->d_namlen) ||
367 		__copy_to_user(dirent->d_name, name, namlen) ||
368 		__put_user(0, dirent->d_name + namlen))
369 		goto efault;
370 	return 0;
371 efault:
372 	buf->result = -EFAULT;
373 	return -EFAULT;
374 }
375 
376 COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
377 		struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
378 {
379 	int error;
380 	struct fd f = fdget_pos(fd);
381 	struct compat_readdir_callback buf = {
382 		.ctx.actor = compat_fillonedir,
383 		.dirent = dirent
384 	};
385 
386 	if (!f.file)
387 		return -EBADF;
388 
389 	error = iterate_dir(f.file, &buf.ctx);
390 	if (buf.result)
391 		error = buf.result;
392 
393 	fdput_pos(f);
394 	return error;
395 }
396 
397 struct compat_linux_dirent {
398 	compat_ulong_t	d_ino;
399 	compat_ulong_t	d_off;
400 	unsigned short	d_reclen;
401 	char		d_name[1];
402 };
403 
404 struct compat_getdents_callback {
405 	struct dir_context ctx;
406 	struct compat_linux_dirent __user *current_dir;
407 	struct compat_linux_dirent __user *previous;
408 	int count;
409 	int error;
410 };
411 
412 static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
413 		loff_t offset, u64 ino, unsigned int d_type)
414 {
415 	struct compat_linux_dirent __user * dirent;
416 	struct compat_getdents_callback *buf =
417 		container_of(ctx, struct compat_getdents_callback, ctx);
418 	compat_ulong_t d_ino;
419 	int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
420 		namlen + 2, sizeof(compat_long_t));
421 
422 	buf->error = -EINVAL;	/* only used if we fail.. */
423 	if (reclen > buf->count)
424 		return -EINVAL;
425 	d_ino = ino;
426 	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
427 		buf->error = -EOVERFLOW;
428 		return -EOVERFLOW;
429 	}
430 	dirent = buf->previous;
431 	if (dirent) {
432 		if (signal_pending(current))
433 			return -EINTR;
434 		if (__put_user(offset, &dirent->d_off))
435 			goto efault;
436 	}
437 	dirent = buf->current_dir;
438 	if (__put_user(d_ino, &dirent->d_ino))
439 		goto efault;
440 	if (__put_user(reclen, &dirent->d_reclen))
441 		goto efault;
442 	if (copy_to_user(dirent->d_name, name, namlen))
443 		goto efault;
444 	if (__put_user(0, dirent->d_name + namlen))
445 		goto efault;
446 	if (__put_user(d_type, (char  __user *) dirent + reclen - 1))
447 		goto efault;
448 	buf->previous = dirent;
449 	dirent = (void __user *)dirent + reclen;
450 	buf->current_dir = dirent;
451 	buf->count -= reclen;
452 	return 0;
453 efault:
454 	buf->error = -EFAULT;
455 	return -EFAULT;
456 }
457 
458 COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
459 		struct compat_linux_dirent __user *, dirent, unsigned int, count)
460 {
461 	struct fd f;
462 	struct compat_linux_dirent __user * lastdirent;
463 	struct compat_getdents_callback buf = {
464 		.ctx.actor = compat_filldir,
465 		.current_dir = dirent,
466 		.count = count
467 	};
468 	int error;
469 
470 	if (!access_ok(VERIFY_WRITE, dirent, count))
471 		return -EFAULT;
472 
473 	f = fdget_pos(fd);
474 	if (!f.file)
475 		return -EBADF;
476 
477 	error = iterate_dir(f.file, &buf.ctx);
478 	if (error >= 0)
479 		error = buf.error;
480 	lastdirent = buf.previous;
481 	if (lastdirent) {
482 		if (put_user(buf.ctx.pos, &lastdirent->d_off))
483 			error = -EFAULT;
484 		else
485 			error = count - buf.count;
486 	}
487 	fdput_pos(f);
488 	return error;
489 }
490 #endif
491