diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2018-05-26 21:39:52 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2018-06-11 02:14:04 -0400 |
commit | 87a3002af9e30852ce90a028f8259c9e399fd888 (patch) | |
tree | a98b9ac17c4cecfc7f4f4d53acf4a8b7977b1bc0 | |
parent | 4faa99965e027cc057c5145ce45fa772caa04e8d (diff) |
vmsplice(): lift importing iovec into vmsplice(2) and compat counterpart
... getting rid of transformations in the latter - just use
compat_import_iovec().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/splice.c | 144 |
1 files changed, 75 insertions, 69 deletions
diff --git a/fs/splice.c b/fs/splice.c index 005d09cf3fa8..e3d0ae5383e7 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -1242,38 +1242,26 @@ static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf, | |||
1242 | * For lack of a better implementation, implement vmsplice() to userspace | 1242 | * For lack of a better implementation, implement vmsplice() to userspace |
1243 | * as a simple copy of the pipes pages to the user iov. | 1243 | * as a simple copy of the pipes pages to the user iov. |
1244 | */ | 1244 | */ |
1245 | static long vmsplice_to_user(struct file *file, const struct iovec __user *uiov, | 1245 | static long vmsplice_to_user(struct file *file, struct iov_iter *iter, |
1246 | unsigned long nr_segs, unsigned int flags) | 1246 | unsigned int flags) |
1247 | { | 1247 | { |
1248 | struct pipe_inode_info *pipe; | 1248 | struct pipe_inode_info *pipe = get_pipe_info(file); |
1249 | struct splice_desc sd; | 1249 | struct splice_desc sd = { |
1250 | long ret; | 1250 | .total_len = iov_iter_count(iter), |
1251 | struct iovec iovstack[UIO_FASTIOV]; | 1251 | .flags = flags, |
1252 | struct iovec *iov = iovstack; | 1252 | .u.data = iter |
1253 | struct iov_iter iter; | 1253 | }; |
1254 | long ret = 0; | ||
1254 | 1255 | ||
1255 | pipe = get_pipe_info(file); | ||
1256 | if (!pipe) | 1256 | if (!pipe) |
1257 | return -EBADF; | 1257 | return -EBADF; |
1258 | 1258 | ||
1259 | ret = import_iovec(READ, uiov, nr_segs, | ||
1260 | ARRAY_SIZE(iovstack), &iov, &iter); | ||
1261 | if (ret < 0) | ||
1262 | return ret; | ||
1263 | |||
1264 | sd.total_len = iov_iter_count(&iter); | ||
1265 | sd.len = 0; | ||
1266 | sd.flags = flags; | ||
1267 | sd.u.data = &iter; | ||
1268 | sd.pos = 0; | ||
1269 | |||
1270 | if (sd.total_len) { | 1259 | if (sd.total_len) { |
1271 | pipe_lock(pipe); | 1260 | pipe_lock(pipe); |
1272 | ret = __splice_from_pipe(pipe, &sd, pipe_to_user); | 1261 | ret = __splice_from_pipe(pipe, &sd, pipe_to_user); |
1273 | pipe_unlock(pipe); | 1262 | pipe_unlock(pipe); |
1274 | } | 1263 | } |
1275 | 1264 | ||
1276 | kfree(iov); | ||
1277 | return ret; | 1265 | return ret; |
1278 | } | 1266 | } |
1279 | 1267 | ||
@@ -1282,14 +1270,11 @@ static long vmsplice_to_user(struct file *file, const struct iovec __user *uiov, | |||
1282 | * as splice-from-memory, where the regular splice is splice-from-file (or | 1270 | * as splice-from-memory, where the regular splice is splice-from-file (or |
1283 | * to file). In both cases the output is a pipe, naturally. | 1271 | * to file). In both cases the output is a pipe, naturally. |
1284 | */ | 1272 | */ |
1285 | static long vmsplice_to_pipe(struct file *file, const struct iovec __user *uiov, | 1273 | static long vmsplice_to_pipe(struct file *file, struct iov_iter *iter, |
1286 | unsigned long nr_segs, unsigned int flags) | 1274 | unsigned int flags) |
1287 | { | 1275 | { |
1288 | struct pipe_inode_info *pipe; | 1276 | struct pipe_inode_info *pipe; |
1289 | struct iovec iovstack[UIO_FASTIOV]; | 1277 | long ret = 0; |
1290 | struct iovec *iov = iovstack; | ||
1291 | struct iov_iter from; | ||
1292 | long ret; | ||
1293 | unsigned buf_flag = 0; | 1278 | unsigned buf_flag = 0; |
1294 | 1279 | ||
1295 | if (flags & SPLICE_F_GIFT) | 1280 | if (flags & SPLICE_F_GIFT) |
@@ -1299,22 +1284,31 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *uiov, | |||
1299 | if (!pipe) | 1284 | if (!pipe) |
1300 | return -EBADF; | 1285 | return -EBADF; |
1301 | 1286 | ||
1302 | ret = import_iovec(WRITE, uiov, nr_segs, | ||
1303 | ARRAY_SIZE(iovstack), &iov, &from); | ||
1304 | if (ret < 0) | ||
1305 | return ret; | ||
1306 | |||
1307 | pipe_lock(pipe); | 1287 | pipe_lock(pipe); |
1308 | ret = wait_for_space(pipe, flags); | 1288 | ret = wait_for_space(pipe, flags); |
1309 | if (!ret) | 1289 | if (!ret) |
1310 | ret = iter_to_pipe(&from, pipe, buf_flag); | 1290 | ret = iter_to_pipe(iter, pipe, buf_flag); |
1311 | pipe_unlock(pipe); | 1291 | pipe_unlock(pipe); |
1312 | if (ret > 0) | 1292 | if (ret > 0) |
1313 | wakeup_pipe_readers(pipe); | 1293 | wakeup_pipe_readers(pipe); |
1314 | kfree(iov); | ||
1315 | return ret; | 1294 | return ret; |
1316 | } | 1295 | } |
1317 | 1296 | ||
1297 | static int vmsplice_type(struct fd f, int *type) | ||
1298 | { | ||
1299 | if (!f.file) | ||
1300 | return -EBADF; | ||
1301 | if (f.file->f_mode & FMODE_WRITE) { | ||
1302 | *type = WRITE; | ||
1303 | } else if (f.file->f_mode & FMODE_READ) { | ||
1304 | *type = READ; | ||
1305 | } else { | ||
1306 | fdput(f); | ||
1307 | return -EBADF; | ||
1308 | } | ||
1309 | return 0; | ||
1310 | } | ||
1311 | |||
1318 | /* | 1312 | /* |
1319 | * Note that vmsplice only really supports true splicing _from_ user memory | 1313 | * Note that vmsplice only really supports true splicing _from_ user memory |
1320 | * to a pipe, not the other way around. Splicing from user memory is a simple | 1314 | * to a pipe, not the other way around. Splicing from user memory is a simple |
@@ -1331,57 +1325,69 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *uiov, | |||
1331 | * Currently we punt and implement it as a normal copy, see pipe_to_user(). | 1325 | * Currently we punt and implement it as a normal copy, see pipe_to_user(). |
1332 | * | 1326 | * |
1333 | */ | 1327 | */ |
1334 | static long do_vmsplice(int fd, const struct iovec __user *iov, | 1328 | static long do_vmsplice(struct file *f, struct iov_iter *iter, unsigned int flags) |
1335 | unsigned long nr_segs, unsigned int flags) | ||
1336 | { | 1329 | { |
1337 | struct fd f; | ||
1338 | long error; | ||
1339 | |||
1340 | if (unlikely(flags & ~SPLICE_F_ALL)) | 1330 | if (unlikely(flags & ~SPLICE_F_ALL)) |
1341 | return -EINVAL; | 1331 | return -EINVAL; |
1342 | if (unlikely(nr_segs > UIO_MAXIOV)) | ||
1343 | return -EINVAL; | ||
1344 | else if (unlikely(!nr_segs)) | ||
1345 | return 0; | ||
1346 | 1332 | ||
1347 | error = -EBADF; | 1333 | if (!iov_iter_count(iter)) |
1348 | f = fdget(fd); | 1334 | return 0; |
1349 | if (f.file) { | ||
1350 | if (f.file->f_mode & FMODE_WRITE) | ||
1351 | error = vmsplice_to_pipe(f.file, iov, nr_segs, flags); | ||
1352 | else if (f.file->f_mode & FMODE_READ) | ||
1353 | error = vmsplice_to_user(f.file, iov, nr_segs, flags); | ||
1354 | |||
1355 | fdput(f); | ||
1356 | } | ||
1357 | 1335 | ||
1358 | return error; | 1336 | if (iov_iter_rw(iter) == WRITE) |
1337 | return vmsplice_to_pipe(f, iter, flags); | ||
1338 | else | ||
1339 | return vmsplice_to_user(f, iter, flags); | ||
1359 | } | 1340 | } |
1360 | 1341 | ||
1361 | SYSCALL_DEFINE4(vmsplice, int, fd, const struct iovec __user *, iov, | 1342 | SYSCALL_DEFINE4(vmsplice, int, fd, const struct iovec __user *, uiov, |
1362 | unsigned long, nr_segs, unsigned int, flags) | 1343 | unsigned long, nr_segs, unsigned int, flags) |
1363 | { | 1344 | { |
1364 | return do_vmsplice(fd, iov, nr_segs, flags); | 1345 | struct iovec iovstack[UIO_FASTIOV]; |
1346 | struct iovec *iov = iovstack; | ||
1347 | struct iov_iter iter; | ||
1348 | long error; | ||
1349 | struct fd f; | ||
1350 | int type; | ||
1351 | |||
1352 | f = fdget(fd); | ||
1353 | error = vmsplice_type(f, &type); | ||
1354 | if (error) | ||
1355 | return error; | ||
1356 | |||
1357 | error = import_iovec(type, uiov, nr_segs, | ||
1358 | ARRAY_SIZE(iovstack), &iov, &iter); | ||
1359 | if (!error) { | ||
1360 | error = do_vmsplice(f.file, &iter, flags); | ||
1361 | kfree(iov); | ||
1362 | } | ||
1363 | fdput(f); | ||
1364 | return error; | ||
1365 | } | 1365 | } |
1366 | 1366 | ||
1367 | #ifdef CONFIG_COMPAT | 1367 | #ifdef CONFIG_COMPAT |
1368 | COMPAT_SYSCALL_DEFINE4(vmsplice, int, fd, const struct compat_iovec __user *, iov32, | 1368 | COMPAT_SYSCALL_DEFINE4(vmsplice, int, fd, const struct compat_iovec __user *, iov32, |
1369 | unsigned int, nr_segs, unsigned int, flags) | 1369 | unsigned int, nr_segs, unsigned int, flags) |
1370 | { | 1370 | { |
1371 | unsigned i; | 1371 | struct iovec iovstack[UIO_FASTIOV]; |
1372 | struct iovec __user *iov; | 1372 | struct iovec *iov = iovstack; |
1373 | if (nr_segs > UIO_MAXIOV) | 1373 | struct iov_iter iter; |
1374 | return -EINVAL; | 1374 | long error; |
1375 | iov = compat_alloc_user_space(nr_segs * sizeof(struct iovec)); | 1375 | struct fd f; |
1376 | for (i = 0; i < nr_segs; i++) { | 1376 | int type; |
1377 | struct compat_iovec v; | 1377 | |
1378 | if (get_user(v.iov_base, &iov32[i].iov_base) || | 1378 | f = fdget(fd); |
1379 | get_user(v.iov_len, &iov32[i].iov_len) || | 1379 | error = vmsplice_type(f, &type); |
1380 | put_user(compat_ptr(v.iov_base), &iov[i].iov_base) || | 1380 | if (error) |
1381 | put_user(v.iov_len, &iov[i].iov_len)) | 1381 | return error; |
1382 | return -EFAULT; | 1382 | |
1383 | error = compat_import_iovec(type, iov32, nr_segs, | ||
1384 | ARRAY_SIZE(iovstack), &iov, &iter); | ||
1385 | if (!error) { | ||
1386 | error = do_vmsplice(f.file, &iter, flags); | ||
1387 | kfree(iov); | ||
1383 | } | 1388 | } |
1384 | return do_vmsplice(fd, iov, nr_segs, flags); | 1389 | fdput(f); |
1390 | return error; | ||
1385 | } | 1391 | } |
1386 | #endif | 1392 | #endif |
1387 | 1393 | ||