Btrfs: implement inode_operations callback tmpfile
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>
This commit is contained in:
		
					parent
					
						
							
								e4ef90ff61
							
						
					
				
			
			
				commit
				
					
						ef3b9af50b
					
				
			
		
					 1 changed files with 98 additions and 20 deletions
				
			
		| 
						 | 
					@ -5553,6 +5553,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 | 
				
			||||||
	struct btrfs_inode_ref *ref;
 | 
						struct btrfs_inode_ref *ref;
 | 
				
			||||||
	struct btrfs_key key[2];
 | 
						struct btrfs_key key[2];
 | 
				
			||||||
	u32 sizes[2];
 | 
						u32 sizes[2];
 | 
				
			||||||
 | 
						int nitems = name ? 2 : 1;
 | 
				
			||||||
	unsigned long ptr;
 | 
						unsigned long ptr;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5572,7 +5573,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	inode->i_ino = objectid;
 | 
						inode->i_ino = objectid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dir) {
 | 
						if (dir && name) {
 | 
				
			||||||
		trace_btrfs_inode_request(dir);
 | 
							trace_btrfs_inode_request(dir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = btrfs_set_inode_index(dir, index);
 | 
							ret = btrfs_set_inode_index(dir, index);
 | 
				
			||||||
| 
						 | 
					@ -5581,6 +5582,8 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 | 
				
			||||||
			iput(inode);
 | 
								iput(inode);
 | 
				
			||||||
			return ERR_PTR(ret);
 | 
								return ERR_PTR(ret);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else if (dir) {
 | 
				
			||||||
 | 
							*index = 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * index_cnt is ignored for everything but a dir,
 | 
						 * index_cnt is ignored for everything but a dir,
 | 
				
			||||||
| 
						 | 
					@ -5605,6 +5608,9 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 | 
				
			||||||
	btrfs_set_key_type(&key[0], BTRFS_INODE_ITEM_KEY);
 | 
						btrfs_set_key_type(&key[0], BTRFS_INODE_ITEM_KEY);
 | 
				
			||||||
	key[0].offset = 0;
 | 
						key[0].offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sizes[0] = sizeof(struct btrfs_inode_item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (name) {
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * Start new inodes with an inode_ref. This is slightly more
 | 
							 * Start new inodes with an inode_ref. This is slightly more
 | 
				
			||||||
		 * efficient for small numbers of hard links since they will
 | 
							 * efficient for small numbers of hard links since they will
 | 
				
			||||||
| 
						 | 
					@ -5615,11 +5621,11 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 | 
				
			||||||
		btrfs_set_key_type(&key[1], BTRFS_INODE_REF_KEY);
 | 
							btrfs_set_key_type(&key[1], BTRFS_INODE_REF_KEY);
 | 
				
			||||||
		key[1].offset = ref_objectid;
 | 
							key[1].offset = ref_objectid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sizes[0] = sizeof(struct btrfs_inode_item);
 | 
					 | 
				
			||||||
		sizes[1] = name_len + sizeof(*ref);
 | 
							sizes[1] = name_len + sizeof(*ref);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	path->leave_spinning = 1;
 | 
						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)
 | 
						if (ret != 0)
 | 
				
			||||||
		goto fail;
 | 
							goto fail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5632,12 +5638,14 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 | 
				
			||||||
			     sizeof(*inode_item));
 | 
								     sizeof(*inode_item));
 | 
				
			||||||
	fill_inode_item(trans, path->nodes[0], inode_item, inode);
 | 
						fill_inode_item(trans, path->nodes[0], inode_item, inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (name) {
 | 
				
			||||||
		ref = btrfs_item_ptr(path->nodes[0], path->slots[0] + 1,
 | 
							ref = btrfs_item_ptr(path->nodes[0], path->slots[0] + 1,
 | 
				
			||||||
				     struct btrfs_inode_ref);
 | 
									     struct btrfs_inode_ref);
 | 
				
			||||||
		btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
 | 
							btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
 | 
				
			||||||
		btrfs_set_inode_ref_index(path->nodes[0], ref, *index);
 | 
							btrfs_set_inode_ref_index(path->nodes[0], ref, *index);
 | 
				
			||||||
		ptr = (unsigned long)(ref + 1);
 | 
							ptr = (unsigned long)(ref + 1);
 | 
				
			||||||
		write_extent_buffer(path->nodes[0], name, ptr, name_len);
 | 
							write_extent_buffer(path->nodes[0], name, ptr, name_len);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	btrfs_mark_buffer_dirty(path->nodes[0]);
 | 
						btrfs_mark_buffer_dirty(path->nodes[0]);
 | 
				
			||||||
	btrfs_free_path(path);
 | 
						btrfs_free_path(path);
 | 
				
			||||||
| 
						 | 
					@ -5673,7 +5681,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return inode;
 | 
						return inode;
 | 
				
			||||||
fail:
 | 
					fail:
 | 
				
			||||||
	if (dir)
 | 
						if (dir && name)
 | 
				
			||||||
		BTRFS_I(dir)->index_cnt--;
 | 
							BTRFS_I(dir)->index_cnt--;
 | 
				
			||||||
	btrfs_free_path(path);
 | 
						btrfs_free_path(path);
 | 
				
			||||||
	iput(inode);
 | 
						iput(inode);
 | 
				
			||||||
| 
						 | 
					@ -5958,6 +5966,15 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
 | 
				
			||||||
		err = btrfs_update_inode(trans, root, inode);
 | 
							err = btrfs_update_inode(trans, root, inode);
 | 
				
			||||||
		if (err)
 | 
							if (err)
 | 
				
			||||||
			goto fail;
 | 
								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);
 | 
							d_instantiate(dentry, inode);
 | 
				
			||||||
		btrfs_log_new_name(trans, inode, NULL, parent);
 | 
							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);
 | 
						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 = {
 | 
					static const struct inode_operations btrfs_dir_inode_operations = {
 | 
				
			||||||
	.getattr	= btrfs_getattr,
 | 
						.getattr	= btrfs_getattr,
 | 
				
			||||||
	.lookup		= btrfs_lookup,
 | 
						.lookup		= btrfs_lookup,
 | 
				
			||||||
| 
						 | 
					@ -8904,6 +8981,7 @@ static const struct inode_operations btrfs_dir_inode_operations = {
 | 
				
			||||||
	.get_acl	= btrfs_get_acl,
 | 
						.get_acl	= btrfs_get_acl,
 | 
				
			||||||
	.set_acl	= btrfs_set_acl,
 | 
						.set_acl	= btrfs_set_acl,
 | 
				
			||||||
	.update_time	= btrfs_update_time,
 | 
						.update_time	= btrfs_update_time,
 | 
				
			||||||
 | 
						.tmpfile        = btrfs_tmpfile,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
static const struct inode_operations btrfs_dir_ro_inode_operations = {
 | 
					static const struct inode_operations btrfs_dir_ro_inode_operations = {
 | 
				
			||||||
	.lookup		= btrfs_lookup,
 | 
						.lookup		= btrfs_lookup,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue