bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 1999, 2001 by Andries Brouwer |
| 3 | * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o |
| 4 | * Copyright (C) 2008 Karel Zak <kzak@redhat.com> |
| 5 | * |
| 6 | * This file may be redistributed under the terms of the |
| 7 | * GNU Lesser General Public License. |
| 8 | */ |
| 9 | #include <stdio.h> |
| 10 | #include <stdlib.h> |
| 11 | #include <unistd.h> |
| 12 | #include <string.h> |
| 13 | #include <errno.h> |
| 14 | #include <ctype.h> |
| 15 | #include <stdint.h> |
| 16 | #ifdef __linux__ |
| 17 | #include <sys/utsname.h> |
| 18 | #endif |
| 19 | #include <time.h> |
| 20 | |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 21 | #include "superblocks.h" |
| 22 | |
| 23 | struct ext2_super_block { |
| 24 | uint32_t s_inodes_count; |
| 25 | uint32_t s_blocks_count; |
| 26 | uint32_t s_r_blocks_count; |
| 27 | uint32_t s_free_blocks_count; |
| 28 | uint32_t s_free_inodes_count; |
| 29 | uint32_t s_first_data_block; |
| 30 | uint32_t s_log_block_size; |
| 31 | uint32_t s_dummy3[7]; |
| 32 | unsigned char s_magic[2]; |
| 33 | uint16_t s_state; |
| 34 | uint16_t s_errors; |
| 35 | uint16_t s_minor_rev_level; |
| 36 | uint32_t s_lastcheck; |
| 37 | uint32_t s_checkinterval; |
| 38 | uint32_t s_creator_os; |
| 39 | uint32_t s_rev_level; |
| 40 | uint16_t s_def_resuid; |
| 41 | uint16_t s_def_resgid; |
| 42 | uint32_t s_first_ino; |
| 43 | uint16_t s_inode_size; |
| 44 | uint16_t s_block_group_nr; |
| 45 | uint32_t s_feature_compat; |
| 46 | uint32_t s_feature_incompat; |
| 47 | uint32_t s_feature_ro_compat; |
| 48 | unsigned char s_uuid[16]; |
| 49 | char s_volume_name[16]; |
| 50 | char s_last_mounted[64]; |
| 51 | uint32_t s_algorithm_usage_bitmap; |
| 52 | uint8_t s_prealloc_blocks; |
| 53 | uint8_t s_prealloc_dir_blocks; |
| 54 | uint16_t s_reserved_gdt_blocks; |
| 55 | uint8_t s_journal_uuid[16]; |
| 56 | uint32_t s_journal_inum; |
| 57 | uint32_t s_journal_dev; |
| 58 | uint32_t s_last_orphan; |
| 59 | uint32_t s_hash_seed[4]; |
| 60 | uint8_t s_def_hash_version; |
| 61 | uint8_t s_jnl_backup_type; |
| 62 | uint16_t s_reserved_word_pad; |
| 63 | uint32_t s_default_mount_opts; |
| 64 | uint32_t s_first_meta_bg; |
| 65 | uint32_t s_mkfs_time; |
| 66 | uint32_t s_jnl_blocks[17]; |
| 67 | uint32_t s_blocks_count_hi; |
| 68 | uint32_t s_r_blocks_count_hi; |
| 69 | uint32_t s_free_blocks_hi; |
| 70 | uint16_t s_min_extra_isize; |
| 71 | uint16_t s_want_extra_isize; |
| 72 | uint32_t s_flags; |
| 73 | uint16_t s_raid_stride; |
| 74 | uint16_t s_mmp_interval; |
| 75 | uint64_t s_mmp_block; |
| 76 | uint32_t s_raid_stripe_width; |
| 77 | uint32_t s_reserved[163]; |
| 78 | } __attribute__((packed)); |
| 79 | |
| 80 | /* magic string */ |
| 81 | #define EXT_SB_MAGIC "\123\357" |
| 82 | /* supper block offset */ |
| 83 | #define EXT_SB_OFF 0x400 |
| 84 | /* supper block offset in kB */ |
| 85 | #define EXT_SB_KBOFF (EXT_SB_OFF >> 10) |
| 86 | /* magic string offset within super block */ |
| 87 | #define EXT_MAG_OFF 0x38 |
| 88 | |
| 89 | |
| 90 | |
| 91 | /* for s_flags */ |
| 92 | #define EXT2_FLAGS_TEST_FILESYS 0x0004 |
| 93 | |
| 94 | /* for s_feature_compat */ |
| 95 | #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 |
| 96 | |
| 97 | /* for s_feature_ro_compat */ |
| 98 | #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 |
| 99 | #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 |
| 100 | #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 |
| 101 | #define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 |
| 102 | #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 |
| 103 | #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 |
| 104 | #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 |
| 105 | |
| 106 | /* for s_feature_incompat */ |
| 107 | #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 |
| 108 | #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 |
| 109 | #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 |
| 110 | #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 |
| 111 | #define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */ |
| 112 | #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 |
| 113 | #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 |
| 114 | #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 |
| 115 | |
| 116 | #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ |
| 117 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ |
| 118 | EXT2_FEATURE_RO_COMPAT_BTREE_DIR) |
| 119 | #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ |
| 120 | EXT2_FEATURE_INCOMPAT_META_BG) |
| 121 | #define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP |
| 122 | #define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP |
| 123 | |
| 124 | #define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ |
| 125 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ |
| 126 | EXT2_FEATURE_RO_COMPAT_BTREE_DIR) |
| 127 | #define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ |
| 128 | EXT3_FEATURE_INCOMPAT_RECOVER| \ |
| 129 | EXT2_FEATURE_INCOMPAT_META_BG) |
| 130 | #define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP |
| 131 | #define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP |
| 132 | |
| 133 | /* |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 134 | * Starting in 2.6.29, ext4 can be used to support filesystems |
| 135 | * without a journal. |
| 136 | */ |
| 137 | #define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29) |
| 138 | |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 139 | /* |
| 140 | * reads superblock and returns: |
| 141 | * fc = feature_compat |
| 142 | * fi = feature_incompat |
| 143 | * frc = feature_ro_compat |
| 144 | */ |
| 145 | static struct ext2_super_block *ext_get_super( |
| 146 | blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc) |
| 147 | { |
| 148 | struct ext2_super_block *es; |
| 149 | |
| 150 | es = (struct ext2_super_block *) |
| 151 | blkid_probe_get_buffer(pr, EXT_SB_OFF, 0x200); |
| 152 | if (!es) |
| 153 | return NULL; |
| 154 | if (fc) |
| 155 | *fc = le32_to_cpu(es->s_feature_compat); |
| 156 | if (fi) |
| 157 | *fi = le32_to_cpu(es->s_feature_incompat); |
| 158 | if (frc) |
| 159 | *frc = le32_to_cpu(es->s_feature_ro_compat); |
| 160 | |
| 161 | return es; |
| 162 | } |
| 163 | |
| 164 | static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es) |
| 165 | { |
| 166 | struct blkid_chain *chn = blkid_probe_get_chain(pr); |
| 167 | |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 168 | DBG(PROBE, ul_debug("ext2_sb.compat = %08X:%08X:%08X", |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 169 | le32_to_cpu(es->s_feature_compat), |
| 170 | le32_to_cpu(es->s_feature_incompat), |
| 171 | le32_to_cpu(es->s_feature_ro_compat))); |
| 172 | |
| 173 | if (strlen(es->s_volume_name)) |
| 174 | blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name, |
| 175 | sizeof(es->s_volume_name)); |
| 176 | blkid_probe_set_uuid(pr, es->s_uuid); |
| 177 | |
| 178 | if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) |
| 179 | blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL"); |
| 180 | |
| 181 | if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) && |
| 182 | ((le32_to_cpu(es->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0)) |
| 183 | blkid_probe_set_value(pr, "SEC_TYPE", |
| 184 | (unsigned char *) "ext2", |
| 185 | sizeof("ext2")); |
| 186 | |
| 187 | blkid_probe_sprintf_version(pr, "%u.%u", |
| 188 | le32_to_cpu(es->s_rev_level), |
| 189 | le16_to_cpu(es->s_minor_rev_level)); |
| 190 | } |
| 191 | |
| 192 | |
| 193 | static int probe_jbd(blkid_probe pr, |
| 194 | const struct blkid_idmag *mag __attribute__((__unused__))) |
| 195 | { |
| 196 | struct ext2_super_block *es; |
| 197 | uint32_t fi; |
| 198 | |
| 199 | es = ext_get_super(pr, NULL, &fi, NULL); |
| 200 | if (!es) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 201 | return errno ? -errno : 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 202 | if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 203 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 204 | |
| 205 | ext_get_info(pr, 2, es); |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 206 | blkid_probe_set_uuid_as(pr, es->s_uuid, "LOGUUID"); |
| 207 | |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 208 | return 0; |
| 209 | } |
| 210 | |
| 211 | static int probe_ext2(blkid_probe pr, |
| 212 | const struct blkid_idmag *mag __attribute__((__unused__))) |
| 213 | { |
| 214 | struct ext2_super_block *es; |
| 215 | uint32_t fc, frc, fi; |
| 216 | |
| 217 | es = ext_get_super(pr, &fc, &fi, &frc); |
| 218 | if (!es) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 219 | return errno ? -errno : 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 220 | |
| 221 | /* Distinguish between ext3 and ext2 */ |
| 222 | if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 223 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 224 | |
| 225 | /* Any features which ext2 doesn't understand */ |
| 226 | if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) || |
| 227 | (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED)) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 228 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 229 | |
| 230 | ext_get_info(pr, 2, es); |
| 231 | return 0; |
| 232 | } |
| 233 | |
| 234 | static int probe_ext3(blkid_probe pr, |
| 235 | const struct blkid_idmag *mag __attribute__((__unused__))) |
| 236 | { |
| 237 | struct ext2_super_block *es; |
| 238 | uint32_t fc, frc, fi; |
| 239 | |
| 240 | es = ext_get_super(pr, &fc, &fi, &frc); |
| 241 | if (!es) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 242 | return errno ? -errno : 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 243 | |
| 244 | /* ext3 requires journal */ |
| 245 | if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 246 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 247 | |
| 248 | /* Any features which ext3 doesn't understand */ |
| 249 | if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) || |
| 250 | (fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 251 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 252 | |
| 253 | ext_get_info(pr, 3, es); |
| 254 | return 0; |
| 255 | } |
| 256 | |
| 257 | |
| 258 | static int probe_ext4dev(blkid_probe pr, |
| 259 | const struct blkid_idmag *mag __attribute__((__unused__))) |
| 260 | { |
| 261 | struct ext2_super_block *es; |
| 262 | uint32_t fc, frc, fi; |
| 263 | |
| 264 | es = ext_get_super(pr, &fc, &fi, &frc); |
| 265 | if (!es) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 266 | return errno ? -errno : 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 267 | |
| 268 | /* Distinguish from jbd */ |
| 269 | if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 270 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 271 | |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 272 | if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS)) |
| 273 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 274 | |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 275 | ext_get_info(pr, 4, es); |
| 276 | return 0; |
| 277 | } |
| 278 | |
| 279 | static int probe_ext4(blkid_probe pr, |
| 280 | const struct blkid_idmag *mag __attribute__((__unused__))) |
| 281 | { |
| 282 | struct ext2_super_block *es; |
| 283 | uint32_t fc, frc, fi; |
| 284 | |
| 285 | es = ext_get_super(pr, &fc, &fi, &frc); |
| 286 | if (!es) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 287 | return errno ? -errno : 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 288 | |
| 289 | /* Distinguish from jbd */ |
| 290 | if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 291 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 292 | |
| 293 | /* Ext4 has at least one feature which ext3 doesn't understand */ |
| 294 | if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) && |
| 295 | !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 296 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 297 | |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 298 | /* |
| 299 | * If the filesystem is a OK for use by in-development |
| 300 | * filesystem code, and ext4dev is supported or ext4 is not |
| 301 | * supported, then don't call ourselves ext4, so we can redo |
| 302 | * the detection and mark the filesystem as ext4dev. |
| 303 | * |
| 304 | * If the filesystem is marked as in use by production |
| 305 | * filesystem, then it can only be used by ext4 and NOT by |
| 306 | * ext4dev. |
| 307 | */ |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 308 | if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) |
| 309 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 310 | |
| 311 | ext_get_info(pr, 4, es); |
| 312 | return 0; |
| 313 | } |
| 314 | |
| 315 | #define BLKID_EXT_MAGICS \ |
| 316 | { \ |
| 317 | { \ |
| 318 | .magic = EXT_SB_MAGIC, \ |
| 319 | .len = sizeof(EXT_SB_MAGIC) - 1, \ |
| 320 | .kboff = EXT_SB_KBOFF, \ |
| 321 | .sboff = EXT_MAG_OFF \ |
| 322 | }, \ |
| 323 | { NULL } \ |
| 324 | } |
| 325 | |
| 326 | const struct blkid_idinfo jbd_idinfo = |
| 327 | { |
| 328 | .name = "jbd", |
| 329 | .usage = BLKID_USAGE_OTHER, |
| 330 | .probefunc = probe_jbd, |
| 331 | .magics = BLKID_EXT_MAGICS |
| 332 | }; |
| 333 | |
| 334 | const struct blkid_idinfo ext2_idinfo = |
| 335 | { |
| 336 | .name = "ext2", |
| 337 | .usage = BLKID_USAGE_FILESYSTEM, |
| 338 | .probefunc = probe_ext2, |
| 339 | .magics = BLKID_EXT_MAGICS |
| 340 | }; |
| 341 | |
| 342 | const struct blkid_idinfo ext3_idinfo = |
| 343 | { |
| 344 | .name = "ext3", |
| 345 | .usage = BLKID_USAGE_FILESYSTEM, |
| 346 | .probefunc = probe_ext3, |
| 347 | .magics = BLKID_EXT_MAGICS |
| 348 | }; |
| 349 | |
| 350 | const struct blkid_idinfo ext4_idinfo = |
| 351 | { |
| 352 | .name = "ext4", |
| 353 | .usage = BLKID_USAGE_FILESYSTEM, |
| 354 | .probefunc = probe_ext4, |
| 355 | .magics = BLKID_EXT_MAGICS |
| 356 | }; |
| 357 | |
| 358 | const struct blkid_idinfo ext4dev_idinfo = |
| 359 | { |
| 360 | .name = "ext4dev", |
| 361 | .usage = BLKID_USAGE_FILESYSTEM, |
| 362 | .probefunc = probe_ext4dev, |
| 363 | .magics = BLKID_EXT_MAGICS |
| 364 | }; |
| 365 | |