Blame view

fs/read_write.c 21.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
  /*
   *  linux/fs/read_write.c
   *
   *  Copyright (C) 1991, 1992  Linus Torvalds
   */
  
  #include <linux/slab.h> 
  #include <linux/stat.h>
  #include <linux/fcntl.h>
  #include <linux/file.h>
  #include <linux/uio.h>
0eeca2830   Robert Love   [PATCH] inotify
12
  #include <linux/fsnotify.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
  #include <linux/security.h>
  #include <linux/module.h>
  #include <linux/syscalls.h>
e28cc7157   Linus Torvalds   Relax the rw_veri...
16
  #include <linux/pagemap.h>
d6b29d7ce   Jens Axboe   splice: divorce t...
17
  #include <linux/splice.h>
ee0b3e671   Badari Pulavarty   [PATCH] Remove re...
18
  #include "read_write.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
  
  #include <asm/uaccess.h>
  #include <asm/unistd.h>
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
22
  const struct file_operations generic_ro_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  	.llseek		= generic_file_llseek,
543ade1fc   Badari Pulavarty   [PATCH] Streamlin...
24
25
  	.read		= do_sync_read,
  	.aio_read	= generic_file_aio_read,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
  	.mmap		= generic_file_readonly_mmap,
534f2aaa6   Jens Axboe   sys_sendfile: swi...
27
  	.splice_read	= generic_file_splice_read,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
  };
  
  EXPORT_SYMBOL(generic_ro_fops);
cccb5a1e6   Al Viro   fix signedness me...
31
  static inline int unsigned_offsets(struct file *file)
4a3956c79   KAMEZAWA Hiroyuki   vfs: introduce FM...
32
  {
cccb5a1e6   Al Viro   fix signedness me...
33
  	return file->f_mode & FMODE_UNSIGNED_OFFSET;
4a3956c79   KAMEZAWA Hiroyuki   vfs: introduce FM...
34
  }
3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
35
36
37
38
39
40
41
42
43
  /**
   * generic_file_llseek_unlocked - lockless generic llseek implementation
   * @file:	file structure to seek on
   * @offset:	file offset to seek to
   * @origin:	type of seek
   *
   * Updates the file offset to the value specified by @offset and @origin.
   * Locking must be provided by the caller.
   */
9465efc9e   Andi Kleen   Remove BKL from r...
44
45
  loff_t
  generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
  	struct inode *inode = file->f_mapping->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  	switch (origin) {
3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
49
50
51
52
  	case SEEK_END:
  		offset += inode->i_size;
  		break;
  	case SEEK_CUR:
5b6f1eb97   Alain Knaff   vfs: lseek(fd, 0,...
53
54
55
56
57
58
59
60
  		/*
  		 * Here we special-case the lseek(fd, 0, SEEK_CUR)
  		 * position-querying operation.  Avoid rewriting the "same"
  		 * f_pos value back to the file because a concurrent read(),
  		 * write() or lseek() might have altered it
  		 */
  		if (offset == 0)
  			return file->f_pos;
3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
61
62
  		offset += file->f_pos;
  		break;
982d81658   Josef Bacik   fs: add SEEK_HOLE...
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  	case SEEK_DATA:
  		/*
  		 * In the generic case the entire file is data, so as long as
  		 * offset isn't at the end of the file then the offset is data.
  		 */
  		if (offset >= inode->i_size)
  			return -ENXIO;
  		break;
  	case SEEK_HOLE:
  		/*
  		 * There is a virtual hole at the end of the file, so as long as
  		 * offset isn't i_size or larger, return i_size.
  		 */
  		if (offset >= inode->i_size)
  			return -ENXIO;
  		offset = inode->i_size;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
  	}
3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
81

cccb5a1e6   Al Viro   fix signedness me...
82
  	if (offset < 0 && !unsigned_offsets(file))
4a3956c79   KAMEZAWA Hiroyuki   vfs: introduce FM...
83
84
  		return -EINVAL;
  	if (offset > inode->i_sb->s_maxbytes)
3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
85
86
87
88
89
90
  		return -EINVAL;
  
  	/* Special lock needed here? */
  	if (offset != file->f_pos) {
  		file->f_pos = offset;
  		file->f_version = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  	}
3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
92
93
  
  	return offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  }
9465efc9e   Andi Kleen   Remove BKL from r...
95
  EXPORT_SYMBOL(generic_file_llseek_unlocked);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96

3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
97
98
99
100
101
102
103
104
105
106
  /**
   * generic_file_llseek - generic llseek implementation for regular files
   * @file:	file structure to seek on
   * @offset:	file offset to seek to
   * @origin:	type of seek
   *
   * This is a generic implemenation of ->llseek useable for all normal local
   * filesystems.  It just updates the file offset to the value specified by
   * @offset and @origin under i_mutex.
   */
9465efc9e   Andi Kleen   Remove BKL from r...
107
  loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
  {
3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
109
  	loff_t rval;
9465efc9e   Andi Kleen   Remove BKL from r...
110
  	mutex_lock(&file->f_dentry->d_inode->i_mutex);
3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
111
  	rval = generic_file_llseek_unlocked(file, offset, origin);
9465efc9e   Andi Kleen   Remove BKL from r...
112
  	mutex_unlock(&file->f_dentry->d_inode->i_mutex);
3a8cff4f0   Christoph Hellwig   [PATCH] generic_f...
113
114
  
  	return rval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  }
