+
+/*
+ * Check to see if we should backup the master sb to the backup super
+ * blocks. Returns non-zero if the sb should be backed up.
+ */
+
+/*
+ * A few flags are set on the fly by the kernel, but only in the
+ * primary superblock. This is actually a bad thing, and we should
+ * try to discourage it in the future. In particular, for the newer
+ * ext4 files, especially EXT4_FEATURE_RO_COMPAT_DIR_NLINK and
+ * EXT3_FEATURE_INCOMPAT_EXTENTS. So some of these may go away in the
+ * future. EXT3_FEATURE_INCOMPAT_RECOVER may also get set when
+ * copying the primary superblock during online resize.
+ *
+ * The kernel will set EXT2_FEATURE_COMPAT_EXT_ATTR, but
+ * unfortunately, we shouldn't ignore it since if it's not set in the
+ * backup, the extended attributes in the filesystem will be stripped
+ * away.
+ */
+#define FEATURE_RO_COMPAT_IGNORE (EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+ EXT4_FEATURE_RO_COMPAT_DIR_NLINK)
+#define FEATURE_INCOMPAT_IGNORE (EXT3_FEATURE_INCOMPAT_EXTENTS| \
+ EXT3_FEATURE_INCOMPAT_RECOVER)
+
+int check_backup_super_block(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+ dgrp_t g;
+ blk64_t sb;
+ int ret = 0;
+ char buf[SUPERBLOCK_SIZE];
+ struct ext2_super_block *backup_sb;
+
+ /*
+ * If we are already writing out the backup blocks, then we
+ * don't need to test. Also, if the filesystem is invalid, or
+ * the check was aborted or cancelled, we also don't want to
+ * do the backup. If the filesystem was opened read-only then
+ * we can't do the backup.
+ */
+ if (((fs->flags & EXT2_FLAG_MASTER_SB_ONLY) == 0) ||
+ !ext2fs_test_valid(fs) ||
+ (fs->super->s_state & EXT2_ERROR_FS) ||
+ (ctx->flags & (E2F_FLAG_ABORT | E2F_FLAG_CANCEL)) ||
+ (ctx->options & E2F_OPT_READONLY))
+ return 0;
+
+ for (g = 1; g < fs->group_desc_count; g++) {
+ if (!ext2fs_bg_has_super(fs, g))
+ continue;
+
+ sb = ext2fs_group_first_block2(fs, g);
+
+ retval = io_channel_read_blk(fs->io, sb, -SUPERBLOCK_SIZE,
+ buf);
+ if (retval)
+ continue;
+ backup_sb = (struct ext2_super_block *) buf;
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_super(backup_sb);
+#endif
+ if ((backup_sb->s_magic != EXT2_SUPER_MAGIC) ||
+ (backup_sb->s_rev_level > EXT2_LIB_CURRENT_REV) ||
+ ((backup_sb->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE) >
+ EXT2_MAX_BLOCK_LOG_SIZE) ||
+ (EXT2_INODE_SIZE(backup_sb) < EXT2_GOOD_OLD_INODE_SIZE))
+ continue;
+
+#define SUPER_INCOMPAT_DIFFERENT(x) \
+ ((fs->super->x & ~FEATURE_INCOMPAT_IGNORE) != \
+ (backup_sb->x & ~FEATURE_INCOMPAT_IGNORE))
+#define SUPER_RO_COMPAT_DIFFERENT(x) \
+ ((fs->super->x & ~FEATURE_RO_COMPAT_IGNORE) != \
+ (backup_sb->x & ~FEATURE_RO_COMPAT_IGNORE))
+#define SUPER_DIFFERENT(x) \
+ (fs->super->x != backup_sb->x)
+
+ if (SUPER_DIFFERENT(s_feature_compat) ||
+ SUPER_INCOMPAT_DIFFERENT(s_feature_incompat) ||
+ SUPER_RO_COMPAT_DIFFERENT(s_feature_ro_compat) ||
+ SUPER_DIFFERENT(s_blocks_count) ||
+ SUPER_DIFFERENT(s_blocks_count_hi) ||
+ SUPER_DIFFERENT(s_inodes_count) ||
+ memcmp(fs->super->s_uuid, backup_sb->s_uuid,
+ sizeof(fs->super->s_uuid)))
+ ret = 1;
+ break;
+ }
+ return ret;
+}