Hi,

This patch adds nilfs_ss_header_is_valid() function with the purpose of making 
segment summary header check.

With the best regards,
Vyacheslav Dubeyko.
--
From: Vyacheslav Dubeyko <[email protected]>
Subject: [PATCH v4 07/15] nilfs-utils: fsck: NILFS segment summary header check 
implementation

This patch adds nilfs_ss_header_is_valid() function with the purpose of making 
segment summary header check.

Signed-off-by: Vyacheslav Dubeyko <[email protected]>
---
 lib/segment.c |  509 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 509 insertions(+)
 create mode 100644 lib/segment.c

diff --git a/lib/segment.c b/lib/segment.c
new file mode 100644
index 0000000..9c14fb6
--- /dev/null
+++ b/lib/segment.c
@@ -0,0 +1,509 @@
+/*
+ * segment.c - NILFS2 segment checking functionality
+ *
+ * Copyright (C) 2012 Vyacheslav Dubeyko <[email protected]>
+ *
+ * This file is part of NILFS.
+ *
+ * NILFS is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * NILFS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with NILFS; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Written by Vyacheslav Dubeyko <[email protected]>
+ */
+
+#include "nilfs.h"
+#include "nilfs_messages.h"
+#include "fsck_nilfs2.h"
+
+/*
+ * ss_check_magic - Check magic signature of segment summary header.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_OK - no internal errors were occured.
+ * %-INVALID_SS_SIGNATURE - segment summary header has invalid magic.
+ *
+ * In the case of valid successfull call @check_mask keeps
+ * flags with detected errors.
+ */
+static int ss_check_magic(struct nilfs_segment_summary *ss_ptr,
+                                               __u16 *check_mask)
+{
+       int err = NILFS_OK;
+
+       if (!(*check_mask & CHECK_SS_MAGIC))
+               return NILFS_OK; /* nothing to be checked */
+
+       if (le32_to_cpu(ss_ptr->ss_magic) != NILFS_SEGSUM_MAGIC)
+               err = INVALID_SS_SIGNATURE;
+       else
+               *check_mask &= ~CHECK_SS_MAGIC;
+
+       internal_debug("ss_magic %#x.",
+                       le32_to_cpu(ss_ptr->ss_magic));
+       internal_debug("check_mask %x.", *check_mask);
+
+       return (err > 0) ? -err : err;
+} /* ss_check_magic() */
+
+/*
+ * ss_check_header_size - Check segment summary header size in bytes.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_OK - no internal errors were occured.
+ * %-INVALID_SS_HEADER_SZ - size of segment summary header is invalid.
+ * %-UNSUPPORTED_SS_REV - unsupported revision of segment summary.
+ *
+ * In the case of valid successfull call @check_mask keeps
+ * flags with detected errors.
+ */
+static int ss_check_header_size(struct nilfs_segment_summary *ss_ptr,
+                                               __u16 *check_mask)
+{
+       int err = NILFS_OK;
+       __u16 ss_bytes = le16_to_cpu(ss_ptr->ss_bytes);
+
+       if (!(*check_mask & CHECK_SS_BYTES))
+               return NILFS_OK; /* nothing to be checked */
+
+       if (ss_bytes < sizeof(struct nilfs_segment_summary)) {
+               err = INVALID_SS_HEADER_SZ;
+               *check_mask &= (~CHECK_SS_HDR_REV);
+       } else if (ss_bytes > sizeof(struct nilfs_segment_summary)) {
+               err = UNSUPPORTED_SS_REV;
+               *check_mask &= (~CHECK_SS_BYTES);
+       } else
+               *check_mask &= (~(CHECK_SS_BYTES | CHECK_SS_HDR_REV));
+
+       internal_debug("ss_bytes %d.",
+                       le16_to_cpu(ss_ptr->ss_bytes));
+       internal_debug("check_mask %x.", *check_mask);
+
+       return (err > 0) ? -err : err;
+} /* ss_check_header_size() */
+
+/*
+ * ss_check_segment_sequence_number - Check segment sequence number.
+ * @segment: current segment number.
+ * @sbp: pointer on superblock.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_OK - no internal errors were occured.
+ * %-INVALID_SS_SEQ - segment summary header keeps invalid seq number.
+ *
+ * In the case of valid successfull call @check_mask keeps
+ * flags with detected errors.
+ */
+static int ss_check_segment_sequence_number(__u64 segment,
+                               struct nilfs_super_block *sbp,
+                               struct nilfs_segment_summary *ss_ptr,
+                               __u16 *check_mask)
+{
+       int err = NILFS_OK;
+       __u64 sequence_number;
+       __u64 nsegs;
+       __u64 iteration;
+
+       if (!(*check_mask & CHECK_SS_SEQ))
+               return NILFS_OK; /* nothing to be checked */
+
+       sequence_number = le64_to_cpu(ss_ptr->ss_seq);
+       nsegs = le64_to_cpu(sbp->s_nsegments);
+       iteration = sequence_number / nsegs;
+
+       if ((segment + (nsegs * iteration)) != sequence_number)
+               err = INVALID_SS_SEQ;
+       else
+               *check_mask &= ~CHECK_SS_SEQ;
+
+       internal_debug("ss_seq %lld.",
+                       le64_to_cpu(ss_ptr->ss_seq));
+       internal_debug("check_mask %x.", *check_mask);
+
+       return (err > 0) ? -err : err;
+} /* ss_check_segment_sequence_number() */
+
+/*
+ * ss_check_seg_used_blocks_number - Check number of really used blocks.
+ * @sbp: pointer on superblock.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_OK - no internal errors were occured.
+ * %-INVALID_SS_NBLOCKS - invalid number of really used blocks in segment.
+ *
+ * In the case of valid successfull call @check_mask keeps
+ * flags with detected errors.
+ */
+static int ss_check_seg_used_blocks_number(struct nilfs_super_block *sbp,
+                                       struct nilfs_segment_summary *ss_ptr,
+                                       __u16 *check_mask)
+{
+       int err = NILFS_OK;
+       __u32 nblocks;
+       __u32 blk_size;
+       __u32 seg_sum_blocks;
+
+       if (!(*check_mask & CHECK_SS_NBLOCKS))
+               return NILFS_OK; /* nothing to be checked */
+
+       nblocks = le32_to_cpu(ss_ptr->ss_nblocks);
+       blk_size = (1UL << (le32_to_cpu(sbp->s_log_block_size) +
+                                       NILFS_SB_BLOCK_SIZE_SHIFT));
+       seg_sum_blocks =
+               ROUNDUP_DIV(le32_to_cpu(ss_ptr->ss_sumbytes), blk_size);
+
+       if (nblocks > le32_to_cpu(sbp->s_blocks_per_segment) ||
+                       nblocks <= seg_sum_blocks)
+               err = INVALID_SS_NBLOCKS;
+       else
+               *check_mask &= ~CHECK_SS_NBLOCKS;
+
+       internal_debug("ss_nblocks %d.", nblocks);
+       internal_debug("check_mask %x.", *check_mask);
+
+       return (err > 0) ? -err : err;
+} /* ss_check_seg_used_blocks_number() */
+
+/*
+ * ss_check_flags - Check segment summary header flags.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_OK - no internal errors were occured.
+ * %-INVALID_SS_FLAGS - unknown flags were detected.
+ *
+ * In the case of valid successfull call @check_mask keeps
+ * flags with detected errors.
+ */
+static int ss_check_flags(struct nilfs_segment_summary *ss_ptr,
+                                               __u16 *check_mask)
+{
+       int err = NILFS_OK;
+       __u16 known_flags = NILFS_SS_LOGBGN | NILFS_SS_LOGEND |
+                               NILFS_SS_SR | NILFS_SS_SYNDT |
+                               NILFS_SS_GC;
+
+       if (!(*check_mask & CHECK_SS_FLAGS))
+               return NILFS_OK; /* nothing to be checked */
+
+       if (le16_to_cpu(ss_ptr->ss_flags) & ~known_flags)
+               err = INVALID_SS_FLAGS;
+       else
+               *check_mask &= ~CHECK_SS_FLAGS;
+
+       internal_debug("ss_flags %d.",
+                       le16_to_cpu(ss_ptr->ss_flags));
+       internal_debug("check_mask %x.", *check_mask);
+
+       return (err > 0) ? -err : err;
+} /* ss_check_flags() */
+
+/*
+ * ss_check_creation_timestamp - Check segment summary header creation time.
+ * @sbp: pointer on superblock.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_OK - no internal errors were occured.
+ * %-INVALID_SS_CREATE_TIME - creation timestamp has strange value.
+ *
+ * In the case of valid successfull call @check_mask keeps
+ * flags with detected errors.
+ */
+static int ss_check_creation_timestamp(struct nilfs_super_block *sbp,
+                                       struct nilfs_segment_summary *ss_ptr,
+                                                       __u16 *check_mask)
+{
+       int err = NILFS_OK;
+       __u64 ss_create;
+
+       if (!(*check_mask & CHECK_SS_CREATE))
+               return NILFS_OK; /* nothing to be checked */
+
+       ss_create = le64_to_cpu(ss_ptr->ss_create);
+
+       if (ss_create >= time(NULL) || ss_create < le64_to_cpu(sbp->s_ctime))
+               err = INVALID_SS_CREATE_TIME;
+       else
+               *check_mask &= ~CHECK_SS_CREATE;
+
+       internal_debug("ss_create %s.",
+                       ctime((const time_t *)&ss_create));
+       internal_debug("check_mask %x.", *check_mask);
+
+       return (err > 0) ? -err : err;
+} /* ss_check_creation_timestamp() */
+
+/*
+ * ss_check_next_seg_start_block - Check next segment start block.
+ * @segment: segment sequence number.
+ * @sbp: pointer on superblock.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_OK - no internal errors were occured.
+ * %-INVALID_SS_NEXT_SEG_BLK - next segment start block is invalid.
+ *
+ * In the case of valid successfull call @check_mask keeps
+ * flags with detected errors.
+ */
+static int ss_check_next_seg_start_block(__u64 segment,
+                                       struct nilfs_super_block *sbp,
+                                       struct nilfs_segment_summary *ss_ptr,
+                                                       __u16 *check_mask)
+{
+       int err = NILFS_OK;
+       __u64 cur_seg_start_block;
+       __u64 next_seg_start_block;
+       __u64 nsegs;
+       __u32 blks_per_seg;
+       __u64 seg_start_diff;
+       __u64 first_data_block;
+
+       if (!(*check_mask & CHECK_SS_NEXT))
+               return NILFS_OK; /* nothing to be checked */
+
+       cur_seg_start_block = seg_num_to_start_block(segment, sbp);
+       next_seg_start_block = le64_to_cpu(ss_ptr->ss_next);
+       nsegs = le64_to_cpu(sbp->s_nsegments);
+       blks_per_seg = le32_to_cpu(sbp->s_blocks_per_segment);
+       first_data_block = le64_to_cpu(sbp->s_first_data_block);
+       seg_start_diff = (next_seg_start_block > cur_seg_start_block ?
+                               next_seg_start_block - cur_seg_start_block :
+                               cur_seg_start_block - next_seg_start_block);
+
+       if (next_seg_start_block >= (nsegs * blks_per_seg))
+               err = INVALID_SS_NEXT_SEG_BLK;
+       else if (0 == seg_start_diff)
+               err = INVALID_SS_NEXT_SEG_BLK;
+       else if (0 == segment || next_seg_start_block == first_data_block) {
+               if ((seg_start_diff + first_data_block) < blks_per_seg ||
+                           (seg_start_diff + first_data_block) % blks_per_seg)
+                       err = INVALID_SS_NEXT_SEG_BLK;
+               else
+                       *check_mask &= ~CHECK_SS_NEXT;
+       } else {
+               if (seg_start_diff < blks_per_seg ||
+                               seg_start_diff % blks_per_seg)
+                       err = INVALID_SS_NEXT_SEG_BLK;
+               else
+                       *check_mask &= ~CHECK_SS_NEXT;
+       }
+
+       internal_debug("ss_next %lld.",
+                       le64_to_cpu(ss_ptr->ss_next));
+       internal_debug("check_mask %x.", *check_mask);
+
+       return (err > 0) ? -err : err;
+} /* ss_check_next_seg_start_block() */
+
+/*
+ * ss_check_nfifo - Check number of finfo structures.
+ * @sbp: pointer on superblock.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_OK - no internal errors were occured.
+ * %-INVALID_SS_NFINFO - invalid number of finfo structures.
+ *
+ * In the case of valid successfull call @check_mask keeps
+ * flags with detected errors.
+ */
+static int ss_check_nfifo(struct nilfs_super_block *sbp,
+                       struct nilfs_segment_summary *ss_ptr,
+                       __u16 *check_mask)
+{
+       int err = NILFS_OK;
+       __u32 min_finfo_size;
+       __u32 blk_size;
+
+       if (!(*check_mask & CHECK_SS_NFINFO))
+               return NILFS_OK; /* nothing to be checked */
+
+       min_finfo_size = le32_to_cpu(ss_ptr->ss_nfinfo) *
+               (sizeof(struct nilfs_finfo) + sizeof(union nilfs_binfo));
+       blk_size = (1UL << (le32_to_cpu(sbp->s_log_block_size) +
+                                       NILFS_SB_BLOCK_SIZE_SHIFT));
+
+       if (le32_to_cpu(ss_ptr->ss_sumbytes) <=
+                       sizeof(struct nilfs_segment_summary))
+               err = INVALID_SS_NFINFO;
+       else if (min_finfo_size >
+                       (le32_to_cpu(ss_ptr->ss_sumbytes) -
+                               sizeof(struct nilfs_segment_summary)))
+               err = INVALID_SS_NFINFO;
+       else if (ROUNDUP_DIV(min_finfo_size, blk_size) >
+                               le32_to_cpu(ss_ptr->ss_nblocks))
+               err = INVALID_SS_NFINFO;
+       else
+               *check_mask &= ~CHECK_SS_NFINFO;
+
+       internal_debug("ss_nfinfo %d.",
+                       le32_to_cpu(ss_ptr->ss_nfinfo));
+       internal_debug("check_mask %x.", *check_mask);
+
+       return (err > 0) ? -err : err;
+} /* ss_check_nfifo() */
+
+/*
+ * ss_check_cno - Check checkpoint number.
+ * @sbp: pointer on superblock.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_OK - no internal errors were occured.
+ * %-INVALID_SS_CNO - invalid checkpoint number.
+ *
+ * In the case of valid successfull call @check_mask keeps
+ * flags with detected errors.
+ */
+static int ss_check_cno(struct nilfs_super_block *sbp,
+                       struct nilfs_segment_summary *ss_ptr,
+                       __u16 *check_mask)
+{
+       int err = NILFS_OK;
+
+       if (!(*check_mask & CHECK_SS_CNO))
+               return NILFS_OK; /* nothing to be checked */
+
+       if (le64_to_cpu(ss_ptr->ss_cno) > le64_to_cpu(sbp->s_last_cno))
+               err = INVALID_SS_CNO;
+       else
+               *check_mask &= ~CHECK_SS_CNO;
+
+       internal_debug("ss_cno %lld.",
+                               le64_to_cpu(ss_ptr->ss_cno));
+       internal_debug("check_mask %x.", *check_mask);
+
+       return (err > 0) ? -err : err;
+} /* ss_check_cno() */
+
+/*
+ * nilfs_ss_header_is_valid - Check that segment summary header is valid.
+ * @segment: segment sequence number.
+ * @sbp: pointer on superblock.
+ * @ss_ptr: pointer on segment summary header.
+ * @check_mask: mask that defines what should be checked and returns error.
+ *
+ * Return value:
+ * NILFS_TRUE - segment summary header is valid.
+ * NILFS_FALSE - segment summary header is in corrupted state.
+ *
+ * @check_mask contains mask of flags on items which should be checked.
+ * During checking flags of valid items are unset. At the end of execution
+ * @check_mask contains flags of corrupted items as set. These set flags
+ * inform about detected errors.
+ */
+int nilfs_ss_header_is_valid(__u64 segment,
+                               struct nilfs_super_block *sbp,
+                               struct nilfs_segment_summary *ss_ptr,
+                               __u16 *check_mask)
+{
+       int err = NILFS_OK;
+       int sub_err = NILFS_OK;
+
+       internal_debug("SEG: %lld, sbp %p, ss_ptr %p",
+                               segment, sbp, ss_ptr);
+       internal_debug("check_mask ptr %p", check_mask);
+       if (check_mask)
+               internal_debug("check_mask %x", *check_mask);
+
+       if (!sbp || !ss_ptr || !check_mask) {
+               internal_error("%s",
+                               nilfs_message[INVALID_PARAMETER]);
+               err = -INVALID_PARAMETER;
+               goto end_ss_check;
+       }
+
+       if (segment >= le64_to_cpu(sbp->s_nsegments)) {
+               internal_error("%s",
+                               nilfs_message[INVALID_PARAMETER]);
+               err = -INVALID_PARAMETER;
+               goto end_ss_check;
+       }
+
+       sub_err = ss_check_magic(ss_ptr, check_mask);
+       if (sub_err) {
+               err = sub_err;
+               *check_mask = CHECK_SS_MAGIC;
+               goto end_ss_check;
+       }
+
+       sub_err = ss_check_header_size(ss_ptr, check_mask);
+       if (sub_err) {
+               err = sub_err;
+               if (-UNSUPPORTED_SS_REV == sub_err)
+                       *check_mask = CHECK_SS_HDR_REV;
+               else
+                       *check_mask = CHECK_SS_BYTES;
+               goto end_ss_check;
+       }
+
+       sub_err = ss_check_segment_sequence_number(segment, sbp,
+                                                       ss_ptr, check_mask);
+       if (sub_err)
+               err = sub_err;
+
+       sub_err = ss_check_seg_used_blocks_number(sbp, ss_ptr, check_mask);
+       if (sub_err)
+               err = sub_err;
+
+       sub_err = ss_check_flags(ss_ptr, check_mask);
+       if (sub_err)
+               err = sub_err;
+
+       sub_err = ss_check_creation_timestamp(sbp, ss_ptr, check_mask);
+       if (sub_err)
+               err = sub_err;
+
+       sub_err = ss_check_next_seg_start_block(segment, sbp,
+                                               ss_ptr, check_mask);
+       if (sub_err)
+               err = sub_err;
+
+       sub_err = ss_check_nfifo(sbp, ss_ptr, check_mask);
+       if (sub_err)
+               err = sub_err;
+
+       sub_err = ss_check_cno(sbp, ss_ptr, check_mask);
+       if (sub_err)
+               err = sub_err;
+
+end_ss_check:
+       if (-UNSUPPORTED_SS_REV == err) {
+               internal_debug("SEG: %lld unsupported segment summary",
+                               segment);
+               return NILFS_TRUE;
+       } else if (err) {
+               internal_debug("SEG: %lld segment summary is corrupted",
+                               segment);
+               return NILFS_FALSE;
+       } else {
+               internal_debug("SEG: %lld segment summary is correct",
+                               segment);
+               return NILFS_TRUE;
+       }
+} /* nilfs_ss_header_is_valid() */
-- 
1.7.9.5



--
To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to