9465efc9e   Andi Kleen   Remove BKL from r...
116
  EXPORT_SYMBOL(generic_file_llseek);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117

ae6afc3f5   jan Blunck   vfs: introduce no...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  /**
   * noop_llseek - No Operation Performed llseek implementation
   * @file:	file structure to seek on
   * @offset:	file offset to seek to
   * @origin:	type of seek
   *
   * This is an implementation of ->llseek useable for the rare special case when
   * userspace expects the seek to succeed but the (device) file is actually not
   * able to perform the seek. In this case you use noop_llseek() instead of
   * falling back to the default implementation of ->llseek.
   */
  loff_t noop_llseek(struct file *file, loff_t offset, int origin)
  {
  	return file->f_pos;
  }
  EXPORT_SYMBOL(noop_llseek);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
138
139
140
141
  loff_t no_llseek(struct file *file, loff_t offset, int origin)
  {
  	return -ESPIPE;
  }
  EXPORT_SYMBOL(no_llseek);
  
  loff_t default_llseek(struct file *file, loff_t offset, int origin)
  {
982d81658   Josef Bacik   fs: add SEEK_HOLE...
142
  	struct inode *inode = file->f_path.dentry->d_inode;
16abef0e9   David Sterba   fs: use loff_t ty...
143
  	loff_t retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144

982d81658   Josef Bacik   fs: add SEEK_HOLE...
145
  	mutex_lock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
  	switch (origin) {
7b8e89249   Chris Snook   use symbolic cons...
147
  		case SEEK_END:
982d81658   Josef Bacik   fs: add SEEK_HOLE...
148
  			offset += i_size_read(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  			break;
7b8e89249   Chris Snook   use symbolic cons...
150
  		case SEEK_CUR:
5b6f1eb97   Alain Knaff   vfs: lseek(fd, 0,...
151
152
153
154
  			if (offset == 0) {
  				retval = file->f_pos;
  				goto out;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  			offset += file->f_pos;
982d81658   Josef Bacik   fs: add SEEK_HOLE...
156
157
158
159
160
161
162
  			break;
  		case SEEK_DATA:
  			/*
  			 * In the generic case the entire file is data, so as
  			 * long as offset isn't at the end of the file then the
  			 * offset is data.
  			 */
bacb2d816   Dan Carpenter   fs: add missing u...
163
164
165
166
  			if (offset >= inode->i_size) {
  				retval = -ENXIO;
  				goto out;
  			}
982d81658   Josef Bacik   fs: add SEEK_HOLE...
167
168
169
170
171
172
173
  			break;
  		case SEEK_HOLE:
  			/*
  			 * There is a virtual hole at the end of the file, so
  			 * as long as offset isn't i_size or larger, return
  			 * i_size.
  			 */
bacb2d816   Dan Carpenter   fs: add missing u...
174
175
176
177
  			if (offset >= inode->i_size) {
  				retval = -ENXIO;
  				goto out;
  			}
982d81658   Josef Bacik   fs: add SEEK_HOLE...
178
179
  			offset = inode->i_size;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
  	}
  	retval = -EINVAL;
cccb5a1e6   Al Viro   fix signedness me...
182
  	if (offset >= 0 || unsigned_offsets(file)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
186
187
188
  		if (offset != file->f_pos) {
  			file->f_pos = offset;
  			file->f_version = 0;
  		}
  		retval = offset;
  	}
5b6f1eb97   Alain Knaff   vfs: lseek(fd, 0,...
189
  out:
982d81658   Josef Bacik   fs: add SEEK_HOLE...
190
  	mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
194
195
196
197
198
199
200
  	return retval;
  }
  EXPORT_SYMBOL(default_llseek);
  
  loff_t vfs_llseek(struct file *file, loff_t offset, int origin)
  {
  	loff_t (*fn)(struct file *, loff_t, int);
  
  	fn = no_llseek;
  	if (file->f_mode & FMODE_LSEEK) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
203
204
205
206
  		if (file->f_op && file->f_op->llseek)
  			fn = file->f_op->llseek;
  	}
  	return fn(file, offset, origin);
  }
  EXPORT_SYMBOL(vfs_llseek);
003d7ab47   Heiko Carstens   [CVE-2009-0029] S...
207
  SYSCALL_DEFINE3(lseek, unsigned int, fd, off_t, offset, unsigned int, origin)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
212
213
214
215
216
217
218
  {
  	off_t retval;
  	struct file * file;
  	int fput_needed;
  
  	retval = -EBADF;
  	file = fget_light(fd, &fput_needed);
  	if (!file)
  		goto bad;
  
  	retval = -EINVAL;
1ae7075bc   Chris Snook   use use SEEK_MAX ...
219
  	if (origin <= SEEK_MAX) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
223
224
225
226
227
228
229
230
  		loff_t res = vfs_llseek(file, offset, origin);
  		retval = res;
  		if (res != (loff_t)retval)
  			retval = -EOVERFLOW;	/* LFS: should only happen on 32 bit platforms */
  	}
  	fput_light(file, fput_needed);
  bad:
  	return retval;
  }
  
  #ifdef __ARCH_WANT_SYS_LLSEEK
003d7ab47   Heiko Carstens   [CVE-2009-0029] S...
231
232
233
  SYSCALL_DEFINE5(llseek, unsigned int, fd, unsigned long, offset_high,
  		unsigned long, offset_low, loff_t __user *, result,
  		unsigned int, origin)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
236
237
238
239
240
241
242
243
244
245
  {
  	int retval;
  	struct file * file;
  	loff_t offset;
  	int fput_needed;
  
  	retval = -EBADF;
  	file = fget_light(fd, &fput_needed);
  	if (!file)
  		goto bad;
  
  	retval = -EINVAL;
1ae7075bc   Chris Snook   use use SEEK_MAX ...
246
  	if (origin > SEEK_MAX)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  		goto out_putf;
  
  	offset = vfs_llseek(file, ((loff_t) offset_high << 32) | offset_low,
  			origin);
  
  	retval = (int)offset;
  	if (offset >= 0) {
  		retval = -EFAULT;
  		if (!copy_to_user(result, &offset, sizeof(offset)))
  			retval = 0;
  	}
  out_putf:
  	fput_light(file, fput_needed);
  bad:
  	return retval;
  }
  #endif
4a3956c79   KAMEZAWA Hiroyuki   vfs: introduce FM...
264

e28cc7157   Linus Torvalds   Relax the rw_veri...
265
266
267
268
269
  /*
   * rw_verify_area doesn't like huge counts. We limit
   * them to something that fits in "int" so that others
   * won't have to do range checks all the time.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
272
273
  int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
  {
  	struct inode *inode;
  	loff_t pos;
c43e259cc   James Morris   security: call se...
274
  	int retval = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275

163da958b   Eric Dumazet   [PATCH] FS: speed...
276
  	inode = file->f_path.dentry->d_inode;
e28cc7157   Linus Torvalds   Relax the rw_veri...
277
  	if (unlikely((ssize_t) count < 0))
c43e259cc   James Morris   security: call se...
278
  		return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
  	pos = *ppos;
cccb5a1e6   Al Viro   fix signedness me...
280
281
282
283
284
285
286
  	if (unlikely(pos < 0)) {
  		if (!unsigned_offsets(file))
  			return retval;
  		if (count >= -pos) /* both values are in 0..LLONG_MAX */
  			return -EOVERFLOW;
  	} else if (unlikely((loff_t) (pos + count) < 0)) {
  		if (!unsigned_offsets(file))
4a3956c79   KAMEZAWA Hiroyuki   vfs: introduce FM...
287
288
  			return retval;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289

a16877ca9   Pavel Emelyanov   Cleanup macros fo...
290
  	if (unlikely(inode->i_flock && mandatory_lock(inode))) {
c43e259cc   James Morris   security: call se...
291
  		retval = locks_mandatory_area(
e28cc7157   Linus Torvalds   Relax the rw_veri...
292
293
294
295
296
  			read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
  			inode, file, pos, count);
  		if (retval < 0)
  			return retval;
  	}
c43e259cc   James Morris   security: call se...
297
298
299
300
  	retval = security_file_permission(file,
  				read_write == READ ? MAY_READ : MAY_WRITE);
  	if (retval)
  		return retval;
e28cc7157   Linus Torvalds   Relax the rw_veri...
301
  	return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  }
63e688091   Benjamin LaHaise   [PATCH] aio: fix ...
303
304
305
306
307
308
309
310
311
  static void wait_on_retry_sync_kiocb(struct kiocb *iocb)
  {
  	set_current_state(TASK_UNINTERRUPTIBLE);
  	if (!kiocbIsKicked(iocb))
  		schedule();
  	else
  		kiocbClearKicked(iocb);
  	__set_current_state(TASK_RUNNING);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
  ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
  {
027445c37   Badari Pulavarty   [PATCH] Vectorize...
314
  	struct iovec iov = { .iov_base = buf, .iov_len = len };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
318
319
  	struct kiocb kiocb;
  	ssize_t ret;
  
  	init_sync_kiocb(&kiocb, filp);
  	kiocb.ki_pos = *ppos;
027445c37   Badari Pulavarty   [PATCH] Vectorize...
320
  	kiocb.ki_left = len;
61964eba5   David Howells   do_sync_read/writ...
321
  	kiocb.ki_nbytes = len;
027445c37   Badari Pulavarty   [PATCH] Vectorize...
322
323
324
325
326
  
  	for (;;) {
  		ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);
  		if (ret != -EIOCBRETRY)
  			break;
63e688091   Benjamin LaHaise   [PATCH] aio: fix ...
327
  		wait_on_retry_sync_kiocb(&kiocb);
027445c37   Badari Pulavarty   [PATCH] Vectorize...
328
  	}
63e688091   Benjamin LaHaise   [PATCH] aio: fix ...
329

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  	if (-EIOCBQUEUED == ret)
  		ret = wait_on_sync_kiocb(&kiocb);
  	*ppos = kiocb.ki_pos;
  	return ret;
  }
  
  EXPORT_SYMBOL(do_sync_read);
  
  ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
  {
  	ssize_t ret;
  
  	if (!(file->f_mode & FMODE_READ))
  		return -EBADF;
  	if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
  		return -EINVAL;
  	if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
  		return -EFAULT;
  
  	ret = rw_verify_area(READ, file, pos, count);
e28cc7157   Linus Torvalds   Relax the rw_veri...
350
351
  	if (ret >= 0) {
  		count = ret;
c43e259cc   James Morris   security: call se...
352
353
354
355
356
  		if (file->f_op->read)
  			ret = file->f_op->read(file, buf, count, pos);
  		else
  			ret = do_sync_read(file, buf, count, pos);
  		if (ret > 0) {
2a12a9d78   Eric Paris   fsnotify: pass a ...
357
  			fsnotify_access(file);
c43e259cc   James Morris   security: call se...
358
  			add_rchar(current, ret);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
  		}
c43e259cc   James Morris   security: call se...
360
  		inc_syscr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
365
366
367
368
369
  	}
  
  	return ret;
  }
  
  EXPORT_SYMBOL(vfs_read);
  
  ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
  {
027445c37   Badari Pulavarty   [PATCH] Vectorize...
370
  	struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
373
374
375
  	struct kiocb kiocb;
  	ssize_t ret;
  
  	init_sync_kiocb(&kiocb, filp);
  	kiocb.ki_pos = *ppos;
027445c37   Badari Pulavarty   [PATCH] Vectorize...
376
  	kiocb.ki_left = len;
61964eba5   David Howells   do_sync_read/writ...
377
  	kiocb.ki_nbytes = len;
027445c37   Badari Pulavarty   [PATCH] Vectorize...
378
379
380
381
382
  
  	for (;;) {
  		ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
  		if (ret != -EIOCBRETRY)
  			break;
63e688091   Benjamin LaHaise   [PATCH] aio: fix ...
383
  		wait_on_retry_sync_kiocb(&kiocb);
027445c37   Badari Pulavarty   [PATCH] Vectorize...
384
  	}
63e688091   Benjamin LaHaise   [PATCH] aio: fix ...
385

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  	if (-EIOCBQUEUED == ret)
  		ret = wait_on_sync_kiocb(&kiocb);
  	*ppos = kiocb.ki_pos;
  	return ret;
  }
  
  EXPORT_SYMBOL(do_sync_write);
  
  ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
  {
  	ssize_t ret;
  
  	if (!(file->f_mode & FMODE_WRITE))
  		return -EBADF;
  	if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write))
  		return -EINVAL;
  	if (unlikely(!access_ok(VERIFY_READ, buf, count)))
  		return -EFAULT;
  
  	ret = rw_verify_area(WRITE, file, pos, count);
