Blame view
fs/read_write.c
30.1 KB
1da177e4c
|
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> |
a27bb332c
|
12 |
#include <linux/aio.h> |
0eeca2830
|
13 |
#include <linux/fsnotify.h> |
1da177e4c
|
14 |
#include <linux/security.h> |
630d9c472
|
15 |
#include <linux/export.h> |
1da177e4c
|
16 |
#include <linux/syscalls.h> |
e28cc7157
|
17 |
#include <linux/pagemap.h> |
d6b29d7ce
|
18 |
#include <linux/splice.h> |
561c67319
|
19 |
#include <linux/compat.h> |
06ae43f34
|
20 |
#include "internal.h" |
1da177e4c
|
21 22 23 |
#include <asm/uaccess.h> #include <asm/unistd.h> |
c0bd14af5
|
24 25 26 |
typedef ssize_t (*io_fn_t)(struct file *, char __user *, size_t, loff_t *); typedef ssize_t (*iov_fn_t)(struct kiocb *, const struct iovec *, unsigned long, loff_t); |
293bc9822
|
27 |
typedef ssize_t (*iter_fn_t)(struct kiocb *, struct iov_iter *); |
c0bd14af5
|
28 |
|
4b6f5d20b
|
29 |
const struct file_operations generic_ro_fops = { |
1da177e4c
|
30 |
.llseek = generic_file_llseek, |
aad4f8bb4
|
31 32 |
.read = new_sync_read, .read_iter = generic_file_read_iter, |
1da177e4c
|
33 |
.mmap = generic_file_readonly_mmap, |
534f2aaa6
|
34 |
.splice_read = generic_file_splice_read, |
1da177e4c
|
35 36 37 |
}; EXPORT_SYMBOL(generic_ro_fops); |
cccb5a1e6
|
38 |
static inline int unsigned_offsets(struct file *file) |
4a3956c79
|
39 |
{ |
cccb5a1e6
|
40 |
return file->f_mode & FMODE_UNSIGNED_OFFSET; |
4a3956c79
|
41 |
} |
46a1c2c7a
|
42 43 44 45 46 47 48 49 50 51 52 53 54 |
/** * vfs_setpos - update the file offset for lseek * @file: file structure in question * @offset: file offset to seek to * @maxsize: maximum file size * * This is a low-level filesystem helper for updating the file offset to * the value specified by @offset if the given offset is valid and it is * not equal to the current file offset. * * Return the specified offset on success and -EINVAL on invalid offset. */ loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize) |
ef3d0fd27
|
55 56 57 58 59 60 61 62 63 64 65 66 |
{ if (offset < 0 && !unsigned_offsets(file)) return -EINVAL; if (offset > maxsize) return -EINVAL; if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } return offset; } |
46a1c2c7a
|
67 |
EXPORT_SYMBOL(vfs_setpos); |
ef3d0fd27
|
68 |
|
3a8cff4f0
|
69 |
/** |
5760495a8
|
70 |
* generic_file_llseek_size - generic llseek implementation for regular files |
3a8cff4f0
|
71 72 |
* @file: file structure to seek on * @offset: file offset to seek to |
965c8e59c
|
73 |
* @whence: type of seek |
e8b96eb50
|
74 75 |
* @size: max size of this file in file system * @eof: offset used for SEEK_END position |
3a8cff4f0
|
76 |
* |
5760495a8
|
77 |
* This is a variant of generic_file_llseek that allows passing in a custom |
e8b96eb50
|
78 |
* maximum file size and a custom EOF position, for e.g. hashed directories |
ef3d0fd27
|
79 80 |
* * Synchronization: |
5760495a8
|
81 |
* SEEK_SET and SEEK_END are unsynchronized (but atomic on 64bit platforms) |
ef3d0fd27
|
82 83 |
* SEEK_CUR is synchronized against other SEEK_CURs, but not read/writes. * read/writes behave like SEEK_SET against seeks. |
3a8cff4f0
|
84 |
*/ |
9465efc9e
|
85 |
loff_t |
965c8e59c
|
86 |
generic_file_llseek_size(struct file *file, loff_t offset, int whence, |
e8b96eb50
|
87 |
loff_t maxsize, loff_t eof) |
1da177e4c
|
88 |
{ |
965c8e59c
|
89 |
switch (whence) { |
3a8cff4f0
|
90 |
case SEEK_END: |
e8b96eb50
|
91 |
offset += eof; |
3a8cff4f0
|
92 93 |
break; case SEEK_CUR: |
5b6f1eb97
|
94 95 96 97 98 99 100 101 |
/* * 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; |
ef3d0fd27
|
102 103 104 105 106 107 |
/* * f_lock protects against read/modify/write race with other * SEEK_CURs. Note that parallel writes and reads behave * like SEEK_SET. */ spin_lock(&file->f_lock); |
46a1c2c7a
|
108 |
offset = vfs_setpos(file, file->f_pos + offset, maxsize); |
ef3d0fd27
|
109 110 |
spin_unlock(&file->f_lock); return offset; |
982d81658
|
111 112 113 114 115 |
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. */ |
e8b96eb50
|
116 |
if (offset >= eof) |
982d81658
|
117 118 119 120 121 122 123 |
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. */ |
e8b96eb50
|
124 |
if (offset >= eof) |
982d81658
|
125 |
return -ENXIO; |
e8b96eb50
|
126 |
offset = eof; |
982d81658
|
127 |
break; |
1da177e4c
|
128 |
} |
3a8cff4f0
|
129 |
|
46a1c2c7a
|
130 |
return vfs_setpos(file, offset, maxsize); |
5760495a8
|
131 132 133 134 135 136 137 |
} EXPORT_SYMBOL(generic_file_llseek_size); /** * generic_file_llseek - generic llseek implementation for regular files * @file: file structure to seek on * @offset: file offset to seek to |
965c8e59c
|
138 |
* @whence: type of seek |
5760495a8
|
139 140 141 |
* * This is a generic implemenation of ->llseek useable for all normal local * filesystems. It just updates the file offset to the value specified by |
546ae2d2f
|
142 |
* @offset and @whence. |
5760495a8
|
143 |
*/ |
965c8e59c
|
144 |
loff_t generic_file_llseek(struct file *file, loff_t offset, int whence) |
5760495a8
|
145 146 |
{ struct inode *inode = file->f_mapping->host; |
965c8e59c
|
147 |
return generic_file_llseek_size(file, offset, whence, |
e8b96eb50
|
148 149 |
inode->i_sb->s_maxbytes, i_size_read(inode)); |
1da177e4c
|
150 |
} |
9465efc9e
|
151 |
EXPORT_SYMBOL(generic_file_llseek); |
1da177e4c
|
152 |
|
ae6afc3f5
|
153 |
/** |
1bf9d14df
|
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
* fixed_size_llseek - llseek implementation for fixed-sized devices * @file: file structure to seek on * @offset: file offset to seek to * @whence: type of seek * @size: size of the file * */ loff_t fixed_size_llseek(struct file *file, loff_t offset, int whence, loff_t size) { switch (whence) { case SEEK_SET: case SEEK_CUR: case SEEK_END: return generic_file_llseek_size(file, offset, whence, size, size); default: return -EINVAL; } } EXPORT_SYMBOL(fixed_size_llseek); /** |
ae6afc3f5
|
174 175 176 |
* noop_llseek - No Operation Performed llseek implementation * @file: file structure to seek on * @offset: file offset to seek to |
965c8e59c
|
177 |
* @whence: type of seek |
ae6afc3f5
|
178 179 180 181 182 183 |
* * 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. */ |
965c8e59c
|
184 |
loff_t noop_llseek(struct file *file, loff_t offset, int whence) |
ae6afc3f5
|
185 186 187 188 |
{ return file->f_pos; } EXPORT_SYMBOL(noop_llseek); |
965c8e59c
|
189 |
loff_t no_llseek(struct file *file, loff_t offset, int whence) |
1da177e4c
|
190 191 192 193 |
{ return -ESPIPE; } EXPORT_SYMBOL(no_llseek); |
965c8e59c
|
194 |
loff_t default_llseek(struct file *file, loff_t offset, int whence) |
1da177e4c
|
195 |
{ |
496ad9aa8
|
196 |
struct inode *inode = file_inode(file); |
16abef0e9
|
197 |
loff_t retval; |
1da177e4c
|
198 |
|
982d81658
|
199 |
mutex_lock(&inode->i_mutex); |
965c8e59c
|
200 |
switch (whence) { |
7b8e89249
|
201 |
case SEEK_END: |
982d81658
|
202 |
offset += i_size_read(inode); |
1da177e4c
|
203 |
break; |
7b8e89249
|
204 |
case SEEK_CUR: |
5b6f1eb97
|
205 206 207 208 |
if (offset == 0) { retval = file->f_pos; goto out; } |
1da177e4c
|
209 |
offset += file->f_pos; |
982d81658
|
210 211 212 213 214 215 216 |
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
|
217 218 219 220 |
if (offset >= inode->i_size) { retval = -ENXIO; goto out; } |
982d81658
|
221 222 223 224 225 226 227 |
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
|
228 229 230 231 |
if (offset >= inode->i_size) { retval = -ENXIO; goto out; } |
982d81658
|
232 233 |
offset = inode->i_size; break; |
1da177e4c
|
234 235 |
} retval = -EINVAL; |
cccb5a1e6
|
236 |
if (offset >= 0 || unsigned_offsets(file)) { |
1da177e4c
|
237 238 239 240 241 242 |
if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } retval = offset; } |
5b6f1eb97
|
243 |
out: |
982d81658
|
244 |
mutex_unlock(&inode->i_mutex); |
1da177e4c
|
245 246 247 |
return retval; } EXPORT_SYMBOL(default_llseek); |
965c8e59c
|
248 |
loff_t vfs_llseek(struct file *file, loff_t offset, int whence) |
1da177e4c
|
249 250 251 252 253 |
{ loff_t (*fn)(struct file *, loff_t, int); fn = no_llseek; if (file->f_mode & FMODE_LSEEK) { |
72c2d5319
|
254 |
if (file->f_op->llseek) |
1da177e4c
|
255 256 |
fn = file->f_op->llseek; } |
965c8e59c
|
257 |
return fn(file, offset, whence); |
1da177e4c
|
258 259 |
} EXPORT_SYMBOL(vfs_llseek); |
9c225f265
|
260 261 |
static inline struct fd fdget_pos(int fd) { |
bd2a31d52
|
262 |
return __to_fd(__fdget_pos(fd)); |
9c225f265
|
263 264 265 266 267 268 269 270 |
} static inline void fdput_pos(struct fd f) { if (f.flags & FDPUT_POS_UNLOCK) mutex_unlock(&f.file->f_pos_lock); fdput(f); } |
965c8e59c
|
271 |
SYSCALL_DEFINE3(lseek, unsigned int, fd, off_t, offset, unsigned int, whence) |
1da177e4c
|
272 273 |
{ off_t retval; |
9c225f265
|
274 |
struct fd f = fdget_pos(fd); |
2903ff019
|
275 276 |
if (!f.file) return -EBADF; |
1da177e4c
|
277 278 |
retval = -EINVAL; |
965c8e59c
|
279 280 |
if (whence <= SEEK_MAX) { loff_t res = vfs_llseek(f.file, offset, whence); |
1da177e4c
|
281 282 283 284 |
retval = res; if (res != (loff_t)retval) retval = -EOVERFLOW; /* LFS: should only happen on 32 bit platforms */ } |
9c225f265
|
285 |
fdput_pos(f); |
1da177e4c
|
286 287 |
return retval; } |
561c67319
|
288 289 290 291 292 293 |
#ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE3(lseek, unsigned int, fd, compat_off_t, offset, unsigned int, whence) { return sys_lseek(fd, offset, whence); } #endif |
1da177e4c
|
294 |
#ifdef __ARCH_WANT_SYS_LLSEEK |
003d7ab47
|
295 296 |
SYSCALL_DEFINE5(llseek, unsigned int, fd, unsigned long, offset_high, unsigned long, offset_low, loff_t __user *, result, |
965c8e59c
|
297 |
unsigned int, whence) |
1da177e4c
|
298 299 |
{ int retval; |
d7a15f8d0
|
300 |
struct fd f = fdget_pos(fd); |
1da177e4c
|
301 |
loff_t offset; |
1da177e4c
|
302 |
|
2903ff019
|
303 304 |
if (!f.file) return -EBADF; |
1da177e4c
|
305 306 |
retval = -EINVAL; |
965c8e59c
|
307 |
if (whence > SEEK_MAX) |
1da177e4c
|
308 |
goto out_putf; |
2903ff019
|
309 |
offset = vfs_llseek(f.file, ((loff_t) offset_high << 32) | offset_low, |
965c8e59c
|
310 |
whence); |
1da177e4c
|
311 312 313 314 315 316 317 318 |
retval = (int)offset; if (offset >= 0) { retval = -EFAULT; if (!copy_to_user(result, &offset, sizeof(offset))) retval = 0; } out_putf: |
d7a15f8d0
|
319 |
fdput_pos(f); |
1da177e4c
|
320 321 322 |
return retval; } #endif |
e28cc7157
|
323 324 325 326 327 |
/* * 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. */ |
68d70d03f
|
328 |
int rw_verify_area(int read_write, struct file *file, const loff_t *ppos, size_t count) |
1da177e4c
|
329 330 331 |
{ struct inode *inode; loff_t pos; |
c43e259cc
|
332 |
int retval = -EINVAL; |
1da177e4c
|
333 |
|
496ad9aa8
|
334 |
inode = file_inode(file); |
e28cc7157
|
335 |
if (unlikely((ssize_t) count < 0)) |
c43e259cc
|
336 |
return retval; |
1da177e4c
|
337 |
pos = *ppos; |
cccb5a1e6
|
338 339 340 341 342 343 344 |
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
|
345 346 |
return retval; } |
1da177e4c
|
347 |
|
a16877ca9
|
348 |
if (unlikely(inode->i_flock && mandatory_lock(inode))) { |
c43e259cc
|
349 |
retval = locks_mandatory_area( |
e28cc7157
|
350 351 352 353 354 |
read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, inode, file, pos, count); if (retval < 0) return retval; } |
c43e259cc
|
355 356 357 358 |
retval = security_file_permission(file, read_write == READ ? MAY_READ : MAY_WRITE); if (retval) return retval; |
e28cc7157
|
359 |
return count > MAX_RW_COUNT ? MAX_RW_COUNT : count; |
1da177e4c
|
360 361 362 363 |
} ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { |
027445c37
|
364 |
struct iovec iov = { .iov_base = buf, .iov_len = len }; |
1da177e4c
|
365 366 367 368 369 |
struct kiocb kiocb; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; |
61964eba5
|
370 |
kiocb.ki_nbytes = len; |
027445c37
|
371 |
|
41003a7bc
|
372 |
ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos); |
1da177e4c
|
373 374 375 376 377 378 379 |
if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret; } EXPORT_SYMBOL(do_sync_read); |
293bc9822
|
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { struct iovec iov = { .iov_base = buf, .iov_len = len }; struct kiocb kiocb; struct iov_iter iter; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; kiocb.ki_nbytes = len; iov_iter_init(&iter, READ, &iov, 1, len); ret = filp->f_op->read_iter(&kiocb, &iter); if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret; } EXPORT_SYMBOL(new_sync_read); |
1da177e4c
|
400 401 402 403 404 405 |
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; |
7f7f25e82
|
406 |
if (!(file->f_mode & FMODE_CAN_READ)) |
1da177e4c
|
407 408 409 410 411 |
return -EINVAL; if (unlikely(!access_ok(VERIFY_WRITE, buf, count))) return -EFAULT; ret = rw_verify_area(READ, file, pos, count); |
e28cc7157
|
412 413 |
if (ret >= 0) { count = ret; |
c43e259cc
|
414 415 |
if (file->f_op->read) ret = file->f_op->read(file, buf, count, pos); |
293bc9822
|
416 |
else if (file->f_op->aio_read) |
c43e259cc
|
417 |
ret = do_sync_read(file, buf, count, pos); |
293bc9822
|
418 419 |
else ret = new_sync_read(file, buf, count, pos); |
c43e259cc
|
420 |
if (ret > 0) { |
2a12a9d78
|
421 |
fsnotify_access(file); |
c43e259cc
|
422 |
add_rchar(current, ret); |
1da177e4c
|
423 |
} |
c43e259cc
|
424 |
inc_syscr(current); |
1da177e4c
|
425 426 427 428 429 430 431 432 433 |
} 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
|
434 |
struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; |
1da177e4c
|
435 436 437 438 439 |
struct kiocb kiocb; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; |
61964eba5
|
440 |
kiocb.ki_nbytes = len; |
027445c37
|
441 |
|
41003a7bc
|
442 |
ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos); |
1da177e4c
|
443 444 445 446 447 448 449 |
if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret; } EXPORT_SYMBOL(do_sync_write); |
293bc9822
|
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 |
ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) { struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; struct kiocb kiocb; struct iov_iter iter; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; kiocb.ki_nbytes = len; iov_iter_init(&iter, WRITE, &iov, 1, len); ret = filp->f_op->write_iter(&kiocb, &iter); if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret; } EXPORT_SYMBOL(new_sync_write); |
06ae43f34
|
470 471 472 473 474 |
ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) { mm_segment_t old_fs; const char __user *p; ssize_t ret; |
7f7f25e82
|
475 |
if (!(file->f_mode & FMODE_CAN_WRITE)) |
3e84f48ed
|
476 |
return -EINVAL; |
06ae43f34
|
477 478 479 480 481 482 483 |
old_fs = get_fs(); set_fs(get_ds()); p = (__force const char __user *)buf; if (count > MAX_RW_COUNT) count = MAX_RW_COUNT; if (file->f_op->write) ret = file->f_op->write(file, p, count, pos); |
293bc9822
|
484 |
else if (file->f_op->aio_write) |
06ae43f34
|
485 |
ret = do_sync_write(file, p, count, pos); |
293bc9822
|
486 487 |
else ret = new_sync_write(file, p, count, pos); |
06ae43f34
|
488 489 490 491 492 493 494 495 |
set_fs(old_fs); if (ret > 0) { fsnotify_modify(file); add_wchar(current, ret); } inc_syscw(current); return ret; } |
1da177e4c
|
496 497 498 499 500 501 |
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; |
7f7f25e82
|
502 |
if (!(file->f_mode & FMODE_CAN_WRITE)) |
1da177e4c
|
503 504 505 506 507 |
return -EINVAL; if (unlikely(!access_ok(VERIFY_READ, buf, count))) return -EFAULT; ret = rw_verify_area(WRITE, file, pos, count); |
e28cc7157
|
508 509 |
if (ret >= 0) { count = ret; |
03d95eb2f
|
510 |
file_start_write(file); |
c43e259cc
|
511 512 |
if (file->f_op->write) ret = file->f_op->write(file, buf, count, pos); |
293bc9822
|
513 |
else if (file->f_op->aio_write) |
c43e259cc
|
514 |
ret = do_sync_write(file, buf, count, pos); |
293bc9822
|
515 516 |
else ret = new_sync_write(file, buf, count, pos); |
c43e259cc
|
517 |
if (ret > 0) { |
2a12a9d78
|
518 |
fsnotify_modify(file); |
c43e259cc
|
519 |
add_wchar(current, ret); |
1da177e4c
|
520 |
} |
c43e259cc
|
521 |
inc_syscw(current); |
03d95eb2f
|
522 |
file_end_write(file); |
1da177e4c
|
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 |
} 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
|
539 |
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) |
1da177e4c
|
540 |
{ |
9c225f265
|
541 |
struct fd f = fdget_pos(fd); |
1da177e4c
|
542 |
ssize_t ret = -EBADF; |
1da177e4c
|
543 |
|
2903ff019
|
544 545 546 |
if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_read(f.file, buf, count, &pos); |
5faf153eb
|
547 548 |
if (ret >= 0) file_pos_write(f.file, pos); |
9c225f265
|
549 |
fdput_pos(f); |
1da177e4c
|
550 |
} |
1da177e4c
|
551 552 |
return ret; } |
1da177e4c
|
553 |
|
3cdad4288
|
554 555 |
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count) |
1da177e4c
|
556 |
{ |
9c225f265
|
557 |
struct fd f = fdget_pos(fd); |
1da177e4c
|
558 |
ssize_t ret = -EBADF; |
1da177e4c
|
559 |
|
2903ff019
|
560 561 562 |
if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_write(f.file, buf, count, &pos); |
5faf153eb
|
563 564 |
if (ret >= 0) file_pos_write(f.file, pos); |
9c225f265
|
565 |
fdput_pos(f); |
1da177e4c
|
566 567 568 569 |
} return ret; } |
4a0fd5bf0
|
570 571 |
SYSCALL_DEFINE4(pread64, unsigned int, fd, char __user *, buf, size_t, count, loff_t, pos) |
1da177e4c
|
572 |
{ |
2903ff019
|
573 |
struct fd f; |
1da177e4c
|
574 |
ssize_t ret = -EBADF; |
1da177e4c
|
575 576 577 |
if (pos < 0) return -EINVAL; |
2903ff019
|
578 579 |
f = fdget(fd); if (f.file) { |
1da177e4c
|
580 |
ret = -ESPIPE; |
2903ff019
|
581 582 583 |
if (f.file->f_mode & FMODE_PREAD) ret = vfs_read(f.file, buf, count, &pos); fdput(f); |
1da177e4c
|
584 585 586 587 |
} return ret; } |
4a0fd5bf0
|
588 589 |
SYSCALL_DEFINE4(pwrite64, unsigned int, fd, const char __user *, buf, size_t, count, loff_t, pos) |
1da177e4c
|
590 |
{ |
2903ff019
|
591 |
struct fd f; |
1da177e4c
|
592 |
ssize_t ret = -EBADF; |
1da177e4c
|
593 594 595 |
if (pos < 0) return -EINVAL; |
2903ff019
|
596 597 |
f = fdget(fd); if (f.file) { |
1da177e4c
|
598 |
ret = -ESPIPE; |
2903ff019
|
599 600 601 |
if (f.file->f_mode & FMODE_PWRITE) ret = vfs_write(f.file, buf, count, &pos); fdput(f); |
1da177e4c
|
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 |
} return ret; } /* * 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
|
626 |
EXPORT_SYMBOL(iov_shorten); |
1da177e4c
|
627 |
|
293bc9822
|
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
static ssize_t do_iter_readv_writev(struct file *filp, int rw, const struct iovec *iov, unsigned long nr_segs, size_t len, loff_t *ppos, iter_fn_t fn) { struct kiocb kiocb; struct iov_iter iter; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; kiocb.ki_nbytes = len; iov_iter_init(&iter, rw, iov, nr_segs, len); ret = fn(&kiocb, &iter); if (ret == -EIOCBQUEUED) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret; } |
72ec35163
|
646 |
static ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, |
ee0b3e671
|
647 648 649 650 651 652 653 |
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; |
ee0b3e671
|
654 |
kiocb.ki_nbytes = len; |
41003a7bc
|
655 |
ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos); |
ee0b3e671
|
656 657 658 659 660 661 662 |
if (ret == -EIOCBQUEUED) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret; } /* Do it by hand, with file-ops */ |
72ec35163
|
663 |
static ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov, |
ee0b3e671
|
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 |
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
|
693 694 |
/* A write operation does a read from user space and vice versa */ #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) |
eed4e51fb
|
695 696 697 |
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, |
ac34ebb3a
|
698 |
struct iovec **ret_pointer) |
435f49a51
|
699 |
{ |
eed4e51fb
|
700 |
unsigned long seg; |
435f49a51
|
701 |
ssize_t ret; |
eed4e51fb
|
702 |
struct iovec *iov = fast_pointer; |
435f49a51
|
703 704 705 706 707 |
/* * 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
|
708 709 |
if (nr_segs == 0) { ret = 0; |
435f49a51
|
710 |
goto out; |
eed4e51fb
|
711 |
} |
435f49a51
|
712 713 714 715 |
/* * First get the "struct iovec" from user memory and * verify all the pointers */ |
eed4e51fb
|
716 717 |
if (nr_segs > UIO_MAXIOV) { ret = -EINVAL; |
435f49a51
|
718 |
goto out; |
eed4e51fb
|
719 720 |
} if (nr_segs > fast_segs) { |
435f49a51
|
721 |
iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL); |
eed4e51fb
|
722 723 |
if (iov == NULL) { ret = -ENOMEM; |
435f49a51
|
724 |
goto out; |
eed4e51fb
|
725 |
} |
435f49a51
|
726 |
} |
eed4e51fb
|
727 728 |
if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) { ret = -EFAULT; |
435f49a51
|
729 |
goto out; |
eed4e51fb
|
730 |
} |
435f49a51
|
731 |
/* |
eed4e51fb
|
732 733 734 735 |
* 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
|
736 737 738 739 |
* * Linux caps all read/write calls to MAX_RW_COUNT, and avoids the * overflow case. */ |
eed4e51fb
|
740 |
ret = 0; |
435f49a51
|
741 742 743 |
for (seg = 0; seg < nr_segs; seg++) { void __user *buf = iov[seg].iov_base; ssize_t len = (ssize_t)iov[seg].iov_len; |
eed4e51fb
|
744 745 746 |
/* see if we we're about to use an invalid len or if * it's about to overflow ssize_t */ |
435f49a51
|
747 |
if (len < 0) { |
eed4e51fb
|
748 |
ret = -EINVAL; |
435f49a51
|
749 |
goto out; |
eed4e51fb
|
750 |
} |
ac34ebb3a
|
751 |
if (type >= 0 |
fcf634098
|
752 |
&& unlikely(!access_ok(vrfy_dir(type), buf, len))) { |
eed4e51fb
|
753 |
ret = -EFAULT; |
435f49a51
|
754 755 756 757 758 |
goto out; } if (len > MAX_RW_COUNT - ret) { len = MAX_RW_COUNT - ret; iov[seg].iov_len = len; |
eed4e51fb
|
759 |
} |
eed4e51fb
|
760 |
ret += len; |
435f49a51
|
761 |
} |
eed4e51fb
|
762 763 764 765 |
out: *ret_pointer = iov; return ret; } |
1da177e4c
|
766 767 768 769 |
static ssize_t do_readv_writev(int type, struct file *file, const struct iovec __user * uvector, unsigned long nr_segs, loff_t *pos) { |
1da177e4c
|
770 771 |
size_t tot_len; struct iovec iovstack[UIO_FASTIOV]; |
ee0b3e671
|
772 |
struct iovec *iov = iovstack; |
1da177e4c
|
773 |
ssize_t ret; |
1da177e4c
|
774 775 |
io_fn_t fn; iov_fn_t fnv; |
293bc9822
|
776 |
iter_fn_t iter_fn; |
1da177e4c
|
777 |
|
eed4e51fb
|
778 |
ret = rw_copy_check_uvector(type, uvector, nr_segs, |
ac34ebb3a
|
779 |
ARRAY_SIZE(iovstack), iovstack, &iov); |
eed4e51fb
|
780 |
if (ret <= 0) |
1da177e4c
|
781 |
goto out; |
1da177e4c
|
782 |
|
eed4e51fb
|
783 |
tot_len = ret; |
1da177e4c
|
784 |
ret = rw_verify_area(type, file, pos, tot_len); |
e28cc7157
|
785 |
if (ret < 0) |
1da177e4c
|
786 787 788 789 790 |
goto out; fnv = NULL; if (type == READ) { fn = file->f_op->read; |
ee0b3e671
|
791 |
fnv = file->f_op->aio_read; |
293bc9822
|
792 |
iter_fn = file->f_op->read_iter; |
1da177e4c
|
793 794 |
} else { fn = (io_fn_t)file->f_op->write; |
ee0b3e671
|
795 |
fnv = file->f_op->aio_write; |
293bc9822
|
796 |
iter_fn = file->f_op->write_iter; |
03d95eb2f
|
797 |
file_start_write(file); |
1da177e4c
|
798 |
} |
293bc9822
|
799 800 801 802 |
if (iter_fn) ret = do_iter_readv_writev(file, type, iov, nr_segs, tot_len, pos, iter_fn); else if (fnv) |
ee0b3e671
|
803 804 805 806 |
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
|
807 |
|
03d95eb2f
|
808 809 |
if (type != READ) file_end_write(file); |
1da177e4c
|
810 811 812 |
out: if (iov != iovstack) kfree(iov); |
0eeca2830
|
813 814 |
if ((ret + (type == READ)) > 0) { if (type == READ) |
2a12a9d78
|
815 |
fsnotify_access(file); |
0eeca2830
|
816 |
else |
2a12a9d78
|
817 |
fsnotify_modify(file); |
0eeca2830
|
818 |
} |
1da177e4c
|
819 |
return ret; |
1da177e4c
|
820 821 822 823 824 825 826 |
} 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; |
7f7f25e82
|
827 |
if (!(file->f_mode & FMODE_CAN_READ)) |
1da177e4c
|
828 829 830 831 832 833 834 835 836 837 838 839 |
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; |
7f7f25e82
|
840 |
if (!(file->f_mode & FMODE_CAN_WRITE)) |
1da177e4c
|
841 842 843 844 845 846 |
return -EINVAL; return do_readv_writev(WRITE, file, vec, vlen, pos); } EXPORT_SYMBOL(vfs_writev); |
3cdad4288
|
847 848 |
SYSCALL_DEFINE3(readv, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen) |
1da177e4c
|
849 |
{ |
9c225f265
|
850 |
struct fd f = fdget_pos(fd); |
1da177e4c
|
851 |
ssize_t ret = -EBADF; |
1da177e4c
|
852 |
|
2903ff019
|
853 854 855 |
if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_readv(f.file, vec, vlen, &pos); |
5faf153eb
|
856 857 |
if (ret >= 0) file_pos_write(f.file, pos); |
9c225f265
|
858 |
fdput_pos(f); |
1da177e4c
|
859 860 861 |
} if (ret > 0) |
4b98d11b4
|
862 863 |
add_rchar(current, ret); inc_syscr(current); |
1da177e4c
|
864 865 |
return ret; } |
3cdad4288
|
866 867 |
SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen) |
1da177e4c
|
868 |
{ |
9c225f265
|
869 |
struct fd f = fdget_pos(fd); |
1da177e4c
|
870 |
ssize_t ret = -EBADF; |
1da177e4c
|
871 |
|
2903ff019
|
872 873 874 |
if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_writev(f.file, vec, vlen, &pos); |
5faf153eb
|
875 876 |
if (ret >= 0) file_pos_write(f.file, pos); |
9c225f265
|
877 |
fdput_pos(f); |
1da177e4c
|
878 879 880 |
} if (ret > 0) |
4b98d11b4
|
881 882 |
add_wchar(current, ret); inc_syscw(current); |
1da177e4c
|
883 884 |
return ret; } |
601cc11d0
|
885 886 887 888 889 |
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
|
890 |
SYSCALL_DEFINE5(preadv, unsigned long, fd, const struct iovec __user *, vec, |
601cc11d0
|
891 |
unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h) |
f3554f4bc
|
892 |
{ |
601cc11d0
|
893 |
loff_t pos = pos_from_hilo(pos_h, pos_l); |
2903ff019
|
894 |
struct fd f; |
f3554f4bc
|
895 |
ssize_t ret = -EBADF; |
f3554f4bc
|
896 897 898 |
if (pos < 0) return -EINVAL; |
2903ff019
|
899 900 |
f = fdget(fd); if (f.file) { |
f3554f4bc
|
901 |
ret = -ESPIPE; |
2903ff019
|
902 903 904 |
if (f.file->f_mode & FMODE_PREAD) ret = vfs_readv(f.file, vec, vlen, &pos); fdput(f); |
f3554f4bc
|
905 906 907 908 909 910 911 912 913 |
} if (ret > 0) add_rchar(current, ret); inc_syscr(current); return ret; } SYSCALL_DEFINE5(pwritev, unsigned long, fd, const struct iovec __user *, vec, |
601cc11d0
|
914 |
unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h) |
f3554f4bc
|
915 |
{ |
601cc11d0
|
916 |
loff_t pos = pos_from_hilo(pos_h, pos_l); |
2903ff019
|
917 |
struct fd f; |
f3554f4bc
|
918 |
ssize_t ret = -EBADF; |
f3554f4bc
|
919 920 921 |
if (pos < 0) return -EINVAL; |
2903ff019
|
922 923 |
f = fdget(fd); if (f.file) { |
f3554f4bc
|
924 |
ret = -ESPIPE; |
2903ff019
|
925 926 927 |
if (f.file->f_mode & FMODE_PWRITE) ret = vfs_writev(f.file, vec, vlen, &pos); fdput(f); |
f3554f4bc
|
928 929 930 931 932 933 934 |
} if (ret > 0) add_wchar(current, ret); inc_syscw(current); return ret; } |
72ec35163
|
935 936 937 938 939 940 941 942 943 944 945 946 |
#ifdef CONFIG_COMPAT static ssize_t compat_do_readv_writev(int type, struct file *file, const struct compat_iovec __user *uvector, unsigned long nr_segs, loff_t *pos) { compat_ssize_t tot_len; struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov = iovstack; ssize_t ret; io_fn_t fn; iov_fn_t fnv; |
293bc9822
|
947 |
iter_fn_t iter_fn; |
72ec35163
|
948 |
|
72ec35163
|
949 950 951 952 953 954 955 956 957 958 959 960 961 962 |
ret = compat_rw_copy_check_uvector(type, uvector, nr_segs, UIO_FASTIOV, iovstack, &iov); if (ret <= 0) goto out; tot_len = ret; ret = rw_verify_area(type, file, pos, tot_len); if (ret < 0) goto out; fnv = NULL; if (type == READ) { fn = file->f_op->read; fnv = file->f_op->aio_read; |
293bc9822
|
963 |
iter_fn = file->f_op->read_iter; |
72ec35163
|
964 965 966 |
} else { fn = (io_fn_t)file->f_op->write; fnv = file->f_op->aio_write; |
293bc9822
|
967 |
iter_fn = file->f_op->write_iter; |
03d95eb2f
|
968 |
file_start_write(file); |
72ec35163
|
969 |
} |
293bc9822
|
970 971 972 973 |
if (iter_fn) ret = do_iter_readv_writev(file, type, iov, nr_segs, tot_len, pos, iter_fn); else if (fnv) |
72ec35163
|
974 975 |
ret = do_sync_readv_writev(file, iov, nr_segs, tot_len, pos, fnv); |
03d95eb2f
|
976 |
else |
72ec35163
|
977 |
ret = do_loop_readv_writev(file, iov, nr_segs, pos, fn); |
03d95eb2f
|
978 979 |
if (type != READ) file_end_write(file); |
72ec35163
|
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 |
out: if (iov != iovstack) kfree(iov); if ((ret + (type == READ)) > 0) { if (type == READ) fsnotify_access(file); else fsnotify_modify(file); } return ret; } static size_t compat_readv(struct file *file, const struct compat_iovec __user *vec, unsigned long vlen, loff_t *pos) { ssize_t ret = -EBADF; if (!(file->f_mode & FMODE_READ)) goto out; ret = -EINVAL; |
7f7f25e82
|
1002 |
if (!(file->f_mode & FMODE_CAN_READ)) |
72ec35163
|
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 |
goto out; ret = compat_do_readv_writev(READ, file, vec, vlen, pos); out: if (ret > 0) add_rchar(current, ret); inc_syscr(current); return ret; } |
dfd948e32
|
1013 |
COMPAT_SYSCALL_DEFINE3(readv, compat_ulong_t, fd, |
72ec35163
|
1014 |
const struct compat_iovec __user *,vec, |
dfd948e32
|
1015 |
compat_ulong_t, vlen) |
72ec35163
|
1016 |
{ |
9c225f265
|
1017 |
struct fd f = fdget_pos(fd); |
72ec35163
|
1018 1019 1020 1021 1022 1023 1024 |
ssize_t ret; loff_t pos; if (!f.file) return -EBADF; pos = f.file->f_pos; ret = compat_readv(f.file, vec, vlen, &pos); |
5faf153eb
|
1025 1026 |
if (ret >= 0) f.file->f_pos = pos; |
9c225f265
|
1027 |
fdput_pos(f); |
72ec35163
|
1028 1029 |
return ret; } |
378a10f3a
|
1030 1031 1032 |
static long __compat_sys_preadv64(unsigned long fd, const struct compat_iovec __user *vec, unsigned long vlen, loff_t pos) |
72ec35163
|
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 |
{ struct fd f; ssize_t ret; if (pos < 0) return -EINVAL; f = fdget(fd); if (!f.file) return -EBADF; ret = -ESPIPE; if (f.file->f_mode & FMODE_PREAD) ret = compat_readv(f.file, vec, vlen, &pos); fdput(f); return ret; } |
378a10f3a
|
1048 1049 1050 1051 1052 1053 1054 1055 |
#ifdef __ARCH_WANT_COMPAT_SYS_PREADV64 COMPAT_SYSCALL_DEFINE4(preadv64, unsigned long, fd, const struct compat_iovec __user *,vec, unsigned long, vlen, loff_t, pos) { return __compat_sys_preadv64(fd, vec, vlen, pos); } #endif |
dfd948e32
|
1056 |
COMPAT_SYSCALL_DEFINE5(preadv, compat_ulong_t, fd, |
72ec35163
|
1057 |
const struct compat_iovec __user *,vec, |
dfd948e32
|
1058 |
compat_ulong_t, vlen, u32, pos_low, u32, pos_high) |
72ec35163
|
1059 1060 |
{ loff_t pos = ((loff_t)pos_high << 32) | pos_low; |
378a10f3a
|
1061 1062 |
return __compat_sys_preadv64(fd, vec, vlen, pos); |
72ec35163
|
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 |
} static size_t compat_writev(struct file *file, const struct compat_iovec __user *vec, unsigned long vlen, loff_t *pos) { ssize_t ret = -EBADF; if (!(file->f_mode & FMODE_WRITE)) goto out; ret = -EINVAL; |
7f7f25e82
|
1075 |
if (!(file->f_mode & FMODE_CAN_WRITE)) |
72ec35163
|
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 |
goto out; ret = compat_do_readv_writev(WRITE, file, vec, vlen, pos); out: if (ret > 0) add_wchar(current, ret); inc_syscw(current); return ret; } |
dfd948e32
|
1086 |
COMPAT_SYSCALL_DEFINE3(writev, compat_ulong_t, fd, |
72ec35163
|
1087 |
const struct compat_iovec __user *, vec, |
dfd948e32
|
1088 |
compat_ulong_t, vlen) |
72ec35163
|
1089 |
{ |
9c225f265
|
1090 |
struct fd f = fdget_pos(fd); |
72ec35163
|
1091 1092 1093 1094 1095 1096 1097 |
ssize_t ret; loff_t pos; if (!f.file) return -EBADF; pos = f.file->f_pos; ret = compat_writev(f.file, vec, vlen, &pos); |
5faf153eb
|
1098 1099 |
if (ret >= 0) f.file->f_pos = pos; |
9c225f265
|
1100 |
fdput_pos(f); |
72ec35163
|
1101 1102 |
return ret; } |
378a10f3a
|
1103 1104 1105 |
static long __compat_sys_pwritev64(unsigned long fd, const struct compat_iovec __user *vec, unsigned long vlen, loff_t pos) |
72ec35163
|
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 |
{ struct fd f; ssize_t ret; if (pos < 0) return -EINVAL; f = fdget(fd); if (!f.file) return -EBADF; ret = -ESPIPE; if (f.file->f_mode & FMODE_PWRITE) ret = compat_writev(f.file, vec, vlen, &pos); fdput(f); return ret; } |
378a10f3a
|
1121 1122 1123 1124 1125 1126 1127 1128 |
#ifdef __ARCH_WANT_COMPAT_SYS_PWRITEV64 COMPAT_SYSCALL_DEFINE4(pwritev64, unsigned long, fd, const struct compat_iovec __user *,vec, unsigned long, vlen, loff_t, pos) { return __compat_sys_pwritev64(fd, vec, vlen, pos); } #endif |
dfd948e32
|
1129 |
COMPAT_SYSCALL_DEFINE5(pwritev, compat_ulong_t, fd, |
72ec35163
|
1130 |
const struct compat_iovec __user *,vec, |
dfd948e32
|
1131 |
compat_ulong_t, vlen, u32, pos_low, u32, pos_high) |
72ec35163
|
1132 1133 |
{ loff_t pos = ((loff_t)pos_high << 32) | pos_low; |
378a10f3a
|
1134 1135 |
return __compat_sys_pwritev64(fd, vec, vlen, pos); |
72ec35163
|
1136 1137 |
} #endif |
19f4fc3ae
|
1138 1139 |
static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, size_t count, loff_t max) |
1da177e4c
|
1140 |
{ |
2903ff019
|
1141 1142 |
struct fd in, out; struct inode *in_inode, *out_inode; |
1da177e4c
|
1143 |
loff_t pos; |
7995bd287
|
1144 |
loff_t out_pos; |
1da177e4c
|
1145 |
ssize_t retval; |
2903ff019
|
1146 |
int fl; |
1da177e4c
|
1147 1148 1149 1150 1151 |
/* * Get input file, and verify that it is ok.. */ retval = -EBADF; |
2903ff019
|
1152 1153 |
in = fdget(in_fd); if (!in.file) |
1da177e4c
|
1154 |
goto out; |
2903ff019
|
1155 |
if (!(in.file->f_mode & FMODE_READ)) |
1da177e4c
|
1156 |
goto fput_in; |
1da177e4c
|
1157 |
retval = -ESPIPE; |
7995bd287
|
1158 1159 1160 1161 |
if (!ppos) { pos = in.file->f_pos; } else { pos = *ppos; |
2903ff019
|
1162 |
if (!(in.file->f_mode & FMODE_PREAD)) |
1da177e4c
|
1163 |
goto fput_in; |
7995bd287
|
1164 1165 |
} retval = rw_verify_area(READ, in.file, &pos, count); |
e28cc7157
|
1166 |
if (retval < 0) |
1da177e4c
|
1167 |
goto fput_in; |
e28cc7157
|
1168 |
count = retval; |
1da177e4c
|
1169 |
|
1da177e4c
|
1170 1171 1172 1173 |
/* * Get output file, and verify that it is ok.. */ retval = -EBADF; |
2903ff019
|
1174 1175 |
out = fdget(out_fd); if (!out.file) |
1da177e4c
|
1176 |
goto fput_in; |
2903ff019
|
1177 |
if (!(out.file->f_mode & FMODE_WRITE)) |
1da177e4c
|
1178 1179 |
goto fput_out; retval = -EINVAL; |
496ad9aa8
|
1180 1181 |
in_inode = file_inode(in.file); out_inode = file_inode(out.file); |
7995bd287
|
1182 1183 |
out_pos = out.file->f_pos; retval = rw_verify_area(WRITE, out.file, &out_pos, count); |
e28cc7157
|
1184 |
if (retval < 0) |
1da177e4c
|
1185 |
goto fput_out; |
e28cc7157
|
1186 |
count = retval; |
1da177e4c
|
1187 |
|
1da177e4c
|
1188 1189 |
if (!max) max = min(in_inode->i_sb->s_maxbytes, out_inode->i_sb->s_maxbytes); |
1da177e4c
|
1190 1191 1192 1193 1194 1195 |
if (unlikely(pos + count > max)) { retval = -EOVERFLOW; if (pos >= max) goto fput_out; count = max - pos; } |
d96e6e716
|
1196 |
fl = 0; |
534f2aaa6
|
1197 |
#if 0 |
d96e6e716
|
1198 1199 1200 1201 1202 1203 |
/* * 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. */ |
2903ff019
|
1204 |
if (in.file->f_flags & O_NONBLOCK) |
d96e6e716
|
1205 |
fl = SPLICE_F_NONBLOCK; |
534f2aaa6
|
1206 |
#endif |
50cd2c577
|
1207 |
file_start_write(out.file); |
7995bd287
|
1208 |
retval = do_splice_direct(in.file, &pos, out.file, &out_pos, count, fl); |
50cd2c577
|
1209 |
file_end_write(out.file); |
1da177e4c
|
1210 1211 |
if (retval > 0) { |
4b98d11b4
|
1212 1213 |
add_rchar(current, retval); add_wchar(current, retval); |
a68c2f12b
|
1214 1215 |
fsnotify_access(in.file); fsnotify_modify(out.file); |
7995bd287
|
1216 1217 1218 1219 1220 |
out.file->f_pos = out_pos; if (ppos) *ppos = pos; else in.file->f_pos = pos; |
1da177e4c
|
1221 |
} |
1da177e4c
|
1222 |
|
4b98d11b4
|
1223 1224 |
inc_syscr(current); inc_syscw(current); |
7995bd287
|
1225 |
if (pos > max) |
1da177e4c
|
1226 1227 1228 |
retval = -EOVERFLOW; fput_out: |
2903ff019
|
1229 |
fdput(out); |
1da177e4c
|
1230 |
fput_in: |
2903ff019
|
1231 |
fdput(in); |
1da177e4c
|
1232 1233 1234 |
out: return retval; } |
002c8976e
|
1235 |
SYSCALL_DEFINE4(sendfile, int, out_fd, int, in_fd, off_t __user *, offset, size_t, count) |
1da177e4c
|
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 |
{ 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
|
1253 |
SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count) |
1da177e4c
|
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 |
{ 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); } |
19f4fc3ae
|
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 |
#ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE4(sendfile, int, out_fd, int, in_fd, compat_off_t __user *, offset, compat_size_t, count) { 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); } COMPAT_SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, compat_loff_t __user *, offset, compat_size_t, count) { 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); } #endif |