f2fs: catch up to v4.14-rc1
[GitHub/exynos8895/android_kernel_samsung_universal8895.git] / fs / f2fs / recovery.c
index 4a3c48c24c102a056de8433ccf31741f9fccfc7c..9626758bc76242ca4a91057924d7492e6c217a85 100644 (file)
@@ -69,20 +69,34 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
 }
 
 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 *inode, struct page *ipage,
 
        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);
@@ -140,6 +155,13 @@ retry:
                                err = -EEXIST;
                        goto out_unmap_put;
                }
+
+               err = dquot_initialize(einode);
+               if (err) {
+                       iput(einode);
+                       goto out_unmap_put;
+               }
+
                err = acquire_orphan_inode(F2FS_I_SB(inode));
                if (err) {
                        iput(einode);
@@ -198,7 +220,8 @@ static void recover_inode(struct inode *inode, struct page *page)
                        ino_of_node(page), name);
 }
 
-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;
@@ -225,17 +248,22 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
 
                entry = get_fsync_inode(head, ino_of_node(page));
                if (!entry) {
-                       if (IS_INODE(page) && is_dent_dnode(page)) {
+                       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) {
@@ -326,10 +354,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;
        }
@@ -359,7 +395,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);
@@ -378,11 +415,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
        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, blkaddr);
+               if (!err)
+                       recovered++;
                goto out;
        }
 
@@ -414,8 +449,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)
@@ -428,8 +463,9 @@ retry_dn:
                }
 
                if (!file_keep_isize(inode) &&
-                               (i_size_read(inode) <= (start << PAGE_SHIFT)))
-                       f2fs_i_size_write(inode, (start + 1) << PAGE_SHIFT);
+                       (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
@@ -556,12 +592,27 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
        struct list_head dir_list;
        int err;
        int ret = 0;
+       unsigned long s_flags = sbi->sb->s_flags;
        bool need_writecp = false;
 
+       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 */
+       f2fs_enable_quota_files(sbi);
+#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);
@@ -570,13 +621,13 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
        mutex_lock(&sbi->cp_mutex);
 
        /* 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;
@@ -585,7 +636,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
        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 */
@@ -598,8 +649,6 @@ out:
        }
 
        clear_sbi_flag(sbi, SBI_POR_DOING);
-       if (err)
-               set_ckpt_flags(sbi, CP_ERROR_FLAG);
        mutex_unlock(&sbi->cp_mutex);
 
        /* let's drop all the directory inodes for clean checkpoint */
@@ -613,5 +662,12 @@ out:
        }
 
        kmem_cache_destroy(fsync_entry_slab);
+out:
+#ifdef CONFIG_QUOTA
+       /* Turn quotas off */
+       f2fs_quota_off_umount(sbi->sb);
+#endif
+       sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */
+
        return ret ? ret: err;
 }