e28cc7157   Linus Torvalds   Relax the rw_veri...
406
407
  	if (ret >= 0) {
  		count = ret;
c43e259cc   James Morris   security: call se...
408
409
410
411
412
  		if (file->f_op->write)
  			ret = file->f_op->write(file, buf, count, pos);
  		else
  			ret = do_sync_write(file, buf, count, pos);
  		if (ret > 0) {
2a12a9d78   Eric Paris   fsnotify: pass a ...
413
  			fsnotify_modify(file);
c43e259cc   James Morris   security: call se...
414
  			add_wchar(current, ret);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
  		}
c43e259cc   James Morris   security: call se...
416
  		inc_syscw(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
  	}
  
  	return ret;
  }
  
  EXPORT_SYMBOL(vfs_write);
  
  static inline loff_t file_pos_read(struct file *file)
  {
  	return file->f_pos;
  }
  
  static inline void file_pos_write(struct file *file, loff_t pos)
  {
  	file->f_pos = pos;
  }
3cdad4288   Heiko Carstens   [CVE-2009-0029] S...
433
  SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
  {
  	struct file *file;
  	ssize_t ret = -EBADF;
  	int fput_needed;
  
  	file = fget_light(fd, &fput_needed);
  	if (file) {
  		loff_t pos = file_pos_read(file);
  		ret = vfs_read(file, buf, count, &pos);
  		file_pos_write(file, pos);
  		fput_light(file, fput_needed);
  	}
  
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449

3cdad4288   Heiko Carstens   [CVE-2009-0029] S...
450
451
  SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
  		size_t, count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
  {
  	struct file *file;
  	ssize_t ret = -EBADF;
  	int fput_needed;
  
  	file = fget_light(fd, &fput_needed);
  	if (file) {
  		loff_t pos = file_pos_read(file);
  		ret = vfs_write(file, buf, count, &pos);
  		file_pos_write(file, pos);
  		fput_light(file, fput_needed);
  	}
  
  	return ret;
  }
6673e0c3f   Heiko Carstens   [CVE-2009-0029] S...
467
468
  SYSCALL_DEFINE(pread64)(unsigned int fd, char __user *buf,
  			size_t count, loff_t pos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
  {
  	struct file *file;
  	ssize_t ret = -EBADF;
  	int fput_needed;
  
  	if (pos < 0)
  		return -EINVAL;
  
  	file = fget_light(fd, &fput_needed);
  	if (file) {
  		ret = -ESPIPE;
  		if (file->f_mode & FMODE_PREAD)
  			ret = vfs_read(file, buf, count, &pos);
  		fput_light(file, fput_needed);
  	}
  
  	return ret;
  }
6673e0c3f   Heiko Carstens   [CVE-2009-0029] S...
487
488
489
490
491
492
493
494
  #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
  asmlinkage long SyS_pread64(long fd, long buf, long count, loff_t pos)
  {
  	return SYSC_pread64((unsigned int) fd, (char __user *) buf,
  			    (size_t) count, pos);
  }
  SYSCALL_ALIAS(sys_pread64, SyS_pread64);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495

6673e0c3f   Heiko Carstens   [CVE-2009-0029] S...
496
497
  SYSCALL_DEFINE(pwrite64)(unsigned int fd, const char __user *buf,
  			 size_t count, loff_t pos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
  {
  	struct file *file;
  	ssize_t ret = -EBADF;
  	int fput_needed;
  
  	if (pos < 0)
  		return -EINVAL;
  
  	file = fget_light(fd, &fput_needed);
  	if (file) {
  		ret = -ESPIPE;
  		if (file->f_mode & FMODE_PWRITE)  
  			ret = vfs_write(file, buf, count, &pos);
  		fput_light(file, fput_needed);
  	}
  
  	return ret;
  }
6673e0c3f   Heiko Carstens   [CVE-2009-0029] S...
516
517
518
519
520
521
522
523
  #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
  asmlinkage long SyS_pwrite64(long fd, long buf, long count, loff_t pos)
  {
  	return SYSC_pwrite64((unsigned int) fd, (const char __user *) buf,
  			     (size_t) count, pos);
  }
  SYSCALL_ALIAS(sys_pwrite64, SyS_pwrite64);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
  
  /*
   * Reduce an iovec's length in-place.  Return the resulting number of segments
   */
  unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to)
  {
  	unsigned long seg = 0;
  	size_t len = 0;
  
  	while (seg < nr_segs) {
  		seg++;
  		if (len + iov->iov_len >= to) {
  			iov->iov_len = to - len;
  			break;
  		}
  		len += iov->iov_len;
  		iov++;
  	}
  	return seg;
  }
19295529d   Eric Sandeen   ext4: export iov_...
544
  EXPORT_SYMBOL(iov_shorten);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545

ee0b3e671   Badari Pulavarty   [PATCH] Remove re...
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
  ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov,
  		unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn)
  {
  	struct kiocb kiocb;
  	ssize_t ret;
  
  	init_sync_kiocb(&kiocb, filp);
  	kiocb.ki_pos = *ppos;
  	kiocb.ki_left = len;
  	kiocb.ki_nbytes = len;
  
  	for (;;) {
  		ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos);
  		if (ret != -EIOCBRETRY)
  			break;
  		wait_on_retry_sync_kiocb(&kiocb);
  	}
  
  	if (ret == -EIOCBQUEUED)
  		ret = wait_on_sync_kiocb(&kiocb);
  	*ppos = kiocb.ki_pos;
  	return ret;
  }
  
  /* Do it by hand, with file-ops */
  ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov,
  		unsigned long nr_segs, loff_t *ppos, io_fn_t fn)
  {
  	struct iovec *vector = iov;
  	ssize_t ret = 0;
  
  	while (nr_segs > 0) {
  		void __user *base;
  		size_t len;
  		ssize_t nr;
  
  		base = vector->iov_base;
  		len = vector->iov_len;
  		vector++;
  		nr_segs--;
  
  		nr = fn(filp, base, len, ppos);
  
  		if (nr < 0) {
  			if (!ret)
  				ret = nr;
  			break;
  		}
  		ret += nr;
  		if (nr != len)
  			break;
  	}
  
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
602
  /* A write operation does a read from user space and vice versa */
  #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
603
604
605
606
  ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
  			      unsigned long nr_segs, unsigned long fast_segs,
  			      struct iovec *fast_pointer,
  			      struct iovec **ret_pointer)
