Hi, This patch adds nilfs_sb_read_unchecked() function for reading superblocks without checking. Moreover, it modifies nilfs_sb_is_valid() function with the purpose of making full superblock check. With the best regards, Vyacheslav Dubeyko. -- From: Vyacheslav Dubeyko <slava@xxxxxxxxxxx> Subject: [PATCH v4 06/15] nilfs-utils: fsck: NILFS superblock check implementation This patch adds nilfs_sb_read_unchecked() function for reading superblocks without checking. Moreover, it modifies nilfs_sb_is_valid() function with the purpose of making full superblock check. Signed-off-by: Vyacheslav Dubeyko <slava@xxxxxxxxxxx> --- lib/sb.c | 1429 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1419 insertions(+), 10 deletions(-) diff --git a/lib/sb.c b/lib/sb.c index 94bccaf..286def9 100644 --- a/lib/sb.c +++ b/lib/sb.c @@ -30,6 +30,8 @@ #include <stdlib.h> #endif /* HAVE_STDLIB_H */ +#include <stddef.h> + #if HAVE_STRING_H #include <string.h> #endif /* HAVE_STRING_H */ @@ -60,9 +62,15 @@ #include <sys/ioctl.h> #endif /* HAVE_SYS_IOCTL_H */ +#include <time.h> +#include <pwd.h> +#include <grp.h> #include <errno.h> #include <assert.h> + #include "nilfs.h" +#include "nilfs_messages.h" +#include "fsck_nilfs2.h" #define NILFS_MAX_SB_SIZE 1024 @@ -81,26 +89,1374 @@ static __u32 nilfs_sb_check_sum(struct nilfs_super_block *sbp) return crc; } -static int nilfs_sb_is_valid(struct nilfs_super_block *sbp, int check_crc) +/* + * sb_check_magic - Check magic signature of superblock. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no internal errors were occured. + * %-INVALID_NILFS_SIGNATURE - superblock signature is invalid. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_magic(struct nilfs_super_block *sbp, + __u64 *check_mask) { - __u32 crc; + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_MAGIC)) + return NILFS_OK; /* nothing to be checked */ if (le16_to_cpu(sbp->s_magic) != NILFS_SUPER_MAGIC) - return 0; - if (le16_to_cpu(sbp->s_bytes) > NILFS_MAX_SB_SIZE) - return 0; - if (!check_crc) - return 1; + err = INVALID_NILFS_SIGNATURE; + else + *check_mask &= ~CHECK_SB_MAGIC; + + internal_debug("s_magic %#x.", + le16_to_cpu(sbp->s_magic)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_size - Check size of superblock in bytes. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no internal errors were occured. + * %-UNSUPPORTED_SB_REV - detected unsupported superblock revision. + * %-INVALID_SB_SIZE - superblock contains incorrect size in bytes. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_size(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u16 sb_size; + + if (!(*check_mask & CHECK_SB_BYTES)) + return NILFS_OK; /* nothing to be checked */ + + sb_size = le16_to_cpu(sbp->s_bytes); + + if (sb_size > sizeof(struct nilfs_super_block)) + err = INVALID_SB_SIZE; + else if (sb_size < + offsetof(struct nilfs_super_block, s_reserved)) + err = INVALID_SB_SIZE; + else if (sb_size > + offsetof(struct nilfs_super_block, s_reserved)) { + err = UNSUPPORTED_SB_REV; + *check_mask &= ~CHECK_SB_BYTES; + *check_mask |= CHECK_SB_REV_LEVEL; + } else /* superblock size is correct */ + *check_mask &= ~CHECK_SB_BYTES; + + internal_debug("s_bytes %d.", sb_size); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_crc32 - Check CRC32 of superblock. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no internal errors were occured. + * %-INVALID_CRC - calculated and keeped in superblock CRC32 are not identical. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_crc32(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u32 crc; + + if (!(*check_mask & CHECK_SB_CRC)) + return NILFS_OK; /* nothing to be checked */ crc = nilfs_sb_check_sum(sbp); - return crc == le32_to_cpu(sbp->s_sum); + if (!(crc == le32_to_cpu(sbp->s_sum))) { + internal_debug("calculated CRC %d != on-disk CRC %d.", + crc, le32_to_cpu(sbp->s_sum)); + err = INVALID_CRC; + } else + *check_mask &= ~CHECK_SB_CRC; + + return (err > 0) ? -err : err; +} + +/* + * sb_check_rev_level - Check revision level of superblock. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no internal errors were occured. + * %-UNSUPPORTED_SB_REV - detected unsupported superblock revision. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_rev_level(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_REV_LEVEL)) + return NILFS_OK; /* nothing to be checked */ + + if (NILFS_MIN_SUPP_REV < le32_to_cpu(sbp->s_rev_level) || + NILFS_MINOR_REV < le16_to_cpu(sbp->s_minor_rev_level)) + err = UNSUPPORTED_SB_REV; + else + *check_mask &= ~CHECK_SB_REV_LEVEL; + + internal_debug("s_rev_level %d.", + le32_to_cpu(sbp->s_rev_level)); + internal_debug("s_minor_rev_level %d.", + le16_to_cpu(sbp->s_minor_rev_level)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_feature_set - Check compatible, RO and incompatible feature set. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no internal errors were occured. + * %-UNSUPPORTED_SB_REV - detected unsupported superblock revision. + * %-UNSUPPORTED_SB_RO_FEATURE - detected unsupported RO feature flags. + * %-INVALID_SB_FEATURE - detected invalid set of feature flags. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_feature_set(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_FEATURE)) + return NILFS_OK; /* nothing to be checked */ + + if (le64_to_cpu(sbp->s_feature_incompat) & + ~NILFS_FEATURE_INCOMPAT_SUPP) { + err = UNSUPPORTED_SB_REV; + *check_mask &= ~CHECK_SB_FEATURE; + *check_mask |= CHECK_SB_REV_LEVEL; + } else + *check_mask &= ~CHECK_SB_FEATURE; + + if (le64_to_cpu(sbp->s_feature_compat_ro) & + ~NILFS_FEATURE_COMPAT_RO_SUPP) { + if (err == NILFS_OK) + err = UNSUPPORTED_SB_RO_FEATURE; + } else + *check_mask &= ~CHECK_SB_RO_FEATURE; + + internal_debug("s_feature_compat %lld.", + le64_to_cpu(sbp->s_feature_compat)); + internal_debug("s_feature_compat_ro %lld.", + le64_to_cpu(sbp->s_feature_compat_ro)); + internal_debug("s_feature_incompat %lld.", + le64_to_cpu(sbp->s_feature_incompat)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_fs_flags - Check file system independent flags in superblock. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_FLAGS - invalid set of fs independent flags. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_fs_flags(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_FLAGS)) + return NILFS_OK; /* nothing to be checked */ + + if (sbp->s_flags) + err = INVALID_SB_FLAGS; + else + *check_mask &= ~CHECK_SB_FLAGS; + + internal_debug("s_flags %d.", + le16_to_cpu(sbp->s_flags)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_block_size - Check block size value in superblock. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_BLOCK_SIZE - superblock contains invalid block size value. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_block_size(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + size_t block_size; + + if (!(*check_mask & CHECK_SB_BLOCK_SZ)) + return NILFS_OK; /* nothing to be checked */ + + block_size = + 1UL << (le32_to_cpu(sbp->s_log_block_size) + + NILFS_SB_BLOCK_SIZE_SHIFT); + + if (NILFS_MIN_BLOCK_SIZE > block_size || + NILFS_MAX_BLOCK_SIZE < block_size) + err = INVALID_BLOCK_SIZE; + else + *check_mask &= ~CHECK_SB_BLOCK_SZ; + + internal_debug("s_log_block_size %d.", + le32_to_cpu(sbp->s_log_block_size)); + internal_debug("block size %ld.", block_size); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_device_size - Check device size value in superblock. + * @devfd: file descriptor of opened device. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-OUTSIZED_DEV_SIZE - superblock keeps value that greater of partition size. + * %-UNDERSIZED_DEV_SIZE - superblock keeps value that lesser of partition size. + * %-OP_FAILED - cannot check device size because of internal error. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_device_size(int devfd, + struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u64 dev_size, sb_dev_size; + + if (!(*check_mask & CHECK_SB_DEV_SZ)) + return NILFS_OK; /* nothing to be checked */ + + if (ioctl(devfd, BLKGETSIZE64, &dev_size) != 0) { + internal_perror("%s", nilfs_message[OP_FAILED]); + return -OP_FAILED; + } + + sb_dev_size = le64_to_cpu(sbp->s_dev_size); + + if (sb_dev_size < dev_size) + err = UNDERSIZED_DEV_SIZE; + else if (sb_dev_size > dev_size) + err = OUTSIZED_DEV_SIZE; + else + *check_mask &= ~CHECK_SB_DEV_SZ; + + internal_debug("device size %lld.", dev_size); + internal_debug("s_dev_size %lld.", sb_dev_size); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_blocks_per_segment - Check number of blocks per full segment. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_BLOCKS_PER_SEG - invalid value of blocks per segment. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_blocks_per_segment(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u32 sb_blks_per_seg; + size_t sb_block_size; + __u64 reserved_segs_size; + + if (!(*check_mask & CHECK_SB_BLOCKS_PER_SEG)) + return NILFS_OK; /* nothing to be checked */ + + sb_blks_per_seg = le32_to_cpu(sbp->s_blocks_per_segment); + sb_block_size = + 1UL << (le32_to_cpu(sbp->s_log_block_size) + + NILFS_SB_BLOCK_SIZE_SHIFT); + reserved_segs_size = sb_blks_per_seg * sb_block_size * + NILFS_MIN_NRSVSEGS; + + if (sb_blks_per_seg < NILFS_SEG_MIN_BLOCKS) + err = INVALID_SB_BLOCKS_PER_SEG; + else if (reserved_segs_size >= + le64_to_cpu(sbp->s_dev_size)) + err = INVALID_SB_BLOCKS_PER_SEG; + else + *check_mask &= ~CHECK_SB_BLOCKS_PER_SEG; + + internal_debug("s_blocks_per_segment %d.", + sb_blks_per_seg); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_nsegments - Check number of segments in file system. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_NSEGMENTS - invalid number of segments in filesystem. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_nsegments(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u64 sb_nsegments; + __u64 calculated_nsegs; + __u32 segment_size; + __u64 dev_size; + + if (!(*check_mask & CHECK_SB_NSEGMENTS)) + return NILFS_OK; /* nothing to be checked */ + + sb_nsegments = le64_to_cpu(sbp->s_nsegments); + segment_size = le32_to_cpu(sbp->s_blocks_per_segment) * + (1UL << (le32_to_cpu(sbp->s_log_block_size) + + NILFS_SB_BLOCK_SIZE_SHIFT)); + dev_size = le64_to_cpu(sbp->s_dev_size); + calculated_nsegs = NILFS_SB2_OFFSET_BYTES(dev_size) / segment_size; + + if (sb_nsegments <= NILFS_MIN_NRSVSEGS) + err = INVALID_SB_NSEGMENTS; + else if (sb_nsegments != calculated_nsegs) + err = INVALID_SB_NSEGMENTS; + else + *check_mask &= ~CHECK_SB_NSEGMENTS; + + internal_debug("s_nsegments %lld.", sb_nsegments); + internal_debug("calculated segments number %lld.", + calculated_nsegs); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_first_data_block - Check first segment disk block number. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_FIRST_DATA_BLOCK - invalid value of first data block. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_first_data_block(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + size_t sb_block_size; + + if (!(*check_mask & CHECK_SB_FIRST_DATA_BLOCK)) + return NILFS_OK; /* nothing to be checked */ + + sb_block_size = + 1UL << (le32_to_cpu(sbp->s_log_block_size) + + NILFS_SB_BLOCK_SIZE_SHIFT); + + if (le64_to_cpu(sbp->s_first_data_block) != + ROUNDUP_DIV(NILFS_SB_OFFSET_BYTES + + sizeof(struct nilfs_super_block), + sb_block_size)) + err = INVALID_SB_FIRST_DATA_BLOCK; + else + *check_mask &= ~CHECK_SB_FIRST_DATA_BLOCK; + + internal_debug("s_first_data_block %lld.", + le64_to_cpu(sbp->s_first_data_block)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_r_segs_percentage - Check reserved segments percentage. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_R_SEGS_PERCENTAGE - invalid reserved segments percentage. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_r_segs_percentage(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u32 r_segs_percentage; + __u64 reserved_segs; + + if (!(*check_mask & CHECK_SB_R_SEGS_PERCENT)) + return NILFS_OK; /* nothing to be checked */ + + r_segs_percentage = le32_to_cpu(sbp->s_r_segments_percentage); + reserved_segs = (le64_to_cpu(sbp->s_nsegments) * + r_segs_percentage + 99) / 100; + + if (r_segs_percentage >= 100 || + reserved_segs < NILFS_MIN_NRSVSEGS) + err = INVALID_SB_R_SEGS_PERCENTAGE; + else + *check_mask &= ~CHECK_SB_R_SEGS_PERCENT; + + internal_debug("s_r_segments_percentage %d.", + r_segs_percentage); + internal_debug("calculated reserved segments %lld.", + reserved_segs); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_lite_check_last_cno - Check last checkpoint number. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_LAST_CNO - invalid last checkpoint number. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_lite_check_last_cno(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_LAST_CNO)) + return NILFS_OK; /* nothing to be checked */ + + if (le64_to_cpu(sbp->s_last_cno) < NILFS_CNO_MIN) + err = INVALID_SB_LAST_CNO; + else + *check_mask &= ~CHECK_SB_LAST_CNO; + + internal_debug("s_last_cno %lld.", + le64_to_cpu(sbp->s_last_cno)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_last_pseg - Check pseg start block. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_LAST_PSEG - invalid disk block of partial segment. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_last_pseg(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u64 last_pseg; + __u32 blks_per_seg; + __u64 nsegs; + __u64 first_data_block; + + if (!(*check_mask & CHECK_SB_LAST_PSEG)) + return NILFS_OK; /* nothing to be checked */ + + last_pseg = le64_to_cpu(sbp->s_last_pseg); + blks_per_seg = le32_to_cpu(sbp->s_blocks_per_segment); + nsegs = le64_to_cpu(sbp->s_nsegments); + first_data_block = le64_to_cpu(sbp->s_first_data_block); + + if (last_pseg >= (nsegs * blks_per_seg) || + last_pseg < first_data_block) + err = INVALID_SB_LAST_PSEG; + else + *check_mask &= ~CHECK_SB_LAST_PSEG; + + internal_debug("s_last_pseg %lld.", last_pseg); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_last_seq - Check lastly written segment sequential number. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_LAST_SEQ - invalid sequential number of partial segment. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_last_seq(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u64 last_seq; + + if (!(*check_mask & CHECK_SB_LAST_SEQ)) + return NILFS_OK; /* nothing to be checked */ + + last_seq = le64_to_cpu(sbp->s_last_seq); + + /*INVALID_SB_LAST_SEQ*/ + + *check_mask &= ~CHECK_SB_LAST_SEQ; + + internal_debug("s_last_seq %lld.", last_seq); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_lite_check_free_blks_count - Preliminary check of free blocks count. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_FREE_BLKS - invalid free blocks count. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_lite_check_free_blks_count(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u64 free_blks; + __u64 nsegs; + __u32 blks_per_seg; + __u32 r_segs_percentage; + __u64 reserved_segs; + + if (!(*check_mask & CHECK_SB_FREE_BLKS)) + return NILFS_OK; /* nothing to be checked */ + + free_blks = le64_to_cpu(sbp->s_free_blocks_count); + blks_per_seg = le32_to_cpu(sbp->s_blocks_per_segment); + nsegs = le64_to_cpu(sbp->s_nsegments); + r_segs_percentage = le32_to_cpu(sbp->s_r_segments_percentage); + reserved_segs = (nsegs * r_segs_percentage + 99) / 100; + + if (free_blks > ((nsegs - 1) * blks_per_seg) || + free_blks < (reserved_segs * blks_per_seg)) + err = INVALID_SB_FREE_BLKS; + else + *check_mask &= ~CHECK_SB_FREE_BLKS; + + internal_debug("s_free_blocks_count %lld.", free_blks); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_timestamps - Check timestamps. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_TIMES - superblock keeps invalid timestamps. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_timestamps(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + time_t current_time; + __u64 sb_ctime, sb_mtime, sb_wtime; + __u16 mount_cnt; + + if (!(*check_mask & CHECK_SB_TIMES)) + return NILFS_OK; /* nothing to be checked */ + + current_time = time(NULL); + sb_ctime = le64_to_cpu(sbp->s_ctime); + sb_mtime = le64_to_cpu(sbp->s_mtime); + sb_wtime = le64_to_cpu(sbp->s_wtime); + mount_cnt = le16_to_cpu(sbp->s_mnt_count); + + if (0 == mount_cnt) { + if (!(sb_ctime <= sb_wtime) || + !(sb_ctime < current_time) || + !(sb_wtime < current_time)) { + err = INVALID_SB_TIMES; + } else + *check_mask &= ~CHECK_SB_TIMES; + } else { + if (!(sb_ctime < sb_mtime) || + !(sb_mtime < sb_wtime) || + !(sb_ctime < sb_mtime) || + !(sb_ctime < current_time) || + !(sb_mtime < current_time) || + !(sb_wtime < current_time)) + err = INVALID_SB_TIMES; + else + *check_mask &= ~CHECK_SB_TIMES; + } + + internal_debug("current time %s.", + ctime(¤t_time)); + internal_debug("creation time s_ctime %s.", + ctime((const time_t *)&sb_ctime)); + internal_debug("mount time s_mtime %s.", + ctime((const time_t *)&sb_mtime)); + internal_debug("write time s_wtime %s.", + ctime((const time_t *)&sb_wtime)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_fs_state_flags - Check file system state flags. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_FS_STATE - invalid file system state flags were detected. + * %-NOT_CLEAN_UMOUNT_DETECTED - NILFS had been umounted not cleanly. + * %-FS_ERRORS_DETECTED_BY_DRIVER - FS errors detected by drivers. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_fs_state_flags(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u16 known_fs_state_flags = + NILFS_VALID_FS | NILFS_ERROR_FS | NILFS_RESIZE_FS; + __u16 sb_state; + + if (!(*check_mask & CHECK_SB_STATE) && + !(*check_mask & CHECK_SB_CLEAN_UMOUNT_FLAG) && + !(*check_mask & CHECK_SB_ERRS_DETECTED_FLAG)) + return NILFS_OK; /* nothing to be checked */ + + sb_state = le16_to_cpu(sbp->s_state); + + if (*check_mask & CHECK_SB_ERRS_DETECTED_FLAG) { + if (sb_state & NILFS_ERROR_FS) + err = FS_ERRORS_DETECTED_BY_DRIVER; + else + *check_mask &= ~CHECK_SB_ERRS_DETECTED_FLAG; + } + + if (*check_mask & CHECK_SB_STATE) { + if (sb_state & ~known_fs_state_flags) { + if (NILFS_OK == err) + err = INVALID_SB_FS_STATE; + } else + *check_mask &= ~CHECK_SB_STATE; + } + + if (*check_mask & CHECK_SB_CLEAN_UMOUNT_FLAG) { + if (!(sb_state & NILFS_VALID_FS)) { + if (NILFS_OK == err) + err = NOT_CLEAN_UMOUNT_DETECTED; + } else + *check_mask &= ~CHECK_SB_CLEAN_UMOUNT_FLAG; + } + + internal_debug("s_state %d.", + le16_to_cpu(sbp->s_state)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_errors_flag - Check s_errors flag. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_FS_ERRORS - invalid behavior in the case of errors detection. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_errors_flag(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_ERRORS)) + return NILFS_OK; /* nothing to be checked */ + + if (le16_to_cpu(sbp->s_errors) != NILFS_ERRORS_CONTINUE) + err = INVALID_SB_FS_ERRORS; + else + *check_mask &= ~CHECK_SB_ERRORS; + + internal_debug("s_errors %d.", + le16_to_cpu(sbp->s_errors)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_lastcheck - Check lastcheck timestamp. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_LAST_CHECK - invalid timestamp of last check. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_lastcheck(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + time_t current_time; + __u64 sb_ctime, sb_lastcheck; + + if (!(*check_mask & CHECK_SB_LAST_CHECK)) + return NILFS_OK; /* nothing to be checked */ + + current_time = time(NULL); + sb_ctime = le64_to_cpu(sbp->s_ctime); + sb_lastcheck = le64_to_cpu(sbp->s_lastcheck); + + if (!(sb_ctime <= sb_lastcheck) || !(sb_lastcheck < current_time)) + err = INVALID_SB_TIMES; + else + *check_mask &= ~CHECK_SB_LAST_CHECK; + + internal_debug("current time %s.", + ctime(¤t_time)); + internal_debug("creation time s_ctime %s.", + ctime((const time_t *)&sb_ctime)); + internal_debug("s_lastcheck %s.", + ctime((const time_t *)&sb_lastcheck)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_mnt_count - Compare mount count and max possible mount count. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-MAX_POSSIBLE_MNT_COUNT - Maximum possible mount count has been reached. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_mnt_count(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_MNT_COUNT)) + return NILFS_OK; /* nothing to be checked */ + + if (le16_to_cpu(sbp->s_mnt_count) >= + le16_to_cpu(sbp->s_max_mnt_count)) + err = MAX_POSSIBLE_MNT_COUNT; + else + *check_mask &= ~CHECK_SB_MNT_COUNT; + + internal_debug("mount count %d.", + le16_to_cpu(sbp->s_mnt_count)); + internal_debug("max mount count %d.", + le16_to_cpu(sbp->s_max_mnt_count)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_creator_os - Check code for OS. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_OS - invalid code of creator OS. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_creator_os(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u32 creator_os; + + if (!(*check_mask & CHECK_SB_CREATOR_OS)) + return NILFS_OK; /* nothing to be checked */ + + creator_os = le32_to_cpu(sbp->s_creator_os); + + /* Code for Linux is 0. Codes from 1 to 4 are reserved + to keep compatibility with ext2 creator-OS */ + if (creator_os > 4) + err = INVALID_SB_OS; + else + *check_mask &= ~CHECK_SB_CREATOR_OS; + + internal_debug("s_creator_os %d.", creator_os); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_res_uid_gid - Check default UID and GUID for reserved blocks. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_DEF_ID - invalid default UID or/and GID for reserved blocks. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_res_uid_gid(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u16 def_resuid, def_resgid; + struct passwd *found_passwd_ptr; + struct group *found_group_ptr; + + if (!(*check_mask & CHECK_SB_DEF_RES_UID_GID)) + return NILFS_OK; /* nothing to be checked */ + + def_resuid = le16_to_cpu(sbp->s_def_resuid); + def_resgid = le16_to_cpu(sbp->s_def_resgid); + + errno = 0; + found_passwd_ptr = getpwuid(def_resuid); + if (!found_passwd_ptr) { + err = INVALID_SB_DEF_ID; + internal_perror("%s", "Can't find user ID."); + } + + errno = 0; + found_group_ptr = getgrgid(def_resgid); + if (!found_group_ptr) { + err = INVALID_SB_DEF_ID; + internal_perror("%s", "Can't find group ID."); + } + + if (NILFS_OK == err) + *check_mask &= ~CHECK_SB_DEF_RES_UID_GID; + + internal_debug("s_def_resuid %d.", def_resuid); + internal_debug("s_def_resgid %d.", def_resgid); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_first_ino - Check first user's file inode number. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_FIRST_INO - invalid first user's file inode number. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_first_ino(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_FIRST_INO)) + return NILFS_OK; /* nothing to be checked */ + + if (le32_to_cpu(sbp->s_first_ino) != NILFS_USER_INO) + err = INVALID_SB_FIRST_INO; + else + *check_mask &= ~CHECK_SB_FIRST_INO; + + internal_debug("s_first_ino %d.", + le32_to_cpu(sbp->s_first_ino)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_inode_size - Check size of on-disk inode. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_INO_SZ - invalid size of on-disk inode. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_inode_size(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_INODE_SZ)) + return NILFS_OK; /* nothing to be checked */ + + if (le16_to_cpu(sbp->s_inode_size) != sizeof(struct nilfs_inode)) + err = INVALID_SB_INO_SZ; + else + *check_mask &= ~CHECK_SB_INODE_SZ; + + internal_debug("s_inode_size %d.", + le16_to_cpu(sbp->s_inode_size)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_dat_entry_size - Check DAT entry size. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_DAT_ENTRY_SZ - invalid DAT entry size. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_dat_entry_size(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_DAT_ENTRY_SZ)) + return NILFS_OK; /* nothing to be checked */ + + if (le16_to_cpu(sbp->s_dat_entry_size) != + sizeof(struct nilfs_dat_entry)) + err = INVALID_SB_DAT_ENTRY_SZ; + else + *check_mask &= ~CHECK_SB_DAT_ENTRY_SZ; + + internal_debug("s_dat_entry_size %d.", + le16_to_cpu(sbp->s_dat_entry_size)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_checkpoint_size - Check size of a checkpoint. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_CHECKPOINT_SZ - invalid size of a checkpoint. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_checkpoint_size(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_CHECKPOINT_SZ)) + return NILFS_OK; /* nothing to be checked */ + + if (le16_to_cpu(sbp->s_checkpoint_size) != + sizeof(struct nilfs_checkpoint)) + err = INVALID_SB_CHECKPOINT_SZ; + else + *check_mask &= ~CHECK_SB_CHECKPOINT_SZ; + + internal_debug("s_checkpoint_size %d.", + le16_to_cpu(sbp->s_checkpoint_size)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_segment_usage_size - Check size of a segment usage. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_SEG_USAGE_SZ - invalid size of a segment usage. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_segment_usage_size(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_SEG_USAGE_SZ)) + return NILFS_OK; /* nothing to be checked */ + + if (le16_to_cpu(sbp->s_segment_usage_size) != + sizeof(struct nilfs_segment_usage)) + err = INVALID_SB_SEG_USAGE_SZ; + else + *check_mask &= ~CHECK_SB_SEG_USAGE_SZ; + + internal_debug("s_segment_usage_size %d.", + le16_to_cpu(sbp->s_segment_usage_size)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_commit_seg_interval - Check commit interval of segment. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_C_INTERVAL - invalid value of commit interval of segment. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_commit_seg_interval(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + + if (!(*check_mask & CHECK_SB_C_INTERVAL)) + return NILFS_OK; /* nothing to be checked */ + + if (le32_to_cpu(sbp->s_c_interval) > 10*60) + err = INVALID_SB_C_INTERVAL; + else + *check_mask &= ~CHECK_SB_C_INTERVAL; + + internal_debug("s_c_interval %d.", + le32_to_cpu(sbp->s_c_interval)); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * sb_check_commit_blks_max - Check threshold of data for segment construction. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-INVALID_SB_C_BLK_MAX - invalid threshold of data for segment construction. + * + * In the case of valid successfull call @check_mask keeps + * flags with detected errors. + */ +static int sb_check_commit_blks_max(struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + __u32 commit_blks_max; + __u32 blks_per_seg; + __u64 nsegs; + + if (!(*check_mask & CHECK_SB_C_BLOCK_MAX)) + return NILFS_OK; /* nothing to be checked */ + + commit_blks_max = le32_to_cpu(sbp->s_c_block_max); + blks_per_seg = le32_to_cpu(sbp->s_blocks_per_segment); + nsegs = le64_to_cpu(sbp->s_nsegments); + + if (commit_blks_max) { + if (commit_blks_max < blks_per_seg || + commit_blks_max >= (nsegs * blks_per_seg)) + err = INVALID_SB_C_BLK_MAX; + } else + *check_mask &= ~CHECK_SB_C_BLOCK_MAX; + + internal_debug("s_c_block_max %d.", commit_blks_max); + internal_debug("check_mask %llx.", *check_mask); + + return (err > 0) ? -err : err; +} + +/* + * nilfs_sb_is_valid - Check that superblock is valid. + * @devfd: file descriptor of opened device. + * @sbp: pointer on superblock. + * @check_mask: mask that defines what should be checked and returns error. + * + * Return value: + * NILFS_TRUE - superblock is valid. + * NILFS_FALSE - superblock 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 working + * @check_mask contains flags of corrupted items as set. These set flags + * inform about detected errors. + */ +int nilfs_sb_is_valid(int devfd, + struct nilfs_super_block *sbp, + __u64 *check_mask) +{ + int err = NILFS_OK; + int err2; + + internal_debug("devfd %d", devfd); + internal_debug("sbp %p", sbp); + internal_debug("check_mask ptr %p", check_mask); + if (check_mask) + internal_debug("check_mask %lld", *check_mask); + + if (!sbp || !check_mask) { + internal_error("%s", nilfs_message[OP_FAILED]); + return NILFS_FALSE; + } + + err = sb_check_magic(sbp, check_mask); + if (err) { + *check_mask = CHECK_SB_MAGIC; + goto end_check; + } + + err = sb_check_size(sbp, check_mask); + if (err) { + *check_mask = CHECK_SB_BYTES; + goto end_check; + } + + /* Here function may be finished for the compatibility + with previous way of function using */ + if (!(*check_mask & CHECK_SB_CRC)) + goto end_check; + + err = sb_check_crc32(sbp, check_mask); + if (err) { + *check_mask = CHECK_SB_CRC; + goto end_check; + } + + err = sb_check_rev_level(sbp, check_mask); + if (err) { + if (-UNSUPPORTED_SB_REV == err) { + *check_mask = CHECK_SB_REV_LEVEL; + goto end_check; + } + } + + /* Check s_feature_compat, s_feature_compat_ro, s_feature_incompat */ + err2 = sb_check_feature_set(sbp, check_mask); + if (err2) { + if (-UNSUPPORTED_SB_REV == err2) { + *check_mask = CHECK_SB_REV_LEVEL; + goto end_check; + } else + err = err2; + } + + err2 = sb_check_fs_flags(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_block_size(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_device_size(devfd, sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_blocks_per_segment(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_nsegments(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_first_data_block(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_r_segs_percentage(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_lite_check_last_cno(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_last_pseg(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_last_seq(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_lite_check_free_blks_count(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_timestamps(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_mnt_count(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_fs_state_flags(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_errors_flag(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_lastcheck(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_creator_os(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_res_uid_gid(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_first_ino(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_inode_size(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_dat_entry_size(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_checkpoint_size(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_segment_usage_size(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_commit_seg_interval(sbp, check_mask); + if (err2) + err = err2; + + err2 = sb_check_commit_blks_max(sbp, check_mask); + if (err2) + err = err2; + +end_check: + if (err) { + internal_debug("%s", "superblock corruption is detected"); + return NILFS_FALSE; + } else { + internal_debug("%s", "superblock is correct"); + return NILFS_TRUE; + } } static int __nilfs_sb_read(int devfd, struct nilfs_super_block **sbp, __u64 *offsets) { __u64 devsize, sb2_offset; + __u64 check_mask = CHECK_SB_MAGIC | CHECK_SB_BYTES; sbp[0] = malloc(NILFS_MAX_SB_SIZE); sbp[1] = malloc(NILFS_MAX_SB_SIZE); @@ -112,7 +1468,7 @@ static int __nilfs_sb_read(int devfd, struct nilfs_super_block **sbp, if (lseek(devfd, NILFS_SB_OFFSET_BYTES, SEEK_SET) < 0 || read(devfd, sbp[0], NILFS_MAX_SB_SIZE) < 0 || - !nilfs_sb_is_valid(sbp[0], 0)) { + !nilfs_sb_is_valid(devfd, sbp[0], &check_mask)) { free(sbp[0]); sbp[0] = NULL; } @@ -125,7 +1481,7 @@ static int __nilfs_sb_read(int devfd, struct nilfs_super_block **sbp, if (lseek(devfd, sb2_offset, SEEK_SET) < 0 || read(devfd, sbp[1], NILFS_MAX_SB_SIZE) < 0 || - !nilfs_sb_is_valid(sbp[1], 0)) + !nilfs_sb_is_valid(devfd, sbp[1], &check_mask)) goto sb2_failed; if (sb2_offset < @@ -169,6 +1525,59 @@ struct nilfs_super_block *nilfs_sb_read(int devfd) return sbp[0]; } +/* + * nilfs_sb_read_unchecked - Read superblocks without check. + * @devfd: file descriptor of opened device. + * @sbp: array of pointers on superblocks. + * + * Return value: + * NILFS_OK - no errors were detected. + * %-CANNOT_READ_SUPERBLOCK - cannot read superblock from disk. + * %-INVALID_SB_OFFSET - superblock offset has invalid value. + */ +int nilfs_sb_read_unchecked(int devfd, + struct nilfs_super_block **sbp) +{ + __u64 devsize, sb2_offset; + __u16 sb_size = sizeof(struct nilfs_super_block); + + internal_debug("devfd %d, sbp %p", devfd, sbp); + + if (sbp[0] == NULL || sbp[1] == NULL) + return 0; + + if (ioctl(devfd, BLKGETSIZE64, &devsize) != 0) + goto failed_read; + + if (sbp[0]) { + if (lseek(devfd, NILFS_SB_OFFSET_BYTES, SEEK_SET) < 0 || + read(devfd, sbp[0], sb_size) < 0) + goto failed_read; + } + + if (sbp[1]) { + sb2_offset = NILFS_SB2_OFFSET_BYTES(devsize); + if (sb2_offset <= (NILFS_SB_OFFSET_BYTES + sb_size)) + goto invalid_sb_offset; + + if (lseek(devfd, sb2_offset, SEEK_SET) < 0 || + read(devfd, sbp[1], sb_size) < 0) + goto failed_read; + } + + internal_debug("%s", "superblocks read successfully"); + return NILFS_OK; + +invalid_sb_offset: + internal_debug("%s", nilfs_message[INVALID_SB_OFFSET]); + return -INVALID_SB_OFFSET; + +failed_read: + internal_debug("%s", + nilfs_message[CANNOT_READ_SUPERBLOCK]); + return -CANNOT_READ_SUPERBLOCK; +} + int nilfs_sb_write(int devfd, struct nilfs_super_block *sbp, int mask) { __u64 offsets[2]; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html