chiark / gitweb /
delete all Makefiles and move udev source to udev/
[elogind.git] / extras / volume_id / lib / linux_raid.c
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
30 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;
48
49 struct 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;
57
58 #define MD_RESERVED_BYTES               0x10000
59 #define MD_SB_MAGIC                     0xa92b4efc
60
61 static int volume_id_probe_linux_raid0(struct volume_id *id, uint64_t off, uint64_t size)
62 {
63         const uint8_t *buf;
64         struct mdp0_super_block *mdp0;
65         union {
66                 uint32_t ints[4];
67                 uint8_t bytes[16];
68         } uuid;
69
70         info("probing at offset 0x%llx, size 0x%llx\n",
71             (unsigned long long) off, (unsigned long long) size);
72         if (size < 0x10000)
73                 return -1;
74
75         buf = volume_id_get_buffer(id, off, 0x800);
76         if (buf == NULL)
77                 return -1;
78         mdp0 = (struct mdp0_super_block *) buf;
79
80         if (le32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) {
81                 uuid.ints[0] = bswap_32(mdp0->set_uuid0);
82                 if (le32_to_cpu(mdp0->minor_version >= 90)) {
83                         uuid.ints[1] = bswap_32(mdp0->set_uuid1);
84                         uuid.ints[2] = bswap_32(mdp0->set_uuid2);
85                         uuid.ints[3] = bswap_32(mdp0->set_uuid3);
86                 } else {
87                         uuid.ints[1] = 0;
88                         uuid.ints[2] = 0;
89                         uuid.ints[3] = 0;
90                 }
91                 volume_id_set_uuid(id, uuid.bytes, 0, UUID_MD);
92                 snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u.%u",
93                          le32_to_cpu(mdp0->major_version),
94                          le32_to_cpu(mdp0->minor_version),
95                          le32_to_cpu(mdp0->patch_version));
96         } else if (be32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) {
97                 uuid.ints[0] = mdp0->set_uuid0;
98                 if (be32_to_cpu(mdp0->minor_version >= 90)) {
99                         uuid.ints[1] = mdp0->set_uuid1;
100                         uuid.ints[2] = mdp0->set_uuid2;
101                         uuid.ints[3] = mdp0->set_uuid3;
102                 } else {
103                         uuid.ints[1] = 0;
104                         uuid.ints[2] = 0;
105                         uuid.ints[3] = 0;
106                 }
107                 volume_id_set_uuid(id, uuid.bytes, 0, UUID_MD);
108                 snprintf(id->type_version, sizeof(id->type_version)-1, "%u.%u.%u",
109                          be32_to_cpu(mdp0->major_version),
110                          be32_to_cpu(mdp0->minor_version),
111                          be32_to_cpu(mdp0->patch_version));
112         } else
113                 return -1;
114
115         volume_id_set_usage(id, VOLUME_ID_RAID);
116         id->type = "linux_raid_member";
117         return 0;
118 }
119
120 static int volume_id_probe_linux_raid1(struct volume_id *id, uint64_t off, uint64_t size)
121 {
122         const uint8_t *buf;
123         struct mdp1_super_block *mdp1;
124
125         info("probing at offset 0x%llx, size 0x%llx\n",
126             (unsigned long long) off, (unsigned long long) size);
127
128         buf = volume_id_get_buffer(id, off, 0x800);
129         if (buf == NULL)
130                 return -1;
131         mdp1 = (struct mdp1_super_block *) buf;
132
133         if (le32_to_cpu(mdp1->magic) != MD_SB_MAGIC)
134                 return -1;
135
136         if (le32_to_cpu(mdp1->major_version) != 1)
137                 return -1;
138
139         volume_id_set_uuid(id, mdp1->set_uuid, 0, UUID_MD);
140         volume_id_set_label_raw(id, mdp1->set_name, 32);
141         volume_id_set_label_string(id, mdp1->set_name, 32);
142         volume_id_set_usage(id, VOLUME_ID_RAID);
143         id->type = "linux_raid_member";
144         return 0;
145 }
146
147 int volume_id_probe_linux_raid(struct volume_id *id, uint64_t off, uint64_t size)
148 {
149         uint64_t sboff;
150
151         /* version 0 at the end of the device */
152         sboff = (size & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES;
153         if (volume_id_probe_linux_raid0(id, off + sboff, size) == 0)
154                 return 0;
155
156         /* version 1.0 at the end of the device */
157         sboff = (size & ~(0x1000 - 1)) - 0x2000;
158         if (volume_id_probe_linux_raid1(id, off + sboff, size) == 0)
159                 strcpy(id->type_version, "1.0");
160
161         /* version 1.1 at the start of the device */
162         else if (volume_id_probe_linux_raid1(id, off, size) == 0)
163                 strcpy(id->type_version, "1.1");
164
165         /* version 1.2 at 4k offset from the start */
166         else if (volume_id_probe_linux_raid1(id, off + 0x1000, size) == 0)
167                 strcpy(id->type_version, "1.2");
168
169         else
170                 return -1;
171
172         return 0;
173 }