]> err.no Git - linux-2.6/blobdiff - fs/open.c
[NETFILTER] ctnetlink: add marking support from userspace
[linux-2.6] / fs / open.c
index f0d90cf0495c490316d427e309c38695ceb4e005..f53a5b9ffb7dce308f6e0e05d4982d343261564c 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -194,7 +194,7 @@ out:
        return error;
 }
 
-int do_truncate(struct dentry *dentry, loff_t length)
+int do_truncate(struct dentry *dentry, loff_t length, struct file *filp)
 {
        int err;
        struct iattr newattrs;
@@ -205,6 +205,10 @@ int do_truncate(struct dentry *dentry, loff_t length)
 
        newattrs.ia_size = length;
        newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+       if (filp) {
+               newattrs.ia_file = filp;
+               newattrs.ia_valid |= ATTR_FILE;
+       }
 
        down(&dentry->d_inode->i_sem);
        err = notify_change(dentry, &newattrs);
@@ -236,7 +240,7 @@ static inline long do_sys_truncate(const char __user * path, loff_t length)
        if (!S_ISREG(inode->i_mode))
                goto dput_and_out;
 
-       error = permission(inode,MAY_WRITE,&nd);
+       error = vfs_permission(&nd, MAY_WRITE);
        if (error)
                goto dput_and_out;
 
@@ -262,7 +266,7 @@ static inline long do_sys_truncate(const char __user * path, loff_t length)
        error = locks_verify_truncate(inode, NULL, length);
        if (!error) {
                DQUOT_INIT(inode);
-               error = do_truncate(nd.dentry, length);
+               error = do_truncate(nd.dentry, length, NULL);
        }
        put_write_access(inode);
 
@@ -314,7 +318,7 @@ static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
 
        error = locks_verify_truncate(inode, file, length);
        if (!error)
-               error = do_truncate(dentry, length);
+               error = do_truncate(dentry, length, file);
 out_putf:
        fput(file);
 out:
@@ -390,7 +394,7 @@ asmlinkage long sys_utime(char __user * filename, struct utimbuf __user * times)
                         goto dput_and_out;
 
                if (current->fsuid != inode->i_uid &&
-                   (error = permission(inode,MAY_WRITE,&nd)) != 0)
+                   (error = vfs_permission(&nd, MAY_WRITE)) != 0)
                        goto dput_and_out;
        }
        down(&inode->i_sem);
@@ -443,7 +447,7 @@ long do_utimes(char __user * filename, struct timeval * times)
                         goto dput_and_out;
 
                if (current->fsuid != inode->i_uid &&
-                   (error = permission(inode,MAY_WRITE,&nd)) != 0)
+                   (error = vfs_permission(&nd, MAY_WRITE)) != 0)
                        goto dput_and_out;
        }
        down(&inode->i_sem);
@@ -502,7 +506,7 @@ asmlinkage long sys_access(const char __user * filename, int mode)
 
        res = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
        if (!res) {
-               res = permission(nd.dentry->d_inode, mode, &nd);
+               res = vfs_permission(&nd, mode);
                /* SuS v2 requires we report a read only fs too */
                if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode)
                   && !special_file(nd.dentry->d_inode->i_mode))
@@ -526,7 +530,7 @@ asmlinkage long sys_chdir(const char __user * filename)
        if (error)
                goto out;
 
-       error = permission(nd.dentry->d_inode,MAY_EXEC,&nd);
+       error = vfs_permission(&nd, MAY_EXEC);
        if (error)
                goto dput_and_out;
 
@@ -559,7 +563,7 @@ asmlinkage long sys_fchdir(unsigned int fd)
        if (!S_ISDIR(inode->i_mode))
                goto out_putf;
 
-       error = permission(inode, MAY_EXEC, NULL);
+       error = file_permission(file, MAY_EXEC);
        if (!error)
                set_fs_pwd(current->fs, mnt, dentry);
 out_putf:
@@ -577,7 +581,7 @@ asmlinkage long sys_chroot(const char __user * filename)
        if (error)
                goto out;
 