435f49a51   Linus Torvalds   readv/writev: do ...
607
  {
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
608
  	unsigned long seg;
435f49a51   Linus Torvalds   readv/writev: do ...
609
  	ssize_t ret;
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
610
  	struct iovec *iov = fast_pointer;
435f49a51   Linus Torvalds   readv/writev: do ...
611
612
613
614
615
  	/*
  	 * SuS says "The readv() function *may* fail if the iovcnt argument
  	 * was less than or equal to 0, or greater than {IOV_MAX}.  Linux has
  	 * traditionally returned zero for zero segments, so...
  	 */
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
616
617
  	if (nr_segs == 0) {
  		ret = 0;
435f49a51   Linus Torvalds   readv/writev: do ...
618
  		goto out;
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
619
  	}
435f49a51   Linus Torvalds   readv/writev: do ...
620
621
622
623
  	/*
  	 * First get the "struct iovec" from user memory and
  	 * verify all the pointers
  	 */
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
624
625
  	if (nr_segs > UIO_MAXIOV) {
  		ret = -EINVAL;
435f49a51   Linus Torvalds   readv/writev: do ...
626
  		goto out;
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
627
628
  	}
  	if (nr_segs > fast_segs) {
435f49a51   Linus Torvalds   readv/writev: do ...
629
  		iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
630
631
  		if (iov == NULL) {
  			ret = -ENOMEM;
435f49a51   Linus Torvalds   readv/writev: do ...
632
  			goto out;
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
633
  		}
435f49a51   Linus Torvalds   readv/writev: do ...
634
  	}
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
635
636
  	if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
  		ret = -EFAULT;
435f49a51   Linus Torvalds   readv/writev: do ...
637
  		goto out;
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
638
  	}
