#define DATA_TYPE_UINT64 8
#define DATA_TYPE_STRING 9
+#define DATA_TYPE_DIRECTORY 19
struct nvpair {
uint32_t nvp_size;
uint32_t nvu_type;
uint32_t nvu_elem;
uint64_t nvu_value;
+} __attribute__((packed));
+
+struct nvdirectory {
+ uint32_t nvd_type;
+ uint32_t nvd_unknown[3];
};
struct nvlist {
struct nvpair nvl_nvpair;
};
+static void zfs_process_value(blkid_probe pr, char *name, size_t namelen,
+ void *value, size_t max_value_size, unsigned directory_level)
+{
+ if (strncmp(name, "name", namelen) == 0 &&
+ sizeof(struct nvstring) <= max_value_size &&
+ !directory_level) {
+ struct nvstring *nvs = value;
+ uint32_t nvs_type = be32_to_cpu(nvs->nvs_type);
+ uint32_t nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
+
+ if (nvs_type != DATA_TYPE_STRING ||
+ (uint64_t)nvs_strlen + sizeof(*nvs) > max_value_size)
+ return;
+
+ DBG(LOWPROBE, ul_debug("nvstring: type %u string %*s\n",
+ nvs_type, nvs_strlen, nvs->nvs_string));
+
+ blkid_probe_set_label(pr, nvs->nvs_string, nvs_strlen);
+ } else if (strncmp(name, "guid", namelen) == 0 &&
+ sizeof(struct nvuint64) <= max_value_size &&
+ !directory_level) {
+ struct nvuint64 *nvu = value;
+ uint32_t nvu_type = be32_to_cpu(nvu->nvu_type);
+ uint64_t nvu_value;
+
+ memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+ nvu_value = be64_to_cpu(nvu_value);
+
+ if (nvu_type != DATA_TYPE_UINT64)
+ return;
+
+ DBG(LOWPROBE, ul_debug("nvuint64: type %u value %"PRIu64"\n",
+ nvu_type, nvu_value));
+
+ blkid_probe_sprintf_value(pr, "UUID_SUB",
+ "%"PRIu64, nvu_value);
+ } else if (strncmp(name, "pool_guid", namelen) == 0 &&
+ sizeof(struct nvuint64) <= max_value_size &&
+ !directory_level) {
+ struct nvuint64 *nvu = value;
+ uint32_t nvu_type = be32_to_cpu(nvu->nvu_type);
+ uint64_t nvu_value;
+
+ memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+ nvu_value = be64_to_cpu(nvu_value);
+
+ if (nvu_type != DATA_TYPE_UINT64)
+ return;
+
+ DBG(LOWPROBE, ul_debug("nvuint64: type %u value %"PRIu64"\n",
+ nvu_type, nvu_value));
+
+ blkid_probe_sprintf_uuid(pr, (unsigned char *) &nvu_value,
+ sizeof(nvu_value),
+ "%"PRIu64, nvu_value);
+ } else if (strncmp(name, "ashift", namelen) == 0 &&
+ sizeof(struct nvuint64) <= max_value_size) {
+ struct nvuint64 *nvu = value;
+ uint32_t nvu_type = be32_to_cpu(nvu->nvu_type);
+ uint64_t nvu_value;
+
+ memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+ nvu_value = be64_to_cpu(nvu_value);
+
+ if (nvu_type != DATA_TYPE_UINT64)
+ return;
+
+ if (nvu_value < 32)
+ blkid_probe_set_block_size(pr, 1U << nvu_value);
+ }
+}
+
static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
{
- unsigned char *p, buff[4096];
+ unsigned char *p;
struct nvlist *nvl;
struct nvpair *nvp;
- size_t left = sizeof(buff);
- int found = 0;
+ size_t left = 4096;
+ unsigned directory_level = 0;
offset = (offset & ~(VDEV_LABEL_SIZE - 1)) + VDEV_LABEL_NVPAIR;
if (!p)
return;
- /* libblkid buffers are strictly readonly, but the code below modifies nvpair etc. */
- memcpy(buff, p, sizeof(buff));
- nvl = (struct nvlist *) buff;
-
- DBG(LOWPROBE, ul_debug("zfs_extract: nvlist offset %jd\n", (intmax_t)offset));
+ DBG(LOWPROBE, ul_debug("zfs_extract: nvlist offset %jd\n",
+ (intmax_t)offset));
+ nvl = (struct nvlist *) p;
nvp = &nvl->nvl_nvpair;
- while (left > sizeof(*nvp) && nvp->nvp_size != 0 && found < 3) {
- int avail; /* tracks that name/value data fits in nvp_size */
- int namesize;
+ left -= (unsigned char *)nvp - p; /* Already used up 12 bytes */
- nvp->nvp_size = be32_to_cpu(nvp->nvp_size);
- nvp->nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
- avail = nvp->nvp_size - nvp->nvp_namelen - sizeof(*nvp);
+ while (left > sizeof(*nvp)) {
+ uint32_t nvp_size = be32_to_cpu(nvp->nvp_size);
+ uint32_t nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
+ uint64_t namesize = ((uint64_t)nvp_namelen + 3) & ~3;
+ size_t max_value_size;
+ void *value;
- DBG(LOWPROBE, ul_debug("left %zd nvp_size %u\n", left, nvp->nvp_size));
- if (left < nvp->nvp_size || avail < 0)
- break;
+ if (!nvp->nvp_size) {
+ if (!directory_level)
+ break;
+ directory_level--;
+ nvp_size = 8;
+ goto cont;
+ }
- namesize = (nvp->nvp_namelen + 3) & ~3;
+ DBG(LOWPROBE, ul_debug("left %zd nvp_size %u\n",
+ left, nvp_size));
- DBG(LOWPROBE, ul_debug("nvlist: size %u, namelen %u, name %*s\n",
- nvp->nvp_size, nvp->nvp_namelen, nvp->nvp_namelen,
- nvp->nvp_name));
- if (strncmp(nvp->nvp_name, "name", nvp->nvp_namelen) == 0) {
- struct nvstring *nvs = (void *)(nvp->nvp_name+namesize);
+ /* nvpair fits in buffer and name fits in nvpair? */
+ if (nvp_size > left || namesize + sizeof(*nvp) > nvp_size)
+ break;
- nvs->nvs_type = be32_to_cpu(nvs->nvs_type);
- nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
- if (nvs->nvs_strlen > INT_MAX - sizeof(*nvs))
- break;
- avail -= nvs->nvs_strlen + sizeof(*nvs);
- DBG(LOWPROBE, ul_debug("nvstring: type %u string %*s\n", nvs->nvs_type,
- nvs->nvs_strlen, nvs->nvs_string));
- if (nvs->nvs_type == DATA_TYPE_STRING && avail >= 0)
- blkid_probe_set_label(pr, nvs->nvs_string,
- nvs->nvs_strlen);
- found++;
- } else if (strncmp(nvp->nvp_name, "guid",
- nvp->nvp_namelen) == 0) {
- struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
- uint64_t nvu_value;
-
- memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
- nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
- nvu_value = be64_to_cpu(nvu_value);
- avail -= sizeof(*nvu);
- DBG(LOWPROBE, ul_debug("nvuint64: type %u value %"PRIu64"\n",
- nvu->nvu_type, nvu_value));
- if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
- blkid_probe_sprintf_value(pr, "UUID_SUB",
- "%"PRIu64, nvu_value);
- found++;
- } else if (strncmp(nvp->nvp_name, "pool_guid",
- nvp->nvp_namelen) == 0) {
- struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
- uint64_t nvu_value;
-
- memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
- nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
- nvu_value = be64_to_cpu(nvu_value);
- avail -= sizeof(*nvu);
- DBG(LOWPROBE, ul_debug("nvuint64: type %u value %"PRIu64"\n",
- nvu->nvu_type, nvu_value));
- if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
- blkid_probe_sprintf_uuid(pr, (unsigned char *)
- &nvu_value,
- sizeof(nvu_value),
- "%"PRIu64, nvu_value);
- found++;
+ DBG(LOWPROBE,
+ ul_debug("nvlist: size %u, namelen %u, name %*s\n",
+ nvp_size, nvp_namelen, nvp_namelen,
+ nvp->nvp_name));
+
+ max_value_size = nvp_size - (namesize + sizeof(*nvp));
+ value = nvp->nvp_name + namesize;
+
+ if (sizeof(struct nvdirectory) <= max_value_size) {
+ struct nvdirectory *nvu = value;
+ if (be32_to_cpu(nvu->nvd_type) == DATA_TYPE_DIRECTORY) {
+ nvp_size = sizeof(*nvp) + namesize + sizeof(*nvu);
+ directory_level++;
+ goto cont;
+ }
}
- if (left > nvp->nvp_size)
- left -= nvp->nvp_size;
- else
- left = 0;
- nvp = (struct nvpair *)((char *)nvp + nvp->nvp_size);
+
+ zfs_process_value(pr, nvp->nvp_name, nvp_namelen,
+ value, max_value_size, directory_level);
+
+cont:
+ if (nvp_size > left)
+ break;
+ left -= nvp_size;
+
+ nvp = (struct nvpair *)((char *)nvp + nvp_size);
}
}
static int find_uberblocks(const void *label, loff_t *ub_offset, int *swap_endian)
{
uint64_t swab_magic = swab64((uint64_t)UBERBLOCK_MAGIC);
- struct zfs_uberblock *ub;
+ const struct zfs_uberblock *ub;
int i, found = 0;
loff_t offset = VDEV_LABEL_UBERBLOCK;
for (i = 0; i < UBERBLOCKS_COUNT; i++, offset += UBERBLOCK_SIZE) {
- ub = (struct zfs_uberblock *)((char *) label + offset);
+ ub = (const struct zfs_uberblock *)((const char *) label + offset);
if (ub->ub_magic == UBERBLOCK_MAGIC) {
*ub_offset = offset;
const struct blkid_idmag *mag __attribute__((__unused__)))
{
int swab_endian = 0;
- struct zfs_uberblock *ub;
- loff_t offset, ub_offset = 0;
+ struct zfs_uberblock *ub = NULL;
+ loff_t offset = 0, ub_offset = 0;
int label_no, found = 0, found_in_label;
void *label;
loff_t blk_align = (pr->size % (256 * 1024ULL));
break;
}
+ if ((S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) &&
+ blkid_probe_is_covered_by_pt(pr, offset, VDEV_LABEL_SIZE))
+ /* ignore this area, it's within any partition and
+ * we are working with whole-disk now */
+ continue;
+
label = blkid_probe_get_buffer(pr, offset, VDEV_LABEL_SIZE);
if (label == NULL)
return errno ? -errno : 1;