diff options
-rw-r--r-- | fs/fcntl.c | 37 | ||||
-rw-r--r-- | fs/file_table.c | 53 | ||||
-rw-r--r-- | fs/namei.c | 2 | ||||
-rw-r--r-- | fs/open.c | 35 | ||||
-rw-r--r-- | include/asm-generic/fcntl.h | 4 | ||||
-rw-r--r-- | include/linux/file.h | 2 | ||||
-rw-r--r-- | include/linux/fs.h | 3 |
7 files changed, 119 insertions, 17 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c index cb1026181bdc..6c82e5bac039 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c | |||
@@ -131,7 +131,7 @@ SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd) | |||
131 | SYSCALL_DEFINE1(dup, unsigned int, fildes) | 131 | SYSCALL_DEFINE1(dup, unsigned int, fildes) |
132 | { | 132 | { |
133 | int ret = -EBADF; | 133 | int ret = -EBADF; |
134 | struct file *file = fget(fildes); | 134 | struct file *file = fget_raw(fildes); |
135 | 135 | ||
136 | if (file) { | 136 | if (file) { |
137 | ret = get_unused_fd(); | 137 | ret = get_unused_fd(); |
@@ -426,15 +426,35 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, | |||
426 | return err; | 426 | return err; |
427 | } | 427 | } |
428 | 428 | ||
429 | static int check_fcntl_cmd(unsigned cmd) | ||
430 | { | ||
431 | switch (cmd) { | ||
432 | case F_DUPFD: | ||
433 | case F_DUPFD_CLOEXEC: | ||
434 | case F_GETFD: | ||
435 | case F_SETFD: | ||
436 | case F_GETFL: | ||
437 | return 1; | ||
438 | } | ||
439 | return 0; | ||
440 | } | ||
441 | |||
429 | SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) | 442 | SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) |
430 | { | 443 | { |
431 | struct file *filp; | 444 | struct file *filp; |
432 | long err = -EBADF; | 445 | long err = -EBADF; |
433 | 446 | ||
434 | filp = fget(fd); | 447 | filp = fget_raw(fd); |
435 | if (!filp) | 448 | if (!filp) |
436 | goto out; | 449 | goto out; |
437 | 450 | ||
451 | if (unlikely(filp->f_mode & FMODE_PATH)) { | ||
452 | if (!check_fcntl_cmd(cmd)) { | ||
453 | fput(filp); | ||
454 | goto out; | ||
455 | } | ||
456 | } | ||
457 | |||
438 | err = security_file_fcntl(filp, cmd, arg); | 458 | err = security_file_fcntl(filp, cmd, arg); |
439 | if (err) { | 459 | if (err) { |
440 | fput(filp); | 460 | fput(filp); |
@@ -456,10 +476,17 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd, | |||
456 | long err; | 476 | long err; |
457 | 477 | ||
458 | err = -EBADF; | 478 | err = -EBADF; |
459 | filp = fget(fd); | 479 | filp = fget_raw(fd); |
460 | if (!filp) | 480 | if (!filp) |
461 | goto out; | 481 | goto out; |
462 | 482 | ||
483 | if (unlikely(filp->f_mode & FMODE_PATH)) { | ||
484 | if (!check_fcntl_cmd(cmd)) { | ||
485 | fput(filp); | ||
486 | goto out; | ||
487 | } | ||
488 | } | ||
489 | |||
463 | err = security_file_fcntl(filp, cmd, arg); | 490 | err = security_file_fcntl(filp, cmd, arg); |
464 | if (err) { | 491 | if (err) { |
465 | fput(filp); | 492 | fput(filp); |
@@ -808,14 +835,14 @@ static int __init fcntl_init(void) | |||
808 | * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY | 835 | * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY |
809 | * is defined as O_NONBLOCK on some platforms and not on others. | 836 | * is defined as O_NONBLOCK on some platforms and not on others. |
810 | */ | 837 | */ |
811 | BUILD_BUG_ON(18 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( | 838 | BUILD_BUG_ON(19 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( |
812 | O_RDONLY | O_WRONLY | O_RDWR | | 839 | O_RDONLY | O_WRONLY | O_RDWR | |
813 | O_CREAT | O_EXCL | O_NOCTTY | | 840 | O_CREAT | O_EXCL | O_NOCTTY | |
814 | O_TRUNC | O_APPEND | /* O_NONBLOCK | */ | 841 | O_TRUNC | O_APPEND | /* O_NONBLOCK | */ |
815 | __O_SYNC | O_DSYNC | FASYNC | | 842 | __O_SYNC | O_DSYNC | FASYNC | |
816 | O_DIRECT | O_LARGEFILE | O_DIRECTORY | | 843 | O_DIRECT | O_LARGEFILE | O_DIRECTORY | |
817 | O_NOFOLLOW | O_NOATIME | O_CLOEXEC | | 844 | O_NOFOLLOW | O_NOATIME | O_CLOEXEC | |
818 | __FMODE_EXEC | 845 | __FMODE_EXEC | O_PATH |
819 | )); | 846 | )); |
820 | 847 | ||
821 | fasync_cache = kmem_cache_create("fasync_cache", | 848 | fasync_cache = kmem_cache_create("fasync_cache", |
diff --git a/fs/file_table.c b/fs/file_table.c index eb36b6b17e26..3c16e1ca163e 100644 --- a/fs/file_table.c +++ b/fs/file_table.c | |||
@@ -276,11 +276,10 @@ struct file *fget(unsigned int fd) | |||
276 | rcu_read_lock(); | 276 | rcu_read_lock(); |
277 | file = fcheck_files(files, fd); | 277 | file = fcheck_files(files, fd); |
278 | if (file) { | 278 | if (file) { |
279 | if (!atomic_long_inc_not_zero(&file->f_count)) { | 279 | /* File object ref couldn't be taken */ |
280 | /* File object ref couldn't be taken */ | 280 | if (file->f_mode & FMODE_PATH || |
281 | rcu_read_unlock(); | 281 | !atomic_long_inc_not_zero(&file->f_count)) |
282 | return NULL; | 282 | file = NULL; |
283 | } | ||
284 | } | 283 | } |
285 | rcu_read_unlock(); | 284 | rcu_read_unlock(); |
286 | 285 | ||
@@ -289,6 +288,23 @@ struct file *fget(unsigned int fd) | |||
289 | 288 | ||
290 | EXPORT_SYMBOL(fget); | 289 | EXPORT_SYMBOL(fget); |
291 | 290 | ||
291 | struct file *fget_raw(unsigned int fd) | ||
292 | { | ||
293 | struct file *file; | ||
294 | struct files_struct *files = current->files; | ||
295 | |||
296 | rcu_read_lock(); | ||
297 | file = fcheck_files(files, fd); | ||
298 | if (file) { | ||
299 | /* File object ref couldn't be taken */ | ||
300 | if (!atomic_long_inc_not_zero(&file->f_count)) | ||
301 | file = NULL; | ||
302 | } | ||
303 | rcu_read_unlock(); | ||
304 | |||
305 | return file; | ||
306 | } | ||
307 | |||
292 | /* | 308 | /* |
293 | * Lightweight file lookup - no refcnt increment if fd table isn't shared. | 309 | * Lightweight file lookup - no refcnt increment if fd table isn't shared. |
294 | * | 310 | * |
@@ -313,6 +329,33 @@ struct file *fget_light(unsigned int fd, int *fput_needed) | |||
313 | *fput_needed = 0; | 329 | *fput_needed = 0; |
314 | if (atomic_read(&files->count) == 1) { | 330 | if (atomic_read(&files->count) == 1) { |
315 | file = fcheck_files(files, fd); | 331 | file = fcheck_files(files, fd); |
332 | if (file && (file->f_mode & FMODE_PATH)) | ||
333 | file = NULL; | ||
334 | } else { | ||
335 | rcu_read_lock(); | ||
336 | file = fcheck_files(files, fd); | ||
337 | if (file) { | ||
338 | if (!(file->f_mode & FMODE_PATH) && | ||
339 | atomic_long_inc_not_zero(&file->f_count)) | ||
340 | *fput_needed = 1; | ||
341 | else | ||
342 | /* Didn't get the reference, someone's freed */ | ||
343 | file = NULL; | ||
344 | } | ||
345 | rcu_read_unlock(); | ||
346 | } | ||
347 | |||
348 | return file; | ||
349 | } | ||
350 | |||
351 | struct file *fget_raw_light(unsigned int fd, int *fput_needed) | ||
352 | { | ||
353 | struct file *file; | ||
354 | struct files_struct *files = current->files; | ||
355 | |||
356 | *fput_needed = 0; | ||
357 | if (atomic_read(&files->count) == 1) { | ||
358 | file = fcheck_files(files, fd); | ||
316 | } else { | 359 | } else { |
317 | rcu_read_lock(); | 360 | rcu_read_lock(); |
318 | file = fcheck_files(files, fd); | 361 | file = fcheck_files(files, fd); |
diff --git a/fs/namei.c b/fs/namei.c index 33be51a2ddb7..e1d9f90d9776 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1544,7 +1544,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, | |||
1544 | } else { | 1544 | } else { |
1545 | struct dentry *dentry; | 1545 | struct dentry *dentry; |
1546 | 1546 | ||
1547 | file = fget_light(dfd, &fput_needed); | 1547 | file = fget_raw_light(dfd, &fput_needed); |
1548 | retval = -EBADF; | 1548 | retval = -EBADF; |
1549 | if (!file) | 1549 | if (!file) |
1550 | goto out_fail; | 1550 | goto out_fail; |
@@ -669,11 +669,16 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
669 | int (*open)(struct inode *, struct file *), | 669 | int (*open)(struct inode *, struct file *), |
670 | const struct cred *cred) | 670 | const struct cred *cred) |
671 | { | 671 | { |
672 | static const struct file_operations empty_fops = {}; | ||
672 | struct inode *inode; | 673 | struct inode *inode; |
673 | int error; | 674 | int error; |
674 | 675 | ||
675 | f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | | 676 | f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | |
676 | FMODE_PREAD | FMODE_PWRITE; | 677 | FMODE_PREAD | FMODE_PWRITE; |
678 | |||
679 | if (unlikely(f->f_flags & O_PATH)) | ||
680 | f->f_mode = FMODE_PATH; | ||
681 | |||
677 | inode = dentry->d_inode; | 682 | inode = dentry->d_inode; |
678 | if (f->f_mode & FMODE_WRITE) { | 683 | if (f->f_mode & FMODE_WRITE) { |
679 | error = __get_file_write_access(inode, mnt); | 684 | error = __get_file_write_access(inode, mnt); |
@@ -687,9 +692,15 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
687 | f->f_path.dentry = dentry; | 692 | f->f_path.dentry = dentry; |
688 | f->f_path.mnt = mnt; | 693 | f->f_path.mnt = mnt; |
689 | f->f_pos = 0; | 694 | f->f_pos = 0; |
690 | f->f_op = fops_get(inode->i_fop); | ||
691 | file_sb_list_add(f, inode->i_sb); | 695 | file_sb_list_add(f, inode->i_sb); |
692 | 696 | ||
697 | if (unlikely(f->f_mode & FMODE_PATH)) { | ||
698 | f->f_op = &empty_fops; | ||
699 | return f; | ||
700 | } | ||
701 | |||
702 | f->f_op = fops_get(inode->i_fop); | ||
703 | |||
693 | error = security_dentry_open(f, cred); | 704 | error = security_dentry_open(f, cred); |
694 | if (error) | 705 | if (error) |
695 | goto cleanup_all; | 706 | goto cleanup_all; |
@@ -911,9 +922,18 @@ static inline int build_open_flags(int flags, int mode, struct open_flags *op) | |||
911 | if (flags & __O_SYNC) | 922 | if (flags & __O_SYNC) |
912 | flags |= O_DSYNC; | 923 | flags |= O_DSYNC; |
913 | 924 | ||
914 | op->open_flag = flags; | 925 | /* |
926 | * If we have O_PATH in the open flag. Then we | ||
927 | * cannot have anything other than the below set of flags | ||
928 | */ | ||
929 | if (flags & O_PATH) { | ||
930 | flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH; | ||
931 | acc_mode = 0; | ||
932 | } else { | ||
933 | acc_mode = MAY_OPEN | ACC_MODE(flags); | ||
934 | } | ||
915 | 935 | ||
916 | acc_mode = MAY_OPEN | ACC_MODE(flags); | 936 | op->open_flag = flags; |
917 | 937 | ||
918 | /* O_TRUNC implies we need access checks for write permissions */ | 938 | /* O_TRUNC implies we need access checks for write permissions */ |
919 | if (flags & O_TRUNC) | 939 | if (flags & O_TRUNC) |
@@ -926,7 +946,8 @@ static inline int build_open_flags(int flags, int mode, struct open_flags *op) | |||
926 | 946 | ||
927 | op->acc_mode = acc_mode; | 947 | op->acc_mode = acc_mode; |
928 | 948 | ||
929 | op->intent = LOOKUP_OPEN; | 949 | op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN; |
950 | |||
930 | if (flags & O_CREAT) { | 951 | if (flags & O_CREAT) { |
931 | op->intent |= LOOKUP_CREATE; | 952 | op->intent |= LOOKUP_CREATE; |
932 | if (flags & O_EXCL) | 953 | if (flags & O_EXCL) |
@@ -1053,8 +1074,10 @@ int filp_close(struct file *filp, fl_owner_t id) | |||
1053 | if (filp->f_op && filp->f_op->flush) | 1074 | if (filp->f_op && filp->f_op->flush) |
1054 | retval = filp->f_op->flush(filp, id); | 1075 | retval = filp->f_op->flush(filp, id); |
1055 | 1076 | ||
1056 | dnotify_flush(filp, id); | 1077 | if (likely(!(filp->f_mode & FMODE_PATH))) { |
1057 | locks_remove_posix(filp, id); | 1078 | dnotify_flush(filp, id); |
1079 | locks_remove_posix(filp, id); | ||
1080 | } | ||
1058 | fput(filp); | 1081 | fput(filp); |
1059 | return retval; | 1082 | return retval; |
1060 | } | 1083 | } |
diff --git a/include/asm-generic/fcntl.h b/include/asm-generic/fcntl.h index 0fc16e3f0bfc..84793c7025e2 100644 --- a/include/asm-generic/fcntl.h +++ b/include/asm-generic/fcntl.h | |||
@@ -80,6 +80,10 @@ | |||
80 | #define O_SYNC (__O_SYNC|O_DSYNC) | 80 | #define O_SYNC (__O_SYNC|O_DSYNC) |
81 | #endif | 81 | #endif |
82 | 82 | ||
83 | #ifndef O_PATH | ||
84 | #define O_PATH 010000000 | ||
85 | #endif | ||
86 | |||
83 | #ifndef O_NDELAY | 87 | #ifndef O_NDELAY |
84 | #define O_NDELAY O_NONBLOCK | 88 | #define O_NDELAY O_NONBLOCK |
85 | #endif | 89 | #endif |
diff --git a/include/linux/file.h b/include/linux/file.h index e85baebf6279..21a79958541c 100644 --- a/include/linux/file.h +++ b/include/linux/file.h | |||
@@ -29,6 +29,8 @@ static inline void fput_light(struct file *file, int fput_needed) | |||
29 | 29 | ||
30 | extern struct file *fget(unsigned int fd); | 30 | extern struct file *fget(unsigned int fd); |
31 | extern struct file *fget_light(unsigned int fd, int *fput_needed); | 31 | extern struct file *fget_light(unsigned int fd, int *fput_needed); |
32 | extern struct file *fget_raw(unsigned int fd); | ||
33 | extern struct file *fget_raw_light(unsigned int fd, int *fput_needed); | ||
32 | extern void set_close_on_exec(unsigned int fd, int flag); | 34 | extern void set_close_on_exec(unsigned int fd, int flag); |
33 | extern void put_filp(struct file *); | 35 | extern void put_filp(struct file *); |
34 | extern int alloc_fd(unsigned start, unsigned flags); | 36 | extern int alloc_fd(unsigned start, unsigned flags); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index f2143e0942c2..13df14e2c42e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -102,6 +102,9 @@ struct inodes_stat_t { | |||
102 | /* File is huge (eg. /dev/kmem): treat loff_t as unsigned */ | 102 | /* File is huge (eg. /dev/kmem): treat loff_t as unsigned */ |
103 | #define FMODE_UNSIGNED_OFFSET ((__force fmode_t)0x2000) | 103 | #define FMODE_UNSIGNED_OFFSET ((__force fmode_t)0x2000) |
104 | 104 | ||
105 | /* File is opened with O_PATH; almost nothing can be done with it */ | ||
106 | #define FMODE_PATH ((__force fmode_t)0x4000) | ||
107 | |||
105 | /* File was opened by fanotify and shouldn't generate fanotify events */ | 108 | /* File was opened by fanotify and shouldn't generate fanotify events */ |
106 | #define FMODE_NONOTIFY ((__force fmode_t)0x1000000) | 109 | #define FMODE_NONOTIFY ((__force fmode_t)0x1000000) |
107 | 110 | ||