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