diff options
Diffstat (limited to 'fs/open.c')
-rw-r--r-- | fs/open.c | 147 |
1 files changed, 136 insertions, 11 deletions
@@ -223,11 +223,24 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) | |||
223 | return -EINVAL; | 223 | return -EINVAL; |
224 | 224 | ||
225 | /* Return error if mode is not supported */ | 225 | /* Return error if mode is not supported */ |
226 | if (mode && !(mode & FALLOC_FL_KEEP_SIZE)) | 226 | if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) |
227 | return -EOPNOTSUPP; | ||
228 | |||
229 | /* Punch hole must have keep size set */ | ||
230 | if ((mode & FALLOC_FL_PUNCH_HOLE) && | ||
231 | !(mode & FALLOC_FL_KEEP_SIZE)) | ||
227 | return -EOPNOTSUPP; | 232 | return -EOPNOTSUPP; |
228 | 233 | ||
229 | if (!(file->f_mode & FMODE_WRITE)) | 234 | if (!(file->f_mode & FMODE_WRITE)) |
230 | 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 | |||
231 | /* | 244 | /* |
232 | * Revalidate the write permissions, in case security policy has | 245 | * Revalidate the write permissions, in case security policy has |
233 | * changed since the files were opened. | 246 | * changed since the files were opened. |
@@ -250,10 +263,10 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) | |||
250 | if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) | 263 | if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) |
251 | return -EFBIG; | 264 | return -EFBIG; |
252 | 265 | ||
253 | if (!inode->i_op->fallocate) | 266 | if (!file->f_op->fallocate) |
254 | return -EOPNOTSUPP; | 267 | return -EOPNOTSUPP; |
255 | 268 | ||
256 | return inode->i_op->fallocate(inode, mode, offset, len); | 269 | return file->f_op->fallocate(file, mode, offset, len); |
257 | } | 270 | } |
258 | 271 | ||
259 | SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len) | 272 | SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len) |
@@ -560,13 +573,15 @@ SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user, | |||
560 | { | 573 | { |
561 | struct path path; | 574 | struct path path; |
562 | int error = -EINVAL; | 575 | int error = -EINVAL; |
563 | int follow; | 576 | int lookup_flags; |
564 | 577 | ||
565 | if ((flag & ~AT_SYMLINK_NOFOLLOW) != 0) | 578 | if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) |
566 | goto out; | 579 | goto out; |
567 | 580 | ||
568 | follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; | 581 | lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; |
569 | 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); | ||
570 | if (error) | 585 | if (error) |
571 | goto out; | 586 | goto out; |
572 | error = mnt_want_write(path.mnt); | 587 | error = mnt_want_write(path.mnt); |
@@ -656,11 +671,16 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
656 | int (*open)(struct inode *, struct file *), | 671 | int (*open)(struct inode *, struct file *), |
657 | const struct cred *cred) | 672 | const struct cred *cred) |
658 | { | 673 | { |
674 | static const struct file_operations empty_fops = {}; | ||
659 | struct inode *inode; | 675 | struct inode *inode; |
660 | int error; | 676 | int error; |
661 | 677 | ||
662 | f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | | 678 | f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | |
663 | 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 | |||
664 | inode = dentry->d_inode; | 684 | inode = dentry->d_inode; |
665 | if (f->f_mode & FMODE_WRITE) { | 685 | if (f->f_mode & FMODE_WRITE) { |
666 | error = __get_file_write_access(inode, mnt); | 686 | error = __get_file_write_access(inode, mnt); |
@@ -674,9 +694,15 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
674 | f->f_path.dentry = dentry; | 694 | f->f_path.dentry = dentry; |
675 | f->f_path.mnt = mnt; | 695 | f->f_path.mnt = mnt; |
676 | f->f_pos = 0; | 696 | f->f_pos = 0; |
677 | f->f_op = fops_get(inode->i_fop); | ||
678 | file_sb_list_add(f, inode->i_sb); | 697 | file_sb_list_add(f, inode->i_sb); |
679 | 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 | |||
680 | error = security_dentry_open(f, cred); | 706 | error = security_dentry_open(f, cred); |
681 | if (error) | 707 | if (error) |
682 | goto cleanup_all; | 708 | goto cleanup_all; |
@@ -785,6 +811,8 @@ struct file *nameidata_to_filp(struct nameidata *nd) | |||
785 | 811 | ||
786 | /* Pick up the filp from the open intent */ | 812 | /* Pick up the filp from the open intent */ |
787 | filp = nd->intent.open.file; | 813 | filp = nd->intent.open.file; |
814 | nd->intent.open.file = NULL; | ||
815 | |||
788 | /* Has the filesystem initialised the file for us? */ | 816 | /* Has the filesystem initialised the file for us? */ |
789 | if (filp->f_path.dentry == NULL) { | 817 | if (filp->f_path.dentry == NULL) { |
790 | path_get(&nd->path); | 818 | path_get(&nd->path); |
@@ -875,15 +903,110 @@ void fd_install(unsigned int fd, struct file *file) | |||
875 | 903 | ||
876 | EXPORT_SYMBOL(fd_install); | 904 | EXPORT_SYMBOL(fd_install); |
877 | 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 | |||
878 | 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) |
879 | { | 1000 | { |
1001 | struct open_flags op; | ||
1002 | int lookup = build_open_flags(flags, mode, &op); | ||
880 | char *tmp = getname(filename); | 1003 | char *tmp = getname(filename); |
881 | int fd = PTR_ERR(tmp); | 1004 | int fd = PTR_ERR(tmp); |
882 | 1005 | ||
883 | if (!IS_ERR(tmp)) { | 1006 | if (!IS_ERR(tmp)) { |
884 | fd = get_unused_fd_flags(flags); | 1007 | fd = get_unused_fd_flags(flags); |
885 | if (fd >= 0) { | 1008 | if (fd >= 0) { |
886 | struct file *f = do_filp_open(dfd, tmp, flags, mode, 0); | 1009 | struct file *f = do_filp_open(dfd, tmp, &op, lookup); |
887 | if (IS_ERR(f)) { | 1010 | if (IS_ERR(f)) { |
888 | put_unused_fd(fd); | 1011 | put_unused_fd(fd); |
889 | fd = PTR_ERR(f); | 1012 | fd = PTR_ERR(f); |
@@ -953,8 +1076,10 @@ int filp_close(struct file *filp, fl_owner_t id) | |||
953 | if (filp->f_op && filp->f_op->flush) | 1076 | if (filp->f_op && filp->f_op->flush) |
954 | retval = filp->f_op->flush(filp, id); | 1077 | retval = filp->f_op->flush(filp, id); |
955 | 1078 | ||
956 | dnotify_flush(filp, id); | 1079 | if (likely(!(filp->f_mode & FMODE_PATH))) { |
957 | locks_remove_posix(filp, id); | 1080 | dnotify_flush(filp, id); |
1081 | locks_remove_posix(filp, id); | ||
1082 | } | ||
958 | fput(filp); | 1083 | fput(filp); |
959 | return retval; | 1084 | return retval; |
960 | } | 1085 | } |