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