435f49a51   Linus Torvalds   readv/writev: do ...
639
  	/*
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
640
641
642
643
  	 * According to the Single Unix Specification we should return EINVAL
  	 * if an element length is < 0 when cast to ssize_t or if the
  	 * total length would overflow the ssize_t return value of the
  	 * system call.
435f49a51   Linus Torvalds   readv/writev: do ...
644
645
646
647
  	 *
  	 * Linux caps all read/write calls to MAX_RW_COUNT, and avoids the
  	 * overflow case.
  	 */
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
648
  	ret = 0;
435f49a51   Linus Torvalds   readv/writev: do ...
649
650
651
  	for (seg = 0; seg < nr_segs; seg++) {
  		void __user *buf = iov[seg].iov_base;
  		ssize_t len = (ssize_t)iov[seg].iov_len;
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
652
653
654
  
  		/* see if we we're about to use an invalid len or if
  		 * it's about to overflow ssize_t */
435f49a51   Linus Torvalds   readv/writev: do ...
655
  		if (len < 0) {
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
656
  			ret = -EINVAL;
435f49a51   Linus Torvalds   readv/writev: do ...
657
  			goto out;
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
658
659
660
  		}
  		if (unlikely(!access_ok(vrfy_dir(type), buf, len))) {
  			ret = -EFAULT;
435f49a51   Linus Torvalds   readv/writev: do ...
661
662
663
664
665
  			goto out;
  		}
  		if (len > MAX_RW_COUNT - ret) {
  			len = MAX_RW_COUNT - ret;
  			iov[seg].iov_len = len;
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
666
  		}
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
667
  		ret += len;
435f49a51   Linus Torvalds   readv/writev: do ...
668
  	}
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
669
670
671
672
  out:
  	*ret_pointer = iov;
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673
674
675
676
  static ssize_t do_readv_writev(int type, struct file *file,
  			       const struct iovec __user * uvector,
  			       unsigned long nr_segs, loff_t *pos)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
  	size_t tot_len;
  	struct iovec iovstack[UIO_FASTIOV];
