Skip to content

Commit f41e355

Browse files
konisakpm00
authored andcommitted
nilfs2: fix incorrect inode allocation from reserved inodes
If the bitmap block that manages the inode allocation status is corrupted, nilfs_ifile_create_inode() may allocate a new inode from the reserved inode area where it should not be allocated. Previous fix commit d325dc6 ("nilfs2: fix use-after-free bug of struct nilfs_root"), fixed the problem that reserved inodes with inode numbers less than NILFS_USER_INO (=11) were incorrectly reallocated due to bitmap corruption, but since the start number of non-reserved inodes is read from the super block and may change, in which case inode allocation may occur from the extended reserved inode area. If that happens, access to that inode will cause an IO error, causing the file system to degrade to an error state. Fix this potential issue by adding a wraparound option to the common metadata object allocation routine and by modifying nilfs_ifile_create_inode() to disable the option so that it only allocates inodes with inode numbers greater than or equal to the inode number read in "nilfs->ns_first_ino", regardless of the bitmap status of reserved inodes. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Ryusuke Konishi <[email protected]> Cc: Hillf Danton <[email protected]> Cc: Jan Kara <[email protected]> Cc: Matthew Wilcox (Oracle) <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 49ae997 commit f41e355

File tree

4 files changed

+20
-12
lines changed

4 files changed

+20
-12
lines changed

fs/nilfs2/alloc.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,11 +377,12 @@ void *nilfs_palloc_block_get_entry(const struct inode *inode, __u64 nr,
377377
* @target: offset number of an entry in the group (start point)
378378
* @bsize: size in bits
379379
* @lock: spin lock protecting @bitmap
380+
* @wrap: whether to wrap around
380381
*/
381382
static int nilfs_palloc_find_available_slot(unsigned char *bitmap,
382383
unsigned long target,
383384
unsigned int bsize,
384-
spinlock_t *lock)
385+
spinlock_t *lock, bool wrap)
385386
{
386387
int pos, end = bsize;
387388

@@ -397,6 +398,8 @@ static int nilfs_palloc_find_available_slot(unsigned char *bitmap,
397398

398399
end = target;
399400
}
401+
if (!wrap)
402+
return -ENOSPC;
400403

401404
/* wrap around */
402405
for (pos = 0; pos < end; pos++) {
@@ -495,9 +498,10 @@ int nilfs_palloc_count_max_entries(struct inode *inode, u64 nused, u64 *nmaxp)
495498
* nilfs_palloc_prepare_alloc_entry - prepare to allocate a persistent object
496499
* @inode: inode of metadata file using this allocator
497500
* @req: nilfs_palloc_req structure exchanged for the allocation
501+
* @wrap: whether to wrap around
498502
*/
499503
int nilfs_palloc_prepare_alloc_entry(struct inode *inode,
500-
struct nilfs_palloc_req *req)
504+
struct nilfs_palloc_req *req, bool wrap)
501505
{
502506
struct buffer_head *desc_bh, *bitmap_bh;
503507
struct nilfs_palloc_group_desc *desc;
@@ -516,7 +520,7 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode,
516520
entries_per_group = nilfs_palloc_entries_per_group(inode);
517521

518522
for (i = 0; i < ngroups; i += n) {
519-
if (group >= ngroups) {
523+
if (group >= ngroups && wrap) {
520524
/* wrap around */
521525
group = 0;
522526
maxgroup = nilfs_palloc_group(inode, req->pr_entry_nr,
@@ -550,7 +554,14 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode,
550554
bitmap_kaddr = kmap_local_page(bitmap_bh->b_page);
551555
bitmap = bitmap_kaddr + bh_offset(bitmap_bh);
552556
pos = nilfs_palloc_find_available_slot(
553-
bitmap, group_offset, entries_per_group, lock);
557+
bitmap, group_offset, entries_per_group, lock,
558+
wrap);
559+
/*
560+
* Since the search for a free slot in the second and
561+
* subsequent bitmap blocks always starts from the
562+
* beginning, the wrap flag only has an effect on the
563+
* first search.
564+
*/
554565
kunmap_local(bitmap_kaddr);
555566
if (pos >= 0)
556567
goto found;

fs/nilfs2/alloc.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ struct nilfs_palloc_req {
5050
struct buffer_head *pr_entry_bh;
5151
};
5252

53-
int nilfs_palloc_prepare_alloc_entry(struct inode *,
54-
struct nilfs_palloc_req *);
53+
int nilfs_palloc_prepare_alloc_entry(struct inode *inode,
54+
struct nilfs_palloc_req *req, bool wrap);
5555
void nilfs_palloc_commit_alloc_entry(struct inode *,
5656
struct nilfs_palloc_req *);
5757
void nilfs_palloc_abort_alloc_entry(struct inode *, struct nilfs_palloc_req *);

fs/nilfs2/dat.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ int nilfs_dat_prepare_alloc(struct inode *dat, struct nilfs_palloc_req *req)
7575
{
7676
int ret;
7777

78-
ret = nilfs_palloc_prepare_alloc_entry(dat, req);
78+
ret = nilfs_palloc_prepare_alloc_entry(dat, req, true);
7979
if (ret < 0)
8080
return ret;
8181

fs/nilfs2/ifile.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,10 @@ int nilfs_ifile_create_inode(struct inode *ifile, ino_t *out_ino,
5656
struct nilfs_palloc_req req;
5757
int ret;
5858

59-
req.pr_entry_nr = 0; /*
60-
* 0 says find free inode from beginning
61-
* of a group. dull code!!
62-
*/
59+
req.pr_entry_nr = NILFS_FIRST_INO(ifile->i_sb);
6360
req.pr_entry_bh = NULL;
6461

65-
ret = nilfs_palloc_prepare_alloc_entry(ifile, &req);
62+
ret = nilfs_palloc_prepare_alloc_entry(ifile, &req, false);
6663
if (!ret) {
6764
ret = nilfs_palloc_get_entry_block(ifile, req.pr_entry_nr, 1,
6865
&req.pr_entry_bh);

0 commit comments

Comments
 (0)