Blame view

fs/readdir.c 7.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
  /*
   *  linux/fs/readdir.c
   *
   *  Copyright (C) 1995  Linus Torvalds
   */
85c9fe8fc   Kevin Winchester   vfs: fix warning:...
6
  #include <linux/stddef.h>
022a16924   Milind Arun Choudhary   ROUND_UP macro cl...
7
  #include <linux/kernel.h>
630d9c472   Paul Gortmaker   fs: reduce the us...
8
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
12
13
  #include <linux/time.h>
  #include <linux/mm.h>
  #include <linux/errno.h>
  #include <linux/stat.h>
  #include <linux/file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <linux/fs.h>
d4c7cf6cf   Heinrich Schuchardt   fanotify: create ...
15
  #include <linux/fsnotify.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
20
21
  #include <linux/dirent.h>
  #include <linux/security.h>
  #include <linux/syscalls.h>
  #include <linux/unistd.h>
  
  #include <asm/uaccess.h>
5c0ba4e07   Al Viro   [readdir] introdu...
22
  int iterate_dir(struct file *file, struct dir_context *ctx)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  {
496ad9aa8   Al Viro   new helper: file_...
24
  	struct inode *inode = file_inode(file);
619226944   Al Viro   introduce a paral...
25
  	bool shared = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
  	int res = -ENOTDIR;
619226944   Al Viro   introduce a paral...
27
28
29
  	if (file->f_op->iterate_shared)
  		shared = true;
  	else if (!file->f_op->iterate)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
  		goto out;
  
  	res = security_file_permission(file, MAY_READ);
  	if (res)
  		goto out;
002354112   Al Viro   restore killabili...
35
  	if (shared) {
619226944   Al Viro   introduce a paral...
36
  		inode_lock_shared(inode);
002354112   Al Viro   restore killabili...
37
38
39
40
41
  	} else {
  		res = down_write_killable(&inode->i_rwsem);
  		if (res)
  			goto out;
  	}
da7845119   Liam R. Howlett   Use mutex_lock_ki...
42

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
  	res = -ENOENT;
  	if (!IS_DEADDIR(inode)) {
2233f31aa   Al Viro   [readdir] ->readd...
45
  		ctx->pos = file->f_pos;
619226944   Al Viro   introduce a paral...
46
47
48
49
  		if (shared)
  			res = file->f_op->iterate_shared(file, ctx);
  		else
  			res = file->f_op->iterate(file, ctx);
2233f31aa   Al Viro   [readdir] ->readd...
50
  		file->f_pos = ctx->pos;
d4c7cf6cf   Heinrich Schuchardt   fanotify: create ...
51
  		fsnotify_access(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
  		file_accessed(file);
  	}
619226944   Al Viro   introduce a paral...
54
55
56
57
  	if (shared)
  		inode_unlock_shared(inode);
  	else
  		inode_unlock(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
  out:
  	return res;
  }
5c0ba4e07   Al Viro   [readdir] introdu...
61
  EXPORT_SYMBOL(iterate_dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
67
68
69
70
  
  /*
   * Traditional linux readdir() handling..
   *
   * "count=1" is a special case, meaning that the buffer is one
   * dirent-structure in size and that the code can't handle more
   * anyway. Thus the special "fillonedir()" function for that
   * case (the low-level handlers don't need to care about this).
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
75
76
77
78
79
80
81
  
  #ifdef __ARCH_WANT_OLD_READDIR
  
  struct old_linux_dirent {
  	unsigned long	d_ino;
  	unsigned long	d_offset;
  	unsigned short	d_namlen;
  	char		d_name[1];
  };
  
  struct readdir_callback {
5c0ba4e07   Al Viro   [readdir] introdu...
82
  	struct dir_context ctx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
  	struct old_linux_dirent __user * dirent;
  	int result;
  };
ac7576f4b   Miklos Szeredi   vfs: make first a...
86
87
  static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
  		      loff_t offset, u64 ino, unsigned int d_type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  {
ac7576f4b   Miklos Szeredi   vfs: make first a...
89
90
  	struct readdir_callback *buf =
  		container_of(ctx, struct readdir_callback, ctx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  	struct old_linux_dirent __user * dirent;
afefdbb28   David Howells   [PATCH] VFS: Make...
92
  	unsigned long d_ino;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
  
  	if (buf->result)
  		return -EINVAL;
afefdbb28   David Howells   [PATCH] VFS: Make...
96
  	d_ino = ino;
8f3f655da   Al Viro   [PATCH] fix regul...
97
98
  	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
  		buf->result = -EOVERFLOW;
afefdbb28   David Howells   [PATCH] VFS: Make...
99
  		return -EOVERFLOW;
8f3f655da   Al Viro   [PATCH] fix regul...
100
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
105
106
  	buf->result++;
  	dirent = buf->dirent;
  	if (!access_ok(VERIFY_WRITE, dirent,
  			(unsigned long)(dirent->d_name + namlen + 1) -
  				(unsigned long)dirent))
  		goto efault;
afefdbb28   David Howells   [PATCH] VFS: Make...
107
  	if (	__put_user(d_ino, &dirent->d_ino) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
113
114
115
116
117
  		__put_user(offset, &dirent->d_offset) ||
  		__put_user(namlen, &dirent->d_namlen) ||
  		__copy_to_user(dirent->d_name, name, namlen) ||
  		__put_user(0, dirent->d_name + namlen))
  		goto efault;
  	return 0;
  efault:
  	buf->result = -EFAULT;
  	return -EFAULT;
  }
d4e82042c   Heiko Carstens   [CVE-2009-0029] S...
118
119
  SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
  		struct old_linux_dirent __user *, dirent, unsigned int, count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
  {
  	int error;
63b6df141   Al Viro   give readdir(2)/g...
122
  	struct fd f = fdget_pos(fd);
ac6614b76   Al Viro   [readdir] constif...
123
124
125
126
  	struct readdir_callback buf = {
  		.ctx.actor = fillonedir,
  		.dirent = dirent
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127

2903ff019   Al Viro   switch simple cas...
128
  	if (!f.file)
863ced7fe   Al Viro   switch readdir/ge...
129
  		return -EBADF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130

5c0ba4e07   Al Viro   [readdir] introdu...
131
  	error = iterate_dir(f.file, &buf.ctx);
53c9c5c0e   Al Viro   [PATCH] prepare v...
132
  	if (buf.result)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  		error = buf.result;
63b6df141   Al Viro   give readdir(2)/g...
134
  	fdput_pos(f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  	return error;
  }
  
  #endif /* __ARCH_WANT_OLD_READDIR */
  
  /*
   * New, all-improved, singing, dancing, iBCS2-compliant getdents()
   * interface. 
   */
  struct linux_dirent {
  	unsigned long	d_ino;
  	unsigned long	d_off;
  	unsigned short	d_reclen;
  	char		d_name[1];
  };
  
  struct getdents_callback {
5c0ba4e07   Al Viro   [readdir] introdu...
152
  	struct dir_context ctx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
  	struct linux_dirent __user * current_dir;
  	struct linux_dirent __user * previous;
  	int count;
  	int error;
  };
ac7576f4b   Miklos Szeredi   vfs: make first a...
158
159
  static int filldir(struct dir_context *ctx, const char *name, int namlen,
  		   loff_t offset, u64 ino, unsigned int d_type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  {
  	struct linux_dirent __user * dirent;
ac7576f4b   Miklos Szeredi   vfs: make first a...
162
163
  	struct getdents_callback *buf =
  		container_of(ctx, struct getdents_callback, ctx);
afefdbb28   David Howells   [PATCH] VFS: Make...
164
  	unsigned long d_ino;
85c9fe8fc   Kevin Winchester   vfs: fix warning:...
165
166
  	int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
  		sizeof(long));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
169
170
  
  	buf->error = -EINVAL;	/* only used if we fail.. */
  	if (reclen > buf->count)
  		return -EINVAL;
afefdbb28   David Howells   [PATCH] VFS: Make...
171
  	d_ino = ino;
8f3f655da   Al Viro   [PATCH] fix regul...
172
173
  	if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
  		buf->error = -EOVERFLOW;
afefdbb28   David Howells   [PATCH] VFS: Make...
174
  		return -EOVERFLOW;
8f3f655da   Al Viro   [PATCH] fix regul...
175
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
  	dirent = buf->previous;
  	if (dirent) {
1f60fbe72   Theodore Ts'o   ext4: allow readd...
178
179
  		if (signal_pending(current))
  			return -EINTR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
183
  		if (__put_user(offset, &dirent->d_off))
  			goto efault;
  	}
  	dirent = buf->current_dir;
afefdbb28   David Howells   [PATCH] VFS: Make...
184
  	if (__put_user(d_ino, &dirent->d_ino))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  		goto efault;
  	if (__put_user(reclen, &dirent->d_reclen))
  		goto efault;
  	if (copy_to_user(dirent->d_name, name, namlen))
  		goto efault;
  	if (__put_user(0, dirent->d_name + namlen))
  		goto efault;
  	if (__put_user(d_type, (char __user *) dirent + reclen - 1))
  		goto efault;
  	buf->previous = dirent;
  	dirent = (void __user *)dirent + reclen;
  	buf->current_dir = dirent;
  	buf->count -= reclen;
  	return 0;
  efault:
  	buf->error = -EFAULT;
  	return -EFAULT;
  }
20f37034f   Heiko Carstens   [CVE-2009-0029] S...
203
204
  SYSCALL_DEFINE3(getdents, unsigned int, fd,
  		struct linux_dirent __user *, dirent, unsigned int, count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
  {
2903ff019   Al Viro   switch simple cas...
206
  	struct fd f;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  	struct linux_dirent __user * lastdirent;
ac6614b76   Al Viro   [readdir] constif...
208
209
210
211
212
  	struct getdents_callback buf = {
  		.ctx.actor = filldir,
  		.count = count,
  		.current_dir = dirent
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  	if (!access_ok(VERIFY_WRITE, dirent, count))
863ced7fe   Al Viro   switch readdir/ge...
215
  		return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216

63b6df141   Al Viro   give readdir(2)/g...
217
  	f = fdget_pos(fd);
2903ff019   Al Viro   switch simple cas...
218
  	if (!f.file)
863ced7fe   Al Viro   switch readdir/ge...
219
  		return -EBADF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220

5c0ba4e07   Al Viro   [readdir] introdu...
221
  	error = iterate_dir(f.file, &buf.ctx);
53c9c5c0e   Al Viro   [PATCH] prepare v...
222
223
  	if (error >= 0)
  		error = buf.error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
  	lastdirent = buf.previous;
  	if (lastdirent) {
bb6f619b3   Al Viro   [readdir] introdu...
226
  		if (put_user(buf.ctx.pos, &lastdirent->d_off))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
  			error = -EFAULT;
  		else
  			error = count - buf.count;
  	}
63b6df141   Al Viro   give readdir(2)/g...
231
  	fdput_pos(f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
  	return error;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
  struct getdents_callback64 {
5c0ba4e07   Al Viro   [readdir] introdu...
235
  	struct dir_context ctx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
240
  	struct linux_dirent64 __user * current_dir;
  	struct linux_dirent64 __user * previous;
  	int count;
  	int error;
  };
ac7576f4b   Miklos Szeredi   vfs: make first a...
241
242
  static int filldir64(struct dir_context *ctx, const char *name, int namlen,
  		     loff_t offset, u64 ino, unsigned int d_type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
  {
  	struct linux_dirent64 __user *dirent;
ac7576f4b   Miklos Szeredi   vfs: make first a...
245
246
  	struct getdents_callback64 *buf =
  		container_of(ctx, struct getdents_callback64, ctx);
85c9fe8fc   Kevin Winchester   vfs: fix warning:...
247
248
  	int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
  		sizeof(u64));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
251
252
253
254
  
  	buf->error = -EINVAL;	/* only used if we fail.. */
  	if (reclen > buf->count)
  		return -EINVAL;
  	dirent = buf->previous;
  	if (dirent) {
1f60fbe72   Theodore Ts'o   ext4: allow readd...
255
256
  		if (signal_pending(current))
  			return -EINTR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
  		if (__put_user(offset, &dirent->d_off))
  			goto efault;
  	}
  	dirent = buf->current_dir;
  	if (__put_user(ino, &dirent->d_ino))
  		goto efault;
  	if (__put_user(0, &dirent->d_off))
  		goto efault;
  	if (__put_user(reclen, &dirent->d_reclen))
  		goto efault;
  	if (__put_user(d_type, &dirent->d_type))
  		goto efault;
  	if (copy_to_user(dirent->d_name, name, namlen))
  		goto efault;
  	if (__put_user(0, dirent->d_name + namlen))
  		goto efault;
  	buf->previous = dirent;
  	dirent = (void __user *)dirent + reclen;
  	buf->current_dir = dirent;
  	buf->count -= reclen;
  	return 0;
  efault:
  	buf->error = -EFAULT;
  	return -EFAULT;
  }
20f37034f   Heiko Carstens   [CVE-2009-0029] S...
282
283
  SYSCALL_DEFINE3(getdents64, unsigned int, fd,
  		struct linux_dirent64 __user *, dirent, unsigned int, count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
  {
2903ff019   Al Viro   switch simple cas...
285
  	struct fd f;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  	struct linux_dirent64 __user * lastdirent;
ac6614b76   Al Viro   [readdir] constif...
287
288
289
290
291
  	struct getdents_callback64 buf = {
  		.ctx.actor = filldir64,
  		.count = count,
  		.current_dir = dirent
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  	if (!access_ok(VERIFY_WRITE, dirent, count))
863ced7fe   Al Viro   switch readdir/ge...
294
  		return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295

63b6df141   Al Viro   give readdir(2)/g...
296
  	f = fdget_pos(fd);
2903ff019   Al Viro   switch simple cas...
297
  	if (!f.file)
863ced7fe   Al Viro   switch readdir/ge...
298
  		return -EBADF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299

5c0ba4e07   Al Viro   [readdir] introdu...
300
  	error = iterate_dir(f.file, &buf.ctx);
53c9c5c0e   Al Viro   [PATCH] prepare v...
301
302
  	if (error >= 0)
  		error = buf.error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
  	lastdirent = buf.previous;
  	if (lastdirent) {
bb6f619b3   Al Viro   [readdir] introdu...
305
  		typeof(lastdirent->d_off) d_off = buf.ctx.pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  		if (__put_user(d_off, &lastdirent->d_off))
53c9c5c0e   Al Viro   [PATCH] prepare v...
307
308
309
  			error = -EFAULT;
  		else
  			error = count - buf.count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  	}
63b6df141   Al Viro   give readdir(2)/g...
311
  	fdput_pos(f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
  	return error;
  }