[O_TMPFILE] it's still short a few helpers, but infrastructure should be OK now...
authorAl Viro <viro@zeniv.linux.org.uk>
Fri, 7 Jun 2013 05:20:27 +0000 (01:20 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 29 Jun 2013 08:57:10 +0000 (12:57 +0400)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
12 files changed:
arch/alpha/include/uapi/asm/fcntl.h
arch/parisc/include/uapi/asm/fcntl.h
arch/sparc/include/uapi/asm/fcntl.h
fs/dcache.c
fs/ext2/namei.c
fs/minix/namei.c
fs/namei.c
fs/open.c
include/linux/dcache.h
include/linux/fs.h
include/uapi/asm-generic/fcntl.h
mm/shmem.c

index 6d9e805f18a77266504ab8217eaeb2310ec9532c..dfdadb0b4befa6619568d8179dd68f1850ef6151 100644 (file)
@@ -32,6 +32,7 @@
 #define O_SYNC         (__O_SYNC|O_DSYNC)
 
 #define O_PATH         040000000
+#define O_TMPFILE      0100000000
 
 #define F_GETLK                7
 #define F_SETLK                8
index 0304b92ccfeaf245f2f403b7163c70a8ec037b6c..cc61c475f2779198e45e72dc16dcc4f51e691dec 100644 (file)
@@ -20,6 +20,7 @@
 #define O_INVISIBLE    004000000 /* invisible I/O, for DMAPI/XDSM */
 
 #define O_PATH         020000000
+#define O_TMPFILE      040000000
 
 #define F_GETLK64      8
 #define F_SETLK64      9
index d0b83f66f356402a651249a2bed2079e98a85d76..d73e5e008b0de953d5e7eaf958d5c7bfed0c45c8 100644 (file)
@@ -35,6 +35,7 @@
 #define O_SYNC         (__O_SYNC|O_DSYNC)
 
 #define O_PATH         0x1000000
+#define O_TMPFILE      0x2000000
 
 #define F_GETOWN       5       /*  for sockets. */
 #define F_SETOWN       6       /*  for sockets. */
index f09b9085f7d849e235a2b8d77c079de71c96f5d2..b7f049c315269cb9161991a04b38606745f3e625 100644 (file)
@@ -2968,6 +2968,22 @@ rename_retry:
        goto again;
 }
 
