diff options
| author | Chris Mason <chris.mason@oracle.com> | 2007-05-24 08:34:10 -0400 | 
|---|---|---|
| committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-05-24 08:34:10 -0400 | 
| commit | 2b8d99a723a3186a69b5bd941a272dbecae9205b (patch) | |
| tree | c3f87b82450110d2df8ac03032bd2e21b8984f19 | |
| parent | e06afa839e726959be2166bec4cb85c117e213f1 (diff) | |
Btrfs: symlinks and hard links
Signed-off-by: Chris Mason <chris.mason@oracle.com>
| -rw-r--r-- | fs/btrfs/super.c | 163 | 
1 files changed, 147 insertions, 16 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index f49cad603ee..c1a0adde9b1 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -26,11 +26,13 @@ struct btrfs_iget_args {  #define BTRFS_SUPER_MAGIC 0x9123682E  static struct inode_operations btrfs_dir_inode_operations; +static struct inode_operations btrfs_symlink_inode_operations;  static struct inode_operations btrfs_dir_ro_inode_operations;  static struct super_operations btrfs_super_ops;  static struct file_operations btrfs_dir_file_operations;  static struct inode_operations btrfs_file_inode_operations;  static struct address_space_operations btrfs_aops; +static struct address_space_operations btrfs_symlink_aops;  static struct file_operations btrfs_file_operations;  static void btrfs_read_locked_inode(struct inode *inode) @@ -103,7 +105,8 @@ static void btrfs_read_locked_inode(struct inode *inode)  			inode->i_op = &btrfs_dir_inode_operations;  		break;  	case S_IFLNK: -		// inode->i_op = &page_symlink_inode_operations; +		inode->i_op = &btrfs_symlink_inode_operations; +		inode->i_mapping->a_ops = &btrfs_symlink_aops;  		break;  	}  	return; @@ -940,6 +943,41 @@ out_unlock:  	return err;  } +static int btrfs_link(struct dentry *old_dentry, struct inode *dir, +		      struct dentry *dentry) +{ +	struct btrfs_trans_handle *trans; +	struct btrfs_root *root = BTRFS_I(dir)->root; +	struct inode *inode = old_dentry->d_inode; +	int err; +	int drop_inode = 0; + +	if (inode->i_nlink == 0) +		return -ENOENT; + +	inc_nlink(inode); +	mutex_lock(&root->fs_info->fs_mutex); +	trans = btrfs_start_transaction(root, 1); +	btrfs_set_trans_block_group(trans, dir); +	atomic_inc(&inode->i_count); +	err = btrfs_add_nondir(trans, dentry, inode); +	if (err) +		drop_inode = 1; +	dir->i_sb->s_dirt = 1; +	btrfs_update_inode_block_group(trans, dir); +	btrfs_update_inode(trans, root, inode); + +	btrfs_end_transaction(trans, root); +	mutex_unlock(&root->fs_info->fs_mutex); + +	if (drop_inode) { +		inode_dec_link_count(inode); +		iput(inode); +	} +	btrfs_btree_balance_dirty(root); +	return err; +} +  static int btrfs_make_empty_dir(struct btrfs_trans_handle *trans,  				struct btrfs_root *root,  				u64 objectid, u64 dirid) @@ -2577,33 +2615,25 @@ static int btrfs_rename(struct inode * old_dir, struct dentry *old_dentry,  	} -	ret = btrfs_add_link(trans, new_dentry, old_inode); -	if (ret == -EEXIST && new_inode) -		ret = 0; -	else if (ret) -		goto out_fail; -  	ret = btrfs_unlink_trans(trans, root, old_dir, old_dentry);  	if (ret)  		goto out_fail;  	if (new_inode) {  		new_inode->i_ctime = CURRENT_TIME; -		di = btrfs_lookup_dir_index_item(trans, root, path, -						 new_dir->i_ino, -						 new_inode->i_ino, -						 new_dentry->d_name.name, -						 new_dentry->d_name.len, -1); -		if (di && !IS_ERR(di)) { -			btrfs_del_item(trans, root, path); -			btrfs_release_path(root, path); -		} +		ret = btrfs_unlink_trans(trans, root, new_dir, new_dentry); +		if (ret) +			goto out_fail;  		if (S_ISDIR(new_inode->i_mode))  			clear_nlink(new_inode);  		else  			drop_nlink(new_inode);  		btrfs_update_inode(trans, root, new_inode);  	} +	ret = btrfs_add_link(trans, new_dentry, old_inode); +	if (ret) +		goto out_fail; +  out_fail:  	btrfs_free_path(path);  	btrfs_end_transaction(trans, root); @@ -2611,6 +2641,94 @@ out_fail:  	return ret;  } +static int btrfs_symlink(struct inode *dir, struct dentry *dentry, +			 const char *symname) +{ +	struct btrfs_trans_handle *trans; +	struct btrfs_root *root = BTRFS_I(dir)->root; +	struct btrfs_path *path; +	struct btrfs_key key; +	struct inode *inode; +	int err; +	int drop_inode = 0; +	u64 objectid; +	int name_len; +	int datasize; +	char *ptr; +	struct btrfs_file_extent_item *ei; + +	name_len = strlen(symname) + 1; +	if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(root)) +		return -ENAMETOOLONG; +	mutex_lock(&root->fs_info->fs_mutex); +	trans = btrfs_start_transaction(root, 1); +	btrfs_set_trans_block_group(trans, dir); + +	err = btrfs_find_free_objectid(trans, root, dir->i_ino, &objectid); +	if (err) { +		err = -ENOSPC; +		goto out_unlock; +	} + +	inode = btrfs_new_inode(trans, root, objectid, +				BTRFS_I(dir)->block_group, S_IFLNK|S_IRWXUGO); +	err = PTR_ERR(inode); +	if (IS_ERR(inode)) +		goto out_unlock; + +	btrfs_set_trans_block_group(trans, inode); +	err = btrfs_add_nondir(trans, dentry, inode); +	if (err) +		drop_inode = 1; +	else { +		inode->i_mapping->a_ops = &btrfs_aops; +		inode->i_fop = &btrfs_file_operations; +		inode->i_op = &btrfs_file_inode_operations; +	} +	dir->i_sb->s_dirt = 1; +	btrfs_update_inode_block_group(trans, inode); +	btrfs_update_inode_block_group(trans, dir); +	if (drop_inode) +		goto out_unlock; + +	path = btrfs_alloc_path(); +	BUG_ON(!path); +	key.objectid = inode->i_ino; +	key.offset = 0; +	key.flags = 0; +	btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); +	datasize = btrfs_file_extent_calc_inline_size(name_len); +	err = btrfs_insert_empty_item(trans, root, path, &key, +				      datasize); +	BUG_ON(err); +	ei = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), +	       path->slots[0], struct btrfs_file_extent_item); +	btrfs_set_file_extent_generation(ei, trans->transid); +	btrfs_set_file_extent_type(ei, +				   BTRFS_FILE_EXTENT_INLINE); +	ptr = btrfs_file_extent_inline_start(ei); +	btrfs_memcpy(root, path->nodes[0]->b_data, +		     ptr, symname, name_len); +	mark_buffer_dirty(path->nodes[0]); +	btrfs_free_path(path); +	inode->i_op = &btrfs_symlink_inode_operations; +	inode->i_mapping->a_ops = &btrfs_symlink_aops; +	inode->i_size = name_len - 1; +	btrfs_update_inode(trans, root, inode); +	err = 0; + +out_unlock: +	btrfs_end_transaction(trans, root); +	mutex_unlock(&root->fs_info->fs_mutex); + +	if (drop_inode) { +		inode_dec_link_count(inode); +		iput(inode); +	} +	btrfs_btree_balance_dirty(root); +	return err; +} +  static struct file_system_type btrfs_fs_type = {  	.owner		= THIS_MODULE,  	.name		= "btrfs", @@ -2636,9 +2754,11 @@ static struct inode_operations btrfs_dir_inode_operations = {  	.lookup		= btrfs_lookup,  	.create		= btrfs_create,  	.unlink		= btrfs_unlink, +	.link		= btrfs_link,  	.mkdir		= btrfs_mkdir,  	.rmdir		= btrfs_rmdir,  	.rename		= btrfs_rename, +	.symlink	= btrfs_symlink,  };  static struct inode_operations btrfs_dir_ro_inode_operations = { @@ -2660,6 +2780,11 @@ static struct address_space_operations btrfs_aops = {  	.commit_write	= btrfs_commit_write,  }; +static struct address_space_operations btrfs_symlink_aops = { +	.readpage	= btrfs_readpage, +	.writepage	= btrfs_writepage, +}; +  static struct inode_operations btrfs_file_inode_operations = {  	.truncate	= btrfs_truncate,  	.getattr	= btrfs_getattr, @@ -2676,6 +2801,12 @@ static struct file_operations btrfs_file_operations = {  	.fsync		= btrfs_sync_file,  }; +static struct inode_operations btrfs_symlink_inode_operations = { +	.readlink	= generic_readlink, +	.follow_link	= page_follow_link_light, +	.put_link	= page_put_link, +}; +  static int __init init_btrfs_fs(void)  {  	int err;  | 
