diff options
author | Changli Gao <xiaosuo@gmail.com> | 2009-11-04 03:09:52 -0500 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-11-04 03:09:52 -0500 |
commit | cc56f7de7f00d188c7c4da1e9861581853b9e92f (patch) | |
tree | 4794c8e5b7c7aec176f695dfc9d723d4a73e3ba9 /fs/splice.c | |
parent | f21121cde6e617b90cd03ce083652ca543004dc2 (diff) |
sendfile(): check f_op.splice_write() rather than f_op.sendpage()
sendfile(2) was reworked with the splice infrastructure, but it still
checks f_op.sendpage() instead of f_op.splice_write() wrongly. Although
if f_op.sendpage() exists, f_op.splice_write() always exists at the same
time currently, the assumption will be broken in future silently. This
patch also brings a side effect: sendfile(2) can work with any output
file. Some security checks related to f_op are added too.
Signed-off-by: Changli Gao <xiaosuo@gmail.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 24 |
1 files changed, 15 insertions, 9 deletions
diff --git a/fs/splice.c b/fs/splice.c index 7394e9e17534..39208663aaf1 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -648,9 +648,11 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe, | |||
648 | ret = buf->ops->confirm(pipe, buf); | 648 | ret = buf->ops->confirm(pipe, buf); |
649 | if (!ret) { | 649 | if (!ret) { |
650 | more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; | 650 | more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; |
651 | 651 | if (file->f_op && file->f_op->sendpage) | |
652 | ret = file->f_op->sendpage(file, buf->page, buf->offset, | 652 | ret = file->f_op->sendpage(file, buf->page, buf->offset, |
653 | sd->len, &pos, more); | 653 | sd->len, &pos, more); |
654 | else | ||
655 | ret = -EINVAL; | ||
654 | } | 656 | } |
655 | 657 | ||
656 | return ret; | 658 | return ret; |
@@ -1068,8 +1070,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, | |||
1068 | if (unlikely(ret < 0)) | 1070 | if (unlikely(ret < 0)) |
1069 | return ret; | 1071 | return ret; |
1070 | 1072 | ||
1071 | splice_write = out->f_op->splice_write; | 1073 | if (out->f_op && out->f_op->splice_write) |
1072 | if (!splice_write) | 1074 | splice_write = out->f_op->splice_write; |
1075 | else | ||
1073 | splice_write = default_file_splice_write; | 1076 | splice_write = default_file_splice_write; |
1074 | 1077 | ||
1075 | return splice_write(pipe, out, ppos, len, flags); | 1078 | return splice_write(pipe, out, ppos, len, flags); |
@@ -1093,8 +1096,9 @@ static long do_splice_to(struct file *in, loff_t *ppos, | |||
1093 | if (unlikely(ret < 0)) | 1096 | if (unlikely(ret < 0)) |
1094 | return ret; | 1097 | return ret; |
1095 | 1098 | ||
1096 | splice_read = in->f_op->splice_read; | 1099 | if (in->f_op && in->f_op->splice_read) |
1097 | if (!splice_read) | 1100 | splice_read = in->f_op->splice_read; |
1101 | else | ||
1098 | splice_read = default_file_splice_read; | 1102 | splice_read = default_file_splice_read; |
1099 | 1103 | ||
1100 | return splice_read(in, ppos, pipe, len, flags); | 1104 | return splice_read(in, ppos, pipe, len, flags); |
@@ -1316,7 +1320,8 @@ static long do_splice(struct file *in, loff_t __user *off_in, | |||
1316 | if (off_in) | 1320 | if (off_in) |
1317 | return -ESPIPE; | 1321 | return -ESPIPE; |
1318 | if (off_out) { | 1322 | if (off_out) { |
1319 | if (out->f_op->llseek == no_llseek) | 1323 | if (!out->f_op || !out->f_op->llseek || |
1324 | out->f_op->llseek == no_llseek) | ||
1320 | return -EINVAL; | 1325 | return -EINVAL; |
1321 | if (copy_from_user(&offset, off_out, sizeof(loff_t))) | 1326 | if (copy_from_user(&offset, off_out, sizeof(loff_t))) |
1322 | return -EFAULT; | 1327 | return -EFAULT; |
@@ -1336,7 +1341,8 @@ static long do_splice(struct file *in, loff_t __user *off_in, | |||
1336 | if (off_out) | 1341 | if (off_out) |
1337 | return -ESPIPE; | 1342 | return -ESPIPE; |
1338 | if (off_in) { | 1343 | if (off_in) { |
1339 | if (in->f_op->llseek == no_llseek) | 1344 | if (!in->f_op || !in->f_op->llseek || |
1345 | in->f_op->llseek == no_llseek) | ||
1340 | return -EINVAL; | 1346 | return -EINVAL; |
1341 | if (copy_from_user(&offset, off_in, sizeof(loff_t))) | 1347 | if (copy_from_user(&offset, off_in, sizeof(loff_t))) |
1342 | return -EFAULT; | 1348 | return -EFAULT; |