Btrfs: implement inode_operations callback tmpfile
authorFilipe Manana <fdmanana@gmail.com>
Sun, 27 Apr 2014 19:40:45 +0000 (20:40 +0100)
committerChris Mason <clm@fb.com>
Tue, 10 Jun 2014 00:20:30 +0000 (17:20 -0700)
This implements the tmpfile callback of struct inode_operations, introduced
in the linux kernel 3.11, and implemented already by some filesystems. This
callback is invoked by the VFS when the flag O_TMPFILE is passed to the open
system call.

Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
Signed-off-by: Chris Mason <clm@fb.com>
Reviewed-by: David Sterba <dsterba@suse.cz>
fs/btrfs/inode.c

index 24dfa27a4f76f03bf35dc3292cb7f7738a10baf9..5c0def8dd761b41f21964eb5432044636ab873b2 100644 (file)
@@ -5553,6 +5553,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        struct btrfs_inode_ref *ref;
        struct btrfs_key key[2];
        u32 sizes[2];
+       int nitems = name ? 2 : 1;
        unsigned long ptr;
        int ret;
 
@@ -5572,7 +5573,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
         */
        inode->i_ino = objectid;
 
-       if (dir) {
+       if (dir && name) {
                trace_btrfs_inode_request(dir);
 
                ret = btrfs_set_inode_index(dir, index);
@@ -5581,6 +5582,8 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
                        iput(inode);
                        return ERR_PTR(ret);
                }
+       } else if (dir) {
+               *index = 0;
        }
        /*
         * index_cnt is ignored for everything but a dir,
@@ -5605,21 +5608,24 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        btrfs_set_key_type(&key[0], BTRFS_INODE_ITEM_KEY);
        key[0].offset = 0;
 
-       /*
-        * Start new inodes with an inode_ref. This is slightly more
-        * efficient for small numbers of hard links since they will
-        * be packed into one item. Extended refs will kick in if we
-        * add more hard links than can fit in the ref item.
-        */
-       key[1].objectid = objectid;
-       btrfs_set_key_type(&key[1], BTRFS_INODE_REF_KEY);
-       key[1].offset = ref_objectid;
-
        sizes[0] = sizeof(struct btrfs_inode_item);
-       sizes[1] = name_len + sizeof(*ref);
+
+       if (name) {
+               /*
+                * Start new inodes with an inode_ref. This is slightly more
+                * efficient for small numbers of hard links since they will
+                * be packed into one item. Extended refs will kick in if we
+                * add more hard links than can fit in the ref item.
+                */
+               key[1].objectid = objectid;
+               btrfs_set_key_type(&key[1], BTRFS_INODE_REF_KEY);
+               key[1].offset = ref_objectid;
+
+               sizes[1] = name_len + sizeof(*ref);
+       }
 
        path->leave_spinning = 1;
-       ret = btrfs_insert_empty_items(trans, root, path, key, sizes, 2);
+       ret = btrfs_insert_empty_items(trans, root, path, key, sizes, nitems);
        if (ret != 0)
                goto fail;
 
@@ -5632,12 +5638,14 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
                             sizeof(*inode_item));
        fill_inode_item(trans, path->nodes[0], inode_item, inode);
 
-       ref = btrfs_item_ptr(path->nodes[0], path->slots[0] + 1,
-                            struct btrfs_inode_ref);
-       btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
-       btrfs_set_inode_ref_index(path->nodes[0], ref, *index);
-       ptr = (unsigned long)(ref + 1);
-       write_extent_buffer(path->nodes[0], name, ptr, name_len);
+       if (name) {
+               ref = btrfs_item_ptr(path->nodes[0], path->slots[0] + 1,
+                                    struct btrfs_inode_ref);
+               btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
+               btrfs_set_inode_ref_index(path->nodes[0], ref, *index);
+               ptr = (unsigned long)(ref + 1);
+               write_extent_buffer(path->nodes[0], name, ptr, name_len);
+       }
 
        btrfs_mark_buffer_dirty(path->nodes[0]);
        btrfs_free_path(path);
@@ -5673,7 +5681,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 
        return inode;
 fail:
-       if (dir)
+       if (dir && name)
                BTRFS_I(dir)->index_cnt--;
        btrfs_free_path(path);
        iput(inode);
@@ -5958,6 +5966,15 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
                err = btrfs_update_inode(trans, root, inode);
                if (err)
                        goto fail;
+               if (inode->i_nlink == 1) {
+                       /*
+                        * If new hard link count is 1, it's a file created
+                        * with open(2) O_TMPFILE flag.
+                        */
+                       err = btrfs_orphan_del(trans, inode);
+                       if (err)
+                               goto fail;
+               }
                d_instantiate(dentry, inode);
                btrfs_log_new_name(trans, inode, NULL, parent);
        }
@@ -8884,6 +8901,66 @@ static int btrfs_permission(struct inode *inode, int mask)
        return generic_permission(inode, mask);
 }
 
+static int btrfs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+       struct btrfs_trans_handle *trans;
+       struct btrfs_root *root = BTRFS_I(dir)->root;
+       struct inode *inode = NULL;
+       u64 objectid;
+       u64 index;
+       int ret = 0;
+
+       /*
+        * 5 units required for adding orphan entry
+        */
+       trans = btrfs_start_transaction(root, 5);
+       if (IS_ERR(trans))
+               return PTR_ERR(trans);
+
+       ret = btrfs_find_free_ino(root, &objectid);
+       if (ret)
+               goto out;
+
+       inode = btrfs_new_inode(trans, root, dir, NULL, 0,
+                               btrfs_ino(dir), objectid, mode, &index);
+       if (IS_ERR(inode)) {
+               ret = PTR_ERR(inode);
+               inode = NULL;
+               goto out;
+       }
+
+       ret = btrfs_init_inode_security(trans, inode, dir, NULL);
+       if (ret)
+               goto out;
+
+       ret = btrfs_update_inode(trans, root, inode);
+       if (ret)
+               goto out;
+
+       inode->i_fop = &btrfs_file_operations;
+       inode->i_op = &btrfs_file_inode_operations;
+
+       inode->i_mapping->a_ops = &btrfs_aops;
+       inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
+       BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
+
+       ret = btrfs_orphan_add(trans, inode);
+       if (ret)
+               goto out;
+
+       d_tmpfile(dentry, inode);
+       mark_inode_dirty(inode);
+
+out:
+       btrfs_end_transaction(trans, root);
+       if (ret)
+               iput(inode);
+       btrfs_balance_delayed_items(root);
+       btrfs_btree_balance_dirty(root);
+
+       return ret;
+}
+
 static const struct inode_operations btrfs_dir_inode_operations = {
        .getattr        = btrfs_getattr,
        .lookup         = btrfs_lookup,
@@ -8904,6 +8981,7 @@ static const struct inode_operations btrfs_dir_inode_operations = {
        .get_acl        = btrfs_get_acl,
        .set_acl        = btrfs_set_acl,
        .update_time    = btrfs_update_time,
+       .tmpfile        = btrfs_tmpfile,
 };
 static const struct inode_operations btrfs_dir_ro_inode_operations = {
        .lookup         = btrfs_lookup,