ee0b3e671   Badari Pulavarty   [PATCH] Remove re...
679
  	struct iovec *iov = iovstack;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
  	ssize_t ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
  	io_fn_t fn;
  	iov_fn_t fnv;
eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
683
684
  	if (!file->f_op) {
  		ret = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687

eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
688
689
690
  	ret = rw_copy_check_uvector(type, uvector, nr_segs,
  			ARRAY_SIZE(iovstack), iovstack, &iov);
  	if (ret <= 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692

eed4e51fb   Badari Pulavarty   [PATCH] Add vecto...
693
  	tot_len = ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
  	ret = rw_verify_area(type, file, pos, tot_len);
e28cc7157   Linus Torvalds   Relax the rw_veri...
695
  	if (ret < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
697
698
699
700
  		goto out;
  
  	fnv = NULL;
  	if (type == READ) {
  		fn = file->f_op->read;
ee0b3e671   Badari Pulavarty   [PATCH] Remove re...
701
  		fnv = file->f_op->aio_read;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
703
  	} else {
  		fn = (io_fn_t)file->f_op->write;
ee0b3e671   Badari Pulavarty   [PATCH] Remove re...
704
  		fnv = file->f_op->aio_write;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
  	}
ee0b3e671   Badari Pulavarty   [PATCH] Remove re...
706
707
708
709
710
  	if (fnv)
  		ret = do_sync_readv_writev(file, iov, nr_segs, tot_len,
  						pos, fnv);
  	else
  		ret = do_loop_readv_writev(file, iov, nr_segs, pos, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
712
713
714
  out:
  	if (iov != iovstack)
  		kfree(iov);
0eeca2830   Robert Love   [PATCH] inotify
715
716
  	if ((ret + (type == READ)) > 0) {
  		if (type == READ)
2a12a9d78   Eric Paris   fsnotify: pass a ...
717
  			fsnotify_access(file);
0eeca2830   Robert Love   [PATCH] inotify
718
  		else
2a12a9d78   Eric Paris   fsnotify: pass a ...
719
  			fsnotify_modify(file);
0eeca2830   Robert Love   [PATCH] inotify
720
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
723
724
725
726
727
728
  }
  
  ssize_t vfs_readv(struct file *file, const struct iovec __user *vec,
  		  unsigned long vlen, loff_t *pos)
  {
  	if (!(file->f_mode & FMODE_READ))
  		return -EBADF;
ee0b3e671   Badari Pulavarty   [PATCH] Remove re...
729
  	if (!file->f_op || (!file->f_op->aio_read && !file->f_op->read))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
733
734
735
736
737
738
739
740
741
  		return -EINVAL;
  
  	return do_readv_writev(READ, file, vec, vlen, pos);
  }
  
  EXPORT_SYMBOL(vfs_readv);
  
  ssize_t vfs_writev(struct file *file, const struct iovec __user *vec,
  		   unsigned long vlen, loff_t *pos)
  {
  	if (!(file->f_mode & FMODE_WRITE))
  		return -EBADF;
ee0b3e671   Badari Pulavarty   [PATCH] Remove re...
742
  	if (!file->f_op || (!file->f_op->aio_write && !file->f_op->write))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
743
744
745
746
747
748
  		return -EINVAL;
  
  	return do_readv_writev(WRITE, file, vec, vlen, pos);
  }
  
  EXPORT_SYMBOL(vfs_writev);
3cdad4288   Heiko Carstens   [CVE-2009-0029] S...
749
750
  SYSCALL_DEFINE3(readv, unsigned long, fd, const struct iovec __user *, vec,
  		unsigned long, vlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
751
752
753
754
755
756
757
758
759
760
761
762
763
764
  {
  	struct file *file;
  	ssize_t ret = -EBADF;
  	int fput_needed;
  
  	file = fget_light(fd, &fput_needed);
  	if (file) {
  		loff_t pos = file_pos_read(file);
  		ret = vfs_readv(file, vec, vlen, &pos);
  		file_pos_write(file, pos);
  		fput_light(file, fput_needed);
  	}
  
  	if (ret > 0)
4b98d11b4   Alexey Dobriyan   [PATCH] ifdef ->r...
765
766
  		add_rchar(current, ret);
  	inc_syscr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
768
  	return ret;
  }
3cdad4288   Heiko Carstens   [CVE-2009-0029] S...
769
770
  SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec,
  		unsigned long, vlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771
772
773
774
775
776
777
778
779
780
781
782
783
784
  {
  	struct file *file;
  	ssize_t ret = -EBADF;
  	int fput_needed;
  
  	file = fget_light(fd, &fput_needed);
  	if (file) {
  		loff_t pos = file_pos_read(file);
  		ret = vfs_writev(file, vec, vlen, &pos);
  		file_pos_write(file, pos);
  		fput_light(file, fput_needed);
  	}
  
  	if (ret > 0)
4b98d11b4   Alexey Dobriyan   [PATCH] ifdef ->r...
785
786
  		add_wchar(current, ret);
  	inc_syscw(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
788
  	return ret;
  }
601cc11d0   Linus Torvalds   Make non-compat p...
789
790
791
792
793
  static inline loff_t pos_from_hilo(unsigned long high, unsigned long low)
  {
  #define HALF_LONG_BITS (BITS_PER_LONG / 2)
  	return (((loff_t)high << HALF_LONG_BITS) << HALF_LONG_BITS) | low;
  }
f3554f4bc   Gerd Hoffmann   preadv/pwritev: A...
794
  SYSCALL_DEFINE5(preadv, unsigned long, fd, const struct iovec __user *, vec,
601cc11d0   Linus Torvalds   Make non-compat p...
795
  		unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h)
f3554f4bc   Gerd Hoffmann   preadv/pwritev: A...
796
  {
601cc11d0   Linus Torvalds   Make non-compat p...
797
  	loff_t pos = pos_from_hilo(pos_h, pos_l);
f3554f4bc   Gerd Hoffmann   preadv/pwritev: A...
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
  	struct file *file;
  	ssize_t ret = -EBADF;
  	int fput_needed;
  
  	if (pos < 0)
  		return -EINVAL;
  
  	file = fget_light(fd, &fput_needed);
  	if (file) {
  		ret = -ESPIPE;
  		if (file->f_mode & FMODE_PREAD)
  			ret = vfs_readv(file, vec, vlen, &pos);
  		fput_light(file, fput_needed);
  	}
  
  	if (ret > 0)
  		add_rchar(current, ret);
  	inc_syscr(current);
  	return ret;
  }
  
  SYSCALL_DEFINE5(pwritev, unsigned long, fd, const struct iovec __user *, vec,
601cc11d0   Linus Torvalds   Make non-compat p...
820
  		unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h)
f3554f4bc   Gerd Hoffmann   preadv/pwritev: A...
821
  {
601cc11d0   Linus Torvalds   Make non-compat p...
822
  	loff_t pos = pos_from_hilo(pos_h, pos_l);
f3554f4bc   Gerd Hoffmann   preadv/pwritev: A...
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
  	struct file *file;
  	ssize_t ret = -EBADF;
  	int fput_needed;
  
  	if (pos < 0)
  		return -EINVAL;
  
  	file = fget_light(fd, &fput_needed);
  	if (file) {
  		ret = -ESPIPE;
  		if (file->f_mode & FMODE_PWRITE)
  			ret = vfs_writev(file, vec, vlen, &pos);
  		fput_light(file, fput_needed);
  	}
  
  	if (ret > 0)
  		add_wchar(current, ret);
  	inc_syscw(current);
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
844
845
846
847
848
849
  static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
  			   size_t count, loff_t max)
  {
  	struct file * in_file, * out_file;
  	struct inode * in_inode, * out_inode;
  	loff_t pos;
  	ssize_t retval;
534f2aaa6   Jens Axboe   sys_sendfile: swi...
850
  	int fput_needed_in, fput_needed_out, fl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
852
853
854
855
856
857
858
859
860
  
  	/*
  	 * Get input file, and verify that it is ok..
  	 */
  	retval = -EBADF;
  	in_file = fget_light(in_fd, &fput_needed_in);
  	if (!in_file)
  		goto out;
  	if (!(in_file->f_mode & FMODE_READ))
  		goto fput_in;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861
862
863
864
865
866
867
  	retval = -ESPIPE;
  	if (!ppos)
  		ppos = &in_file->f_pos;
  	else
  		if (!(in_file->f_mode & FMODE_PREAD))
  			goto fput_in;
  	retval = rw_verify_area(READ, in_file, ppos, count);
e28cc7157   Linus Torvalds   Relax the rw_veri...
868
  	if (retval < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
  		goto fput_in;
e28cc7157   Linus Torvalds   Relax the rw_veri...
870
  	count = retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
871

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
873
874
875
876
877
878
879
880
881
  	/*
  	 * Get output file, and verify that it is ok..
  	 */
  	retval = -EBADF;
  	out_file = fget_light(out_fd, &fput_needed_out);
  	if (!out_file)
  		goto fput_in;
  	if (!(out_file->f_mode & FMODE_WRITE))
  		goto fput_out;
  	retval = -EINVAL;
6818173bd   Miklos Szeredi   splice: implement...
882
  	in_inode = in_file->f_path.dentry->d_inode;
0f7fc9e4d   Josef "Jeff" Sipek   [PATCH] VFS: chan...
883
  	out_inode = out_file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
884
  	retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count);
e28cc7157   Linus Torvalds   Relax the rw_veri...
885
  	if (retval < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
886
  		goto fput_out;
e28cc7157   Linus Torvalds   Relax the rw_veri...
887
  	count = retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
891
892
  	if (!max)
  		max = min(in_inode->i_sb->s_maxbytes, out_inode->i_sb->s_maxbytes);
  
  	pos = *ppos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
897
898
  	if (unlikely(pos + count > max)) {
  		retval = -EOVERFLOW;
  		if (pos >= max)
  			goto fput_out;
  		count = max - pos;
  	}
d96e6e716   Jens Axboe   Remove remnants o...
899
  	fl = 0;
534f2aaa6   Jens Axboe   sys_sendfile: swi...
900
  #if 0
d96e6e716   Jens Axboe   Remove remnants o...
901
902
903
904
905
906
907
908
  	/*
  	 * We need to debate whether we can enable this or not. The
  	 * man page documents EAGAIN return for the output at least,
  	 * and the application is arguably buggy if it doesn't expect
  	 * EAGAIN on a non-blocking file descriptor.
  	 */
  	if (in_file->f_flags & O_NONBLOCK)
  		fl = SPLICE_F_NONBLOCK;
534f2aaa6   Jens Axboe   sys_sendfile: swi...
909
  #endif
d96e6e716   Jens Axboe   Remove remnants o...
910
  	retval = do_splice_direct(in_file, ppos, out_file, count, fl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
912
  
  	if (retval > 0) {
4b98d11b4   Alexey Dobriyan   [PATCH] ifdef ->r...
913
914
  		add_rchar(current, retval);
  		add_wchar(current, retval);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
916

4b98d11b4   Alexey Dobriyan   [PATCH] ifdef ->r...
917
918
  	inc_syscr(current);
  	inc_syscw(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
919
920
921
922
923
924
925
926
927
928
  	if (*ppos > max)
  		retval = -EOVERFLOW;
  
  fput_out:
  	fput_light(out_file, fput_needed_out);
  fput_in:
  	fput_light(in_file, fput_needed_in);
  out:
  	return retval;
  }
002c8976e   Heiko Carstens   [CVE-2009-0029] S...
929
  SYSCALL_DEFINE4(sendfile, int, out_fd, int, in_fd, off_t __user *, offset, size_t, count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
  {
  	loff_t pos;
  	off_t off;
  	ssize_t ret;
  
  	if (offset) {
  		if (unlikely(get_user(off, offset)))
  			return -EFAULT;
  		pos = off;
  		ret = do_sendfile(out_fd, in_fd, &pos, count, MAX_NON_LFS);
  		if (unlikely(put_user(pos, offset)))
  			return -EFAULT;
  		return ret;
  	}
  
  	return do_sendfile(out_fd, in_fd, NULL, count, 0);
  }
002c8976e   Heiko Carstens   [CVE-2009-0029] S...
947
  SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
  {
  	loff_t pos;
  	ssize_t ret;
  
  	if (offset) {
  		if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t))))
  			return -EFAULT;
  		ret = do_sendfile(out_fd, in_fd, &pos, count, 0);
  		if (unlikely(put_user(pos, offset)))
  			return -EFAULT;
  		return ret;
  	}
  
  	return do_sendfile(out_fd, in_fd, NULL, count, 0);
  }