*/
#include "fsck.h"
#include "quotaio.h"
+#include <time.h>
char *tree_mark;
uint32_t tree_mark_size = 256;
fsck->main_area_bitmap);
}
+static inline int f2fs_clear_main_bitmap(struct f2fs_sb_info *sbi, u32 blk)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+
+ return f2fs_clear_bit(BLKOFF_FROM_MAIN(sbi, blk),
+ fsck->main_area_bitmap);
+}
+
static inline int f2fs_test_sit_bitmap(struct f2fs_sb_info *sbi, u32 blk)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
/* workaround to fix later */
if (ftype != F2FS_FT_ORPHAN ||
- f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0)
+ f2fs_test_bit(nid, fsck->nat_area_bitmap) != 0) {
f2fs_clear_bit(nid, fsck->nat_area_bitmap);
- else
+ /* avoid reusing nid when reconnecting files */
+ f2fs_set_bit(nid, NM_I(sbi)->nid_bitmap);
+ } else
ASSERT_MSG("orphan or xattr nid is duplicated [0x%x]\n",
nid);
return err;
}
+static struct f2fs_node *fsck_get_lpf(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_node *node;
+ struct node_info ni;
+ nid_t lpf_ino;
+ int err;
+
+ /* read root inode first */
+ node = calloc(F2FS_BLKSIZE, 1);
+ ASSERT(node);
+ get_node_info(sbi, F2FS_ROOT_INO(sbi), &ni);
+ err = dev_read_block(node, ni.blk_addr);
+ ASSERT(err >= 0);
+
+ /* lookup lost+found in root directory */
+ lpf_ino = f2fs_lookup(sbi, node, (u8 *)LPF, strlen(LPF));
+ if (lpf_ino) { /* found */
+ get_node_info(sbi, lpf_ino, &ni);
+ err = dev_read_block(node, ni.blk_addr);
+ ASSERT(err >= 0);
+ DBG(1, "Found lost+found 0x%x at blkaddr [0x%x]\n",
+ lpf_ino, ni.blk_addr);
+ if (!S_ISDIR(le16_to_cpu(node->i.i_mode))) {
+ ASSERT_MSG("lost+found is not directory [0%o]\n",
+ le16_to_cpu(node->i.i_mode));
+ /* FIXME: give up? */
+ goto out;
+ }
+ } else { /* not found, create it */
+ struct dentry de;
+
+ memset(&de, 0, sizeof(de));
+ de.name = (u8 *) LPF;
+ de.len = strlen(LPF);
+ de.mode = 0x41c0;
+ de.pino = F2FS_ROOT_INO(sbi),
+ de.file_type = F2FS_FT_DIR,
+ de.uid = getuid();
+ de.gid = getgid();
+ de.mtime = time(NULL);
+
+ err = f2fs_mkdir(sbi, &de);
+ if (err) {
+ ASSERT_MSG("Failed create lost+found");
+ goto out;
+ }
+
+ get_node_info(sbi, de.ino, &ni);
+ err = dev_read_block(node, ni.blk_addr);
+ ASSERT(err >= 0);
+ DBG(1, "Create lost+found 0x%x at blkaddr [0x%x]\n",
+ de.ino, ni.blk_addr);
+ }
+
+ c.lpf_ino = le32_to_cpu(node->footer.ino);
+ return node;
+out:
+ free(node);
+ return NULL;
+}
+
+static int fsck_do_reconnect_file(struct f2fs_sb_info *sbi,
+ struct f2fs_node *lpf,
+ struct f2fs_node *fnode)
+{
+ char name[80];
+ size_t namelen;
+ nid_t ino = le32_to_cpu(fnode->footer.ino);
+ struct node_info ni;
+ int ftype, ret;
+
+ namelen = snprintf(name, 80, "%u", ino);
+ if (namelen >= 80)
+ /* ignore terminating '\0', should never happen */
+ namelen = 79;
+
+ if (f2fs_lookup(sbi, lpf, (u8 *)name, namelen)) {
+ ASSERT_MSG("Name %s already exist in lost+found", name);
+ return -EEXIST;
+ }
+
+ get_node_info(sbi, le32_to_cpu(lpf->footer.ino), &ni);
+ ftype = map_de_type(le16_to_cpu(fnode->i.i_mode));
+ ret = f2fs_add_link(sbi, lpf, (unsigned char *)name, namelen,
+ ino, ftype, ni.blk_addr, 0);
+ if (ret) {
+ ASSERT_MSG("Failed to add inode [0x%x] to lost+found", ino);
+ return -EINVAL;
+ }
+
+ /* update fnode */
+ memcpy(fnode->i.i_name, name, namelen);
+ fnode->i.i_namelen = cpu_to_le32(namelen);
+ fnode->i.i_pino = c.lpf_ino;
+ get_node_info(sbi, le32_to_cpu(fnode->footer.ino), &ni);
+ ret = dev_write_block(fnode, ni.blk_addr);
+ ASSERT(ret >= 0);
+
+ DBG(1, "Reconnect inode [0x%x] to lost+found\n", ino);
+ return 0;
+}
+
+static void fsck_failed_reconnect_file_dnode(struct f2fs_sb_info *sbi,
+ nid_t nid)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_node *node;
+ struct node_info ni;
+ u32 addr;
+ int i, err;
+
+ node = calloc(F2FS_BLKSIZE, 1);
+ ASSERT(node);
+
+ get_node_info(sbi, nid, &ni);
+ err = dev_read_block(node, ni.blk_addr);
+ ASSERT(err >= 0);
+
+ fsck->chk.valid_node_cnt--;
+ fsck->chk.valid_blk_cnt--;
+ f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+
+ for (i = 0; i < ADDRS_PER_BLOCK; i++) {
+ addr = le32_to_cpu(node->dn.addr[i]);
+ if (!addr)
+ continue;
+ fsck->chk.valid_blk_cnt--;
+ if (addr == NEW_ADDR)
+ continue;
+ f2fs_clear_main_bitmap(sbi, addr);
+ }
+
+ free(node);
+}
+
+static void fsck_failed_reconnect_file_idnode(struct f2fs_sb_info *sbi,
+ nid_t nid)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_node *node;
+ struct node_info ni;
+ nid_t tmp;
+ int i, err;
+
+ node = calloc(F2FS_BLKSIZE, 1);
+ ASSERT(node);
+
+ get_node_info(sbi, nid, &ni);
+ err = dev_read_block(node, ni.blk_addr);
+ ASSERT(err >= 0);
+
+ fsck->chk.valid_node_cnt--;
+ fsck->chk.valid_blk_cnt--;
+ f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+
+ for (i = 0; i < NIDS_PER_BLOCK; i++) {
+ tmp = le32_to_cpu(node->in.nid[i]);
+ if (!tmp)
+ continue;
+ fsck_failed_reconnect_file_dnode(sbi, tmp);
+ }
+
+ free(node);
+}
+
+static void fsck_failed_reconnect_file_didnode(struct f2fs_sb_info *sbi,
+ nid_t nid)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_node *node;
+ struct node_info ni;
+ nid_t tmp;
+ int i, err;
+
+ node = calloc(F2FS_BLKSIZE, 1);
+ ASSERT(node);
+
+ get_node_info(sbi, nid, &ni);
+ err = dev_read_block(node, ni.blk_addr);
+ ASSERT(err >= 0);
+
+ fsck->chk.valid_node_cnt--;
+ fsck->chk.valid_blk_cnt--;
+ f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+
+ for (i = 0; i < NIDS_PER_BLOCK; i++) {
+ tmp = le32_to_cpu(node->in.nid[i]);
+ if (!tmp)
+ continue;
+ fsck_failed_reconnect_file_idnode(sbi, tmp);
+ }
+
+ free(node);
+}
+
+/*
+ * Counters and main_area_bitmap are already changed during checking
+ * inode block, so clear them. There is no need to clear new blocks
+ * allocted to lost+found.
+ */
+static void fsck_failed_reconnect_file(struct f2fs_sb_info *sbi, nid_t ino)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_node *node;
+ struct node_info ni;
+ nid_t nid;
+ int ofs, i, err;
+
+ node = calloc(F2FS_BLKSIZE, 1);
+ ASSERT(node);
+
+ get_node_info(sbi, ino, &ni);
+ err = dev_read_block(node, ni.blk_addr);
+ ASSERT(err >= 0);
+
+ /* clear inode counters */
+ fsck->chk.valid_inode_cnt--;
+ fsck->chk.valid_node_cnt--;
+ fsck->chk.valid_blk_cnt--;
+ f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+
+ /* clear xnid counters */
+ if (node->i.i_xattr_nid) {
+ nid = le32_to_cpu(node->i.i_xattr_nid);
+ fsck->chk.valid_node_cnt--;
+ fsck->chk.valid_blk_cnt--;
+ get_node_info(sbi, nid, &ni);
+ f2fs_clear_main_bitmap(sbi, ni.blk_addr);
+ }
+
+ /* clear data counters */
+ if(!(node->i.i_inline & F2FS_INLINE_DATA)) {
+ ofs = get_extra_isize(node);
+ for (i = 0; i < ADDRS_PER_INODE(&node->i); i++) {
+ block_t addr = le32_to_cpu(node->i.i_addr[ofs + i]);
+ if (!addr)
+ continue;
+ fsck->chk.valid_blk_cnt--;
+ if (addr == NEW_ADDR)
+ continue;
+ f2fs_clear_main_bitmap(sbi, addr);
+ }
+ }
+
+ for (i = 0; i < 5; i++) {
+ nid = le32_to_cpu(node->i.i_nid[i]);
+ if (!nid)
+ continue;
+
+ switch (i) {
+ case 0: /* direct node */
+ case 1:
+ fsck_failed_reconnect_file_dnode(sbi, nid);
+ break;
+ case 2: /* indirect node */
+ case 3:
+ fsck_failed_reconnect_file_idnode(sbi, nid);
+ break;
+ case 4: /* double indirect node */
+ fsck_failed_reconnect_file_didnode(sbi, nid);
+ break;
+ }
+ }
+
+ free(node);
+}
+
+/*
+ * Scan unreachable nids and find only regular file inodes. If these files
+ * are not corrupted, reconnect them to lost+found.
+ *
+ * Since all unreachable nodes are already checked, we can allocate new
+ * blocks safely.
+ *
+ * This function returns the number of files been reconnected.
+ */
+static int fsck_reconnect_file(struct f2fs_sb_info *sbi)
+{
+ struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
+ struct f2fs_node *lpf_node, *node;
+ struct node_info ni;
+ char *reconnect_bitmap;
+ u32 blk_cnt;
+ nid_t nid;
+ int err, cnt = 0, ftype;
+
+ node = calloc(F2FS_BLKSIZE, 1);
+ ASSERT(node);
+
+ reconnect_bitmap = calloc(fsck->nat_area_bitmap_sz, 1);
+ ASSERT(reconnect_bitmap);
+
+ for (nid = 0; nid < fsck->nr_nat_entries; nid++) {
+ if (f2fs_test_bit(nid, fsck->nat_area_bitmap)) {
+ if (is_qf_ino(F2FS_RAW_SUPER(sbi), nid)) {
+ DBG(1, "Not support quota inode [0x%x]\n",
+ nid);
+ continue;
+ }
+
+ get_node_info(sbi, nid, &ni);
+ err = dev_read_block(node, ni.blk_addr);
+ ASSERT(err >= 0);
+
+ /* reconnection will restore these nodes if needed */
+ if (node->footer.ino != node->footer.nid) {
+ DBG(1, "Not support non-inode node [0x%x]\n",
+ nid);
+ continue;
+ }
+
+ if (S_ISDIR(le16_to_cpu(node->i.i_mode))) {
+ DBG(1, "Not support directory inode [0x%x]\n",
+ nid);
+ continue;
+ }
+
+ ftype = map_de_type(le16_to_cpu(node->i.i_mode));
+ if (sanity_check_nid(sbi, nid, node, ftype,
+ TYPE_INODE, &ni)) {
+ ASSERT_MSG("Invalid nid [0x%x]\n", nid);
+ continue;
+ }
+
+ DBG(1, "Check inode 0x%x\n", nid);
+ blk_cnt = 1;
+ fsck_chk_inode_blk(sbi, nid, ftype, node,
+ &blk_cnt, &ni, NULL);
+
+ f2fs_set_bit(nid, reconnect_bitmap);
+ }
+ }
+
+ lpf_node = fsck_get_lpf(sbi);
+ if (!lpf_node)
+ goto out;
+
+ for (nid = 0; nid < fsck->nr_nat_entries; nid++) {
+ if (f2fs_test_bit(nid, reconnect_bitmap)) {
+ get_node_info(sbi, nid, &ni);
+ err = dev_read_block(node, ni.blk_addr);
+ ASSERT(err >= 0);
+
+ if (fsck_do_reconnect_file(sbi, lpf_node, node)) {
+ DBG(1, "Failed to reconnect inode [0x%x]\n",
+ nid);
+ fsck_failed_reconnect_file(sbi, nid);
+ continue;
+ }
+
+ quota_add_inode_usage(fsck->qctx, nid, &node->i);
+
+ DBG(1, "Reconnected inode [0x%x] to lost+found\n", nid);
+ cnt++;
+ }
+ }
+
+out:
+ free(node);
+ free(lpf_node);
+ free(reconnect_bitmap);
+ return cnt;
+}
+
int fsck_verify(struct f2fs_sb_info *sbi)
{
unsigned int i = 0;
printf("\n");
+ if (c.feature & cpu_to_le32(F2FS_FEATURE_LOST_FOUND)) {
+ for (i = 0; i < fsck->nr_nat_entries; i++)
+ if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0)
+ break;
+ if (i < fsck->nr_nat_entries) {
+ i = fsck_reconnect_file(sbi);
+ printf("[FSCK] Reconnect %u files to lost+found\n", i);
+ }
+ }
+
for (i = 0; i < fsck->nr_nat_entries; i++) {
if (f2fs_test_bit(i, fsck->nat_area_bitmap) != 0) {
printf("NID[0x%x] is unreachable\n", i);