static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag) { unsigned char *ext; const unsigned char *data; int version = 0, swabme = 0; data = blkid_probe_get_buffer(pr, 1024, max(sizeof(struct minix_super_block), sizeof(struct minix3_super_block))); if (!data) return errno ? -errno : 1; version = get_minix_version(data, &swabme); if (version < 1) return 1; if (version <= 2) { struct minix_super_block *sb = (struct minix_super_block *) data; int zones, ninodes, imaps, zmaps, firstz; if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0 || sb->s_log_zone_size != 0) return 1; zones = version == 2 ? minix_swab32(swabme, sb->s_zones) : minix_swab16(swabme, sb->s_nzones); ninodes = minix_swab16(swabme, sb->s_ninodes); imaps = minix_swab16(swabme, sb->s_imap_blocks); zmaps = minix_swab16(swabme, sb->s_zmap_blocks); firstz = minix_swab16(swabme, sb->s_firstdatazone); /* sanity checks to be sure that the FS is really minix */ if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1) return 1; if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1) return 1; } else if (version == 3) { struct minix3_super_block *sb = (struct minix3_super_block *) data; if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0) return 1; } /* unfortunately, some parts of ext3 is sometimes possible to * interpreted as minix superblock. So check for extN magic * string. (For extN magic string and offsets see ext.c.) */ ext = blkid_probe_get_buffer(pr, 0x400 + 0x38, 2); if (!ext) return errno ? -errno : 1; else if (memcmp(ext, "\123\357", 2) == 0) return 1; blkid_probe_sprintf_version(pr, "%d", version); return 0; }
/* * Look for LABEL (name) in the FAT root directory. */ static unsigned char *search_fat_label(blkid_probe pr, uint64_t offset, uint32_t entries) { struct vfat_dir_entry *ent, *dir = NULL; uint32_t i; DBG(DEBUG_LOWPROBE, printf("\tlook for label in root-dir " "(entries: %d, offset: %jd)\n", entries, offset)); if (!blkid_probe_is_tiny(pr)) { /* large disk, read whole root directory */ dir = (struct vfat_dir_entry *) blkid_probe_get_buffer(pr, offset, (blkid_loff_t) entries * sizeof(struct vfat_dir_entry)); if (!dir) return NULL; } for (i = 0; i < entries; i++) { /* * The root directory could be relatively large (4-16kB). * Fortunately, the LABEL is usually the first entry in the * directory. On tiny disks we call read() per entry. */ if (!dir) ent = (struct vfat_dir_entry *) blkid_probe_get_buffer(pr, (blkid_loff_t) offset + (i * sizeof(struct vfat_dir_entry)), sizeof(struct vfat_dir_entry)); else ent = &dir[i]; if (!ent || ent->name[0] == 0x00) break; if ((ent->name[0] == FAT_ENTRY_FREE) || (ent->cluster_high != 0 || ent->cluster_low != 0) || ((ent->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)) continue; if ((ent->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) { DBG(DEBUG_LOWPROBE, printf("\tfound fs LABEL at entry %d\n", i)); return ent->name; } } return NULL; }
static int swap_set_info(blkid_probe pr, const char *version) { struct swap_header_v1_2 *hdr; /* Swap header always located at offset of 1024 bytes */ hdr = (struct swap_header_v1_2 *) blkid_probe_get_buffer(pr, 1024, sizeof(struct swap_header_v1_2)); if (!hdr) return -1; /* SWAPSPACE2 - check for wrong version or zeroed pagecount */ if (strcmp(version, "2") == 0 && (hdr->version != 1 || hdr->lastpage == 0)) return -1; /* arbitrary sanity check.. is there any garbage down there? */ if (hdr->padding[32] == 0 && hdr->padding[33] == 0) { if (hdr->volume[0] && blkid_probe_set_label(pr, hdr->volume, sizeof(hdr->volume)) < 0) return -1; if (hdr->uuid[0] && blkid_probe_set_uuid(pr, hdr->uuid) < 0) return -1; } blkid_probe_set_version(pr, version); return 0; }
static int probe_swap(blkid_probe pr, const struct blkid_idmag *mag) { unsigned char *buf; if (!mag) return -1; /* TuxOnIce keeps valid swap header at the end of the 1st page */ buf = blkid_probe_get_buffer(pr, 0, TOI_MAGIC_STRLEN); if (!buf) return -1; if (memcmp(buf, TOI_MAGIC_STRING, TOI_MAGIC_STRLEN) == 0) return 1; /* Ignore swap signature, it's TuxOnIce */ if (!memcmp(mag->magic, "SWAP-SPACE", mag->len)) { /* swap v0 doesn't support LABEL or UUID */ blkid_probe_set_version(pr, "0"); return 0; } else if (!memcmp(mag->magic, "SWAPSPACE2", mag->len)) return swap_set_info(pr, "1"); return -1; }
static struct exfat_entry_label *find_label(blkid_probe pr, const struct exfat_super_block *sb) { uint32_t cluster = le32_to_cpu(sb->rootdir_cluster); uint64_t offset = cluster_to_offset(sb, cluster); uint8_t *entry; const size_t max_iter = 10000; size_t i = 0; for (; i < max_iter; i++) { entry = (uint8_t *) blkid_probe_get_buffer(pr, offset, EXFAT_ENTRY_SIZE); if (!entry) return NULL; if (entry[0] == EXFAT_ENTRY_EOD) return NULL; if (entry[0] == EXFAT_ENTRY_LABEL) return (struct exfat_entry_label *) entry; offset += EXFAT_ENTRY_SIZE; if (offset % CLUSTER_SIZE(sb) == 0) { cluster = next_cluster(pr, sb, cluster); if (cluster < EXFAT_FIRST_DATA_CLUSTER) return NULL; if (cluster > EXFAT_LAST_DATA_CLUSTER) return NULL; offset = cluster_to_offset(sb, cluster); } } return NULL; }
static int probe_viaraid(blkid_probe pr, const struct blkid_idmag *mag) { uint64_t off; struct via_metadata *v; if (pr->size < 0x10000) return -1; if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return -1; off = ((pr->size / 0x200)-1) * 0x200; v = (struct via_metadata *) blkid_probe_get_buffer(pr, off, sizeof(struct via_metadata)); if (!v) return -1; if (le16_to_cpu(v->signature) != VIA_SIGNATURE) return -1; if (v->version_number > 2) return -1; if (!via_checksum(v)) return -1; if (blkid_probe_sprintf_version(pr, "%u", v->version_number) != 0) return -1; if (blkid_probe_set_magic(pr, off, sizeof(v->signature), (unsigned char *) &v->signature)) return -1; return 0; }
static inline unsigned char *get_mac_block( blkid_probe pr, struct mac_driver_desc *md, uint32_t num) { return blkid_probe_get_buffer(pr, (blkid_loff_t) num * md->block_size, num); }
static inline unsigned char *get_mac_block( blkid_probe pr, uint16_t block_size, uint32_t num) { return blkid_probe_get_buffer(pr, (blkid_loff_t) num * block_size, block_size); }
/* iso9660 [+ Microsoft Joliet Extension] */ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) { struct iso_volume_descriptor *iso; unsigned char label[32]; int i; int off; if (strcmp(mag->magic, "CDROM") == 0) return probe_iso9660_hsfs(pr, mag); iso = blkid_probe_get_sb(pr, mag, struct iso_volume_descriptor); if (!iso) return -1; memcpy(label, iso->volume_id, sizeof(label)); /* Joliet Extension */ off = ISO_VD_OFFSET; for (i = 0; i < ISO_VD_MAX; i++) { iso = (struct iso_volume_descriptor *) blkid_probe_get_buffer(pr, off, sizeof(struct iso_volume_descriptor)); if (iso == NULL || iso->vd_type == ISO_VD_END) break; if (iso->vd_type != ISO_VD_SUPPLEMENTARY) continue; if (memcmp(iso->escape_sequences, "%/@", 3) == 0 || memcmp(iso->escape_sequences, "%/C", 3) == 0 || memcmp(iso->escape_sequences, "%/E", 3) == 0) { blkid_probe_set_version(pr, "Joliet Extension"); /* Is the Joliet (UTF16BE) label equal to the label in * the PVD? If yes, use PVD label. The Jolied version * of the label could be trimed (because UTF16..). */ if (ascii_eq_utf16be(label, iso->volume_id, 32)) break; blkid_probe_set_utf8label(pr, iso->volume_id, sizeof(iso->volume_id), BLKID_ENC_UTF16BE); goto has_label; } off += ISO_SECTOR_SIZE; } /* Joliet not found, let use standard iso label */ blkid_probe_set_label(pr, label, sizeof(label)); has_label: return 0; }
static uint32_t next_cluster(blkid_probe pr, const struct exfat_super_block *sb, uint32_t cluster) { uint32_t *next; uint64_t fat_offset; fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start)) + (uint64_t) cluster * sizeof(cluster); next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset, sizeof(uint32_t)); if (!next) return 0; return le32_to_cpu(*next); }
/* * reads superblock and returns: * fc = feature_compat * fi = feature_incompat * frc = feature_ro_compat */ static struct ext2_super_block *ext_get_super( blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc) { struct ext2_super_block *es; es = (struct ext2_super_block *) blkid_probe_get_buffer(pr, EXT_SB_OFF, 0x200); if (!es) return NULL; if (fc) *fc = le32_to_cpu(es->s_feature_compat); if (fi) *fi = le32_to_cpu(es->s_feature_incompat); if (frc) *frc = le32_to_cpu(es->s_feature_ro_compat); return es; }
static int probe_lsiraid(blkid_probe pr, const struct blkid_idmag *mag) { uint64_t off; struct lsi_metadata *lsi; if (pr->size < 0x10000) return -1; off = ((pr->size / 0x200) - 1) * 0x200; lsi = (struct lsi_metadata *) blkid_probe_get_buffer(pr, off, sizeof(struct lsi_metadata)); if (!lsi) return -1; if (memcmp(lsi->sig, LSI_SIGNATURE, sizeof(LSI_SIGNATURE)-1) != 0) return -1; return 0; }
static int probe_iswraid(blkid_probe pr, const struct blkid_idmag *mag) { uint64_t off; struct isw_metadata *isw; if (pr->size < 0x10000) return -1; off = ((pr->size / 0x200) - 2) * 0x200; isw = (struct isw_metadata *) blkid_probe_get_buffer(pr, off, sizeof(struct isw_metadata)); if (!isw) return -1; if (memcmp(isw->sig, ISW_SIGNATURE, sizeof(ISW_SIGNATURE)-1) != 0) return -1; if (blkid_probe_sprintf_version(pr, "%6s", &isw->sig[sizeof(ISW_SIGNATURE)-1]) != 0) return -1; return 0; }
static int probe_nvraid(blkid_probe pr, const struct blkid_idmag *mag) { uint64_t off; struct nv_metadata *nv; if (pr->size < 0x10000) return -1; off = ((pr->size / 0x200) - 2) * 0x200; nv = (struct nv_metadata *) blkid_probe_get_buffer(pr, off, sizeof(struct nv_metadata)); if (!nv) return -1; if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0) return -1; if (blkid_probe_sprintf_version(pr, "%u", le16_to_cpu(nv->version)) != 0) return -1; return 0; }
static inline unsigned char *get_lba_buffer(blkid_probe pr, uint64_t lba, size_t bytes) { return blkid_probe_get_buffer(pr, blkid_probe_get_sectorsize(pr) * lba, bytes); }
static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag) { struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; struct hfsplus_bnode_descriptor *descr; struct hfsplus_bheader_record *bnode; struct hfsplus_catalog_key *key; struct hfsplus_vol_header *hfsplus; struct hfs_mdb *sbd; unsigned int alloc_block_size; unsigned int alloc_first_block; unsigned int embed_first_block; unsigned int off = 0; unsigned int blocksize; unsigned int cat_block; unsigned int ext_block_start; unsigned int ext_block_count; unsigned int record_count; unsigned int leaf_node_head; unsigned int leaf_node_count; unsigned int leaf_node_size; unsigned int leaf_block; int ext; uint64_t leaf_off; unsigned char *buf; sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb); if (!sbd) return -1; /* Check for a HFS+ volume embedded in a HFS volume */ if (memcmp(sbd->signature, "BD", 2) == 0) { if ((memcmp(sbd->embed_sig, "H+", 2) != 0) && (memcmp(sbd->embed_sig, "HX", 2) != 0)) /* This must be an HFS volume, so fail */ return 1; alloc_block_size = be32_to_cpu(sbd->al_blk_size); alloc_first_block = be16_to_cpu(sbd->al_bl_st); embed_first_block = be16_to_cpu(sbd->embed_startblock); off = (alloc_first_block * 512) + (embed_first_block * alloc_block_size); buf = blkid_probe_get_buffer(pr, off + (mag->kboff * 1024), sizeof(struct hfsplus_vol_header)); hfsplus = (struct hfsplus_vol_header *) buf; } else hfsplus = blkid_probe_get_sb(pr, mag, struct hfsplus_vol_header); if (!hfsplus) return -1; if ((memcmp(hfsplus->signature, "H+", 2) != 0) && (memcmp(hfsplus->signature, "HX", 2) != 0)) return 1; hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id)); blocksize = be32_to_cpu(hfsplus->blocksize); if (blocksize < HFSPLUS_SECTOR_SIZE) return -1; memcpy(extents, hfsplus->cat_file.extents, sizeof(extents)); cat_block = be32_to_cpu(extents[0].start_block); buf = blkid_probe_get_buffer(pr, off + ((blkid_loff_t) cat_block * blocksize), 0x2000); if (!buf) return 0; bnode = (struct hfsplus_bheader_record *) &buf[sizeof(struct hfsplus_bnode_descriptor)]; leaf_node_head = be32_to_cpu(bnode->leaf_head); leaf_node_size = be16_to_cpu(bnode->node_size); leaf_node_count = be32_to_cpu(bnode->leaf_count); if (leaf_node_count == 0) return 0; leaf_block = (leaf_node_head * leaf_node_size) / blocksize; /* get physical location */ for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) { ext_block_start = be32_to_cpu(extents[ext].start_block); ext_block_count = be32_to_cpu(extents[ext].block_count); if (ext_block_count == 0) return 0; /* this is our extent */ if (leaf_block < ext_block_count) break; leaf_block -= ext_block_count; } if (ext == HFSPLUS_EXTENT_COUNT) return 0; leaf_off = (ext_block_start + leaf_block) * blocksize; buf = blkid_probe_get_buffer(pr, (blkid_loff_t) off + leaf_off, leaf_node_size); if (!buf) return 0; descr = (struct hfsplus_bnode_descriptor *) buf; record_count = be16_to_cpu(descr->num_recs); if (record_count == 0) return 0; if (descr->type != HFS_NODE_LEAF) return 0; key = (struct hfsplus_catalog_key *) &buf[sizeof(struct hfsplus_bnode_descriptor)]; if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID) return 0; return 0; }
static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) { struct nvlist *nvl; struct nvpair *nvp; size_t left = 4096; int found = 0; offset = (offset & ~(VDEV_LABEL_SIZE - 1)) + VDEV_LABEL_NVPAIR; /* Note that we currently assume that the desired fields are within * the first 4k (left) of the nvlist. This is true for all pools * I've seen, and simplifies this code somewhat, because we don't * have to handle an nvpair crossing a buffer boundary. */ nvl = (struct nvlist *)blkid_probe_get_buffer(pr, offset, left); if (nvl == NULL) return; nvdebug("zfs_extract: nvlist offset %llu\n", offset); 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; 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); nvdebug("left %zd nvp_size %u\n", left, nvp->nvp_size); if (left < nvp->nvp_size || avail < 0) break; namesize = (nvp->nvp_namelen + 3) & ~3; nvdebug("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); nvs->nvs_type = be32_to_cpu(nvs->nvs_type); nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen); if (nvs->nvs_strlen > UINT_MAX - sizeof(*nvs)) break; avail -= nvs->nvs_strlen + sizeof(*nvs); nvdebug("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); nvdebug("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); nvdebug("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++; } if (left > nvp->nvp_size) left -= nvp->nvp_size; else left = 0; nvp = (struct nvpair *)((char *)nvp + nvp->nvp_size); } }
/* FAT label extraction from the root directory taken from Kay * Sievers's volume_id library */ static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag) { struct vfat_super_block *vs; struct msdos_super_block *ms; const unsigned char *vol_label = 0; unsigned char *vol_serno = NULL, vol_label_buf[11]; uint16_t sector_size = 0, reserved; uint32_t cluster_count, fat_size; const char *version = NULL; ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block); if (!ms) return errno ? -errno : 1; vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block); if (!vs) return errno ? -errno : 1; if (!fat_valid_superblock(pr, mag, ms, vs, &cluster_count, &fat_size)) return 1; sector_size = unaligned_le16(&ms->ms_sector_size); reserved = le16_to_cpu(ms->ms_reserved); if (ms->ms_fat_length) { /* the label may be an attribute in the root directory */ uint32_t root_start = (reserved + fat_size) * sector_size; uint32_t root_dir_entries = unaligned_le16(&vs->vs_dir_entries); vol_label = search_fat_label(pr, root_start, root_dir_entries); if (vol_label) { memcpy(vol_label_buf, vol_label, 11); vol_label = vol_label_buf; } if (!vol_label || !memcmp(vol_label, no_name, 11)) vol_label = ms->ms_label; vol_serno = ms->ms_serno; blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos", sizeof("msdos")); if (cluster_count < FAT12_MAX) version = "FAT12"; else if (cluster_count < FAT16_MAX) version = "FAT16"; } else if (vs->vs_fat32_length) { unsigned char *buf; uint16_t fsinfo_sect; int maxloop = 100; /* Search the FAT32 root dir for the label attribute */ uint32_t buf_size = vs->vs_cluster_size * sector_size; uint32_t start_data_sect = reserved + fat_size; uint32_t entries = le32_to_cpu(vs->vs_fat32_length) * sector_size / sizeof(uint32_t); uint32_t next = le32_to_cpu(vs->vs_root_cluster); while (next && next < entries && --maxloop) { uint32_t next_sect_off; uint64_t next_off, fat_entry_off; int count; next_sect_off = (next - 2) * vs->vs_cluster_size; next_off = (uint64_t)(start_data_sect + next_sect_off) * sector_size; count = buf_size / sizeof(struct vfat_dir_entry); vol_label = search_fat_label(pr, next_off, count); if (vol_label) { memcpy(vol_label_buf, vol_label, 11); vol_label = vol_label_buf; break; } /* get FAT entry */ fat_entry_off = ((uint64_t) reserved * sector_size) + (next * sizeof(uint32_t)); buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size); if (buf == NULL) break; /* set next cluster */ next = le32_to_cpu(*((uint32_t *) buf)) & 0x0fffffff; } version = "FAT32"; if (!vol_label || !memcmp(vol_label, no_name, 11)) vol_label = vs->vs_label; vol_serno = vs->vs_serno; /* * FAT32 should have a valid signature in the fsinfo block, * but also allow all bytes set to '\0', because some volumes * do not set the signature at all. */ fsinfo_sect = le16_to_cpu(vs->vs_fsinfo_sector); if (fsinfo_sect) { struct fat32_fsinfo *fsinfo; buf = blkid_probe_get_buffer(pr, (blkid_loff_t) fsinfo_sect * sector_size, sizeof(struct fat32_fsinfo)); if (buf == NULL) return errno ? -errno : 1; fsinfo = (struct fat32_fsinfo *) buf; if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 && memcmp(fsinfo->signature1, "\x52\x52\x64\x41", 4) != 0 && memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0) return 1; if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 && memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0) return 1; } } if (vol_label && memcmp(vol_label, no_name, 11)) blkid_probe_set_label(pr, (unsigned char *) vol_label, 11); /* We can't just print them as %04X, because they are unaligned */ if (vol_serno) blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02X%02X-%02X%02X", vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]); if (version) blkid_probe_set_version(pr, version); return 0; }