chiark / gitweb /
volume_id: escape % character
[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 static 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
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 *mdp1;
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         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
119 static 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
143 int 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 }