mirror of https://github.com/OpenIPC/firmware.git
409 lines
11 KiB
Diff
409 lines
11 KiB
Diff
diff -drupN a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
|
|
--- a/fs/f2fs/recovery.c 2018-08-06 17:23:04.000000000 +0300
|
|
+++ b/fs/f2fs/recovery.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -69,20 +69,34 @@ static struct fsync_inode_entry *get_fsy
|
|
}
|
|
|
|
static struct fsync_inode_entry *add_fsync_inode(struct f2fs_sb_info *sbi,
|
|
- struct list_head *head, nid_t ino)
|
|
+ struct list_head *head, nid_t ino, bool quota_inode)
|
|
{
|
|
struct inode *inode;
|
|
struct fsync_inode_entry *entry;
|
|
+ int err;
|
|
|
|
inode = f2fs_iget_retry(sbi->sb, ino);
|
|
if (IS_ERR(inode))
|
|
return ERR_CAST(inode);
|
|
|
|
+ err = dquot_initialize(inode);
|
|
+ if (err)
|
|
+ goto err_out;
|
|
+
|
|
+ if (quota_inode) {
|
|
+ err = dquot_alloc_inode(inode);
|
|
+ if (err)
|
|
+ goto err_out;
|
|
+ }
|
|
+
|
|
entry = f2fs_kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO);
|
|
entry->inode = inode;
|
|
list_add_tail(&entry->list, head);
|
|
|
|
return entry;
|
|
+err_out:
|
|
+ iput(inode);
|
|
+ return ERR_PTR(err);
|
|
}
|
|
|
|
static void del_fsync_inode(struct fsync_inode_entry *entry)
|
|
@@ -107,7 +121,8 @@ static int recover_dentry(struct inode *
|
|
|
|
entry = get_fsync_inode(dir_list, pino);
|
|
if (!entry) {
|
|
- entry = add_fsync_inode(F2FS_I_SB(inode), dir_list, pino);
|
|
+ entry = add_fsync_inode(F2FS_I_SB(inode), dir_list,
|
|
+ pino, false);
|
|
if (IS_ERR(entry)) {
|
|
dir = ERR_CAST(entry);
|
|
err = PTR_ERR(entry);
|
|
@@ -129,7 +144,7 @@ static int recover_dentry(struct inode *
|
|
retry:
|
|
de = __f2fs_find_entry(dir, &fname, &page);
|
|
if (de && inode->i_ino == le32_to_cpu(de->ino))
|
|
- goto out_unmap_put;
|
|
+ goto out_put;
|
|
|
|
if (de) {
|
|
einode = f2fs_iget_retry(inode->i_sb, le32_to_cpu(de->ino));
|
|
@@ -138,12 +153,19 @@ retry:
|
|
err = PTR_ERR(einode);
|
|
if (err == -ENOENT)
|
|
err = -EEXIST;
|
|
- goto out_unmap_put;
|
|
+ goto out_put;
|
|
+ }
|
|
+
|
|
+ err = dquot_initialize(einode);
|
|
+ if (err) {
|
|
+ iput(einode);
|
|
+ goto out_put;
|
|
}
|
|
+
|
|
err = acquire_orphan_inode(F2FS_I_SB(inode));
|
|
if (err) {
|
|
iput(einode);
|
|
- goto out_unmap_put;
|
|
+ goto out_put;
|
|
}
|
|
f2fs_delete_entry(de, page, dir, einode);
|
|
iput(einode);
|
|
@@ -158,8 +180,7 @@ retry:
|
|
goto retry;
|
|
goto out;
|
|
|
|
-out_unmap_put:
|
|
- f2fs_dentry_kunmap(dir, page);
|
|
+out_put:
|
|
f2fs_put_page(page, 0);
|
|
out:
|
|
if (file_enc_name(inode))
|
|
@@ -173,6 +194,20 @@ out:
|
|
return err;
|
|
}
|
|
|
|
+static void recover_inline_flags(struct inode *inode, struct f2fs_inode *ri)
|
|
+{
|
|
+ if (ri->i_inline & F2FS_PIN_FILE)
|
|
+ set_inode_flag(inode, FI_PIN_FILE);
|
|
+ else
|
|
+ clear_inode_flag(inode, FI_PIN_FILE);
|
|
+ if (ri->i_inline & F2FS_DATA_EXIST)
|
|
+ set_inode_flag(inode, FI_DATA_EXIST);
|
|
+ else
|
|
+ clear_inode_flag(inode, FI_DATA_EXIST);
|
|
+ if (!(ri->i_inline & F2FS_INLINE_DOTS))
|
|
+ clear_inode_flag(inode, FI_INLINE_DOTS);
|
|
+}
|
|
+
|
|
static void recover_inode(struct inode *inode, struct page *page)
|
|
{
|
|
struct f2fs_inode *raw = F2FS_INODE(page);
|
|
@@ -180,53 +215,36 @@ static void recover_inode(struct inode *
|
|
|
|
inode->i_mode = le16_to_cpu(raw->i_mode);
|
|
f2fs_i_size_write(inode, le64_to_cpu(raw->i_size));
|
|
- inode->i_atime.tv_sec = le64_to_cpu(raw->i_mtime);
|
|
+ inode->i_atime.tv_sec = le64_to_cpu(raw->i_atime);
|
|
inode->i_ctime.tv_sec = le64_to_cpu(raw->i_ctime);
|
|
inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime);
|
|
- inode->i_atime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec);
|
|
+ inode->i_atime.tv_nsec = le32_to_cpu(raw->i_atime_nsec);
|
|
inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec);
|
|
inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec);
|
|
|
|
+ F2FS_I(inode)->i_advise = raw->i_advise;
|
|
+
|
|
+ recover_inline_flags(inode, raw);
|
|
+
|
|
if (file_enc_name(inode))
|
|
name = "<encrypted>";
|
|
else
|
|
name = F2FS_INODE(page)->i_name;
|
|
|
|
- f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s",
|
|
- ino_of_node(page), name);
|
|
-}
|
|
-
|
|
-static bool is_same_inode(struct inode *inode, struct page *ipage)
|
|
-{
|
|
- struct f2fs_inode *ri = F2FS_INODE(ipage);
|
|
- struct timespec disk;
|
|
-
|
|
- if (!IS_INODE(ipage))
|
|
- return true;
|
|
-
|
|
- disk.tv_sec = le64_to_cpu(ri->i_ctime);
|
|
- disk.tv_nsec = le32_to_cpu(ri->i_ctime_nsec);
|
|
- if (timespec_compare(&inode->i_ctime, &disk) > 0)
|
|
- return false;
|
|
-
|
|
- disk.tv_sec = le64_to_cpu(ri->i_atime);
|
|
- disk.tv_nsec = le32_to_cpu(ri->i_atime_nsec);
|
|
- if (timespec_compare(&inode->i_atime, &disk) > 0)
|
|
- return false;
|
|
-
|
|
- disk.tv_sec = le64_to_cpu(ri->i_mtime);
|
|
- disk.tv_nsec = le32_to_cpu(ri->i_mtime_nsec);
|
|
- if (timespec_compare(&inode->i_mtime, &disk) > 0)
|
|
- return false;
|
|
-
|
|
- return true;
|
|
+ f2fs_msg(inode->i_sb, KERN_NOTICE,
|
|
+ "recover_inode: ino = %x, name = %s, inline = %x",
|
|
+ ino_of_node(page), name, raw->i_inline);
|
|
}
|
|
|
|
-static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
|
+static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
|
|
+ bool check_only)
|
|
{
|
|
struct curseg_info *curseg;
|
|
struct page *page = NULL;
|
|
block_t blkaddr;
|
|
+ unsigned int loop_cnt = 0;
|
|
+ unsigned int free_blocks = sbi->user_block_count -
|
|
+ valid_user_blocks(sbi);
|
|
int err = 0;
|
|
|
|
/* get node pages in the current segment */
|
|
@@ -248,21 +266,23 @@ static int find_fsync_dnodes(struct f2fs
|
|
goto next;
|
|
|
|
entry = get_fsync_inode(head, ino_of_node(page));
|
|
- if (entry) {
|
|
- if (!is_same_inode(entry->inode, page))
|
|
- goto next;
|
|
- } else {
|
|
- if (IS_INODE(page) && is_dent_dnode(page)) {
|
|
+ if (!entry) {
|
|
+ bool quota_inode = false;
|
|
+
|
|
+ if (!check_only &&
|
|
+ IS_INODE(page) && is_dent_dnode(page)) {
|
|
err = recover_inode_page(sbi, page);
|
|
if (err)
|
|
break;
|
|
+ quota_inode = true;
|
|
}
|
|
|
|
/*
|
|
* CP | dnode(F) | inode(DF)
|
|
* For this case, we should not give up now.
|
|
*/
|
|
- entry = add_fsync_inode(sbi, head, ino_of_node(page));
|
|
+ entry = add_fsync_inode(sbi, head, ino_of_node(page),
|
|
+ quota_inode);
|
|
if (IS_ERR(entry)) {
|
|
err = PTR_ERR(entry);
|
|
if (err == -ENOENT) {
|
|
@@ -277,6 +297,17 @@ static int find_fsync_dnodes(struct f2fs
|
|
if (IS_INODE(page) && is_dent_dnode(page))
|
|
entry->last_dentry = blkaddr;
|
|
next:
|
|
+ /* sanity check in order to detect looped node chain */
|
|
+ if (++loop_cnt >= free_blocks ||
|
|
+ blkaddr == next_blkaddr_of_node(page)) {
|
|
+ f2fs_msg(sbi->sb, KERN_NOTICE,
|
|
+ "%s: detect looped node chain, "
|
|
+ "blkaddr:%u, next:%u",
|
|
+ __func__, blkaddr, next_blkaddr_of_node(page));
|
|
+ err = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+
|
|
/* check next segment */
|
|
blkaddr = next_blkaddr_of_node(page);
|
|
f2fs_put_page(page, 1);
|
|
@@ -353,10 +384,18 @@ got_it:
|
|
f2fs_put_page(node_page, 1);
|
|
|
|
if (ino != dn->inode->i_ino) {
|
|
+ int ret;
|
|
+
|
|
/* Deallocate previous index in the node page */
|
|
inode = f2fs_iget_retry(sbi->sb, ino);
|
|
if (IS_ERR(inode))
|
|
return PTR_ERR(inode);
|
|
+
|
|
+ ret = dquot_initialize(inode);
|
|
+ if (ret) {
|
|
+ iput(inode);
|
|
+ return ret;
|
|
+ }
|
|
} else {
|
|
inode = dn->inode;
|
|
}
|
|
@@ -386,7 +425,8 @@ out:
|
|
return 0;
|
|
|
|
truncate_out:
|
|
- if (datablock_addr(tdn.node_page, tdn.ofs_in_node) == blkaddr)
|
|
+ if (datablock_addr(tdn.inode, tdn.node_page,
|
|
+ tdn.ofs_in_node) == blkaddr)
|
|
truncate_data_blocks_range(&tdn, 1);
|
|
if (dn->inode->i_ino == nid && !dn->inode_page_locked)
|
|
unlock_page(dn->inode_page);
|
|
@@ -394,7 +434,7 @@ truncate_out:
|
|
}
|
|
|
|
static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
|
- struct page *page, block_t blkaddr)
|
|
+ struct page *page)
|
|
{
|
|
struct dnode_of_data dn;
|
|
struct node_info ni;
|
|
@@ -405,11 +445,9 @@ static int do_recover_data(struct f2fs_s
|
|
if (IS_INODE(page)) {
|
|
recover_inline_xattr(inode, page);
|
|
} else if (f2fs_has_xattr_block(ofs_of_node(page))) {
|
|
- /*
|
|
- * Deprecated; xattr blocks should be found from cold log.
|
|
- * But, we should remain this for backward compatibility.
|
|
- */
|
|
- recover_xattr_data(inode, page, blkaddr);
|
|
+ err = recover_xattr_data(inode, page);
|
|
+ if (!err)
|
|
+ recovered++;
|
|
goto out;
|
|
}
|
|
|
|
@@ -441,8 +479,8 @@ retry_dn:
|
|
for (; start < end; start++, dn.ofs_in_node++) {
|
|
block_t src, dest;
|
|
|
|
- src = datablock_addr(dn.node_page, dn.ofs_in_node);
|
|
- dest = datablock_addr(page, dn.ofs_in_node);
|
|
+ src = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node);
|
|
+ dest = datablock_addr(dn.inode, page, dn.ofs_in_node);
|
|
|
|
/* skip recovering if dest is the same as src */
|
|
if (src == dest)
|
|
@@ -454,8 +492,10 @@ retry_dn:
|
|
continue;
|
|
}
|
|
|
|
- if ((start + 1) << PAGE_SHIFT > i_size_read(inode))
|
|
- f2fs_i_size_write(inode, (start + 1) << PAGE_SHIFT);
|
|
+ if (!file_keep_isize(inode) &&
|
|
+ (i_size_read(inode) <= ((loff_t)start << PAGE_SHIFT)))
|
|
+ f2fs_i_size_write(inode,
|
|
+ (loff_t)(start + 1) << PAGE_SHIFT);
|
|
|
|
/*
|
|
* dest is reserved block, invalidate src block
|
|
@@ -507,8 +547,10 @@ err:
|
|
f2fs_put_dnode(&dn);
|
|
out:
|
|
f2fs_msg(sbi->sb, KERN_NOTICE,
|
|
- "recover_data: ino = %lx, recovered = %d blocks, err = %d",
|
|
- inode->i_ino, recovered, err);
|
|
+ "recover_data: ino = %lx (i_size: %s) recovered = %d, err = %d",
|
|
+ inode->i_ino,
|
|
+ file_keep_isize(inode) ? "keep" : "recover",
|
|
+ recovered, err);
|
|
return err;
|
|
}
|
|
|
|
@@ -556,7 +598,7 @@ static int recover_data(struct f2fs_sb_i
|
|
break;
|
|
}
|
|
}
|
|
- err = do_recover_data(sbi, entry->inode, page, blkaddr);
|
|
+ err = do_recover_data(sbi, entry->inode, page);
|
|
if (err) {
|
|
f2fs_put_page(page, 1);
|
|
break;
|
|
@@ -576,18 +618,34 @@ next:
|
|
|
|
int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
|
|
{
|
|
- struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
|
|
struct list_head inode_list;
|
|
struct list_head dir_list;
|
|
- block_t blkaddr;
|
|
int err;
|
|
int ret = 0;
|
|
+ unsigned long s_flags = sbi->sb->s_flags;
|
|
bool need_writecp = false;
|
|
+#ifdef CONFIG_QUOTA
|
|
+ int quota_enabled;
|
|
+#endif
|
|
+
|
|
+ if (s_flags & MS_RDONLY) {
|
|
+ f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs");
|
|
+ sbi->sb->s_flags &= ~MS_RDONLY;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_QUOTA
|
|
+ /* Needed for iput() to work correctly and not trash data */
|
|
+ sbi->sb->s_flags |= MS_ACTIVE;
|
|
+ /* Turn on quotas so that they are updated correctly */
|
|
+ quota_enabled = f2fs_enable_quota_files(sbi, s_flags & MS_RDONLY);
|
|
+#endif
|
|
|
|
fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry",
|
|
sizeof(struct fsync_inode_entry));
|
|
- if (!fsync_entry_slab)
|
|
- return -ENOMEM;
|
|
+ if (!fsync_entry_slab) {
|
|
+ err = -ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
|
|
INIT_LIST_HEAD(&inode_list);
|
|
INIT_LIST_HEAD(&dir_list);
|
|
@@ -595,16 +653,14 @@ int recover_fsync_data(struct f2fs_sb_in
|
|
/* prevent checkpoint */
|
|
mutex_lock(&sbi->cp_mutex);
|
|
|
|
- blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
|
|
-
|
|
/* step #1: find fsynced inode numbers */
|
|
- err = find_fsync_dnodes(sbi, &inode_list);
|
|
+ err = find_fsync_dnodes(sbi, &inode_list, check_only);
|
|
if (err || list_empty(&inode_list))
|
|
- goto out;
|
|
+ goto skip;
|
|
|
|
if (check_only) {
|
|
ret = 1;
|
|
- goto out;
|
|
+ goto skip;
|
|
}
|
|
|
|
need_writecp = true;
|
|
@@ -613,7 +669,7 @@ int recover_fsync_data(struct f2fs_sb_in
|
|
err = recover_data(sbi, &inode_list, &dir_list);
|
|
if (!err)
|
|
f2fs_bug_on(sbi, !list_empty(&inode_list));
|
|
-out:
|
|
+skip:
|
|
destroy_fsync_dnodes(&inode_list);
|
|
|
|
/* truncate meta pages to be used by the recovery */
|
|
@@ -639,5 +695,13 @@ out:
|
|
}
|
|
|
|
kmem_cache_destroy(fsync_entry_slab);
|
|
+out:
|
|
+#ifdef CONFIG_QUOTA
|
|
+ /* Turn quotas off */
|
|
+ if (quota_enabled)
|
|
+ f2fs_quota_off_umount(sbi->sb);
|
|
+#endif
|
|
+ sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
|
|
+
|
|
return ret ? ret: err;
|
|
}
|