bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 1 | /* |
| 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> |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 15 | #include <limits.h> |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 16 | |
| 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! */ |
| 25 | struct 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 | |
| 40 | struct 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 | |
| 49 | struct nvstring { |
| 50 | uint32_t nvs_type; |
| 51 | uint32_t nvs_elem; |
| 52 | uint32_t nvs_strlen; |
| 53 | unsigned char nvs_string[0]; |
| 54 | }; |
| 55 | |
| 56 | struct nvuint64 { |
| 57 | uint32_t nvu_type; |
| 58 | uint32_t nvu_elem; |
| 59 | uint64_t nvu_value; |
| 60 | }; |
| 61 | |
| 62 | struct nvlist { |
| 63 | uint32_t nvl_unknown[3]; |
| 64 | struct nvpair nvl_nvpair; |
| 65 | }; |
| 66 | |
| 67 | #define nvdebug(fmt, ...) do { } while(0) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 68 | /*#define nvdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/ |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 69 | |
| 70 | static 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); |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 112 | if (nvs->nvs_strlen > UINT_MAX - sizeof(*nvs)) |
| 113 | break; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 114 | 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) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 163 | /*#define zdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/ |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 164 | |
| 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. */ |
| 168 | static 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; |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 174 | loff_t offset, ub_offset = 0; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 175 | 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) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 191 | return errno ? -errno : 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 192 | |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 193 | if (ub->ub_magic == UBERBLOCK_MAGIC) { |
| 194 | ub_offset = offset; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 195 | found++; |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 196 | } |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 197 | |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 198 | if ((swab_endian = (ub->ub_magic == swab_magic))) { |
| 199 | ub_offset = offset; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 200 | found++; |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 201 | } |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 202 | |
| 203 | zdebug("probe_zfs: found %s-endian uberblock at %llu\n", |
| 204 | swab_endian ? "big" : "little", offset >> 10); |
| 205 | } |
| 206 | |
| 207 | if (found < 4) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 208 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 209 | |
| 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 | |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 217 | if (blkid_probe_set_magic(pr, ub_offset, |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 218 | sizeof(ub->ub_magic), |
| 219 | (unsigned char *) &ub->ub_magic)) |
bigbiff | 7b4c7a6 | 2015-01-01 19:44:14 -0500 | [diff] [blame] | 220 | return 1; |
bigbiff bigbiff | e60683a | 2013-02-22 20:55:50 -0500 | [diff] [blame] | 221 | |
| 222 | return 0; |
| 223 | } |
| 224 | |
| 225 | const 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 | |