]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libblkid/src/superblocks/zfs.c
libblkid: fix compiler warning [-Wunused-variable]
[thirdparty/util-linux.git] / libblkid / src / superblocks / zfs.c
1 /*
2 * Copyright (C) 2009-2010 by Andreas Dilger <adilger@sun.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <ctype.h>
14 #include <inttypes.h>
15 #include <limits.h>
16
17 #include "superblocks.h"
18
19 #define VDEV_LABEL_UBERBLOCK (128 * 1024ULL)
20 #define VDEV_LABEL_NVPAIR ( 16 * 1024ULL)
21 #define VDEV_LABEL_SIZE (256 * 1024ULL)
22 #define UBERBLOCK_SIZE 1024ULL
23 #define UBERBLOCKS_COUNT 128
24
25 /* #include <sys/uberblock_impl.h> */
26 #define UBERBLOCK_MAGIC 0x00bab10c /* oo-ba-bloc! */
27 struct zfs_uberblock {
28 uint64_t ub_magic; /* UBERBLOCK_MAGIC */
29 uint64_t ub_version; /* SPA_VERSION */
30 uint64_t ub_txg; /* txg of last sync */
31 uint64_t ub_guid_sum; /* sum of all vdev guids */
32 uint64_t ub_timestamp; /* UTC time of last sync */
33 char ub_rootbp; /* MOS objset_phys_t */
34 } __attribute__((packed));
35
36 #define ZFS_WANT 4
37
38 #define DATA_TYPE_UINT64 8
39 #define DATA_TYPE_STRING 9
40
41 struct nvpair {
42 uint32_t nvp_size;
43 uint32_t nvp_unkown;
44 uint32_t nvp_namelen;
45 char nvp_name[0]; /* aligned to 4 bytes */
46 /* aligned ptr array for string arrays */
47 /* aligned array of data for value */
48 };
49
50 struct nvstring {
51 uint32_t nvs_type;
52 uint32_t nvs_elem;
53 uint32_t nvs_strlen;
54 unsigned char nvs_string[0];
55 };
56
57 struct nvuint64 {
58 uint32_t nvu_type;
59 uint32_t nvu_elem;
60 uint64_t nvu_value;
61 };
62
63 struct nvlist {
64 uint32_t nvl_unknown[3];
65 struct nvpair nvl_nvpair;
66 };
67
68 #define nvdebug(fmt, ...) do { } while(0)
69 /*#define nvdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
70
71 static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
72 {
73 struct nvlist *nvl;
74 struct nvpair *nvp;
75 size_t left = 4096;
76 int found = 0;
77
78 offset = (offset & ~(VDEV_LABEL_SIZE - 1)) + VDEV_LABEL_NVPAIR;
79
80 /* Note that we currently assume that the desired fields are within
81 * the first 4k (left) of the nvlist. This is true for all pools
82 * I've seen, and simplifies this code somewhat, because we don't
83 * have to handle an nvpair crossing a buffer boundary. */
84 nvl = (struct nvlist *)blkid_probe_get_buffer(pr, offset, left);
85 if (nvl == NULL)
86 return;
87
88 nvdebug("zfs_extract: nvlist offset %llu\n", offset);
89
90 nvp = &nvl->nvl_nvpair;
91 while (left > sizeof(*nvp) && nvp->nvp_size != 0 && found < 3) {
92 int avail; /* tracks that name/value data fits in nvp_size */
93 int namesize;
94
95 nvp->nvp_size = be32_to_cpu(nvp->nvp_size);
96 nvp->nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
97 avail = nvp->nvp_size - nvp->nvp_namelen - sizeof(*nvp);
98
99 nvdebug("left %zd nvp_size %u\n", left, nvp->nvp_size);
100 if (left < nvp->nvp_size || avail < 0)
101 break;
102
103 namesize = (nvp->nvp_namelen + 3) & ~3;
104
105 nvdebug("nvlist: size %u, namelen %u, name %*s\n",
106 nvp->nvp_size, nvp->nvp_namelen, nvp->nvp_namelen,
107 nvp->nvp_name);
108 if (strncmp(nvp->nvp_name, "name", nvp->nvp_namelen) == 0) {
109 struct nvstring *nvs = (void *)(nvp->nvp_name+namesize);
110
111 nvs->nvs_type = be32_to_cpu(nvs->nvs_type);
112 nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
113 if (nvs->nvs_strlen > UINT_MAX - sizeof(*nvs))
114 break;
115 avail -= nvs->nvs_strlen + sizeof(*nvs);
116 nvdebug("nvstring: type %u string %*s\n", nvs->nvs_type,
117 nvs->nvs_strlen, nvs->nvs_string);
118 if (nvs->nvs_type == DATA_TYPE_STRING && avail >= 0)
119 blkid_probe_set_label(pr, nvs->nvs_string,
120 nvs->nvs_strlen);
121 found++;
122 } else if (strncmp(nvp->nvp_name, "guid",
123 nvp->nvp_namelen) == 0) {
124 struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
125 uint64_t nvu_value;
126
127 memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
128 nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
129 nvu_value = be64_to_cpu(nvu_value);
130 avail -= sizeof(*nvu);
131 nvdebug("nvuint64: type %u value %"PRIu64"\n",
132 nvu->nvu_type, nvu_value);
133 if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
134 blkid_probe_sprintf_value(pr, "UUID_SUB",
135 "%"PRIu64, nvu_value);
136 found++;
137 } else if (strncmp(nvp->nvp_name, "pool_guid",
138 nvp->nvp_namelen) == 0) {
139 struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
140 uint64_t nvu_value;
141
142 memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
143 nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
144 nvu_value = be64_to_cpu(nvu_value);
145 avail -= sizeof(*nvu);
146 nvdebug("nvuint64: type %u value %"PRIu64"\n",
147 nvu->nvu_type, nvu_value);
148 if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
149 blkid_probe_sprintf_uuid(pr, (unsigned char *)
150 &nvu_value,
151 sizeof(nvu_value),
152 "%"PRIu64, nvu_value);
153 found++;
154 }
155 if (left > nvp->nvp_size)
156 left -= nvp->nvp_size;
157 else
158 left = 0;
159 nvp = (struct nvpair *)((char *)nvp + nvp->nvp_size);
160 }
161 }
162
163 #define zdebug(fmt, ...) do {} while(0)
164 /*#define zdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
165
166 static int find_uberblocks(const void *label, loff_t *ub_offset, int *swap_endian)
167 {
168 uint64_t swab_magic = swab64(UBERBLOCK_MAGIC);
169 struct zfs_uberblock *ub;
170 int i, found = 0;
171 loff_t offset = VDEV_LABEL_UBERBLOCK;
172
173 for (i = 0; i < UBERBLOCKS_COUNT; i++, offset += UBERBLOCK_SIZE) {
174 ub = (struct zfs_uberblock *)(label + offset);
175
176 if (ub->ub_magic == UBERBLOCK_MAGIC) {
177 *ub_offset = offset;
178 *swap_endian = 0;
179 found++;
180 zdebug("probe_zfs: found little-endian uberblock at %llu\n", offset >> 10);
181 }
182
183 if (ub->ub_magic == swab_magic) {
184 *ub_offset = offset;
185 *swap_endian = 1;
186 found++;
187 zdebug("probe_zfs: found big-endian uberblock at %llu\n", offset >> 10);
188 }
189 }
190
191 return found;
192 }
193
194 /* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start
195 * of the disk, and 2 areas at the end of the disk. Check only some of them...
196 * #4 (@ 132kB) is the first one written on a new filesystem. */
197 static int probe_zfs(blkid_probe pr, const struct blkid_idmag *mag)
198 {
199 int swab_endian = 0;
200 struct zfs_uberblock *ub;
201 loff_t offset, ub_offset = 0;
202 int label_no, found = 0, found_in_label;
203 void *label;
204 loff_t blk_align = (pr->size % (256 * 1024ULL));
205
206 zdebug("probe_zfs\n");
207 /* Look for at least 4 uberblocks to ensure a positive match */
208 for (label_no = 0; label_no < 4; label_no++) {
209 switch(label_no) {
210 case 0: // jump to L0
211 offset = 0;
212 break;
213 case 1: // jump to L1
214 offset = VDEV_LABEL_SIZE;
215 break;
216 case 2: // jump to L2
217 offset = pr->size - 2 * VDEV_LABEL_SIZE - blk_align;
218 break;
219 case 3: // jump to L3
220 offset = pr->size - VDEV_LABEL_SIZE - blk_align;
221 break;
222 }
223
224 label = blkid_probe_get_buffer(pr, offset, VDEV_LABEL_SIZE);
225 if (label == NULL)
226 return errno ? -errno : 1;
227
228 found_in_label = find_uberblocks(label, &ub_offset, &swab_endian);
229
230 if (found_in_label > 0) {
231 found+= found_in_label;
232 ub = (struct zfs_uberblock *)(label + ub_offset);
233 ub_offset += offset;
234
235 if (found >= ZFS_WANT)
236 break;
237 }
238 }
239
240 if (found < ZFS_WANT)
241 return 1;
242
243 /* If we found the 4th uberblock, then we will have exited from the
244 * scanning loop immediately, and ub will be a valid uberblock. */
245 blkid_probe_sprintf_version(pr, "%" PRIu64, swab_endian ?
246 swab64(ub->ub_version) : ub->ub_version);
247
248 zfs_extract_guid_name(pr, offset);
249
250 if (blkid_probe_set_magic(pr, ub_offset,
251 sizeof(ub->ub_magic),
252 (unsigned char *) &ub->ub_magic))
253 return 1;
254
255 return 0;
256 }
257
258 const struct blkid_idinfo zfs_idinfo =
259 {
260 .name = "zfs_member",
261 .usage = BLKID_USAGE_RAID,
262 .probefunc = probe_zfs,
263 .minsz = 64 * 1024 * 1024,
264 .magics = BLKID_NONE_MAGIC
265 };