diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
| -rw-r--r-- | fs/btrfs/tree-log.c | 52 | 
1 files changed, 48 insertions, 4 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 0d9613c3f5e5..79f057c0619a 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -93,7 +93,8 @@   */  #define LOG_WALK_PIN_ONLY 0  #define LOG_WALK_REPLAY_INODES 1 -#define LOG_WALK_REPLAY_ALL 2 +#define LOG_WALK_REPLAY_DIR_INDEX 2 +#define LOG_WALK_REPLAY_ALL 3  static int btrfs_log_inode(struct btrfs_trans_handle *trans,  			     struct btrfs_root *root, struct inode *inode, @@ -393,6 +394,7 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,  		if (inode_item) {  			struct btrfs_inode_item *item;  			u64 nbytes; +			u32 mode;  			item = btrfs_item_ptr(path->nodes[0], path->slots[0],  					      struct btrfs_inode_item); @@ -400,9 +402,19 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,  			item = btrfs_item_ptr(eb, slot,  					      struct btrfs_inode_item);  			btrfs_set_inode_nbytes(eb, item, nbytes); + +			/* +			 * If this is a directory we need to reset the i_size to +			 * 0 so that we can set it up properly when replaying +			 * the rest of the items in this log. +			 */ +			mode = btrfs_inode_mode(eb, item); +			if (S_ISDIR(mode)) +				btrfs_set_inode_size(eb, item, 0);  		}  	} else if (inode_item) {  		struct btrfs_inode_item *item; +		u32 mode;  		/*  		 * New inode, set nbytes to 0 so that the nbytes comes out @@ -410,6 +422,15 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,  		 */  		item = btrfs_item_ptr(eb, slot, struct btrfs_inode_item);  		btrfs_set_inode_nbytes(eb, item, 0); + +		/* +		 * If this is a directory we need to reset the i_size to 0 so +		 * that we can set it up properly when replaying the rest of +		 * the items in this log. +		 */ +		mode = btrfs_inode_mode(eb, item); +		if (S_ISDIR(mode)) +			btrfs_set_inode_size(eb, item, 0);  	}  insert:  	btrfs_release_path(path); @@ -1496,6 +1517,7 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans,  		iput(inode);  		return -EIO;  	} +  	ret = btrfs_add_link(trans, dir, inode, name, name_len, 1, index);  	/* FIXME, put inode into FIXUP list */ @@ -1534,6 +1556,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,  	u8 log_type;  	int exists;  	int ret = 0; +	bool update_size = (key->type == BTRFS_DIR_INDEX_KEY);  	dir = read_one_inode(root, key->objectid);  	if (!dir) @@ -1604,6 +1627,10 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,  		goto insert;  out:  	btrfs_release_path(path); +	if (!ret && update_size) { +		btrfs_i_size_write(dir, dir->i_size + name_len * 2); +		ret = btrfs_update_inode(trans, root, dir); +	}  	kfree(name);  	iput(dir);  	return ret; @@ -1614,6 +1641,7 @@ insert:  			      name, name_len, log_type, &log_key);  	if (ret && ret != -ENOENT)  		goto out; +	update_size = false;  	ret = 0;  	goto out;  } @@ -2027,6 +2055,15 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,  			if (ret)  				break;  		} + +		if (key.type == BTRFS_DIR_INDEX_KEY && +		    wc->stage == LOG_WALK_REPLAY_DIR_INDEX) { +			ret = replay_one_dir_item(wc->trans, root, path, +						  eb, i, &key); +			if (ret) +				break; +		} +  		if (wc->stage < LOG_WALK_REPLAY_ALL)  			continue; @@ -2048,8 +2085,7 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,  						eb, i, &key);  			if (ret)  				break; -		} else if (key.type == BTRFS_DIR_ITEM_KEY || -			   key.type == BTRFS_DIR_INDEX_KEY) { +		} else if (key.type == BTRFS_DIR_ITEM_KEY) {  			ret = replay_one_dir_item(wc->trans, root, path,  						  eb, i, &key);  			if (ret) @@ -3805,6 +3841,7 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,  	int ret = 0;  	struct btrfs_root *root;  	struct dentry *old_parent = NULL; +	struct inode *orig_inode = inode;  	/*  	 * for regular files, if its inode is already on disk, we don't @@ -3824,7 +3861,14 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,  	}  	while (1) { -		BTRFS_I(inode)->logged_trans = trans->transid; +		/* +		 * If we are logging a directory then we start with our inode, +		 * not our parents inode, so we need to skipp setting the +		 * logged_trans so that further down in the log code we don't +		 * think this inode has already been logged. +		 */ +		if (inode != orig_inode) +			BTRFS_I(inode)->logged_trans = trans->transid;  		smp_mb();  		if (BTRFS_I(inode)->last_unlink_trans > last_committed) {  | 
