Skip to content

Commit 1e76592

Browse files
saschahauerrichardweinberger
authored andcommitted
ubifs: Do not update inode size in-place in authenticated mode
In authenticated mode we cannot fixup the inode sizes in-place during recovery as this would invalidate the hashes and HMACs we stored for this inode. Instead, we just write the updated inodes to the journal. We can only do this after ubifs_rcvry_gc_commit() is done though, so for authenticated mode call ubifs_recover_size() after ubifs_rcvry_gc_commit() and not vice versa as normally done. Calling ubifs_recover_size() after ubifs_rcvry_gc_commit() has the drawback that after a commit the size fixup information is gone, so when a powercut happens while recovering from another powercut we may lose some data written right before the first powercut. This is why we only do this in authenticated mode and leave the behaviour for unauthenticated mode untouched. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Richard Weinberger <richard@nod.at>
1 parent 104115a commit 1e76592

File tree

3 files changed

+113
-38
lines changed

3 files changed

+113
-38
lines changed

fs/ubifs/recovery.c

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,16 +1462,82 @@ static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
14621462
return err;
14631463
}
14641464

1465+
/**
1466+
* inode_fix_size - fix inode size
1467+
* @c: UBIFS file-system description object
1468+
* @e: inode size information for recovery
1469+
*/
1470+
static int inode_fix_size(struct ubifs_info *c, struct size_entry *e)
1471+
{
1472+
struct inode *inode;
1473+
struct ubifs_inode *ui;
1474+
int err;
1475+
1476+
if (c->ro_mount)
1477+
ubifs_assert(c, !e->inode);
1478+
1479+
if (e->inode) {
1480+
/* Remounting rw, pick up inode we stored earlier */
1481+
inode = e->inode;
1482+
} else {
1483+
inode = ubifs_iget(c->vfs_sb, e->inum);
1484+
if (IS_ERR(inode))
1485+
return PTR_ERR(inode);
1486+
1487+
if (inode->i_size >= e->d_size) {
1488+
/*
1489+
* The original inode in the index already has a size
1490+
* big enough, nothing to do
1491+
*/
1492+
iput(inode);
1493+
return 0;
1494+
}
1495+
1496+
dbg_rcvry("ino %lu size %lld -> %lld",
1497+
(unsigned long)e->inum,
1498+
inode->i_size, e->d_size);
1499+
1500+
ui = ubifs_inode(inode);
1501+
1502+
inode->i_size = e->d_size;
1503+
ui->ui_size = e->d_size;
1504+
ui->synced_i_size = e->d_size;
1505+
1506+
e->inode = inode;
1507+
}
1508+
1509+
/*
1510+
* In readonly mode just keep the inode pinned in memory until we go
1511+
* readwrite. In readwrite mode write the inode to the journal with the
1512+
* fixed size.
1513+
*/
1514+
if (c->ro_mount)
1515+
return 0;
1516+
1517+
err = ubifs_jnl_write_inode(c, inode);
1518+
1519+
iput(inode);
1520+
1521+
if (err)
1522+
return err;
1523+
1524+
rb_erase(&e->rb, &c->size_tree);
1525+
kfree(e);
1526+
1527+
return 0;
1528+
}
1529+
14651530
/**
14661531
* ubifs_recover_size - recover inode size.
14671532
* @c: UBIFS file-system description object
1533+
* @in_place: If true, do a in-place size fixup
14681534
*
14691535
* This function attempts to fix inode size discrepancies identified by the
14701536
* 'ubifs_recover_size_accum()' function.
14711537
*
14721538
* This functions returns %0 on success and a negative error code on failure.
14731539
*/
1474-
int ubifs_recover_size(struct ubifs_info *c)
1540+
int ubifs_recover_size(struct ubifs_info *c, bool in_place)
14751541
{
14761542
struct rb_node *this = rb_first(&c->size_tree);
14771543

@@ -1480,6 +1546,9 @@ int ubifs_recover_size(struct ubifs_info *c)
14801546
int err;
14811547

14821548
e = rb_entry(this, struct size_entry, rb);
1549+
1550+
this = rb_next(this);
1551+
14831552
if (!e->exists) {
14841553
union ubifs_key key;
14851554

@@ -1503,40 +1572,26 @@ int ubifs_recover_size(struct ubifs_info *c)
15031572
}
15041573

15051574
if (e->exists && e->i_size < e->d_size) {
1506-
if (c->ro_mount) {
1507-
/* Fix the inode size and pin it in memory */
1508-
struct inode *inode;
1509-
struct ubifs_inode *ui;
1510-
1511-
ubifs_assert(c, !e->inode);
1512-
1513-
inode = ubifs_iget(c->vfs_sb, e->inum);
1514-
if (IS_ERR(inode))
1515-
return PTR_ERR(inode);
1516-
1517-
ui = ubifs_inode(inode);
1518-
if (inode->i_size < e->d_size) {
1519-
dbg_rcvry("ino %lu size %lld -> %lld",
1520-
(unsigned long)e->inum,
1521-
inode->i_size, e->d_size);
1522-
inode->i_size = e->d_size;
1523-
ui->ui_size = e->d_size;
1524-
ui->synced_i_size = e->d_size;
1525-
e->inode = inode;
1526-
this = rb_next(this);
1527-
continue;
1528-
}
1529-
iput(inode);
1530-
} else {
1531-
/* Fix the size in place */
1575+
ubifs_assert(c, !(c->ro_mount && in_place));
1576+
1577+
/*
1578+
* We found data that is outside the found inode size,
1579+
* fixup the inode size
1580+
*/
1581+
1582+
if (in_place) {
15321583
err = fix_size_in_place(c, e);
15331584
if (err)
15341585
return err;
15351586
iput(e->inode);
1587+
} else {
1588+
err = inode_fix_size(c, e);
1589+
if (err)
1590+
return err;
1591+
continue;
15361592
}
15371593
}
15381594

1539-
this = rb_next(this);
15401595
rb_erase(&e->rb, &c->size_tree);
15411596
kfree(e);
15421597
}

fs/ubifs/super.c

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,12 +1378,21 @@ static int mount_ubifs(struct ubifs_info *c)
13781378
}
13791379

