]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libblkid/src/superblocks/zfs.c
libblkid: fix compiler warning [-Wunused-variable]
[thirdparty/util-linux.git] / libblkid / src / superblocks / zfs.c
CommitLineData
1f4da8d2 1/*
a1fbeb3d 2 * Copyright (C) 2009-2010 by Andreas Dilger <adilger@sun.com>
1f4da8d2
AD
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>
109df14f 15#include <limits.h>
1f4da8d2 16
1e3cf6a2 17#include "superblocks.h"
1f4da8d2 18
a1fbeb3d
AD
19#define VDEV_LABEL_UBERBLOCK (128 * 1024ULL)
20#define VDEV_LABEL_NVPAIR ( 16 * 1024ULL)
21#define VDEV_LABEL_SIZE (256 * 1024ULL)
5dd705ba 22#define UBERBLOCK_SIZE 1024ULL
e44a4c7a 23#define UBERBLOCKS_COUNT 128
a1fbeb3d 24
1f4da8d2
AD
25/* #include <sys/uberblock_impl.h> */
26#define UBERBLOCK_MAGIC 0x00bab10c /* oo-ba-bloc! */
27struct 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 */
e54a76ca 33 char ub_rootbp; /* MOS objset_phys_t */
11854e2e 34} __attribute__((packed));
1f4da8d2 35
a1fbeb3d
AD
36#define ZFS_WANT 4
37
38#define DATA_TYPE_UINT64 8
39#define DATA_TYPE_STRING 9
40
41struct 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
50struct nvstring {
51 uint32_t nvs_type;
52 uint32_t nvs_elem;
53 uint32_t nvs_strlen;
54 unsigned char nvs_string[0];
55};
56
57struct nvuint64 {
58 uint32_t nvu_type;
59 uint32_t nvu_elem;
60 uint64_t nvu_value;
61};
62
63struct nvlist {
64 uint32_t nvl_unknown[3];
65 struct nvpair nvl_nvpair;
66};
67
68#define nvdebug(fmt, ...) do { } while(0)
0540ea54 69/*#define nvdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
a1fbeb3d
AD
70
71static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
72{
73 struct nvlist *nvl;
74 struct nvpair *nvp;
538a2fe9 75 size_t left = 4096;
a1fbeb3d
AD
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
538a2fe9 99 nvdebug("left %zd nvp_size %u\n", left, nvp->nvp_size);
a1fbeb3d
AD
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);
109df14f
SK
113 if (nvs->nvs_strlen > UINT_MAX - sizeof(*nvs))
114 break;
a1fbeb3d
AD
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 }
538a2fe9
KZ
155 if (left > nvp->nvp_size)
156 left -= nvp->nvp_size;
157 else
158 left = 0;
a1fbeb3d
AD
159 nvp = (struct nvpair *)((char *)nvp + nvp->nvp_size);
160 }
161}
162
163#define zdebug(fmt, ...) do {} while(0)
0540ea54 164/*#define zdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
a1fbeb3d 165
e44a4c7a
MH
166static 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. */
5dd705ba 197static int probe_zfs(blkid_probe pr, const struct blkid_idmag *mag)
1f4da8d2 198{
e44a4c7a 199 int swab_endian = 0;
1f4da8d2 200 struct zfs_uberblock *ub;
59cbbd71 201 loff_t offset, ub_offset = 0;
e44a4c7a
MH
202 int label_no, found = 0, found_in_label;
203 void *label;
5dd705ba 204 loff_t blk_align = (pr->size % (256 * 1024ULL));
1f4da8d2 205
a1fbeb3d 206 zdebug("probe_zfs\n");
e44a4c7a
MH
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;
5dd705ba 212 break;
e44a4c7a
MH
213 case 1: // jump to L1
214 offset = VDEV_LABEL_SIZE;
5dd705ba 215 break;
e44a4c7a
MH
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;
5dd705ba
MH
221 break;
222 }
a1fbeb3d 223
e44a4c7a
MH
224 label = blkid_probe_get_buffer(pr, offset, VDEV_LABEL_SIZE);
225 if (label == NULL)
37f40602 226 return errno ? -errno : 1;
a1fbeb3d 227
e44a4c7a 228 found_in_label = find_uberblocks(label, &ub_offset, &swab_endian);
a1fbeb3d 229
e44a4c7a
MH
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;
983a43e0 237 }
a1fbeb3d
AD
238 }
239
e44a4c7a 240 if (found < ZFS_WANT)
37f40602 241 return 1;
1f4da8d2 242
a1fbeb3d
AD
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
983a43e0 250 if (blkid_probe_set_magic(pr, ub_offset,
abc67e06
KZ
251 sizeof(ub->ub_magic),
252 (unsigned char *) &ub->ub_magic))
37f40602 253 return 1;
abc67e06 254
1f4da8d2
AD
255 return 0;
256}
257
258const struct blkid_idinfo zfs_idinfo =
259{
a1fbeb3d
AD
260 .name = "zfs_member",
261 .usage = BLKID_USAGE_RAID,
1f4da8d2 262 .probefunc = probe_zfs,
8c2b156e 263 .minsz = 64 * 1024 * 1024,
a1fbeb3d 264 .magics = BLKID_NONE_MAGIC
1f4da8d2 265};