blob: 86da59d4a08bf17609707223a145e3922a5c0243 [file] [log] [blame]
bigbiff bigbiffe60683a2013-02-22 20:55:50 -05001/*
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>
bigbiff7b4c7a62015-01-01 19:44:14 -050015#include <limits.h>
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050016
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
23/* #include <sys/uberblock_impl.h> */
24#define UBERBLOCK_MAGIC 0x00bab10c /* oo-ba-bloc! */
25struct zfs_uberblock {
26 uint64_t ub_magic; /* UBERBLOCK_MAGIC */
27 uint64_t ub_version; /* SPA_VERSION */
28 uint64_t ub_txg; /* txg of last sync */
29 uint64_t ub_guid_sum; /* sum of all vdev guids */
30 uint64_t ub_timestamp; /* UTC time of last sync */
31 char ub_rootbp; /* MOS objset_phys_t */
32} __attribute__((packed));
33
34#define ZFS_TRIES 64
35#define ZFS_WANT 4
36
37#define DATA_TYPE_UINT64 8
38#define DATA_TYPE_STRING 9
39
40struct nvpair {
41 uint32_t nvp_size;
42 uint32_t nvp_unkown;
43 uint32_t nvp_namelen;
44 char nvp_name[0]; /* aligned to 4 bytes */
45 /* aligned ptr array for string arrays */
46 /* aligned array of data for value */
47};
48
49struct nvstring {
50 uint32_t nvs_type;
51 uint32_t nvs_elem;
52 uint32_t nvs_strlen;
53 unsigned char nvs_string[0];
54};
55
56struct nvuint64 {
57 uint32_t nvu_type;
58 uint32_t nvu_elem;
59 uint64_t nvu_value;
60};
61
62struct nvlist {
63 uint32_t nvl_unknown[3];
64 struct nvpair nvl_nvpair;
65};
66
67#define nvdebug(fmt, ...) do { } while(0)
bigbiff7b4c7a62015-01-01 19:44:14 -050068/*#define nvdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050069
70static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
71{
72 struct nvlist *nvl;
73 struct nvpair *nvp;
74 size_t left = 4096;
75 int found = 0;
76
77 offset = (offset & ~(VDEV_LABEL_SIZE - 1)) + VDEV_LABEL_NVPAIR;
78
79 /* Note that we currently assume that the desired fields are within
80 * the first 4k (left) of the nvlist. This is true for all pools
81 * I've seen, and simplifies this code somewhat, because we don't
82 * have to handle an nvpair crossing a buffer boundary. */
83 nvl = (struct nvlist *)blkid_probe_get_buffer(pr, offset, left);
84 if (nvl == NULL)
85 return;
86
87 nvdebug("zfs_extract: nvlist offset %llu\n", offset);
88
89 nvp = &nvl->nvl_nvpair;
90 while (left > sizeof(*nvp) && nvp->nvp_size != 0 && found < 3) {
91 int avail; /* tracks that name/value data fits in nvp_size */
92 int namesize;
93
94 nvp->nvp_size = be32_to_cpu(nvp->nvp_size);
95 nvp->nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
96 avail = nvp->nvp_size - nvp->nvp_namelen - sizeof(*nvp);
97
98 nvdebug("left %zd nvp_size %u\n", left, nvp->nvp_size);
99 if (left < nvp->nvp_size || avail < 0)
100 break;
101
102 namesize = (nvp->nvp_namelen + 3) & ~3;
103
104 nvdebug("nvlist: size %u, namelen %u, name %*s\n",
105 nvp->nvp_size, nvp->nvp_namelen, nvp->nvp_namelen,
106 nvp->nvp_name);
107 if (strncmp(nvp->nvp_name, "name", nvp->nvp_namelen) == 0) {
108 struct nvstring *nvs = (void *)(nvp->nvp_name+namesize);
109
110 nvs->nvs_type = be32_to_cpu(nvs->nvs_type);
111 nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
bigbiff7b4c7a62015-01-01 19:44:14 -0500112 if (nvs->nvs_strlen > UINT_MAX - sizeof(*nvs))
113 break;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500114 avail -= nvs->nvs_strlen + sizeof(*nvs);
115 nvdebug("nvstring: type %u string %*s\n", nvs->nvs_type,
116 nvs->nvs_strlen, nvs->nvs_string);
117 if (nvs->nvs_type == DATA_TYPE_STRING && avail >= 0)
118 blkid_probe_set_label(pr, nvs->nvs_string,
119 nvs->nvs_strlen);
120 found++;
121 } else if (strncmp(nvp->nvp_name, "guid",
122 nvp->nvp_namelen) == 0) {
123 struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
124 uint64_t nvu_value;
125
126 memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
127 nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
128 nvu_value = be64_to_cpu(nvu_value);
129 avail -= sizeof(*nvu);
130 nvdebug("nvuint64: type %u value %"PRIu64"\n",
131 nvu->nvu_type, nvu_value);
132 if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
133 blkid_probe_sprintf_value(pr, "UUID_SUB",
134 "%"PRIu64, nvu_value);
135 found++;
136 } else if (strncmp(nvp->nvp_name, "pool_guid",
137 nvp->nvp_namelen) == 0) {
138 struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
139 uint64_t nvu_value;
140
141 memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
142 nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
143 nvu_value = be64_to_cpu(nvu_value);
144 avail -= sizeof(*nvu);
145 nvdebug("nvuint64: type %u value %"PRIu64"\n",
146 nvu->nvu_type, nvu_value);
147 if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
148 blkid_probe_sprintf_uuid(pr, (unsigned char *)
149 &nvu_value,
150 sizeof(nvu_value),
151 "%"PRIu64, nvu_value);
152 found++;
153 }
154 if (left > nvp->nvp_size)
155 left -= nvp->nvp_size;
156 else
157 left = 0;
158 nvp = (struct nvpair *)((char *)nvp + nvp->nvp_size);
159 }
160}
161
162#define zdebug(fmt, ...) do {} while(0)
bigbiff7b4c7a62015-01-01 19:44:14 -0500163/*#define zdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500164
165/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start
166 * of the disk, and 2 areas at the end of the disk. Check only some of them...
167 * #4 (@ 132kB) is the first one written on a new filesystem. */
168static int probe_zfs(blkid_probe pr,
169 const struct blkid_idmag *mag __attribute__((__unused__)))
170{
171 uint64_t swab_magic = swab64(UBERBLOCK_MAGIC);
172 struct zfs_uberblock *ub;
173 int swab_endian;
bigbiff7b4c7a62015-01-01 19:44:14 -0500174 loff_t offset, ub_offset = 0;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500175 int tried;
176 int found;
177
178 zdebug("probe_zfs\n");
179 /* Look for at least 4 uberblocks to ensure a positive match */
180 for (tried = found = 0, offset = VDEV_LABEL_UBERBLOCK;
181 tried < ZFS_TRIES && found < ZFS_WANT;
182 tried++, offset += 4096) {
183 /* also try the second uberblock copy */
184 if (tried == (ZFS_TRIES / 2))
185 offset = VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK;
186
187 ub = (struct zfs_uberblock *)
188 blkid_probe_get_buffer(pr, offset,
189 sizeof(struct zfs_uberblock));
190 if (ub == NULL)
bigbiff7b4c7a62015-01-01 19:44:14 -0500191 return errno ? -errno : 1;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500192
bigbiff7b4c7a62015-01-01 19:44:14 -0500193 if (ub->ub_magic == UBERBLOCK_MAGIC) {
194 ub_offset = offset;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500195 found++;
bigbiff7b4c7a62015-01-01 19:44:14 -0500196 }
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500197
bigbiff7b4c7a62015-01-01 19:44:14 -0500198 if ((swab_endian = (ub->ub_magic == swab_magic))) {
199 ub_offset = offset;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500200 found++;
bigbiff7b4c7a62015-01-01 19:44:14 -0500201 }
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500202
203 zdebug("probe_zfs: found %s-endian uberblock at %llu\n",
204 swab_endian ? "big" : "little", offset >> 10);
205 }
206
207 if (found < 4)
bigbiff7b4c7a62015-01-01 19:44:14 -0500208 return 1;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500209
210 /* If we found the 4th uberblock, then we will have exited from the
211 * scanning loop immediately, and ub will be a valid uberblock. */
212 blkid_probe_sprintf_version(pr, "%" PRIu64, swab_endian ?
213 swab64(ub->ub_version) : ub->ub_version);
214
215 zfs_extract_guid_name(pr, offset);
216
bigbiff7b4c7a62015-01-01 19:44:14 -0500217 if (blkid_probe_set_magic(pr, ub_offset,
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500218 sizeof(ub->ub_magic),
219 (unsigned char *) &ub->ub_magic))
bigbiff7b4c7a62015-01-01 19:44:14 -0500220 return 1;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500221
222 return 0;
223}
224
225const struct blkid_idinfo zfs_idinfo =
226{
227 .name = "zfs_member",
228 .usage = BLKID_USAGE_RAID,
229 .probefunc = probe_zfs,
230 .minsz = 64 * 1024 * 1024,
231 .magics = BLKID_NONE_MAGIC
232};
233