aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/tree-log.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r--fs/btrfs/tree-log.c114
1 files changed, 110 insertions, 4 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 4fd19b4d6675..434457794c27 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -967,7 +967,9 @@ static noinline int backref_in_log(struct btrfs_root *log,
967 ptr = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]); 967 ptr = btrfs_item_ptr_offset(path->nodes[0], path->slots[0]);
968 968
969 if (key->type == BTRFS_INODE_EXTREF_KEY) { 969 if (key->type == BTRFS_INODE_EXTREF_KEY) {
970 if (btrfs_find_name_in_ext_backref(path, ref_objectid, 970 if (btrfs_find_name_in_ext_backref(path->nodes[0],
971 path->slots[0],
972 ref_objectid,
971 name, namelen, NULL)) 973 name, namelen, NULL))
972 match = 1; 974 match = 1;
973 975
@@ -1191,7 +1193,8 @@ static int extref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
1191 read_extent_buffer(eb, *name, (unsigned long)&extref->name, 1193 read_extent_buffer(eb, *name, (unsigned long)&extref->name,
1192 *namelen); 1194 *namelen);
1193 1195
1194 *index = btrfs_inode_extref_index(eb, extref); 1196 if (index)
1197 *index = btrfs_inode_extref_index(eb, extref);
1195 if (parent_objectid) 1198 if (parent_objectid)
1196 *parent_objectid = btrfs_inode_extref_parent(eb, extref); 1199 *parent_objectid = btrfs_inode_extref_parent(eb, extref);
1197 1200
@@ -1212,12 +1215,102 @@ static int ref_get_fields(struct extent_buffer *eb, unsigned long ref_ptr,
1212 1215
1213 read_extent_buffer(eb, *name, (unsigned long)(ref + 1), *namelen); 1216 read_extent_buffer(eb, *name, (unsigned long)(ref + 1), *namelen);
1214 1217
1215 *index = btrfs_inode_ref_index(eb, ref); 1218 if (index)
1219 *index = btrfs_inode_ref_index(eb, ref);
1216 1220
1217 return 0; 1221 return 0;
1218} 1222}
1219 1223
1220/* 1224/*
1225 * Take an inode reference item from the log tree and iterate all names from the
1226 * inode reference item in the subvolume tree with the same key (if it exists).
1227 * For any name that is not in the inode reference item from the log tree, do a
1228 * proper unlink of that name (that is, remove its entry from the inode
1229 * reference item and both dir index keys).
1230 */
1231static int unlink_old_inode_refs(struct btrfs_trans_handle *trans,
1232 struct btrfs_root *root,
1233 struct btrfs_path *path,
1234 struct btrfs_inode *inode,
1235 struct extent_buffer *log_eb,
1236 int log_slot,
1237 struct btrfs_key *key)
1238{
1239 int ret;
1240 unsigned long ref_ptr;
1241 unsigned long ref_end;
1242 struct extent_buffer *eb;
1243
1244again:
1245 btrfs_release_path(path);
1246 ret = btrfs_search_slot(NULL, root, key, path, 0, 0);
1247 if (ret > 0) {
1248 ret = 0;
1249 goto out;
1250 }
1251 if (ret < 0)
1252 goto out;
1253
1254 eb = path->nodes[0];
1255 ref_ptr = btrfs_item_ptr_offset(eb, path->slots[0]);
1256 ref_end = ref_ptr + btrfs_item_size_nr(eb, path->slots[0]);
1257 while (ref_ptr < ref_end) {
1258 char *name = NULL;
1259 int namelen;
1260 u64 parent_id;
1261
1262 if (key->type == BTRFS_INODE_EXTREF_KEY) {
1263 ret = extref_get_fields(eb, ref_ptr, &namelen, &name,
1264 NULL, &parent_id);
1265 } else {
1266 parent_id = key->offset;
1267 ret = ref_get_fields(eb, ref_ptr, &namelen, &name,
1268 NULL);
1269 }
1270 if (ret)
1271 goto out;
1272
1273 if (key->type == BTRFS_INODE_EXTREF_KEY)
1274 ret = btrfs_find_name_in_ext_backref(log_eb, log_slot,
1275 parent_id, name,
1276 namelen, NULL);
1277 else
1278 ret = btrfs_find_name_in_backref(log_eb, log_slot, name,
1279 namelen, NULL);
1280
1281 if (!ret) {
1282 struct inode *dir;
1283
1284 btrfs_release_path(path);
1285 dir = read_one_inode(root, parent_id);
1286 if (!dir) {
1287 ret = -ENOENT;
1288 kfree(name);
1289 goto out;
1290 }
1291 ret = btrfs_unlink_inode(trans, root, BTRFS_I(dir),
1292 inode, name, namelen);
1293 kfree(name);
1294 iput(dir);
1295 if (ret)
1296 goto out;
1297 goto again;
1298 }
1299
1300 kfree(name);
1301 ref_ptr += namelen;
1302 if (key->type == BTRFS_INODE_EXTREF_KEY)
1303 ref_ptr += sizeof(struct btrfs_inode_extref);
1304 else
1305 ref_ptr += sizeof(struct btrfs_inode_ref);
1306 }
1307 ret = 0;
1308 out:
1309 btrfs_release_path(path);
1310 return ret;
1311}
1312
1313/*
1221 * replay one inode back reference item found in the log tree. 1314 * replay one inode back reference item found in the log tree.
1222 * eb, slot and key refer to the buffer and key found in the log tree. 1315 * eb, slot and key refer to the buffer and key found in the log tree.
1223 * root is the destination we are replaying into, and path is for temp 1316 * root is the destination we are replaying into, and path is for temp
@@ -1345,6 +1438,19 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
1345 } 1438 }
1346 } 1439 }
1347 1440
1441 /*
1442 * Before we overwrite the inode reference item in the subvolume tree
1443 * with the item from the log tree, we must unlink all names from the
1444 * parent directory that are in the subvolume's tree inode reference
1445 * item, otherwise we end up with an inconsistent subvolume tree where
1446 * dir index entries exist for a name but there is no inode reference
1447 * item with the same name.
1448 */
1449 ret = unlink_old_inode_refs(trans, root, path, BTRFS_I(inode), eb, slot,
1450 key);
1451 if (ret)
1452 goto out;
1453
1348 /* finally write the back reference in the inode */ 1454 /* finally write the back reference in the inode */
1349 ret = overwrite_item(trans, root, path, eb, slot, key); 1455 ret = overwrite_item(trans, root, path, eb, slot, key);
1350out: 1456out:
@@ -5853,7 +5959,7 @@ int btrfs_log_new_name(struct btrfs_trans_handle *trans,
5853 * this will force the logging code to walk the dentry chain 5959 * this will force the logging code to walk the dentry chain
5854 * up for the file 5960 * up for the file
5855 */ 5961 */
5856 if (S_ISREG(inode->vfs_inode.i_mode)) 5962 if (!S_ISDIR(inode->vfs_inode.i_mode))
5857 inode->last_unlink_trans = trans->transid; 5963 inode->last_unlink_trans = trans->transid;
5858 5964
5859 /* 5965 /*