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