diff options
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 28 |
1 files changed, 25 insertions, 3 deletions
diff --git a/fs/splice.c b/fs/splice.c index d4664a297bab..b150493b6fc3 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -141,7 +141,10 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = { | |||
141 | static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, | 141 | static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, |
142 | struct pipe_buffer *buf) | 142 | struct pipe_buffer *buf) |
143 | { | 143 | { |
144 | return 1; | 144 | if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) |
145 | return 1; | ||
146 | |||
147 | return 0; | ||
145 | } | 148 | } |
146 | 149 | ||
147 | static struct pipe_buf_operations user_page_pipe_buf_ops = { | 150 | static struct pipe_buf_operations user_page_pipe_buf_ops = { |
@@ -186,6 +189,9 @@ static ssize_t splice_to_pipe(struct pipe_inode_info *pipe, | |||
186 | buf->offset = spd->partial[page_nr].offset; | 189 | buf->offset = spd->partial[page_nr].offset; |
187 | buf->len = spd->partial[page_nr].len; | 190 | buf->len = spd->partial[page_nr].len; |
188 | buf->ops = spd->ops; | 191 | buf->ops = spd->ops; |
192 | if (spd->flags & SPLICE_F_GIFT) | ||
193 | buf->flags |= PIPE_BUF_FLAG_GIFT; | ||
194 | |||
189 | pipe->nrbufs++; | 195 | pipe->nrbufs++; |
190 | page_nr++; | 196 | page_nr++; |
191 | ret += buf->len; | 197 | ret += buf->len; |
@@ -1073,7 +1079,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, | |||
1073 | */ | 1079 | */ |
1074 | static int get_iovec_page_array(const struct iovec __user *iov, | 1080 | static int get_iovec_page_array(const struct iovec __user *iov, |
1075 | unsigned int nr_vecs, struct page **pages, | 1081 | unsigned int nr_vecs, struct page **pages, |
1076 | struct partial_page *partial) | 1082 | struct partial_page *partial, int aligned) |
1077 | { | 1083 | { |
1078 | int buffers = 0, error = 0; | 1084 | int buffers = 0, error = 0; |
1079 | 1085 | ||
@@ -1113,6 +1119,15 @@ static int get_iovec_page_array(const struct iovec __user *iov, | |||
1113 | * in the user pages. | 1119 | * in the user pages. |
1114 | */ | 1120 | */ |
1115 | off = (unsigned long) base & ~PAGE_MASK; | 1121 | off = (unsigned long) base & ~PAGE_MASK; |
1122 | |||
1123 | /* | ||
1124 | * If asked for alignment, the offset must be zero and the | ||
1125 | * length a multiple of the PAGE_SIZE. | ||
1126 | */ | ||
1127 | error = -EINVAL; | ||
1128 | if (aligned && (off || len & ~PAGE_MASK)) | ||
1129 | break; | ||
1130 | |||
1116 | npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT; | 1131 | npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
1117 | if (npages > PIPE_BUFFERS - buffers) | 1132 | if (npages > PIPE_BUFFERS - buffers) |
1118 | npages = PIPE_BUFFERS - buffers; | 1133 | npages = PIPE_BUFFERS - buffers; |
@@ -1206,7 +1221,8 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov, | |||
1206 | else if (unlikely(!nr_segs)) | 1221 | else if (unlikely(!nr_segs)) |
1207 | return 0; | 1222 | return 0; |
1208 | 1223 | ||
1209 | spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial); | 1224 | spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial, |
1225 | flags & SPLICE_F_GIFT); | ||
1210 | if (spd.nr_pages <= 0) | 1226 | if (spd.nr_pages <= 0) |
1211 | return spd.nr_pages; | 1227 | return spd.nr_pages; |
1212 | 1228 | ||
@@ -1314,6 +1330,12 @@ static int link_pipe(struct pipe_inode_info *ipipe, | |||
1314 | obuf = opipe->bufs + nbuf; | 1330 | obuf = opipe->bufs + nbuf; |
1315 | *obuf = *ibuf; | 1331 | *obuf = *ibuf; |
1316 | 1332 | ||
1333 | /* | ||
1334 | * Don't inherit the gift flag, we need to | ||
1335 | * prevent multiple steals of this page. | ||
1336 | */ | ||
1337 | obuf->flags &= ~PIPE_BUF_FLAG_GIFT; | ||
1338 | |||
1317 | if (obuf->len > len) | 1339 | if (obuf->len > len) |
1318 | obuf->len = len; | 1340 | obuf->len = len; |
1319 | 1341 | ||