13801380
if (c->need_recovery) {
1381-
err = ubifs_recover_size(c);
1382-
if (err)
1383-
goto out_orphans;
1381+
if (!ubifs_authenticated(c)) {
1382+
err = ubifs_recover_size(c, true);
1383+
if (err)
1384+
goto out_orphans;
1385+
}
1386+
13841387
err = ubifs_rcvry_gc_commit(c);
13851388
if (err)
13861389
goto out_orphans;
1390+
1391+
if (ubifs_authenticated(c)) {
1392+
err = ubifs_recover_size(c, false);
1393+
if (err)
1394+
goto out_orphans;
1395+
}
13871396
} else {
13881397
err = take_gc_lnum(c);
13891398
if (err)
@@ -1402,7 +1411,7 @@ static int mount_ubifs(struct ubifs_info *c)
14021411
if (err)
14031412
goto out_orphans;
14041413
} else if (c->need_recovery) {
1405-
err = ubifs_recover_size(c);
1414+
err = ubifs_recover_size(c, false);
14061415
if (err)
14071416
goto out_orphans;
14081417
} else {
@@ -1629,9 +1638,11 @@ static int ubifs_remount_rw(struct ubifs_info *c)
16291638
err = ubifs_write_rcvrd_mst_node(c);
16301639
if (err)
16311640
goto out;
1632-
err = ubifs_recover_size(c);
1633-
if (err)
1634-
goto out;
1641+
if (!ubifs_authenticated(c)) {
1642+
err = ubifs_recover_size(c, true);
1643+
if (err)
1644+
goto out;
1645+
}
16351646
err = ubifs_clean_lebs(c, c->sbuf);
16361647
if (err)
16371648
goto out;
@@ -1697,10 +1708,19 @@ static int ubifs_remount_rw(struct ubifs_info *c)
16971708
goto out;
16981709
}
16991710

1700-
if (c->need_recovery)
1711+
if (c->need_recovery) {
17011712
err = ubifs_rcvry_gc_commit(c);
1702-
else
1713+
if (err)
1714+
goto out;
1715+
1716+
if (ubifs_authenticated(c)) {
1717+
err = ubifs_recover_size(c, false);
1718+
if (err)
1719+
goto out;
1720+
}
1721+
} else {
17031722
err = ubifs_leb_unmap(c, c->gc_lnum);
1723+
}
17041724
if (err)
17051725
goto out;
17061726

fs/ubifs/ubifs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2050,7 +2050,7 @@ int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf);
20502050
int ubifs_rcvry_gc_commit(struct ubifs_info *c);
20512051
int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key,
20522052
int deletion, loff_t new_size);
2053-
int ubifs_recover_size(struct ubifs_info *c);
2053+
int ubifs_recover_size(struct ubifs_info *c, bool in_place);
20542054
void ubifs_destroy_size_tree(struct ubifs_info *c);
20552055

20562056
/* ioctl.c */

0 commit comments

Comments
 (0)