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#include <byteswap.h>
26
27#include "libvolume_id.h"
28#include "util.h"
29
30static struct mdp0_super_block {
31	uint32_t	md_magic;
32	uint32_t	major_version;
33	uint32_t	minor_version;
34	uint32_t	patch_version;
35	uint32_t	gvalid_words;
36	uint32_t	set_uuid0;
37	uint32_t	ctime;
38	uint32_t	level;
39	uint32_t	size;
40	uint32_t	nr_disks;
41	uint32_t	raid_disks;
42	uint32_t	md_minor;
43	uint32_t	not_persistent;
44	uint32_t	set_uuid1;
45	uint32_t	set_uuid2;
46	uint32_t	set_uuid3;
47} PACKED *mdp0;
48
49struct mdp1_super_block {
50	uint32_t	magic;
51	uint32_t	major_version;
52	uint32_t	feature_map;
53	uint32_t	pad0;
54	uint8_t		set_uuid[16];
55	uint8_t		set_name[32];
56} PACKED *mdp1;
57
58#define MD_RESERVED_BYTES		0x10000
59#define MD_SB_MAGIC			0xa92b4efc
60
61static int volume_id_probe_linux_raid0(struct volume_id *id, uint64_t off, uint64_t size)
62{
63	const uint8_t *buf;
64	union {
65		uint32_t ints[4];
66		uint8_t bytes[16];
67	} uuid;
68
69	info("probing at offset 0x%llx, size 0x%llx",
70	    (unsigned long long) off, (unsigned long long) size);
71	if (size < 0x10000)
72		return -1;
73
74	buf = volume_id_get_buffer(id, off, 0x800);
75	if (buf == NULL)
76		return -1;
77	mdp0 = (struct mdp0_super_block *) buf;
78
79	if (le32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) {
80		uuid.ints[0] = bswap_32(mdp0->set_uuid0);
81		if (le32_to_cpu(mdp0->minor_version >= 90)) {
82			uuid.ints[1] = bswap_32(mdp0->set_uuid1);
83			uuid.ints[2] = bswap_32(mdp0->set_uuid2);
84			uuid.ints[3] = bswap_32(mdp0->set_uuid3);
85		} else {
86			uuid.ints[1] = 0;
87			uuid.ints[2] = 0;
88			uuid.ints[3] = 0;
89		}
90		volume_id_set_uuid(id, uuid.bytes, 0, UUID_FOURINT);
91		snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u.%u",
92			 le32_to_cpu(mdp0->major_version),
93			 le32_to_cpu(mdp0->minor_version),
94			 le32_to_cpu(mdp0->patch_version));
95	} else if (be32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) {
96		uuid.ints[0] = mdp0->set_uuid0;
97		if (be32_to_cpu(mdp0->minor_version >= 90)) {
98			uuid.ints[1] = mdp0->set_uuid1;
99			uuid.ints[2] = mdp0->set_uuid2;
100			uuid.ints[3] = mdp0->set_uuid3;
101		} else {
102			uuid.ints[1] = 0;
103			uuid.ints[2] = 0;
104			uuid.ints[3] = 0;
105		}
106		volume_id_set_uuid(id, uuid.bytes, 0, UUID_FOURINT);
107		snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u.%u",
108			 be32_to_cpu(mdp0->major_version),
109			 be32_to_cpu(mdp0->minor_version),
110			 be32_to_cpu(mdp0->patch_version));
111	} else
112		return -1;
113
114	volume_id_set_usage(id, VOLUME_ID_RAID);
115	id->type = "linux_raid_member";
116	return 0;
117}
118
119static int volume_id_probe_linux_raid1(struct volume_id *id, uint64_t off, uint64_t size)
120{
121	const uint8_t *buf;
122
123	info("probing at offset 0x%llx, size 0x%llx",
124	    (unsigned long long) off, (unsigned long long) size);
125
126	buf = volume_id_get_buffer(id, off, 0x800);
127	if (buf == NULL)
128		return -1;
129	mdp1 = (struct mdp1_super_block *) buf;
130
131	if (le32_to_cpu(mdp1->magic) != MD_SB_MAGIC)
132		return -1;
133
134	volume_id_set_uuid(id, mdp1->set_uuid, 0, UUID_FOURINT);
135	volume_id_set_label_raw(id, mdp1->set_name, 32);
136	volume_id_set_label_string(id, mdp1->set_name, 32);
137	snprintf(id->type_version, sizeof(id->type_version)-1, "%u", le32_to_cpu(mdp1->major_version));
138	volume_id_set_usage(id, VOLUME_ID_RAID);
139	id->type = "linux_raid_member";
140	return 0;
141}
142
143int volume_id_probe_linux_raid(struct volume_id *id, uint64_t off, uint64_t size)
144{
145	uint64_t sboff = (size & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES;
146
147	/* version 0 at the end of the device */
148	if (volume_id_probe_linux_raid0(id, off + sboff, size) == 0)
149		return 0;
150
151	/* version 1.0 at the end of the device */
152	if (volume_id_probe_linux_raid1(id, off + sboff, size) == 0)
153		return 0;
154
155	/* version 1.1 at the start of the device */
156	if (volume_id_probe_linux_raid1(id, off, size) == 0)
157		return 0;
158
159	/* version 1.2 at 4k offset from the start */
160	if (volume_id_probe_linux_raid1(id, off + 0x1000, size) == 0)
161		return 0;
162
163	return -1;
164}
165