aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErnesto A. Fernández <ernesto.mnd.fernandez@gmail.com>2018-10-30 18:06:17 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-10-31 11:54:13 -0400
commit54640c7502e5ed41fbf4eedd499e85f9acc9698f (patch)
tree0745ae35891531719808bf29bdcecf0bf8cf1119
parentd92915c35bfaf763d78bf1d5ac7f183420e3bd99 (diff)
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>
-rw-r--r--fs/hfs/btree.c41
-rw-r--r--fs/hfs/btree.h1
-rw-r--r--fs/hfs/catalog.c16
-rw-r--r--fs/hfs/extent.c4
4 files changed, 46 insertions, 16 deletions
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
index 374b5688e29e..98b96ffb95ed 100644
--- a/fs/hfs/btree.c
+++ b/fs/hfs/btree.c
@@ -220,25 +220,17 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
220 return node; 220 return node;
221} 221}
222 222
223struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) 223/* Make sure @tree has enough space for the @rsvd_nodes */
224int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
224{ 225{
225 struct hfs_bnode *node, *next_node; 226 struct inode *inode = tree->inode;
226 struct page **pagep; 227 u32 count;
227 u32 nidx, idx; 228 int res;
228 unsigned off;
229 u16 off16;
230 u16 len;
231 u8 *data, byte, m;
232 int i;
233
234 while (!tree->free_nodes) {
235 struct inode *inode = tree->inode;
236 u32 count;
237 int res;
238 229
230 while (tree->free_nodes < rsvd_nodes) {
239 res = hfs_extend_file(inode); 231 res = hfs_extend_file(inode);
240 if (res) 232 if (res)
241 return ERR_PTR(res); 233 return res;
242 HFS_I(inode)->phys_size = inode->i_size = 234 HFS_I(inode)->phys_size = inode->i_size =
243 (loff_t)HFS_I(inode)->alloc_blocks * 235 (loff_t)HFS_I(inode)->alloc_blocks *
244 HFS_SB(tree->sb)->alloc_blksz; 236 HFS_SB(tree->sb)->alloc_blksz;
@@ -246,9 +238,26 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
246 tree->sb->s_blocksize_bits; 238 tree->sb->s_blocksize_bits;
247 inode_set_bytes(inode, inode->i_size); 239 inode_set_bytes(inode, inode->i_size);
248 count = inode->i_size >> tree->node_size_shift; 240 count = inode->i_size >> tree->node_size_shift;
249 tree->free_nodes = count - tree->node_count; 241 tree->free_nodes += count - tree->node_count;
250 tree->node_count = count; 242 tree->node_count = count;
251 } 243 }
244 return 0;
245}
246
247struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
248{
249 struct hfs_bnode *node, *next_node;
250 struct page **pagep;
251 u32 nidx, idx;
252 unsigned off;
253 u16 off16;
254 u16 len;
255 u8 *data, byte, m;
256 int i, res;
257
258 res = hfs_bmap_reserve(tree, 1);
259 if (res)
260 return ERR_PTR(res);
252 261
253 nidx = 0; 262 nidx = 0;
254 node = hfs_bnode_find(tree, nidx); 263 node = hfs_bnode_find(tree, nidx);
diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h
index c8b252dbb26c..dcc2aab1b2c4 100644
--- a/fs/hfs/btree.h
+++ b/fs/hfs/btree.h
@@ -82,6 +82,7 @@ struct hfs_find_data {
82extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp); 82extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp);
83extern void hfs_btree_close(struct hfs_btree *); 83extern void hfs_btree_close(struct hfs_btree *);
84extern void hfs_btree_write(struct hfs_btree *); 84extern void hfs_btree_write(struct hfs_btree *);
85extern int hfs_bmap_reserve(struct hfs_btree *, int);
85extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *); 86extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *);
86extern void hfs_bmap_free(struct hfs_bnode *node); 87extern void hfs_bmap_free(struct hfs_bnode *node);
87 88
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c
index 8a66405b0f8b..d365bf0b8c77 100644
--- a/fs/hfs/catalog.c
+++ b/fs/hfs/catalog.c
@@ -97,6 +97,14 @@ int hfs_cat_create(u32 cnid, struct inode *dir, const struct qstr *str, struct i
97 if (err) 97 if (err)
98 return err; 98 return err;
99 99
100 /*
101 * Fail early and avoid ENOSPC during the btree operations. We may
102 * have to split the root node at most once.
103 */
104 err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth);
105 if (err)
106 goto err2;
107
100 hfs_cat_build_key(sb, fd.search_key, cnid, NULL); 108 hfs_cat_build_key(sb, fd.search_key, cnid, NULL);
101 entry_size = hfs_cat_build_thread(sb, &entry, S_ISDIR(inode->i_mode) ? 109 entry_size = hfs_cat_build_thread(sb, &entry, S_ISDIR(inode->i_mode) ?
102 HFS_CDR_THD : HFS_CDR_FTH, 110 HFS_CDR_THD : HFS_CDR_FTH,
@@ -295,6 +303,14 @@ int hfs_cat_move(u32 cnid, struct inode *src_dir, const struct qstr *src_name,
295 return err; 303 return err;
296 dst_fd = src_fd; 304 dst_fd = src_fd;
297 305
306 /*
307 * Fail early and avoid ENOSPC during the btree operations. We may
308 * have to split the root node at most once.
309 */
310 err = hfs_bmap_reserve(src_fd.tree, 2 * src_fd.tree->depth);
311 if (err)
312 goto out;
313
298 /* find the old dir entry and read the data */ 314 /* find the old dir entry and read the data */
299 hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); 315 hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
300 err = hfs_brec_find(&src_fd); 316 err = hfs_brec_find(&src_fd);
diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c
index 5d0182654580..0c638c612152 100644
--- a/fs/hfs/extent.c
+++ b/fs/hfs/extent.c
@@ -117,6 +117,10 @@ static int __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd)
117 if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) { 117 if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) {
118 if (res != -ENOENT) 118 if (res != -ENOENT)
119 return res; 119 return res;
120 /* Fail early and avoid ENOSPC during the btree operation */
121 res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1);
122 if (res)
123 return res;
120 hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec)); 124 hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec));
121 HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); 125 HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
122 } else { 126 } else {