diff options
Diffstat (limited to 'fs/open.c')
-rw-r--r-- | fs/open.c | 152 |
1 files changed, 132 insertions, 20 deletions
@@ -233,6 +233,14 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) | |||
233 | 233 | ||
234 | if (!(file->f_mode & FMODE_WRITE)) | 234 | if (!(file->f_mode & FMODE_WRITE)) |
235 | return -EBADF; | 235 | return -EBADF; |
236 | |||
237 | /* It's not possible punch hole on append only file */ | ||
238 | if (mode & FALLOC_FL_PUNCH_HOLE && IS_APPEND(inode)) | ||
239 | return -EPERM; | ||
240 | |||
241 | if (IS_IMMUTABLE(inode)) | ||
242 | return -EPERM; | ||
243 | |||
236 | /* | 244 | /* |
237 | * Revalidate the write permissions, in case security policy has | 245 | * Revalidate the write permissions, in case security policy has |
238 | * changed since the files were opened. | 246 | * changed since the files were opened. |
@@ -565,13 +573,15 @@ SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user, | |||
565 | { | 573 | { |
566 | struct path path; | 574 | struct path path; |
567 | int error = -EINVAL; | 575 | int error = -EINVAL; |
568 | int follow; | 576 | int lookup_flags; |
569 | 577 | ||
570 | if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) | 578 | if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) |
571 | goto out; | 579 | goto out; |
572 | 580 | ||
573 | follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; | 581 | lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; |
574 | 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); | ||
575 | if (error) | 585 | if (error) |
576 | goto out; | 586 | goto out; |
577 | error = mnt_want_write(path.mnt); | 587 | error = mnt_want_write(path.mnt); |
@@ -661,11 +671,16 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
661 | int (*open)(struct inode *, struct file *), | 671 | int (*open)(struct inode *, struct file *), |
662 | const struct cred *cred) | 672 | const struct cred *cred) |
663 | { | 673 | { |
674 | static const struct file_operations empty_fops = {}; | ||
664 | struct inode *inode; | 675 | struct inode *inode; |
665 | int error; | 676 | int error; |
666 | 677 | ||
667 | f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | | 678 | f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | |
668 | 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 | |||
669 | inode = dentry->d_inode; | 684 | inode = dentry->d_inode; |
670 | if (f->f_mode & FMODE_WRITE) { | 685 | if (f->f_mode & FMODE_WRITE) { |
671 | error = __get_file_write_access(inode, mnt); | 686 | error = __get_file_write_access(inode, mnt); |
@@ -679,9 +694,15 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
679 | f->f_path.dentry = dentry; | 694 | f->f_path.dentry = dentry; |
680 | f->f_path.mnt = mnt; | 695 | f->f_path.mnt = mnt; |
681 | f->f_pos = 0; | 696 | f->f_pos = 0; |
682 | f->f_op = fops_get(inode->i_fop); | ||
683 | file_sb_list_add(f, inode->i_sb); | 697 | file_sb_list_add(f, inode->i_sb); |
684 | 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 | |||
685 | error = security_dentry_open(f, cred); | 706 | error = security_dentry_open(f, cred); |
686 | if (error) | 707 | if (error) |
687 | goto cleanup_all; | 708 | goto cleanup_all; |
@@ -693,7 +714,8 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
693 | if (error) | 714 | if (error) |
694 | goto cleanup_all; | 715 | goto cleanup_all; |
695 | } | 716 | } |
696 | ima_counts_get(f); | 717 | if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) |
718 | i_readcount_inc(inode); | ||
697 | 719 | ||
698 | f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); | 720 | f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); |
699 | 721 | ||
@@ -790,6 +812,8 @@ struct file *nameidata_to_filp(struct nameidata *nd) | |||
790 | 812 | ||
791 | /* Pick up the filp from the open intent */ | 813 | /* Pick up the filp from the open intent */ |
792 | filp = nd->intent.open.file; | 814 | filp = nd->intent.open.file; |
815 | nd->intent.open.file = NULL; | ||
816 | |||
793 | /* Has the filesystem initialised the file for us? */ | 817 | /* Has the filesystem initialised the file for us? */ |
794 | if (filp->f_path.dentry == NULL) { | 818 | if (filp->f_path.dentry == NULL) { |
795 | path_get(&nd->path); | 819 | path_get(&nd->path); |
@@ -811,17 +835,8 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags, | |||
811 | 835 | ||
812 | validate_creds(cred); | 836 | validate_creds(cred); |
813 | 837 | ||
814 | /* | 838 | /* We must always pass in a valid mount pointer. */ |
815 | * We must always pass in a valid mount pointer. Historically | 839 | BUG_ON(!mnt); |
816 | * callers got away with not passing it, but we must enforce this at | ||
817 | * the earliest possible point now to avoid strange problems deep in the | ||
818 | * filesystem stack. | ||
819 | */ | ||
820 | if (!mnt) { | ||
821 | printk(KERN_WARNING "%s called with NULL vfsmount\n", __func__); | ||
822 | dump_stack(); | ||
823 | return ERR_PTR(-EINVAL); | ||
824 | } | ||
825 | 840 | ||
826 | error = -ENFILE; | 841 | error = -ENFILE; |
827 | f = get_empty_filp(); | 842 | f = get_empty_filp(); |
@@ -880,15 +895,110 @@ void fd_install(unsigned int fd, struct file *file) | |||
880 | 895 | ||
881 | EXPORT_SYMBOL(fd_install); | 896 | EXPORT_SYMBOL(fd_install); |
882 | 897 | ||
898 | static inline int build_open_flags(int flags, int mode, struct open_flags *op) | ||
899 | { | ||
900 | int lookup_flags = 0; | ||
901 | int acc_mode; | ||
902 | |||
903 | if (!(flags & O_CREAT)) | ||
904 | mode = 0; | ||
905 | op->mode = mode; | ||
906 | |||
907 | /* Must never be set by userspace */ | ||
908 | flags &= ~FMODE_NONOTIFY; | ||
909 | |||
910 | /* | ||
911 | * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only | ||
912 | * check for O_DSYNC if the need any syncing at all we enforce it's | ||
913 | * always set instead of having to deal with possibly weird behaviour | ||
914 | * for malicious applications setting only __O_SYNC. | ||
915 | */ | ||
916 | if (flags & __O_SYNC) | ||
917 | flags |= O_DSYNC; | ||
918 | |||
919 | /* | ||
920 | * If we have O_PATH in the open flag. Then we | ||
921 | * cannot have anything other than the below set of flags | ||
922 | */ | ||
923 | if (flags & O_PATH) { | ||
924 | flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH; | ||
925 | acc_mode = 0; | ||
926 | } else { | ||
927 | acc_mode = MAY_OPEN | ACC_MODE(flags); | ||
928 | } | ||
929 | |||
930 | op->open_flag = flags; | ||
931 | |||
932 | /* O_TRUNC implies we need access checks for write permissions */ | ||
933 | if (flags & O_TRUNC) | ||
934 | acc_mode |= MAY_WRITE; | ||
935 | |||
936 | /* Allow the LSM permission hook to distinguish append | ||
937 | access from general write access. */ | ||
938 | if (flags & O_APPEND) | ||
939 | acc_mode |= MAY_APPEND; | ||
940 | |||
941 | op->acc_mode = acc_mode; | ||
942 | |||
943 | op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN; | ||
944 | |||
945 | if (flags & O_CREAT) { | ||
946 | op->intent |= LOOKUP_CREATE; | ||
947 | if (flags & O_EXCL) | ||
948 | op->intent |= LOOKUP_EXCL; | ||
949 | } | ||
950 | |||
951 | if (flags & O_DIRECTORY) | ||
952 | lookup_flags |= LOOKUP_DIRECTORY; | ||
953 | if (!(flags & O_NOFOLLOW)) | ||
954 | lookup_flags |= LOOKUP_FOLLOW; | ||
955 | return lookup_flags; | ||
956 | } | ||
957 | |||
958 | /** | ||
959 | * filp_open - open file and return file pointer | ||
960 | * | ||
961 | * @filename: path to open | ||
962 | * @flags: open flags as per the open(2) second argument | ||
963 | * @mode: mode for the new file if O_CREAT is set, else ignored | ||
964 | * | ||
965 | * This is the helper to open a file from kernelspace if you really | ||
966 | * have to. But in generally you should not do this, so please move | ||
967 | * along, nothing to see here.. | ||
968 | */ | ||
969 | struct file *filp_open(const char *filename, int flags, int mode) | ||
970 | { | ||
971 | struct open_flags op; | ||
972 | int lookup = build_open_flags(flags, mode, &op); | ||
973 | return do_filp_open(AT_FDCWD, filename, &op, lookup); | ||
974 | } | ||
975 | EXPORT_SYMBOL(filp_open); | ||
976 | |||
977 | struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt, | ||
978 | const char *filename, int flags) | ||
979 | { | ||
980 | struct open_flags op; | ||
981 | int lookup = build_open_flags(flags, 0, &op); | ||
982 | if (flags & O_CREAT) | ||
983 | return ERR_PTR(-EINVAL); | ||
984 | if (!filename && (flags & O_DIRECTORY)) | ||
985 | if (!dentry->d_inode->i_op->lookup) | ||
986 | return ERR_PTR(-ENOTDIR); | ||
987 | return do_file_open_root(dentry, mnt, filename, &op, lookup); | ||
988 | } | ||
989 | EXPORT_SYMBOL(file_open_root); | ||
990 | |||
883 | long do_sys_open(int dfd, const char __user *filename, int flags, int mode) | 991 | long do_sys_open(int dfd, const char __user *filename, int flags, int mode) |
884 | { | 992 | { |
993 | struct open_flags op; | ||
994 | int lookup = build_open_flags(flags, mode, &op); | ||
885 | char *tmp = getname(filename); | 995 | char *tmp = getname(filename); |
886 | int fd = PTR_ERR(tmp); | 996 | int fd = PTR_ERR(tmp); |
887 | 997 | ||
888 | if (!IS_ERR(tmp)) { | 998 | if (!IS_ERR(tmp)) { |
889 | fd = get_unused_fd_flags(flags); | 999 | fd = get_unused_fd_flags(flags); |
890 | if (fd >= 0) { | 1000 | if (fd >= 0) { |
891 | struct file *f = do_filp_open(dfd, tmp, flags, mode, 0); | 1001 | struct file *f = do_filp_open(dfd, tmp, &op, lookup); |
892 | if (IS_ERR(f)) { | 1002 | if (IS_ERR(f)) { |
893 | put_unused_fd(fd); | 1003 | put_unused_fd(fd); |
894 | fd = PTR_ERR(f); | 1004 | fd = PTR_ERR(f); |
@@ -958,8 +1068,10 @@ int filp_close(struct file *filp, fl_owner_t id) | |||
958 | if (filp->f_op && filp->f_op->flush) | 1068 | if (filp->f_op && filp->f_op->flush) |
959 | retval = filp->f_op->flush(filp, id); | 1069 | retval = filp->f_op->flush(filp, id); |
960 | 1070 | ||
961 | dnotify_flush(filp, id); | 1071 | if (likely(!(filp->f_mode & FMODE_PATH))) { |
962 | locks_remove_posix(filp, id); | 1072 | dnotify_flush(filp, id); |
1073 | locks_remove_posix(filp, id); | ||
1074 | } | ||
963 | fput(filp); | 1075 | fput(filp); |
964 | return retval; | 1076 | return retval; |
965 | } | 1077 | } |