+void d_tmpfile(struct dentry *dentry, struct inode *inode)
+{
+       inode_dec_link_count(inode);
+       BUG_ON(dentry->d_name.name != dentry->d_iname ||
+               !hlist_unhashed(&dentry->d_alias) ||
+               !d_unlinked(dentry));
+       spin_lock(&dentry->d_parent->d_lock);
+       spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
+       dentry->d_name.len = sprintf(dentry->d_iname, "#%llu",
+                               (unsigned long long)inode->i_ino);
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&dentry->d_parent->d_lock);
+       d_instantiate(dentry, inode);
+}
+EXPORT_SYMBOL(d_tmpfile);
+
 /**
  * find_inode_number - check for dentry with name
  * @dir: directory to check
index 73b0d9519836e1181b5b9227bd78ccead322a4d3..256dd5f4c1c4ade3f1ba160726ed0defabfccaf8 100644 (file)
@@ -119,6 +119,29 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, umode_t mode
        return ext2_add_nondir(dentry, inode);
 }
 
+static int ext2_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+       struct inode *inode = ext2_new_inode(dir, mode, NULL);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       inode->i_op = &ext2_file_inode_operations;
+       if (ext2_use_xip(inode->i_sb)) {
+               inode->i_mapping->a_ops = &ext2_aops_xip;
+               inode->i_fop = &ext2_xip_file_operations;
+       } else if (test_opt(inode->i_sb, NOBH)) {
+               inode->i_mapping->a_ops = &ext2_nobh_aops;
+               inode->i_fop = &ext2_file_operations;
+       } else {
+               inode->i_mapping->a_ops = &ext2_aops;
+               inode->i_fop = &ext2_file_operations;
+       }
+       mark_inode_dirty(inode);
+       d_tmpfile(dentry, inode);
+       unlock_new_inode(inode);
+       return 0;
+}
+
 static int ext2_mknod (struct inode * dir, struct dentry *dentry, umode_t mode, dev_t rdev)
 {
        struct inode * inode;
@@ -398,6 +421,7 @@ const struct inode_operations ext2_dir_inode_operations = {
 #endif
        .setattr        = ext2_setattr,
        .get_acl        = ext2_get_acl,
+       .tmpfile        = ext2_tmpfile,
 };
 
 const struct inode_operations ext2_special_inode_operations = {
index 0db73d9dd66845d289f6f1a58e1960ecc1ee3e74..cd950e2331b6c3164376e1b2205b3563f5703525 100644 (file)
@@ -54,6 +54,18 @@ static int minix_mknod(struct inode * dir, struct dentry *dentry, umode_t mode,
        return error;
 }
 
+static int minix_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+       int error;
+       struct inode *inode = minix_new_inode(dir, mode, &error);
+       if (inode) {
+               minix_set_inode(inode, 0);
+               mark_inode_dirty(inode);
+               d_tmpfile(dentry, inode);
+       }
+       return error;
+}
+
 static int minix_create(struct inode *dir, struct dentry *dentry, umode_t mode,
                bool excl)
 {
@@ -254,4 +266,5 @@ const struct inode_operations minix_dir_inode_operations = {
        .mknod          = minix_mknod,
        .rename         = minix_rename,
        .getattr        = minix_getattr,
+       .tmpfile        = minix_tmpfile,
 };
index 402eda351d0752ea8b867344ec5afcc10f0e2660..778e253e3d4850916a07bb7d3856a888471c0424 100644 (file)
@@ -2902,6 +2902,61 @@ stale_open:
        goto retry_lookup;
 }
 
+static int do_tmpfile(int dfd, struct filename *pathname,
+               struct nameidata *nd, int flags,
+               const struct open_flags *op,
+               struct file *file, int *opened)
+{
+       static const struct qstr name = QSTR_INIT("/", 1);
+       struct dentry *dentry, *child;
+       struct inode *dir;
+       int error = path_lookupat(dfd, pathname->name,
+                                 flags | LOOKUP_DIRECTORY, nd);
+       if (unlikely(error))
+               return error;
+       error = mnt_want_write(nd->path.mnt);
+       if (unlikely(error))
+               goto out;
+       /* we want directory to be writable */
+       error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC);
+       if (error)
+               goto out2;
+       dentry = nd->path.dentry;
+       dir = dentry->d_inode;
+       if (!dir->i_op->tmpfile) {
+               error = -EOPNOTSUPP;
+               goto out2;
+       }
+       child = d_alloc(dentry, &name);
+       if (unlikely(!child)) {
+               error = -ENOMEM;
+               goto out2;
+       }
+       nd->flags &= ~LOOKUP_DIRECTORY;
+       nd->flags |= op->intent;
+       dput(nd->path.dentry);
+       nd->path.dentry = child;
+       error = dir->i_op->tmpfile(dir, nd->path.dentry, op->mode);
+       if (error)
+               goto out2;
+       audit_inode(pathname, nd->path.dentry, 0);
+       error = may_open(&nd->path, op->acc_mode, op->open_flag);
+       if (error)
+               goto out2;
+       file->f_path.mnt = nd->path.mnt;
+       error = finish_open(file, nd->path.dentry, NULL, opened);
+       if (error)
+               goto out2;
+       error = open_check_o_direct(file);
+       if (error)
+               fput(file);
+out2:
+       mnt_drop_write(nd->path.mnt);
+out:
+       path_put(&nd->path);
+       return error;
+}
+
 static struct file *path_openat(int dfd, struct filename *pathname,
                struct nameidata *nd, const struct open_flags *op, int flags)
 {
@@ -2917,6 +2972,11 @@ static struct file *path_openat(int dfd, struct filename *pathname,
 
        file->f_flags = op->open_flag;
 
+       if (unlikely(file->f_flags & O_TMPFILE)) {
+               error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened);
+               goto out;
+       }
+
        error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base);
        if (unlikely(error))
                goto out;
