| /* |
| * fstype.c, by rmk |
| * |
| * Detect filesystem type (on stdin) and output strings for two |
| * environment variables: |
| * FSTYPE - filesystem type |
| * FSSIZE - filesystem size (if known) |
| * |
| * We currently detect (in order): |
| * gzip, cramfs, romfs, xfs, minix, ext3, ext2, reiserfs |
| * |
| * MINIX, ext3 and Reiserfs bits are currently untested. |
| */ |
| |
| #include <sys/types.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <endian.h> |
| #include <netinet/in.h> |
| |
| #define cpu_to_be32(x) __cpu_to_be32(x) /* Needed by romfs_fs.h */ |
| |
| #include <linux/romfs_fs.h> |
| #include <linux/cramfs_fs.h> |
| #include <linux/minix_fs.h> |
| #define _LINUX_EXT2_FS_SB |
| #include <linux/ext2_fs.h> |
| #include "ext3_fs.h" |
| #include "xfs_sb.h" |
| |
| /* |
| * reiserfs_fs.h is too sick to include directly. |
| * Use a cleaned up version. |
| */ |
| #include "reiserfs_fs.h" |
| |
| char *progname; |
| |
| #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) |
| |
| #define BLOCK_SIZE_BITS 10 |
| #define BLOCK_SIZE (1 << BLOCK_SIZE_BITS) |
| #define BYTES_TO_BLOCKS(x) (((x) + (BLOCK_SIZE - 1)) >> BLOCK_SIZE_BITS) |
| |
| static int fstype(const char *type, unsigned long size) |
| { |
| fprintf(stdout, "FSTYPE=%s\nFSSIZE=%lu\n", type, size); |
| return 0; |
| } |
| |
| static int readfile(int fd, off_t off, void *buf, size_t size) |
| { |
| int ret; |
| |
| if (lseek(fd, off, SEEK_SET) == -1) { |
| perror("lseek"); |
| return 1; |
| } |
| |
| ret = read(fd, buf, size); |
| if (ret == -1) { |
| perror("read"); |
| return 1; |
| } |
| if (ret != BLOCK_SIZE) { |
| fprintf(stderr, "%s: short read\n", progname); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int gzip_image(const unsigned char *buf, unsigned long *blocks) |
| { |
| if (buf[0] == 037 && (buf[1] == 0213 || buf[1] == 0236)) { |
| *blocks = 0; |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int cramfs_image(const unsigned char *buf, unsigned long *blocks) |
| { |
| const struct cramfs_super *sb = |
| (const struct cramfs_super *)buf; |
| |
| if (sb->magic == CRAMFS_MAGIC) { |
| if (sb->flags & CRAMFS_FLAG_FSID_VERSION_2) |
| *blocks = sb->fsid.blocks; |
| else |
| *blocks = 0; |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int romfs_image(const unsigned char *buf, unsigned long *blocks) |
| { |
| const struct romfs_super_block *sb = |
| (const struct romfs_super_block *)buf; |
| |
| if (sb->word0 == ROMSB_WORD0 && sb->word1 == ROMSB_WORD1) { |
| *blocks = (unsigned long)BYTES_TO_BLOCKS(__be32_to_cpu(sb->size)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int minix_image(const unsigned char *buf, unsigned long *blocks) |
| { |
| const struct minix_super_block *sb = |
| (const struct minix_super_block *)buf; |
| |
| if (sb->s_magic == MINIX_SUPER_MAGIC || |
| sb->s_magic == MINIX_SUPER_MAGIC2) { |
| *blocks = sb->s_nzones << sb->s_log_zone_size; |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int ext3_image(const unsigned char *buf, unsigned long *blocks) |
| { |
| const struct ext3_super_block *sb = |
| (const struct ext3_super_block *)buf; |
| |
| if (sb->s_magic == __cpu_to_le16(EXT2_SUPER_MAGIC) && |
| sb->s_feature_compat & __cpu_to_le32(EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { |
| *blocks = __le32_to_cpu(sb->s_blocks_count); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int ext2_image(const unsigned char *buf, unsigned long *blocks) |
| { |
| const struct ext2_super_block *sb = |
| (const struct ext2_super_block *)buf; |
| |
| if (sb->s_magic == __cpu_to_le16(EXT2_SUPER_MAGIC)) { |
| *blocks = __le32_to_cpu(sb->s_blocks_count); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int reiserfs_image(const unsigned char *buf, unsigned long *blocks) |
| { |
| const struct reiserfs_super_block *sb = |
| (const struct reiserfs_super_block *)buf; |
| |
| if (memcmp(REISERFS_MAGIC(sb), REISERFS_SUPER_MAGIC_STRING, |
| sizeof(REISERFS_SUPER_MAGIC_STRING) - 1) == 0 || |
| memcmp(REISERFS_MAGIC(sb), REISER2FS_SUPER_MAGIC_STRING, |
| sizeof(REISER2FS_SUPER_MAGIC_STRING) - 1) == 0 || |
| memcmp(REISERFS_MAGIC(sb), REISER2FS_JR_SUPER_MAGIC_STRING, |
| sizeof(REISER2FS_JR_SUPER_MAGIC_STRING) - 1) == 0) { |
| *blocks = REISERFS_BLOCK_COUNT(sb) * |
| (REISERFS_BLOCKSIZE(sb) / BLOCK_SIZE); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int xfs_image(const unsigned char *buf, unsigned long *blocks) |
| { |
| const struct xfs_sb *sb = |
| (const struct xfs_sb *)buf; |
| |
| if (__be32_to_cpu(sb->sb_magicnum) == XFS_SB_MAGIC) { |
| *blocks = __be64_to_cpu(sb->sb_dblocks) * |
| (__be32_to_cpu(sb->sb_blocksize) / BLOCK_SIZE); |
| return 1; |
| } |
| return 0; |
| } |
| |
| struct imagetype { |
| off_t block; |
| const char name[12]; |
| int (*identify)(const unsigned char *, unsigned long *); |
| }; |
| |
| static struct imagetype images[] = { |
| { 0, "gzip", gzip_image }, |
| { 0, "cramfs", cramfs_image }, |
| { 0, "romfs", romfs_image }, |
| { 0, "xfs", xfs_image }, |
| { 1, "minix", minix_image }, |
| { 1, "ext3", ext3_image }, |
| { 1, "ext2", ext2_image }, |
| { 8, "reiserfs", reiserfs_image }, |
| { 64, "reiserfs", reiserfs_image } |
| }; |
| |
| int main(int argc, char *argv[]) |
| { |
| unsigned char buf[BLOCK_SIZE]; |
| unsigned long size = 0; |
| off_t cur_block = (off_t)-1; |
| unsigned int i; |
| int ret; |
| |
| progname = argv[0]; |
| |
| if (argc != 1) { |
| fprintf(stderr, "Usage: %s < file\n", progname); |
| return 1; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(images); i++) { |
| if (cur_block != images[i].block) { |
| /* |
| * Read block. |
| */ |
| cur_block = images[i].block; |
| ret = readfile(0, cur_block * BLOCK_SIZE, buf, |
| BLOCK_SIZE); |
| if (ret != 0) |
| return ret; |
| } |
| |
| if (images[i].identify(buf, &size)) |
| return fstype(images[i].name, size); |
| } |
| |
| fstype("unknown", 0); |
| return 0; |
| } |