ext4: Copy fs UUID to superblock
[linux-block.git] / fs / open.c
index e52389e1f05b4c010b49ef9a6e4d8cb11bfac504..48afc5c139d247e634d9926507d2a8d9354015db 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -233,6 +233,14 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
 
        if (!(file->f_mode & FMODE_WRITE))
                return -EBADF;
+
+       /* It's not possible punch hole on append only file */
+       if (mode & FALLOC_FL_PUNCH_HOLE && IS_APPEND(inode))
+               return -EPERM;
+
+       if (IS_IMMUTABLE(inode))
+               return -EPERM;
+
        /*
         * Revalidate the write permissions, in case security policy has
         * changed since the files were opened.
@@ -790,6 +798,8 @@ struct file *nameidata_to_filp(struct nameidata *nd)
 
        /* Pick up the filp from the open intent */
        filp = nd->intent.open.file;
+       nd->intent.open.file = NULL;
+
        /* Has the filesystem initialised the file for us? */
        if (filp->f_path.dentry == NULL) {
                path_get(&nd->path);
@@ -880,15 +890,100 @@ void fd_install(unsigned int fd, struct file *file)
 
 EXPORT_SYMBOL(fd_install);
 
+static inline int build_open_flags(int flags, int mode, struct open_flags *op)
+{
+       int lookup_flags = 0;
+       int acc_mode;
+
+       if (!(flags & O_CREAT))
+               mode = 0;
+       op->mode = mode;
+
+       /* Must never be set by userspace */
+       flags &= ~FMODE_NONOTIFY;
+
+       /*
+        * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
+        * check for O_DSYNC if the need any syncing at all we enforce it's
+        * always set instead of having to deal with possibly weird behaviour
+        * for malicious applications setting only __O_SYNC.
+        */
+       if (flags & __O_SYNC)
+               flags |= O_DSYNC;
+
+       op->open_flag = flags;
+
+       acc_mode = MAY_OPEN | ACC_MODE(flags);
+
+       /* O_TRUNC implies we need access checks for write permissions */
+       if (flags & O_TRUNC)
+               acc_mode |= MAY_WRITE;
+
+       /* Allow the LSM permission hook to distinguish append
+          access from general write access. */
+       if (flags & O_APPEND)
+               acc_mode |= MAY_APPEND;
+
+       op->acc_mode = acc_mode;
+
+       op->intent = LOOKUP_OPEN;
+       if (flags & O_CREAT) {
+               op->intent |= LOOKUP_CREATE;
+               if (flags & O_EXCL)
+                       op->intent |= LOOKUP_EXCL;
+       }
+
+       if (flags & O_DIRECTORY)
+               lookup_flags |= LOOKUP_DIRECTORY;
+       if (!(flags & O_NOFOLLOW))
+               lookup_flags |= LOOKUP_FOLLOW;
+       return lookup_flags;
+}
+
+/**
+ * filp_open - open file and return file pointer
+ *
+ * @filename:  path to open
+ * @flags:     open flags as per the open(2) second argument
+ * @mode:      mode for the new file if O_CREAT is set, else ignored
+ *
+ * This is the helper to open a file from kernelspace if you really
+ * have to.  But in generally you should not do this, so please move
+ * along, nothing to see here..
+ */
+struct file *filp_open(const char *filename, int flags, int mode)
+{
+       struct open_flags op;
+       int lookup = build_open_flags(flags, mode, &op);
+       return do_filp_open(AT_FDCWD, filename, &op, lookup);
+}
+EXPORT_SYMBOL(filp_open);
+
+struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt,
+                           const char *filename, int flags)
+{
+       struct open_flags op;
+       int lookup = build_open_flags(flags, 0, &op);
+       if (flags & O_CREAT)
+               return ERR_PTR(-EINVAL);
+       if (!filename && (flags & O_DIRECTORY))
+               if (!dentry->d_inode->i_op->lookup)
+                       return ERR_PTR(-ENOTDIR);
+       return do_file_open_root(dentry, mnt, filename, &op, lookup);
+}
+EXPORT_SYMBOL(file_open_root);
+
 long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
 {
+       struct open_flags op;
+       int lookup = build_open_flags(flags, mode, &op);
        char *tmp = getname(filename);
        int fd = PTR_ERR(tmp);
 
        if (!IS_ERR(tmp)) {
                fd = get_unused_fd_flags(flags);
                if (fd >= 0) {
-                       struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
+                       struct file *f = do_filp_open(dfd, tmp, &op, lookup);
                        if (IS_ERR(f)) {
                                put_unused_fd(fd);
                                fd = PTR_ERR(f);