diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r-- | fs/btrfs/tree-log.c | 114 |
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 | */ | ||
1231 | static 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 | |||
1244 | again: | ||
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); |
1350 | out: | 1456 | out: |
@@ -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 | /* |