1/*
2 * volume_id - reads filesystem label and uuid
3 *
4 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
5 *
6 *	This program is free software; you can redistribute it and/or modify it
7 *	under the terms of the GNU General Public License as published by the
8 *	Free Software Foundation version 2 of the License.
9 */
10
11#ifndef _GNU_SOURCE
12#define _GNU_SOURCE 1
13#endif
14
15#ifdef HAVE_CONFIG_H
16#  include <config.h>
17#endif
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <string.h>
23#include <errno.h>
24#include <ctype.h>
25
26#include "libvolume_id.h"
27#include "util.h"
28
29struct ext2_super_block {
30	uint32_t	s_inodes_count;
31	uint32_t	s_blocks_count;
32	uint32_t	s_r_blocks_count;
33	uint32_t	s_free_blocks_count;
34	uint32_t	s_free_inodes_count;
35	uint32_t	s_first_data_block;
36	uint32_t	s_log_block_size;
37	uint32_t	s_log_frag_size;
38	uint32_t	s_blocks_per_group;
39	uint32_t	s_frags_per_group;
40	uint32_t	s_inodes_per_group;
41	uint32_t	s_mtime;
42	uint32_t	s_wtime;
43	uint16_t	s_mnt_count;
44	uint16_t	s_max_mnt_count;
45	uint16_t	s_magic;
46	uint16_t	s_state;
47	uint16_t	s_errors;
48	uint16_t	s_minor_rev_level;
49	uint32_t	s_lastcheck;
50	uint32_t	s_checkinterval;
51	uint32_t	s_creator_os;
52	uint32_t	s_rev_level;
53	uint16_t	s_def_resuid;
54	uint16_t	s_def_resgid;
55	uint32_t	s_first_ino;
56	uint16_t	s_inode_size;
57	uint16_t	s_block_group_nr;
58	uint32_t	s_feature_compat;
59	uint32_t	s_feature_incompat;
60	uint32_t	s_feature_ro_compat;
61	uint8_t		s_uuid[16];
62	uint8_t		s_volume_name[16];
63} PACKED;
64
65#define EXT_SUPER_MAGIC				0xEF53
66#define EXT3_FEATURE_COMPAT_HAS_JOURNAL		0x00000004
67#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV	0x00000008
68#define EXT_SUPERBLOCK_OFFSET			0x400
69
70#define EXT3_MIN_BLOCK_SIZE			0x400
71#define EXT3_MAX_BLOCK_SIZE			0x1000
72
73int volume_id_probe_ext(struct volume_id *id, uint64_t off, uint64_t size)
74{
75	struct ext2_super_block *es;
76	size_t bsize;
77
78	info("probing at offset 0x%llx", (unsigned long long) off);
79
80	es = (struct ext2_super_block *) volume_id_get_buffer(id, off + EXT_SUPERBLOCK_OFFSET, 0x200);
81	if (es == NULL)
82		return -1;
83
84	if (es->s_magic != cpu_to_le16(EXT_SUPER_MAGIC))
85		return -1;
86
87	bsize = 0x400 << le32_to_cpu(es->s_log_block_size);
88	dbg("ext blocksize 0x%zx", bsize);
89	if (bsize < EXT3_MIN_BLOCK_SIZE || bsize > EXT3_MAX_BLOCK_SIZE) {
90		dbg("invalid ext blocksize");
91		return -1;
92	}
93
94	volume_id_set_label_raw(id, es->s_volume_name, 16);
95	volume_id_set_label_string(id, es->s_volume_name, 16);
96	volume_id_set_uuid(id, es->s_uuid, 0, UUID_DCE);
97	snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u",
98		 le32_to_cpu(es->s_rev_level), le16_to_cpu(es->s_minor_rev_level));
99
100	/* check for external journal device */
101	if ((le32_to_cpu(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) != 0) {
102		volume_id_set_usage(id, VOLUME_ID_OTHER);
103		id->type = "jbd";
104		return 0;
105	}
106
107	/* check for ext2 / ext3 */
108	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
109	if ((le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0)
110		id->type = "ext3";
111	else
112		id->type = "ext2";
113
114	return 0;
115}
116