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