@@ -462,38 +462,269 @@ ocfs2_filecheck_done_entry(struct ocfs2_
spin_unlock(&ent->fs_fcheck->fc_lock);
}
+static int
+ocfs2_filecheck_inode_block(struct super_block *sb, unsigned long ino,
+ unsigned int flags, struct inode **ret)
+{
+ int rc = 0;
+ struct inode *inode;
+
+ if (flags == OCFS2_FILECHECK_TYPE_CHK)
+ flags = OCFS2_FI_FLAG_FILECHECK_CHK;
+ else
+ flags = OCFS2_FI_FLAG_FILECHECK_FIX;
+
+ inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0);
+ if (IS_ERR(inode))
+ rc = (int)(long)inode;
+ else
+ *ret = inode;
+
+ return rc;
+}
+
+static int
+ocfs2_filecheck_extent_list(handle_t *handle, struct inode *inode,
+ unsigned int flags, struct ocfs2_extent_list *el)
+{
+ int i, rc = 0;
+ u64 blkno;
+ struct buffer_head *bh = NULL;
+ struct ocfs2_extent_block *eb;
+ struct ocfs2_extent_list *ell;
+
+ if (!el || !el->l_tree_depth)
+ return 0;
+
+ mlog(ML_NOTICE, "ocfs2_filecheck_extent_list: inode %llu tree_depth "
+ "%u count %u next_free_rec %u\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno,
+ le16_to_cpu(el->l_tree_depth),
+ le16_to_cpu(el->l_count),
+ le16_to_cpu(el->l_next_free_rec));
+
+ if (le16_to_cpu(el->l_next_free_rec) == 0) {
+ mlog(ML_ERROR,
+ "Inode %llu has empty extent list at depth %u\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno,
+ le16_to_cpu(el->l_tree_depth));
+ rc = -EROFS;
+ goto out;
+ }
+
+ if (le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)) {
+ mlog(ML_ERROR,
+ "Inode %llu has bad count in extent list "
+ "at depth %u (next free=%u, count=%u)\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno,
+ le16_to_cpu(el->l_tree_depth),
+ le16_to_cpu(el->l_next_free_rec),
+ le16_to_cpu(el->l_count));
+ rc = -EROFS;
+ goto out;
+ }
+
+ for (i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) {
+ blkno = le64_to_cpu(el->l_recs[i].e_blkno);
+ if (blkno == 0) {
+ mlog(ML_ERROR,
+ "Inode %llu has bad blkno in extent list "
+ "at depth %u (index %d)\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno,
+ le16_to_cpu(el->l_tree_depth), i);
+ rc = -EROFS;
+ goto out;
+ }
+
+ mlog(ML_NOTICE, "ocfs2_filecheck_extent_list: inode %llu "
+ "rec[%d]: cpos %u clusters %u blkno %llu\n ",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno,
+ i,
+ le32_to_cpu(el->l_recs[i].e_cpos),
+ le32_to_cpu(el->l_recs[i].e_int_clusters),
+ le64_to_cpu(el->l_recs[i].e_blkno));
+
+ brelse(bh);
+ bh = NULL;
+
+ rc = ocfs2_filecheck_read_extent_block(INODE_CACHE(inode),
+ blkno, &bh, flags);
+ if (rc) {
+ mlog_errno(rc);
+ goto out;
+ }
+
+ if (flags && buffer_dirty(bh)) {
+ /* Dirty buffer means that filecheck just repaired
+ * this buffer during reading it from disk.
+ */
+ clear_buffer_dirty(bh);
+
+ rc = ocfs2_extend_trans(handle,
+ OCFS2_EXTENT_BLOCK_UPDATE_CREDITS);
+ if (rc) {
+ mlog_errno(rc);
+ goto out;
+ }
+
+ rc = ocfs2_journal_access_eb(handle,
+ INODE_CACHE(inode), bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
+ if (rc < 0) {
+ mlog_errno(rc);
+ goto out;
+ }
+
+ ocfs2_journal_dirty(handle, bh);
+ }
+
+ eb = (struct ocfs2_extent_block *)bh->b_data;
+ ell = &eb->h_list;
+ rc = ocfs2_filecheck_extent_list(handle, inode, flags, ell);
+ if (rc)
+ goto out;
+ }
+
+out:
+ brelse(bh);
+ return rc;
+}
+
+static int
+ocfs2_filecheck_extent_block(struct inode *inode, struct buffer_head *di_bh,
+ unsigned int flags)
+{
+ int rc = 0;
+ handle_t *handle = NULL;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct ocfs2_dinode *di;
+ struct ocfs2_extent_list *el;
+
+ if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL)
+ return 0;
+
+ if (flags) {
+ handle = ocfs2_start_trans(osb,
+ OCFS2_EXTENT_BLOCK_UPDATE_CREDITS);
+ if (IS_ERR(handle)) {
+ rc = PTR_ERR(handle);
+ mlog_errno(rc);
+ goto out;
+ }
+ }
+
+ di = (struct ocfs2_dinode *)di_bh->b_data;
+ el = &di->id2.i_list;
+ rc = ocfs2_filecheck_extent_list(handle, inode, flags, el);
+
+ if (flags)
+ ocfs2_commit_trans(osb, handle);
+
+out:
+ return rc;
+}
+
+static int
+ocfs2_filecheck_file_block(struct inode *inode, unsigned int flags)
+{
+ int rc;
+ struct buffer_head *di_bh = NULL;
+
+ mutex_lock(&inode->i_mutex);
+
+ rc = ocfs2_rw_lock(inode, 1);
+ if (rc) {
+ mlog_errno(rc);
+ goto out;
+ }
+
+ rc = ocfs2_inode_lock(inode, &di_bh, 1);
+ if (rc) {
+ mlog_errno(rc);
+ goto out_rw_unlock;
+ }
+
+ do {
+ down_write(&OCFS2_I(inode)->ip_alloc_sem);
+ rc = ocfs2_filecheck_extent_block(inode, di_bh, flags);
+ up_write(&OCFS2_I(inode)->ip_alloc_sem);
+ if (rc)
+ break;
+
+ /* TODO: Add check/fix for xattr/refcount blocks */
+
+ } while (0);
+
+ brelse(di_bh);
+ ocfs2_inode_unlock(inode, 1);
+out_rw_unlock:
+ ocfs2_rw_unlock(inode, 1);
+out:
+ mutex_unlock(&inode->i_mutex);
+
+ return rc;
+}
+
+static int
+ocfs2_filecheck_other_block(struct inode *inode, unsigned int flags)
+{
+ int rc = 0;
+
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFREG:
+ rc = ocfs2_filecheck_file_block(inode, flags);
+ break;
+ case S_IFDIR:
+ /* TODO: Add check/fix for directory blocks */
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
static unsigned short
ocfs2_filecheck_handle(struct super_block *sb,
unsigned long ino, unsigned int flags)
{
+ int rc;
unsigned short ret = OCFS2_FILECHECK_ERR_SUCCESS;
struct inode *inode = NULL;
- int rc;
- inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0);
- if (IS_ERR(inode)) {
- rc = (int)(-(long)inode);
+ do {
+ rc = ocfs2_filecheck_inode_block(sb, ino, flags, &inode);
+ if (rc)
+ break;
+
+ rc = ocfs2_filecheck_other_block(inode, flags);
+ } while (0);
+
+ if (rc) {
+ rc = (-rc);
if (rc >= OCFS2_FILECHECK_ERR_START &&
rc < OCFS2_FILECHECK_ERR_END)
ret = rc;
else
ret = OCFS2_FILECHECK_ERR_FAILED;
- } else
+ }
+
+ if (inode)
iput(inode);
return ret;
}
-static void
+static inline void
ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
struct ocfs2_filecheck_entry *entry)
{
if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
- entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
+ entry->fe_ino, OCFS2_FILECHECK_TYPE_CHK);
else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
- entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
+ entry->fe_ino, OCFS2_FILECHECK_TYPE_FIX);
else
entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;