hfs: prevent btree data loss on ENOSPC
Inserting a new record in a btree may require splitting several of its nodes. If we hit ENOSPC halfway through, the new nodes will be left orphaned and their records will be lost. This could mean lost inodes or extents. Henceforth, check the available disk space before making any changes. This still leaves the potential problem of corruption on ENOMEM. There is no need to reserve space before deleting a catalog record, as we do for hfsplus. This difference is because hfs index nodes have fixed length keys. Link: http://lkml.kernel.org/r/ab5fc8a7d5ffccfd5f27b1cf2cb4ceb6c110da74.1536269131.git.ernesto.mnd.fernandez@gmail.com Signed-off-by: Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com> Cc: Christoph Hellwig <hch@lst.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:

committed by
Linus Torvalds

orang tua
d92915c35b
melakukan
54640c7502
@@ -220,6 +220,30 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Make sure @tree has enough space for the @rsvd_nodes */
|
||||
int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
|
||||
{
|
||||
struct inode *inode = tree->inode;
|
||||
u32 count;
|
||||
int res;
|
||||
|
||||
while (tree->free_nodes < rsvd_nodes) {
|
||||
res = hfs_extend_file(inode);
|
||||
if (res)
|
||||
return res;
|
||||
HFS_I(inode)->phys_size = inode->i_size =
|
||||
(loff_t)HFS_I(inode)->alloc_blocks *
|
||||
HFS_SB(tree->sb)->alloc_blksz;
|
||||
HFS_I(inode)->fs_blocks = inode->i_size >>
|
||||
tree->sb->s_blocksize_bits;
|
||||
inode_set_bytes(inode, inode->i_size);
|
||||
count = inode->i_size >> tree->node_size_shift;
|
||||
tree->free_nodes += count - tree->node_count;
|
||||
tree->node_count = count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
|
||||
{
|
||||
struct hfs_bnode *node, *next_node;
|
||||
@@ -229,26 +253,11 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
|
||||
u16 off16;
|
||||
u16 len;
|
||||
u8 *data, byte, m;
|
||||
int i;
|
||||
int i, res;
|
||||
|
||||
while (!tree->free_nodes) {
|
||||
struct inode *inode = tree->inode;
|
||||
u32 count;
|
||||
int res;
|
||||
|
||||
res = hfs_extend_file(inode);
|
||||
if (res)
|
||||
return ERR_PTR(res);
|
||||
HFS_I(inode)->phys_size = inode->i_size =
|
||||
(loff_t)HFS_I(inode)->alloc_blocks *
|
||||
HFS_SB(tree->sb)->alloc_blksz;
|
||||
HFS_I(inode)->fs_blocks = inode->i_size >>
|
||||
tree->sb->s_blocksize_bits;
|
||||
inode_set_bytes(inode, inode->i_size);
|
||||
count = inode->i_size >> tree->node_size_shift;
|
||||
tree->free_nodes = count - tree->node_count;
|
||||
tree->node_count = count;
|
||||
}
|
||||
res = hfs_bmap_reserve(tree, 1);
|
||||
if (res)
|
||||
return ERR_PTR(res);
|
||||
|
||||
nidx = 0;
|
||||
node = hfs_bnode_find(tree, nidx);
|
||||
|
Reference in New Issue
Block a user