index 5a40a4a5175739fd1c08c498a38598f84f457242..fca72c4d3f17ac13758ac5315e413d749bf7667b 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -840,11 +840,15 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
        if (flags & __O_SYNC)
                flags |= O_DSYNC;
 
-       /*
-        * If we have O_PATH in the open flag. Then we
-        * cannot have anything other than the below set of flags
-        */
-       if (flags & O_PATH) {
+       if (flags & O_TMPFILE) {
+               if (!(flags & O_CREAT))
+                       return -EINVAL;
+               acc_mode = MAY_OPEN | ACC_MODE(flags);
+       } else if (flags & O_PATH) {
+               /*
+                * If we have O_PATH in the open flag. Then we
+                * cannot have anything other than the below set of flags
+                */
                flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
                acc_mode = 0;
        } else {
index 1a6bb81f0fe5a13d93c799cfbcd4062d0e288c28..86da7595ba31a63814eb7b08c6965d42bea654fc 100644 (file)
@@ -246,6 +246,8 @@ extern struct dentry * d_make_root(struct inode *);
 /* <clickety>-<click> the ramfs-type tree */
 extern void d_genocide(struct dentry *);
 
+extern void d_tmpfile(struct dentry *, struct inode *);
+
 extern struct dentry *d_find_alias(struct inode *);
 extern void d_prune_aliases(struct inode *);
 
index 7c30e3a62baf94c742e5261f0156e4c6c1c9e985..dd6615f0fd13a8a0e934f7b5c86399f94fe19c0a 100644 (file)
@@ -1580,6 +1580,7 @@ struct inode_operations {
        int (*atomic_open)(struct inode *, struct dentry *,
                           struct file *, unsigned open_flag,
                           umode_t create_mode, int *opened);
+       int (*tmpfile) (struct inode *, struct dentry *, umode_t);
 } ____cacheline_aligned;
 
 ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
index a48937d4a5ea5715fafe813baf4f586a0b747355..06632beaa6d515d80fc2a62c5ddca455c964e0a3 100644 (file)
 #define O_PATH         010000000
 #endif
 
+#ifndef O_TMPFILE
+#define O_TMPFILE      020000000
+#endif
+
 #ifndef O_NDELAY
 #define O_NDELAY       O_NONBLOCK
 #endif
index 5e6a8422658b832921196ca4908e7cc6148eb84a..f887358dabc557e69618d360a61c8dbb51738b8d 100644 (file)
@@ -1965,6 +1965,37 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
        return error;
 }
 
+static int
+shmem_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+       struct inode *inode;
+       int error = -ENOSPC;
+
+       inode = shmem_get_inode(dir->i_sb, dir, mode, 0, VM_NORESERVE);
+       if (inode) {
+               error = security_inode_init_security(inode, dir,
+                                                    NULL,
+                                                    shmem_initxattrs, NULL);
+               if (error) {
+                       if (error != -EOPNOTSUPP) {
+                               iput(inode);
+                               return error;
+                       }
+               }
+#ifdef CONFIG_TMPFS_POSIX_ACL
+               error = generic_acl_init(inode, dir);
+               if (error) {
+                       iput(inode);
+                       return error;
+               }
+#else
+               error = 0;
+#endif
+               d_tmpfile(dentry, inode);
+       }
+       return error;
+}
+
 static int shmem_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
        int error;
@@ -2723,6 +2754,7 @@ static const struct inode_operations shmem_dir_inode_operations = {
        .rmdir          = shmem_rmdir,
        .mknod          = shmem_mknod,
        .rename         = shmem_rename,
+       .tmpfile        = shmem_tmpfile,
 #endif
 #ifdef CONFIG_TMPFS_XATTR
        .setxattr       = shmem_setxattr,