]>
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 static int zfs_process_value(blkid_probe pr
, char *name
, size_t namelen
,
69 void *value
, size_t max_value_size
)
71 if (strncmp(name
, "name", namelen
) == 0 &&
72 sizeof(struct nvstring
) <= max_value_size
) {
73 struct nvstring
*nvs
= value
;
74 uint32_t nvs_type
= be32_to_cpu(nvs
->nvs_type
);
75 uint32_t nvs_strlen
= be32_to_cpu(nvs
->nvs_strlen
);
77 if (nvs_type
!= DATA_TYPE_STRING
||
78 (uint64_t)nvs_strlen
+ sizeof(*nvs
) > max_value_size
)
81 DBG(LOWPROBE
, ul_debug("nvstring: type %u string %*s\n",
82 nvs_type
, nvs_strlen
, nvs
->nvs_string
));
84 blkid_probe_set_label(pr
, nvs
->nvs_string
, nvs_strlen
);
87 } else if (strncmp(name
, "guid", namelen
) == 0 &&
88 sizeof(struct nvuint64
) <= max_value_size
) {
89 struct nvuint64
*nvu
= value
;
90 uint32_t nvu_type
= be32_to_cpu(nvu
->nvu_type
);
93 memcpy(&nvu_value
, &nvu
->nvu_value
, sizeof(nvu_value
));
94 nvu_value
= be64_to_cpu(nvu_value
);
96 if (nvu_type
!= DATA_TYPE_UINT64
)
99 DBG(LOWPROBE
, ul_debug("nvuint64: type %u value %"PRIu64
"\n",
100 nvu_type
, nvu_value
));
102 blkid_probe_sprintf_value(pr
, "UUID_SUB",
103 "%"PRIu64
, nvu_value
);
106 } else if (strncmp(name
, "pool_guid", namelen
) == 0 &&
107 sizeof(struct nvuint64
) <= max_value_size
) {
108 struct nvuint64
*nvu
= value
;
109 uint32_t nvu_type
= be32_to_cpu(nvu
->nvu_type
);
112 memcpy(&nvu_value
, &nvu
->nvu_value
, sizeof(nvu_value
));
113 nvu_value
= be64_to_cpu(nvu_value
);
115 if (nvu_type
!= DATA_TYPE_UINT64
)
118 DBG(LOWPROBE
, ul_debug("nvuint64: type %u value %"PRIu64
"\n",
119 nvu_type
, nvu_value
));
121 blkid_probe_sprintf_uuid(pr
, (unsigned char *) &nvu_value
,
123 "%"PRIu64
, nvu_value
);
130 static void zfs_extract_guid_name(blkid_probe pr
, loff_t offset
)
138 offset
= (offset
& ~(VDEV_LABEL_SIZE
- 1)) + VDEV_LABEL_NVPAIR
;
140 /* Note that we currently assume that the desired fields are within
141 * the first 4k (left) of the nvlist. This is true for all pools
142 * I've seen, and simplifies this code somewhat, because we don't
143 * have to handle an nvpair crossing a buffer boundary. */
144 p
= blkid_probe_get_buffer(pr
, offset
, left
);
148 DBG(LOWPROBE
, ul_debug("zfs_extract: nvlist offset %jd\n",
151 nvl
= (struct nvlist
*) p
;
152 nvp
= &nvl
->nvl_nvpair
;
153 left
-= (unsigned char *)nvp
- p
; /* Already used up 12 bytes */
155 while (left
> sizeof(*nvp
) && nvp
->nvp_size
!= 0 && found
< 3) {
156 uint32_t nvp_size
= be32_to_cpu(nvp
->nvp_size
);
157 uint32_t nvp_namelen
= be32_to_cpu(nvp
->nvp_namelen
);
158 uint64_t namesize
= ((uint64_t)nvp_namelen
+ 3) & ~3;
159 size_t max_value_size
;
162 DBG(LOWPROBE
, ul_debug("left %zd nvp_size %u\n",
165 /* nvpair fits in buffer and name fits in nvpair? */
166 if (nvp_size
> left
|| namesize
+ sizeof(*nvp
) > nvp_size
)
170 ul_debug("nvlist: size %u, namelen %u, name %*s\n",
171 nvp_size
, nvp_namelen
, nvp_namelen
,
174 max_value_size
= nvp_size
- (namesize
+ sizeof(*nvp
));
175 value
= nvp
->nvp_name
+ namesize
;
177 found
+= zfs_process_value(pr
, nvp
->nvp_name
, nvp_namelen
,
178 value
, max_value_size
);
182 nvp
= (struct nvpair
*)((char *)nvp
+ nvp_size
);
186 static int find_uberblocks(const void *label
, loff_t
*ub_offset
, int *swap_endian
)
188 uint64_t swab_magic
= swab64((uint64_t)UBERBLOCK_MAGIC
);
189 struct zfs_uberblock
*ub
;
191 loff_t offset
= VDEV_LABEL_UBERBLOCK
;
193 for (i
= 0; i
< UBERBLOCKS_COUNT
; i
++, offset
+= UBERBLOCK_SIZE
) {
194 ub
= (struct zfs_uberblock
*)((char *) label
+ offset
);
196 if (ub
->ub_magic
== UBERBLOCK_MAGIC
) {
200 DBG(LOWPROBE
, ul_debug("probe_zfs: found little-endian uberblock at %jd\n", (intmax_t)offset
>> 10));
203 if (ub
->ub_magic
== swab_magic
) {
207 DBG(LOWPROBE
, ul_debug("probe_zfs: found big-endian uberblock at %jd\n", (intmax_t)offset
>> 10));
214 /* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start
215 * of the disk, and 2 areas at the end of the disk. Check only some of them...
216 * #4 (@ 132kB) is the first one written on a new filesystem. */
217 static int probe_zfs(blkid_probe pr
,
218 const struct blkid_idmag
*mag
__attribute__((__unused__
)))
221 struct zfs_uberblock
*ub
;
222 loff_t offset
= 0, ub_offset
= 0;
223 int label_no
, found
= 0, found_in_label
;
225 loff_t blk_align
= (pr
->size
% (256 * 1024ULL));
227 DBG(PROBE
, ul_debug("probe_zfs\n"));
228 /* Look for at least 4 uberblocks to ensure a positive match */
229 for (label_no
= 0; label_no
< 4; label_no
++) {
231 case 0: // jump to L0
234 case 1: // jump to L1
235 offset
= VDEV_LABEL_SIZE
;
237 case 2: // jump to L2
238 offset
= pr
->size
- 2 * VDEV_LABEL_SIZE
- blk_align
;
240 case 3: // jump to L3
241 offset
= pr
->size
- VDEV_LABEL_SIZE
- blk_align
;
245 label
= blkid_probe_get_buffer(pr
, offset
, VDEV_LABEL_SIZE
);
247 return errno
? -errno
: 1;
249 found_in_label
= find_uberblocks(label
, &ub_offset
, &swab_endian
);
251 if (found_in_label
> 0) {
252 found
+= found_in_label
;
253 ub
= (struct zfs_uberblock
*)((char *) label
+ ub_offset
);
256 if (found
>= ZFS_WANT
)
261 if (found
< ZFS_WANT
)
264 /* If we found the 4th uberblock, then we will have exited from the
265 * scanning loop immediately, and ub will be a valid uberblock. */
266 blkid_probe_sprintf_version(pr
, "%" PRIu64
, swab_endian
?
267 swab64(ub
->ub_version
) : ub
->ub_version
);
269 zfs_extract_guid_name(pr
, offset
);
271 if (blkid_probe_set_magic(pr
, ub_offset
,
272 sizeof(ub
->ub_magic
),
273 (unsigned char *) &ub
->ub_magic
))
279 const struct blkid_idinfo zfs_idinfo
=
281 .name
= "zfs_member",
282 .usage
= BLKID_USAGE_FILESYSTEM
,
283 .probefunc
= probe_zfs
,
284 .minsz
= 64 * 1024 * 1024,
285 .magics
= BLKID_NONE_MAGIC