541 lines
13 KiB
C
541 lines
13 KiB
C
/*
|
|
* 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
|
|
};
|
|
|