]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libblkid/src/superblocks/zfs.c
2 * Copyright (C) 2009-2010 by Andreas Dilger <adilger@sun.com>
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
17 #include "superblocks.h"
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
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
));
38 #define DATA_TYPE_UINT64 8
39 #define DATA_TYPE_STRING 9
45 char nvp_name
[0]; /* aligned to 4 bytes */
46 /* aligned ptr array for string arrays */
47 /* aligned array of data for value */
54 unsigned char nvs_string
[0];
64 uint32_t nvl_unknown
[3];
65 struct nvpair nvl_nvpair
;
68 #define nvdebug(fmt, ...) do { } while(0)
69 /*#define nvdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
71 static void zfs_extract_guid_name(blkid_probe pr
, loff_t offset
)
78 offset
= (offset
& ~(VDEV_LABEL_SIZE
- 1)) + VDEV_LABEL_NVPAIR
;
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
);
88 nvdebug("zfs_extract: nvlist offset %llu\n", offset
);
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 */
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
);
99 nvdebug("left %zd nvp_size %u\n", left
, nvp
->nvp_size
);
100 if (left
< nvp
->nvp_size
|| avail
< 0)
103 namesize
= (nvp
->nvp_namelen
+ 3) & ~3;
105 nvdebug("nvlist: size %u, namelen %u, name %*s\n",
106 nvp
->nvp_size
, nvp
->nvp_namelen
, nvp
->nvp_namelen
,
108 if (strncmp(nvp
->nvp_name
, "name", nvp
->nvp_namelen
) == 0) {
109 struct nvstring
*nvs
= (void *)(nvp
->nvp_name
+namesize
);
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
))
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
,
122 } else if (strncmp(nvp
->nvp_name
, "guid",
123 nvp
->nvp_namelen
) == 0) {
124 struct nvuint64
*nvu
= (void *)(nvp
->nvp_name
+namesize
);
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
);
137 } else if (strncmp(nvp
->nvp_name
, "pool_guid",
138 nvp
->nvp_namelen
) == 0) {
139 struct nvuint64
*nvu
= (void *)(nvp
->nvp_name
+namesize
);
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 *)
152 "%"PRIu64
, nvu_value
);
155 if (left
> nvp
->nvp_size
)
156 left
-= nvp
->nvp_size
;
159 nvp
= (struct nvpair
*)((char *)nvp
+ nvp
->nvp_size
);
163 #define zdebug(fmt, ...) do {} while(0)
164 /*#define zdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
166 static int find_uberblocks(const void *label
, loff_t
*ub_offset
, int *swap_endian
)
168 uint64_t swab_magic
= swab64(UBERBLOCK_MAGIC
);
169 struct zfs_uberblock
*ub
;
171 loff_t offset
= VDEV_LABEL_UBERBLOCK
;
173 for (i
= 0; i
< UBERBLOCKS_COUNT
; i
++, offset
+= UBERBLOCK_SIZE
) {
174 ub
= (struct zfs_uberblock
*)(label
+ offset
);
176 if (ub
->ub_magic
== UBERBLOCK_MAGIC
) {
180 zdebug("probe_zfs: found little-endian uberblock at %llu\n", offset
>> 10);
183 if (ub
->ub_magic
== swab_magic
) {
187 zdebug("probe_zfs: found big-endian uberblock at %llu\n", offset
>> 10);
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
)
200 struct zfs_uberblock
*ub
;
201 loff_t offset
, ub_offset
= 0;
202 int label_no
, found
= 0, found_in_label
;
204 loff_t blk_align
= (pr
->size
% (256 * 1024ULL));
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
++) {
210 case 0: // jump to L0
213 case 1: // jump to L1
214 offset
= VDEV_LABEL_SIZE
;
216 case 2: // jump to L2
217 offset
= pr
->size
- 2 * VDEV_LABEL_SIZE
- blk_align
;
219 case 3: // jump to L3
220 offset
= pr
->size
- VDEV_LABEL_SIZE
- blk_align
;
224 label
= blkid_probe_get_buffer(pr
, offset
, VDEV_LABEL_SIZE
);
226 return errno
? -errno
: 1;
228 found_in_label
= find_uberblocks(label
, &ub_offset
, &swab_endian
);
230 if (found_in_label
> 0) {
231 found
+= found_in_label
;
232 ub
= (struct zfs_uberblock
*)(label
+ ub_offset
);
235 if (found
>= ZFS_WANT
)
240 if (found
< ZFS_WANT
)
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
);
248 zfs_extract_guid_name(pr
, offset
);
250 if (blkid_probe_set_magic(pr
, ub_offset
,
251 sizeof(ub
->ub_magic
),
252 (unsigned char *) &ub
->ub_magic
))
258 const struct blkid_idinfo zfs_idinfo
=
260 .name
= "zfs_member",
261 .usage
= BLKID_USAGE_RAID
,
262 .probefunc
= probe_zfs
,
263 .minsz
= 64 * 1024 * 1024,
264 .magics
= BLKID_NONE_MAGIC