-       error = permission(nd.dentry->d_inode,MAY_EXEC,&nd);
+       error = vfs_permission(&nd, MAY_EXEC);
        if (error)
                goto dput_and_out;
 
@@ -739,7 +743,8 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
 }
 
 static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
-                                       int flags, struct file *f)
+                                       int flags, struct file *f,
+                                       int (*open)(struct inode *, struct file *))
 {
        struct inode *inode;
        int error;
@@ -761,11 +766,14 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
        f->f_op = fops_get(inode->i_fop);
        file_move(f, &inode->i_sb->s_files);
 
-       if (f->f_op && f->f_op->open) {
-               error = f->f_op->open(inode,f);
+       if (!open && f->f_op)
+               open = f->f_op->open;
+       if (open) {
+               error = open(inode, f);
                if (error)
                        goto cleanup_all;
        }
+
        f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 
        file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
@@ -814,28 +822,79 @@ struct file *filp_open(const char * filename, int flags, int mode)
 {
        int namei_flags, error;
        struct nameidata nd;
-       struct file *f;
 
        namei_flags = flags;
        if ((namei_flags+1) & O_ACCMODE)
                namei_flags++;
-       if (namei_flags & O_TRUNC)
-               namei_flags |= 2;
-
-       error = -ENFILE;
-       f = get_empty_filp();
-       if (f == NULL)
-               return ERR_PTR(error);
 
        error = open_namei(filename, namei_flags, mode, &nd);
        if (!error)
-               return __dentry_open(nd.dentry, nd.mnt, flags, f);
+               return nameidata_to_filp(&nd, flags);
 
-       put_filp(f);
        return ERR_PTR(error);
 }
 EXPORT_SYMBOL(filp_open);
 
+/**
+ * lookup_instantiate_filp - instantiates the open intent filp
+ * @nd: pointer to nameidata
+ * @dentry: pointer to dentry
+ * @open: open callback
+ *
+ * Helper for filesystems that want to use lookup open intents and pass back
+ * a fully instantiated struct file to the caller.
+ * This function is meant to be called from within a filesystem's
+ * lookup method.
+ * Note that in case of error, nd->intent.open.file is destroyed, but the
+ * path information remains valid.
+ * If the open callback is set to NULL, then the standard f_op->open()
+ * filesystem callback is substituted.
+ */
+struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
+               int (*open)(struct inode *, struct file *))
+{
+       if (IS_ERR(nd->intent.open.file))
+               goto out;
+       if (IS_ERR(dentry))
+               goto out_err;
+       nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->mnt),
+                                            nd->intent.open.flags - 1,
+                                            nd->intent.open.file,
+                                            open);
+out:
+       return nd->intent.open.file;
+out_err:
+       release_open_intent(nd);
+       nd->intent.open.file = (struct file *)dentry;
+       goto out;
+}
+EXPORT_SYMBOL_GPL(lookup_instantiate_filp);
+
+/**
+ * nameidata_to_filp - convert a nameidata to an open filp.
+ * @nd: pointer to nameidata
+ * @flags: open flags
+ *
+ * Note that this function destroys the original nameidata
+ */
+struct file *nameidata_to_filp(struct nameidata *nd, int flags)
+{
+       struct file *filp;
+
+       /* Pick up the filp from the open intent */
+       filp = nd->intent.open.file;
+       /* Has the filesystem initialised the file for us? */
+       if (filp->f_dentry == NULL)
+               filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL);
+       else
+               path_release(nd);
+       return filp;
+}
+
+/*
+ * dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an
+ * error.
+ */
 struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
 {
        int error;
@@ -843,10 +902,13 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
 
        error = -ENFILE;
        f = get_empty_filp();
-       if (f == NULL)
+       if (f == NULL) {
+               dput(dentry);
+               mntput(mnt);
                return ERR_PTR(error);
+       }
 
-       return __dentry_open(dentry, mnt, flags, f);
+       return __dentry_open(dentry, mnt, flags, f, NULL);
 }
 EXPORT_SYMBOL(dentry_open);