diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r-- | fs/btrfs/tree-log.c | 112 |
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 | */ | ||
1230 | static 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 | |||
1243 | again: | ||
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); |
1349 | out: | 1455 | out: |