summaryrefslogtreecommitdiff
path: root/fs/ubifs/recovery.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ubifs/recovery.c')
-rw-r--r--fs/ubifs/recovery.c111
1 files changed, 83 insertions, 28 deletions
diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c
index 5c1334e6bc81..8526b7ec4707 100644
--- a/fs/ubifs/recovery.c
+++ b/fs/ubifs/recovery.c
@@ -1463,15 +1463,81 @@ out:
}
/**
+ * inode_fix_size - fix inode size
+ * @c: UBIFS file-system description object
+ * @e: inode size information for recovery
+ */
+static int inode_fix_size(struct ubifs_info *c, struct size_entry *e)
+{
+ struct inode *inode;
+ struct ubifs_inode *ui;
+ int err;
+
+ if (c->ro_mount)
+ ubifs_assert(c, !e->inode);
+
+ if (e->inode) {
+ /* Remounting rw, pick up inode we stored earlier */
+ inode = e->inode;
+ } else {
+ inode = ubifs_iget(c->vfs_sb, e->inum);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+
+ if (inode->i_size >= e->d_size) {
+ /*
+ * The original inode in the index already has a size
+ * big enough, nothing to do
+ */
+ iput(inode);
+ return 0;
+ }
+
+ dbg_rcvry("ino %lu size %lld -> %lld",
+ (unsigned long)e->inum,
+ inode->i_size, e->d_size);
+
+ ui = ubifs_inode(inode);
+
+ inode->i_size = e->d_size;
+ ui->ui_size = e->d_size;
+ ui->synced_i_size = e->d_size;
+
+ e->inode = inode;
+ }
+
+ /*
+ * In readonly mode just keep the inode pinned in memory until we go
+ * readwrite. In readwrite mode write the inode to the journal with the
+ * fixed size.
+ */
+ if (c->ro_mount)
+ return 0;
+
+ err = ubifs_jnl_write_inode(c, inode);
+
+ iput(inode);
+
+ if (err)
+ return err;
+
+ rb_erase(&e->rb, &c->size_tree);
+ kfree(e);
+
+ return 0;
+}
+
+/**
* ubifs_recover_size - recover inode size.
* @c: UBIFS file-system description object
+ * @in_place: If true, do a in-place size fixup
*
* This function attempts to fix inode size discrepancies identified by the
* 'ubifs_recover_size_accum()' function.
*
* This functions returns %0 on success and a negative error code on failure.
*/
-int ubifs_recover_size(struct ubifs_info *c)
+int ubifs_recover_size(struct ubifs_info *c, bool in_place)
{
struct rb_node *this = rb_first(&c->size_tree);
@@ -1480,6 +1546,9 @@ int ubifs_recover_size(struct ubifs_info *c)
int err;
e = rb_entry(this, struct size_entry, rb);
+
+ this = rb_next(this);
+
if (!e->exists) {
union ubifs_key key;
@@ -1503,40 +1572,26 @@ int ubifs_recover_size(struct ubifs_info *c)
}
if (e->exists && e->i_size < e->d_size) {
- if (c->ro_mount) {
- /* Fix the inode size and pin it in memory */
- struct inode *inode;
- struct ubifs_inode *ui;
-
- ubifs_assert(c, !e->inode);
-
- inode = ubifs_iget(c->vfs_sb, e->inum);
- if (IS_ERR(inode))
- return PTR_ERR(inode);
-
- ui = ubifs_inode(inode);
- if (inode->i_size < e->d_size) {
- dbg_rcvry("ino %lu size %lld -> %lld",
- (unsigned long)e->inum,
- inode->i_size, e->d_size);
- inode->i_size = e->d_size;
- ui->ui_size = e->d_size;
- ui->synced_i_size = e->d_size;
- e->inode = inode;
- this = rb_next(this);
- continue;
- }
- iput(inode);
- } else {
- /* Fix the size in place */
+ ubifs_assert(c, !(c->ro_mount && in_place));
+
+ /*
+ * We found data that is outside the found inode size,
+ * fixup the inode size
+ */
+
+ if (in_place) {
err = fix_size_in_place(c, e);
if (err)
return err;
iput(e->inode);
+ } else {
+ err = inode_fix_size(c, e);
+ if (err)
+ return err;
+ continue;
}
}
- this = rb_next(this);
rb_erase(&e->rb, &c->size_tree);
kfree(e);
}