diff options
Diffstat (limited to 'fs/compat.c')
-rw-r--r-- | fs/compat.c | 307 |
1 files changed, 29 insertions, 278 deletions
diff --git a/fs/compat.c b/fs/compat.c index f6fd0a00e6cc..0ea00832de23 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
@@ -262,35 +262,19 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs * | |||
262 | */ | 262 | */ |
263 | asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf) | 263 | asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf) |
264 | { | 264 | { |
265 | struct path path; | 265 | struct kstatfs tmp; |
266 | int error; | 266 | int error = user_statfs(pathname, &tmp); |
267 | 267 | if (!error) | |
268 | error = user_path(pathname, &path); | 268 | error = put_compat_statfs(buf, &tmp); |
269 | if (!error) { | ||
270 | struct kstatfs tmp; | ||
271 | error = vfs_statfs(&path, &tmp); | ||
272 | if (!error) | ||
273 | error = put_compat_statfs(buf, &tmp); | ||
274 | path_put(&path); | ||
275 | } | ||
276 | return error; | 269 | return error; |
277 | } | 270 | } |
278 | 271 | ||
279 | asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf) | 272 | asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf) |
280 | { | 273 | { |
281 | struct file * file; | ||
282 | struct kstatfs tmp; | 274 | struct kstatfs tmp; |
283 | int error; | 275 | int error = fd_statfs(fd, &tmp); |
284 | |||
285 | error = -EBADF; | ||
286 | file = fget(fd); | ||
287 | if (!file) | ||
288 | goto out; | ||
289 | error = vfs_statfs(&file->f_path, &tmp); | ||
290 | if (!error) | 276 | if (!error) |
291 | error = put_compat_statfs(buf, &tmp); | 277 | error = put_compat_statfs(buf, &tmp); |
292 | fput(file); | ||
293 | out: | ||
294 | return error; | 278 | return error; |
295 | } | 279 | } |
296 | 280 | ||
@@ -329,41 +313,29 @@ static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstat | |||
329 | 313 | ||
330 | asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf) | 314 | asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf) |
331 | { | 315 | { |
332 | struct path path; | 316 | struct kstatfs tmp; |
333 | int error; | 317 | int error; |
334 | 318 | ||
335 | if (sz != sizeof(*buf)) | 319 | if (sz != sizeof(*buf)) |
336 | return -EINVAL; | 320 | return -EINVAL; |
337 | 321 | ||
338 | error = user_path(pathname, &path); | 322 | error = user_statfs(pathname, &tmp); |
339 | if (!error) { | 323 | if (!error) |
340 | struct kstatfs tmp; | 324 | error = put_compat_statfs64(buf, &tmp); |
341 | error = vfs_statfs(&path, &tmp); | ||
342 | if (!error) | ||
343 | error = put_compat_statfs64(buf, &tmp); | ||
344 | path_put(&path); | ||
345 | } | ||
346 | return error; | 325 | return error; |
347 | } | 326 | } |
348 | 327 | ||
349 | asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf) | 328 | asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf) |
350 | { | 329 | { |
351 | struct file * file; | ||
352 | struct kstatfs tmp; | 330 | struct kstatfs tmp; |
353 | int error; | 331 | int error; |
354 | 332 | ||
355 | if (sz != sizeof(*buf)) | 333 | if (sz != sizeof(*buf)) |
356 | return -EINVAL; | 334 | return -EINVAL; |
357 | 335 | ||
358 | error = -EBADF; | 336 | error = fd_statfs(fd, &tmp); |
359 | file = fget(fd); | ||
360 | if (!file) | ||
361 | goto out; | ||
362 | error = vfs_statfs(&file->f_path, &tmp); | ||
363 | if (!error) | 337 | if (!error) |
364 | error = put_compat_statfs64(buf, &tmp); | 338 | error = put_compat_statfs64(buf, &tmp); |
365 | fput(file); | ||
366 | out: | ||
367 | return error; | 339 | return error; |
368 | } | 340 | } |
369 | 341 | ||
@@ -1228,7 +1200,9 @@ compat_sys_preadv(unsigned long fd, const struct compat_iovec __user *vec, | |||
1228 | file = fget_light(fd, &fput_needed); | 1200 | file = fget_light(fd, &fput_needed); |
1229 | if (!file) | 1201 | if (!file) |
1230 | return -EBADF; | 1202 | return -EBADF; |
1231 | ret = compat_readv(file, vec, vlen, &pos); | 1203 | ret = -ESPIPE; |
1204 | if (file->f_mode & FMODE_PREAD) | ||
1205 | ret = compat_readv(file, vec, vlen, &pos); | ||
1232 | fput_light(file, fput_needed); | 1206 | fput_light(file, fput_needed); |
1233 | return ret; | 1207 | return ret; |
1234 | } | 1208 | } |
@@ -1285,7 +1259,9 @@ compat_sys_pwritev(unsigned long fd, const struct compat_iovec __user *vec, | |||
1285 | file = fget_light(fd, &fput_needed); | 1259 | file = fget_light(fd, &fput_needed); |
1286 | if (!file) | 1260 | if (!file) |
1287 | return -EBADF; | 1261 | return -EBADF; |
1288 | ret = compat_writev(file, vec, vlen, &pos); | 1262 | ret = -ESPIPE; |
1263 | if (file->f_mode & FMODE_PWRITE) | ||
1264 | ret = compat_writev(file, vec, vlen, &pos); | ||
1289 | fput_light(file, fput_needed); | 1265 | fput_light(file, fput_needed); |
1290 | return ret; | 1266 | return ret; |
1291 | } | 1267 | } |
@@ -1330,241 +1306,6 @@ compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int | |||
1330 | return do_sys_open(dfd, filename, flags, mode); | 1306 | return do_sys_open(dfd, filename, flags, mode); |
1331 | } | 1307 | } |
1332 | 1308 | ||
1333 | /* | ||
1334 | * compat_count() counts the number of arguments/envelopes. It is basically | ||
1335 | * a copy of count() from fs/exec.c, except that it works with 32 bit argv | ||
1336 | * and envp pointers. | ||
1337 | */ | ||
1338 | static int compat_count(compat_uptr_t __user *argv, int max) | ||
1339 | { | ||
1340 | int i = 0; | ||
1341 | |||
1342 | if (argv != NULL) { | ||
1343 | for (;;) { | ||
1344 | compat_uptr_t p; | ||
1345 | |||
1346 | if (get_user(p, argv)) | ||
1347 | return -EFAULT; | ||
1348 | if (!p) | ||
1349 | break; | ||
1350 | argv++; | ||
1351 | if (i++ >= max) | ||
1352 | return -E2BIG; | ||
1353 | |||
1354 | if (fatal_signal_pending(current)) | ||
1355 | return -ERESTARTNOHAND; | ||
1356 | cond_resched(); | ||
1357 | } | ||
1358 | } | ||
1359 | return i; | ||
1360 | } | ||
1361 | |||
1362 | /* | ||
1363 | * compat_copy_strings() is basically a copy of copy_strings() from fs/exec.c | ||
1364 | * except that it works with 32 bit argv and envp pointers. | ||
1365 | */ | ||
1366 | static int compat_copy_strings(int argc, compat_uptr_t __user *argv, | ||
1367 | struct linux_binprm *bprm) | ||
1368 | { | ||
1369 | struct page *kmapped_page = NULL; | ||
1370 | char *kaddr = NULL; | ||
1371 | unsigned long kpos = 0; | ||
1372 | int ret; | ||
1373 | |||
1374 | while (argc-- > 0) { | ||
1375 | compat_uptr_t str; | ||
1376 | int len; | ||
1377 | unsigned long pos; | ||
1378 | |||
1379 | if (get_user(str, argv+argc) || | ||
1380 | !(len = strnlen_user(compat_ptr(str), MAX_ARG_STRLEN))) { | ||
1381 | ret = -EFAULT; | ||
1382 | goto out; | ||
1383 | } | ||
1384 | |||
1385 | if (len > MAX_ARG_STRLEN) { | ||
1386 | ret = -E2BIG; | ||
1387 | goto out; | ||
1388 | } | ||
1389 | |||
1390 | /* We're going to work our way backwords. */ | ||
1391 | pos = bprm->p; | ||
1392 | str += len; | ||
1393 | bprm->p -= len; | ||
1394 | |||
1395 | while (len > 0) { | ||
1396 | int offset, bytes_to_copy; | ||
1397 | |||
1398 | if (fatal_signal_pending(current)) { | ||
1399 | ret = -ERESTARTNOHAND; | ||
1400 | goto out; | ||
1401 | } | ||
1402 | cond_resched(); | ||
1403 | |||
1404 | offset = pos % PAGE_SIZE; | ||
1405 | if (offset == 0) | ||
1406 | offset = PAGE_SIZE; | ||
1407 | |||
1408 | bytes_to_copy = offset; | ||
1409 | if (bytes_to_copy > len) | ||
1410 | bytes_to_copy = len; | ||
1411 | |||
1412 | offset -= bytes_to_copy; | ||
1413 | pos -= bytes_to_copy; | ||
1414 | str -= bytes_to_copy; | ||
1415 | len -= bytes_to_copy; | ||
1416 | |||
1417 | if (!kmapped_page || kpos != (pos & PAGE_MASK)) { | ||
1418 | struct page *page; | ||
1419 | |||
1420 | page = get_arg_page(bprm, pos, 1); | ||
1421 | if (!page) { | ||
1422 | ret = -E2BIG; | ||
1423 | goto out; | ||
1424 | } | ||
1425 | |||
1426 | if (kmapped_page) { | ||
1427 | flush_kernel_dcache_page(kmapped_page); | ||
1428 | kunmap(kmapped_page); | ||
1429 | put_page(kmapped_page); | ||
1430 | } | ||
1431 | kmapped_page = page; | ||
1432 | kaddr = kmap(kmapped_page); | ||
1433 | kpos = pos & PAGE_MASK; | ||
1434 | flush_cache_page(bprm->vma, kpos, | ||
1435 | page_to_pfn(kmapped_page)); | ||
1436 | } | ||
1437 | if (copy_from_user(kaddr+offset, compat_ptr(str), | ||
1438 | bytes_to_copy)) { | ||
1439 | ret = -EFAULT; | ||
1440 | goto out; | ||
1441 | } | ||
1442 | } | ||
1443 | } | ||
1444 | ret = 0; | ||
1445 | out: | ||
1446 | if (kmapped_page) { | ||
1447 | flush_kernel_dcache_page(kmapped_page); | ||
1448 | kunmap(kmapped_page); | ||
1449 | put_page(kmapped_page); | ||
1450 | } | ||
1451 | return ret; | ||
1452 | } | ||
1453 | |||
1454 | /* | ||
1455 | * compat_do_execve() is mostly a copy of do_execve(), with the exception | ||
1456 | * that it processes 32 bit argv and envp pointers. | ||
1457 | */ | ||
1458 | int compat_do_execve(char * filename, | ||
1459 | compat_uptr_t __user *argv, | ||
1460 | compat_uptr_t __user *envp, | ||
1461 | struct pt_regs * regs) | ||
1462 | { | ||
1463 | struct linux_binprm *bprm; | ||
1464 | struct file *file; | ||
1465 | struct files_struct *displaced; | ||
1466 | bool clear_in_exec; | ||
1467 | int retval; | ||
1468 | |||
1469 | retval = unshare_files(&displaced); | ||
1470 | if (retval) | ||
1471 | goto out_ret; | ||
1472 | |||
1473 | retval = -ENOMEM; | ||
1474 | bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); | ||
1475 | if (!bprm) | ||
1476 | goto out_files; | ||
1477 | |||
1478 | retval = prepare_bprm_creds(bprm); | ||
1479 | if (retval) | ||
1480 | goto out_free; | ||
1481 | |||
1482 | retval = check_unsafe_exec(bprm); | ||
1483 | if (retval < 0) | ||
1484 | goto out_free; | ||
1485 | clear_in_exec = retval; | ||
1486 | current->in_execve = 1; | ||
1487 | |||
1488 | file = open_exec(filename); | ||
1489 | retval = PTR_ERR(file); | ||
1490 | if (IS_ERR(file)) | ||
1491 | goto out_unmark; | ||
1492 | |||
1493 | sched_exec(); | ||
1494 | |||
1495 | bprm->file = file; | ||
1496 | bprm->filename = filename; | ||
1497 | bprm->interp = filename; | ||
1498 | |||
1499 | retval = bprm_mm_init(bprm); | ||
1500 | if (retval) | ||
1501 | goto out_file; | ||
1502 | |||
1503 | bprm->argc = compat_count(argv, MAX_ARG_STRINGS); | ||
1504 | if ((retval = bprm->argc) < 0) | ||
1505 | goto out; | ||
1506 | |||
1507 | bprm->envc = compat_count(envp, MAX_ARG_STRINGS); | ||
1508 | if ((retval = bprm->envc) < 0) | ||
1509 | goto out; | ||
1510 | |||
1511 | retval = prepare_binprm(bprm); | ||
1512 | if (retval < 0) | ||
1513 | goto out; | ||
1514 | |||
1515 | retval = copy_strings_kernel(1, &bprm->filename, bprm); | ||
1516 | if (retval < 0) | ||
1517 | goto out; | ||
1518 | |||
1519 | bprm->exec = bprm->p; | ||
1520 | retval = compat_copy_strings(bprm->envc, envp, bprm); | ||
1521 | if (retval < 0) | ||
1522 | goto out; | ||
1523 | |||
1524 | retval = compat_copy_strings(bprm->argc, argv, bprm); | ||
1525 | if (retval < 0) | ||
1526 | goto out; | ||
1527 | |||
1528 | retval = search_binary_handler(bprm, regs); | ||
1529 | if (retval < 0) | ||
1530 | goto out; | ||
1531 | |||
1532 | /* execve succeeded */ | ||
1533 | current->fs->in_exec = 0; | ||
1534 | current->in_execve = 0; | ||
1535 | acct_update_integrals(current); | ||
1536 | free_bprm(bprm); | ||
1537 | if (displaced) | ||
1538 | put_files_struct(displaced); | ||
1539 | return retval; | ||
1540 | |||
1541 | out: | ||
1542 | if (bprm->mm) { | ||
1543 | acct_arg_size(bprm, 0); | ||
1544 | mmput(bprm->mm); | ||
1545 | } | ||
1546 | |||
1547 | out_file: | ||
1548 | if (bprm->file) { | ||
1549 | allow_write_access(bprm->file); | ||
1550 | fput(bprm->file); | ||
1551 | } | ||
1552 | |||
1553 | out_unmark: | ||
1554 | if (clear_in_exec) | ||
1555 | current->fs->in_exec = 0; | ||
1556 | current->in_execve = 0; | ||
1557 | |||
1558 | out_free: | ||
1559 | free_bprm(bprm); | ||
1560 | |||
1561 | out_files: | ||
1562 | if (displaced) | ||
1563 | reset_files_struct(displaced); | ||
1564 | out_ret: | ||
1565 | return retval; | ||
1566 | } | ||
1567 | |||
1568 | #define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t)) | 1309 | #define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t)) |
1569 | 1310 | ||
1570 | static int poll_select_copy_remaining(struct timespec *end_time, void __user *p, | 1311 | static int poll_select_copy_remaining(struct timespec *end_time, void __user *p, |
@@ -1695,9 +1436,6 @@ int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset, | |||
1695 | * Update: ERESTARTSYS breaks at least the xview clock binary, so | 1436 | * Update: ERESTARTSYS breaks at least the xview clock binary, so |
1696 | * I'm trying ERESTARTNOHAND which restart only when you want to. | 1437 | * I'm trying ERESTARTNOHAND which restart only when you want to. |
1697 | */ | 1438 | */ |
1698 | #define MAX_SELECT_SECONDS \ | ||
1699 | ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) | ||
1700 | |||
1701 | int compat_core_sys_select(int n, compat_ulong_t __user *inp, | 1439 | int compat_core_sys_select(int n, compat_ulong_t __user *inp, |
1702 | compat_ulong_t __user *outp, compat_ulong_t __user *exp, | 1440 | compat_ulong_t __user *outp, compat_ulong_t __user *exp, |
1703 | struct timespec *end_time) | 1441 | struct timespec *end_time) |
@@ -2308,3 +2046,16 @@ asmlinkage long compat_sys_timerfd_gettime(int ufd, | |||
2308 | } | 2046 | } |
2309 | 2047 | ||
2310 | #endif /* CONFIG_TIMERFD */ | 2048 | #endif /* CONFIG_TIMERFD */ |
2049 | |||
2050 | #ifdef CONFIG_FHANDLE | ||
2051 | /* | ||
2052 | * Exactly like fs/open.c:sys_open_by_handle_at(), except that it | ||
2053 | * doesn't set the O_LARGEFILE flag. | ||
2054 | */ | ||
2055 | asmlinkage long | ||
2056 | compat_sys_open_by_handle_at(int mountdirfd, | ||
2057 | struct file_handle __user *handle, int flags) | ||
2058 | { | ||
2059 | return do_handle_open(mountdirfd, handle, flags); | ||
2060 | } | ||
2061 | #endif | ||