diff options
Diffstat (limited to 'libblkid/ext.c')
-rw-r--r-- | libblkid/ext.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/libblkid/ext.c b/libblkid/ext.c new file mode 100644 index 000000000..eff96a066 --- /dev/null +++ b/libblkid/ext.c @@ -0,0 +1,540 @@ +/* + * Copyright (C) 1999, 2001 by Andries Brouwer + * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> +#ifdef __linux__ +#include <sys/utsname.h> +#endif +#include <time.h> + +#include "linux_version.h" +#include "superblocks.h" + +struct ext2_super_block { + uint32_t s_inodes_count; + uint32_t s_blocks_count; + uint32_t s_r_blocks_count; + uint32_t s_free_blocks_count; + uint32_t s_free_inodes_count; + uint32_t s_first_data_block; + uint32_t s_log_block_size; + uint32_t s_dummy3[7]; + unsigned char s_magic[2]; + uint16_t s_state; + uint16_t s_errors; + uint16_t s_minor_rev_level; + uint32_t s_lastcheck; + uint32_t s_checkinterval; + uint32_t s_creator_os; + uint32_t s_rev_level; + uint16_t s_def_resuid; + uint16_t s_def_resgid; + uint32_t s_first_ino; + uint16_t s_inode_size; + uint16_t s_block_group_nr; + uint32_t s_feature_compat; + uint32_t s_feature_incompat; + uint32_t s_feature_ro_compat; + unsigned char s_uuid[16]; + char s_volume_name[16]; + char s_last_mounted[64]; + uint32_t s_algorithm_usage_bitmap; + uint8_t s_prealloc_blocks; + uint8_t s_prealloc_dir_blocks; + uint16_t s_reserved_gdt_blocks; + uint8_t s_journal_uuid[16]; + uint32_t s_journal_inum; + uint32_t s_journal_dev; + uint32_t s_last_orphan; + uint32_t s_hash_seed[4]; + uint8_t s_def_hash_version; + uint8_t s_jnl_backup_type; + uint16_t s_reserved_word_pad; + uint32_t s_default_mount_opts; + uint32_t s_first_meta_bg; + uint32_t s_mkfs_time; + uint32_t s_jnl_blocks[17]; + uint32_t s_blocks_count_hi; + uint32_t s_r_blocks_count_hi; + uint32_t s_free_blocks_hi; + uint16_t s_min_extra_isize; + uint16_t s_want_extra_isize; + uint32_t s_flags; + uint16_t s_raid_stride; + uint16_t s_mmp_interval; + uint64_t s_mmp_block; + uint32_t s_raid_stripe_width; + uint32_t s_reserved[163]; +} __attribute__((packed)); + +/* magic string */ +#define EXT_SB_MAGIC "\123\357" +/* supper block offset */ +#define EXT_SB_OFF 0x400 +/* supper block offset in kB */ +#define EXT_SB_KBOFF (EXT_SB_OFF >> 10) +/* magic string offset within super block */ +#define EXT_MAG_OFF 0x38 + + + +/* for s_flags */ +#define EXT2_FLAGS_TEST_FILESYS 0x0004 + +/* for s_feature_compat */ +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 + +/* for s_feature_ro_compat */ +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 + +/* for s_feature_incompat */ +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */ +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 + +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT2_FEATURE_INCOMPAT_META_BG) +#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP +#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP + +#define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT3_FEATURE_INCOMPAT_RECOVER| \ + EXT2_FEATURE_INCOMPAT_META_BG) +#define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP +#define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP + +/* + * Check to see if a filesystem is in /proc/filesystems. + * Returns 1 if found, 0 if not + */ +static int fs_proc_check(const char *fs_name) +{ + FILE *f; + char buf[80], *cp, *t; + + f = fopen("/proc/filesystems", "r"); + if (!f) + return 0; + while (!feof(f)) { + if (!fgets(buf, sizeof(buf), f)) + break; + cp = buf; + if (!isspace(*cp)) { + while (*cp && !isspace(*cp)) + cp++; + } + while (*cp && isspace(*cp)) + cp++; + if ((t = strchr(cp, '\n')) != NULL) + *t = 0; + if ((t = strchr(cp, '\t')) != NULL) + *t = 0; + if ((t = strchr(cp, ' ')) != NULL) + *t = 0; + if (!strcmp(fs_name, cp)) { + fclose(f); + return 1; + } + } + fclose(f); + return (0); +} + +/* + * Check to see if a filesystem is available as a module + * Returns 1 if found, 0 if not + */ +static int check_for_modules(const char *fs_name) +{ +#ifdef __linux__ + struct utsname uts; + FILE *f; + char buf[1024], *cp; + int namesz; + + if (uname(&uts)) + return 0; + snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release); + + f = fopen(buf, "r"); + if (!f) + return 0; + + namesz = strlen(fs_name); + + while (!feof(f)) { + if (!fgets(buf, sizeof(buf), f)) + break; + if ((cp = strchr(buf, ':')) != NULL) + *cp = 0; + else + continue; + if ((cp = strrchr(buf, '/')) == NULL) + continue; + cp++; + + if (!strncmp(cp, fs_name, namesz) && + (!strcmp(cp + namesz, ".ko") || + !strcmp(cp + namesz, ".ko.gz"))) { + fclose(f); + return 1; + } + } + fclose(f); +#endif /* __linux__ */ + return 0; +} + +/* + * Starting in 2.6.29, ext4 can be used to support filesystems + * without a journal. + */ +#define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29) + +static int system_supports_ext2(void) +{ + static time_t last_check = 0; + static int ret = -1; + time_t now = time(0); + + if (ret != -1 || (now - last_check) < 5) + return ret; + last_check = now; + ret = (fs_proc_check("ext2") || check_for_modules("ext2")); + return ret; +} + +static int system_supports_ext4(void) +{ + static time_t last_check = 0; + static int ret = -1; + time_t now = time(0); + + if (ret != -1 || (now - last_check) < 5) + return ret; + last_check = now; + ret = (fs_proc_check("ext4") || check_for_modules("ext4")); + return ret; +} + +static int system_supports_ext4dev(void) +{ + static time_t last_check = 0; + static int ret = -1; + time_t now = time(0); + + if (ret != -1 || (now - last_check) < 5) + return ret; + last_check = now; + ret = (fs_proc_check("ext4dev") || check_for_modules("ext4dev")); + return ret; +} + +static int system_supports_ext4_ext2(void) +{ +#ifdef __linux__ + return get_linux_version() >= EXT4_SUPPORTS_EXT2; +#else + return 0; +#endif +} +/* + * reads superblock and returns: + * fc = feature_compat + * fi = feature_incompat + * frc = feature_ro_compat + */ +static struct ext2_super_block *ext_get_super( + blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc) +{ + struct ext2_super_block *es; + + es = (struct ext2_super_block *) + blkid_probe_get_buffer(pr, EXT_SB_OFF, 0x200); + if (!es) + return NULL; + if (fc) + *fc = le32_to_cpu(es->s_feature_compat); + if (fi) + *fi = le32_to_cpu(es->s_feature_incompat); + if (frc) + *frc = le32_to_cpu(es->s_feature_ro_compat); + + return es; +} + +static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es) +{ + struct blkid_chain *chn = blkid_probe_get_chain(pr); + + DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n", + le32_to_cpu(es->s_feature_compat), + le32_to_cpu(es->s_feature_incompat), + le32_to_cpu(es->s_feature_ro_compat))); + + if (strlen(es->s_volume_name)) + blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name, + sizeof(es->s_volume_name)); + blkid_probe_set_uuid(pr, es->s_uuid); + + if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) + blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL"); + + if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) && + ((le32_to_cpu(es->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0)) + blkid_probe_set_value(pr, "SEC_TYPE", + (unsigned char *) "ext2", + sizeof("ext2")); + + blkid_probe_sprintf_version(pr, "%u.%u", + le32_to_cpu(es->s_rev_level), + le16_to_cpu(es->s_minor_rev_level)); +} + + +static int probe_jbd(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fi; + + es = ext_get_super(pr, NULL, &fi, NULL); + if (!es) + return -BLKID_ERR_PARAM; + if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) + return -BLKID_ERR_PARAM; + + ext_get_info(pr, 2, es); + return 0; +} + +static int probe_ext2(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return -BLKID_ERR_PARAM; + + /* Distinguish between ext3 and ext2 */ + if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) + return -BLKID_ERR_PARAM; + + /* Any features which ext2 doesn't understand */ + if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) || + (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED)) + return -BLKID_ERR_PARAM; + + /* + * If ext2 is not present, but ext4 or ext4dev are, then + * disclaim we are ext2 + */ + if (!system_supports_ext2() && + (system_supports_ext4() || system_supports_ext4dev()) && + system_supports_ext4_ext2()) + return -BLKID_ERR_PARAM; + + ext_get_info(pr, 2, es); + return 0; +} + +static int probe_ext3(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return -BLKID_ERR_PARAM; + + /* ext3 requires journal */ + if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) + return -BLKID_ERR_PARAM; + + /* Any features which ext3 doesn't understand */ + if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) || + (fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) + return -BLKID_ERR_PARAM; + + ext_get_info(pr, 3, es); + return 0; +} + + +static int probe_ext4dev(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return -BLKID_ERR_PARAM; + + /* Distinguish from jbd */ + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return -BLKID_ERR_PARAM; + + /* + * If the filesystem does not have a journal and ext2 and ext4 + * is not present, then force this to be detected as an + * ext4dev filesystem. + */ + if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && + !system_supports_ext2() && !system_supports_ext4() && + system_supports_ext4dev() && + system_supports_ext4_ext2()) + goto force_ext4dev; + + /* + * If the filesystem is marked as OK for use by in-development + * filesystem code, but ext4dev is not supported, and ext4 is, + * then don't call ourselves ext4dev, since we should be + * detected as ext4 in that case. + * + * If the filesystem is marked as in use by production + * filesystem, then it can only be used by ext4 and NOT by + * ext4dev, so always disclaim we are ext4dev in that case. + */ + if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) { + if (!system_supports_ext4dev() && system_supports_ext4()) + return -BLKID_ERR_PARAM; + } else + return -BLKID_ERR_PARAM; + +force_ext4dev: + ext_get_info(pr, 4, es); + return 0; +} + +static int probe_ext4(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct ext2_super_block *es; + uint32_t fc, frc, fi; + + es = ext_get_super(pr, &fc, &fi, &frc); + if (!es) + return -1; + + /* Distinguish from jbd */ + if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + return -BLKID_ERR_PARAM; + + /* + * If the filesystem does not have a journal and ext2 is not + * present, then force this to be detected as an ext2 + * filesystem. + */ + if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && + !system_supports_ext2() && system_supports_ext4() && + system_supports_ext4_ext2()) + goto force_ext4; + + /* Ext4 has at least one feature which ext3 doesn't understand */ + if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) && + !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) + return -BLKID_ERR_PARAM; + +force_ext4: + /* + * If the filesystem is a OK for use by in-development + * filesystem code, and ext4dev is supported or ext4 is not + * supported, then don't call ourselves ext4, so we can redo + * the detection and mark the filesystem as ext4dev. + * + * If the filesystem is marked as in use by production + * filesystem, then it can only be used by ext4 and NOT by + * ext4dev. + */ + if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) { + if (system_supports_ext4dev() || !system_supports_ext4()) + return -BLKID_ERR_PARAM; + } + + ext_get_info(pr, 4, es); + return 0; +} + +#define BLKID_EXT_MAGICS \ + { \ + { \ + .magic = EXT_SB_MAGIC, \ + .len = sizeof(EXT_SB_MAGIC) - 1, \ + .kboff = EXT_SB_KBOFF, \ + .sboff = EXT_MAG_OFF \ + }, \ + { NULL } \ + } + +const struct blkid_idinfo jbd_idinfo = +{ + .name = "jbd", + .usage = BLKID_USAGE_OTHER, + .probefunc = probe_jbd, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext2_idinfo = +{ + .name = "ext2", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext2, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext3_idinfo = +{ + .name = "ext3", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext3, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext4_idinfo = +{ + .name = "ext4", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext4, + .magics = BLKID_EXT_MAGICS +}; + +const struct blkid_idinfo ext4dev_idinfo = +{ + .name = "ext4dev", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_ext4dev, + .magics = BLKID_EXT_MAGICS +}; + |