xref: /openbmc/linux/fs/readdir.c (revision bc5aa3a0)
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 		res = down_write_killable(&inode->i_rwsem);
42 		if (res)
43 			goto out;
44 	}
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