1 /*
2 * Copyright (C) 2018 Tony Asleson <tasleson@redhat.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7
8 /*
9 * Specification for on disk format
10 * https://stratis-storage.github.io/StratisSoftwareDesign.pdf
11 */
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <stdint.h>
17 #include <inttypes.h>
18
19 #include "superblocks.h"
20 #include "crc32c.h"
21
22 /* Macro to avoid error in struct declaration. */
23 #define STRATIS_UUID_LEN 32
24 /* Contains 4 hyphens and trailing null byte. */
25 const int STRATIS_UUID_STR_LEN = 37;
26
27 struct stratis_sb {
28 uint32_t crc32;
29 uint8_t magic[16];
30 uint64_t sectors;
31 uint8_t reserved[4];
32 uint8_t pool_uuid[STRATIS_UUID_LEN];
33 uint8_t dev_uuid[STRATIS_UUID_LEN];
34 uint64_t mda_size;
35 uint64_t reserved_size;
36 uint64_t flags;
37 uint64_t initialization_time;
38 } __attribute__ ((__packed__));
39
40 #define BS 512
41 #define FIRST_COPY_OFFSET BS
42 #define SECOND_COPY_OFFSET (BS * 9)
43 #define SB_AREA_SIZE (BS * 16)
44
45 const char STRATIS_MAGIC[] = "!Stra0tis\x86\xff\x02^\x41rh";
46 #define MAGIC_LEN (sizeof(STRATIS_MAGIC) - 1)
47
48 #define _MAGIC_OFFSET (offsetof(const struct stratis_sb, magic))
49 #define MAGIC_OFFSET_COPY_1 (FIRST_COPY_OFFSET + _MAGIC_OFFSET)
50 #define MAGIC_OFFSET_COPY_2 (SECOND_COPY_OFFSET + _MAGIC_OFFSET)
51
52 static int stratis_valid_sb(uint8_t *p)
53 {
54 const struct stratis_sb *stratis = (const struct stratis_sb *)p;
55 uint32_t crc = 0;
56
57 /* generate CRC from byte position 4 for length 508 == 512 byte sector */
58 crc = crc32c(~0L, p + sizeof(stratis->crc32),
59 BS - sizeof(stratis->crc32));
60 crc ^= ~0L;
61
62 return crc == le32_to_cpu(stratis->crc32);
63 }
64
65 /*
66 * Format UUID string representation to include hyphens given that Stratis stores
67 * UUIDs without hyphens in the superblock to keep the UUID length a power of 2.
68 */
69 static void stratis_format_uuid(const unsigned char *src_uuid,
70 unsigned char *dst_uuid)
71 {
72 int i;
73
74 for (i = 0; i < STRATIS_UUID_LEN; i++) {
75 *dst_uuid++ = *src_uuid++;
76 if (i == 7 || i == 11 || i == 15 || i == 19)
77 *dst_uuid ++ = '-';
78 }
79 *dst_uuid = '\0';
80 }
81
82 static int probe_stratis(blkid_probe pr,
83 const struct blkid_idmag *mag __attribute__((__unused__)))
84 {
85 const struct stratis_sb *stratis = NULL;
86 uint8_t *buf = blkid_probe_get_buffer(pr, 0, SB_AREA_SIZE);
87 unsigned char uuid[STRATIS_UUID_STR_LEN];
88
89 if (!buf)
90 return errno ? -errno : 1;
91
92 if (stratis_valid_sb(buf + FIRST_COPY_OFFSET)) {
93 stratis = (const struct stratis_sb *)(buf + FIRST_COPY_OFFSET);
94 } else {
95 if (!stratis_valid_sb(buf + SECOND_COPY_OFFSET))
96 return 1;
97
98 stratis = (const struct stratis_sb *)
99 (buf + SECOND_COPY_OFFSET);
100 }
101
102 stratis_format_uuid(stratis->dev_uuid, uuid);
103 blkid_probe_strncpy_uuid(pr, uuid, sizeof(uuid));
104
105 stratis_format_uuid(stratis->pool_uuid, uuid);
106 blkid_probe_set_value(pr, "POOL_UUID", uuid, sizeof(uuid));
107
108 blkid_probe_sprintf_value(pr, "BLOCKDEV_SECTORS", "%" PRIu64,
109 le64_to_cpu(stratis->sectors));
110 blkid_probe_sprintf_value(pr, "BLOCKDEV_INITTIME", "%" PRIu64,
111 le64_to_cpu(stratis->initialization_time));
112 return 0;
113 }
114
115 const struct blkid_idinfo stratis_idinfo = {
116 .name = "stratis",
117 .usage = BLKID_USAGE_RAID,
118 .probefunc = probe_stratis,
119 .minsz = SB_AREA_SIZE,
120 .magics = {
121 { .magic = STRATIS_MAGIC, .len = MAGIC_LEN,
122 .sboff = MAGIC_OFFSET_COPY_1},
123 { .magic = STRATIS_MAGIC, .len = MAGIC_LEN,
124 .sboff = MAGIC_OFFSET_COPY_2},
125 { NULL }
126 }
127 };