diff options
Diffstat (limited to 'fs/open.c')
-rw-r--r-- | fs/open.c | 126 |
1 files changed, 118 insertions, 8 deletions
@@ -573,13 +573,15 @@ SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user, | |||
573 | { | 573 | { |
574 | struct path path; | 574 | struct path path; |
575 | int error = -EINVAL; | 575 | int error = -EINVAL; |
576 | int follow; | 576 | int lookup_flags; |
577 | 577 | ||
578 | if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) | 578 | if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) |
579 | goto out; | 579 | goto out; |
580 | 580 | ||
581 | follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; | 581 | lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; |
582 | error = user_path_at(dfd, filename, follow, &path); | 582 | if (flag & AT_EMPTY_PATH) |
583 | lookup_flags |= LOOKUP_EMPTY; | ||
584 | error = user_path_at(dfd, filename, lookup_flags, &path); | ||
583 | if (error) | 585 | if (error) |
584 | goto out; | 586 | goto out; |
585 | error = mnt_want_write(path.mnt); | 587 | error = mnt_want_write(path.mnt); |
@@ -669,11 +671,16 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
669 | int (*open)(struct inode *, struct file *), | 671 | int (*open)(struct inode *, struct file *), |
670 | const struct cred *cred) | 672 | const struct cred *cred) |
671 | { | 673 | { |
674 | static const struct file_operations empty_fops = {}; | ||
672 | struct inode *inode; | 675 | struct inode *inode; |
673 | int error; | 676 | int error; |
674 | 677 | ||
675 | f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | | 678 | f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | |
676 | FMODE_PREAD | FMODE_PWRITE; | 679 | FMODE_PREAD | FMODE_PWRITE; |
680 | |||
681 | if (unlikely(f->f_flags & O_PATH)) | ||
682 | f->f_mode = FMODE_PATH; | ||
683 | |||
677 | inode = dentry->d_inode; | 684 | inode = dentry->d_inode; |
678 | if (f->f_mode & FMODE_WRITE) { | 685 | if (f->f_mode & FMODE_WRITE) { |
679 | error = __get_file_write_access(inode, mnt); | 686 | error = __get_file_write_access(inode, mnt); |
@@ -687,9 +694,15 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
687 | f->f_path.dentry = dentry; | 694 | f->f_path.dentry = dentry; |
688 | f->f_path.mnt = mnt; | 695 | f->f_path.mnt = mnt; |
689 | f->f_pos = 0; | 696 | f->f_pos = 0; |
690 | f->f_op = fops_get(inode->i_fop); | ||
691 | file_sb_list_add(f, inode->i_sb); | 697 | file_sb_list_add(f, inode->i_sb); |
692 | 698 | ||
699 | if (unlikely(f->f_mode & FMODE_PATH)) { | ||
700 | f->f_op = &empty_fops; | ||
701 | return f; | ||
702 | } | ||
703 | |||
704 | f->f_op = fops_get(inode->i_fop); | ||
705 | |||
693 | error = security_dentry_open(f, cred); | 706 | error = security_dentry_open(f, cred); |
694 | if (error) | 707 | if (error) |
695 | goto cleanup_all; | 708 | goto cleanup_all; |
@@ -890,15 +903,110 @@ void fd_install(unsigned int fd, struct file *file) | |||
890 | 903 | ||
891 | EXPORT_SYMBOL(fd_install); | 904 | EXPORT_SYMBOL(fd_install); |
892 | 905 | ||
906 | static inline int build_open_flags(int flags, int mode, struct open_flags *op) | ||
907 | { | ||
908 | int lookup_flags = 0; | ||
909 | int acc_mode; | ||
910 | |||
911 | if (!(flags & O_CREAT)) | ||
912 | mode = 0; | ||
913 | op->mode = mode; | ||
914 | |||
915 | /* Must never be set by userspace */ | ||
916 | flags &= ~FMODE_NONOTIFY; | ||
917 | |||
918 | /* | ||
919 | * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only | ||
920 | * check for O_DSYNC if the need any syncing at all we enforce it's | ||
921 | * always set instead of having to deal with possibly weird behaviour | ||
922 | * for malicious applications setting only __O_SYNC. | ||
923 | */ | ||
924 | if (flags & __O_SYNC) | ||
925 | flags |= O_DSYNC; | ||
926 | |||
927 | /* | ||
928 | * If we have O_PATH in the open flag. Then we | ||
929 | * cannot have anything other than the below set of flags | ||
930 | */ | ||
931 | if (flags & O_PATH) { | ||
932 | flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH; | ||
933 | acc_mode = 0; | ||
934 | } else { | ||
935 | acc_mode = MAY_OPEN | ACC_MODE(flags); | ||
936 | } | ||
937 | |||
938 | op->open_flag = flags; | ||
939 | |||
940 | /* O_TRUNC implies we need access checks for write permissions */ | ||
941 | if (flags & O_TRUNC) | ||
942 | acc_mode |= MAY_WRITE; | ||
943 | |||
944 | /* Allow the LSM permission hook to distinguish append | ||
945 | access from general write access. */ | ||
946 | if (flags & O_APPEND) | ||
947 | acc_mode |= MAY_APPEND; | ||
948 | |||
949 | op->acc_mode = acc_mode; | ||
950 | |||
951 | op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN; | ||
952 | |||
953 | if (flags & O_CREAT) { | ||
954 | op->intent |= LOOKUP_CREATE; | ||
955 | if (flags & O_EXCL) | ||
956 | op->intent |= LOOKUP_EXCL; | ||
957 | } | ||
958 | |||
959 | if (flags & O_DIRECTORY) | ||
960 | lookup_flags |= LOOKUP_DIRECTORY; | ||
961 | if (!(flags & O_NOFOLLOW)) | ||
962 | lookup_flags |= LOOKUP_FOLLOW; | ||
963 | return lookup_flags; | ||
964 | } | ||
965 | |||
966 | /** | ||
967 | * filp_open - open file and return file pointer | ||
968 | * | ||
969 | * @filename: path to open | ||
970 | * @flags: open flags as per the open(2) second argument | ||
971 | * @mode: mode for the new file if O_CREAT is set, else ignored | ||
972 | * | ||
973 | * This is the helper to open a file from kernelspace if you really | ||
974 | * have to. But in generally you should not do this, so please move | ||
975 | * along, nothing to see here.. | ||
976 | */ | ||
977 | struct file *filp_open(const char *filename, int flags, int mode) | ||
978 | { | ||
979 | struct open_flags op; | ||
980 | int lookup = build_open_flags(flags, mode, &op); | ||
981 | return do_filp_open(AT_FDCWD, filename, &op, lookup); | ||
982 | } | ||
983 | EXPORT_SYMBOL(filp_open); | ||
984 | |||
985 | struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt, | ||
986 | const char *filename, int flags) | ||
987 | { | ||
988 | struct open_flags op; | ||
989 | int lookup = build_open_flags(flags, 0, &op); | ||
990 | if (flags & O_CREAT) | ||
991 | return ERR_PTR(-EINVAL); | ||
992 | if (!filename && (flags & O_DIRECTORY)) | ||
993 | if (!dentry->d_inode->i_op->lookup) | ||
994 | return ERR_PTR(-ENOTDIR); | ||
995 | return do_file_open_root(dentry, mnt, filename, &op, lookup); | ||
996 | } | ||
997 | EXPORT_SYMBOL(file_open_root); | ||
998 | |||
893 | long do_sys_open(int dfd, const char __user *filename, int flags, int mode) | 999 | long do_sys_open(int dfd, const char __user *filename, int flags, int mode) |
894 | { | 1000 | { |
1001 | struct open_flags op; | ||
1002 | int lookup = build_open_flags(flags, mode, &op); | ||
895 | char *tmp = getname(filename); | 1003 | char *tmp = getname(filename); |
896 | int fd = PTR_ERR(tmp); | 1004 | int fd = PTR_ERR(tmp); |
897 | 1005 | ||
898 | if (!IS_ERR(tmp)) { | 1006 | if (!IS_ERR(tmp)) { |
899 | fd = get_unused_fd_flags(flags); | 1007 | fd = get_unused_fd_flags(flags); |
900 | if (fd >= 0) { | 1008 | if (fd >= 0) { |
901 | struct file *f = do_filp_open(dfd, tmp, flags, mode, 0); | 1009 | struct file *f = do_filp_open(dfd, tmp, &op, lookup); |
902 | if (IS_ERR(f)) { | 1010 | if (IS_ERR(f)) { |
903 | put_unused_fd(fd); | 1011 | put_unused_fd(fd); |
904 | fd = PTR_ERR(f); | 1012 | fd = PTR_ERR(f); |
@@ -968,8 +1076,10 @@ int filp_close(struct file *filp, fl_owner_t id) | |||
968 | if (filp->f_op && filp->f_op->flush) | 1076 | if (filp->f_op && filp->f_op->flush) |
969 | retval = filp->f_op->flush(filp, id); | 1077 | retval = filp->f_op->flush(filp, id); |
970 | 1078 | ||
971 | dnotify_flush(filp, id); | 1079 | if (likely(!(filp->f_mode & FMODE_PATH))) { |
972 | locks_remove_posix(filp, id); | 1080 | dnotify_flush(filp, id); |
1081 | locks_remove_posix(filp, id); | ||
1082 | } | ||
973 | fput(filp); | 1083 | fput(filp); |
974 | return retval; | 1084 | return retval; |
975 | } | 1085 | } |