Exemplo n.º 1
0
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;
}
Exemplo n.º 2
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;
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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;
}
Exemplo n.º 7
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);
}
Exemplo n.º 8
0
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);
}
Exemplo n.º 9
0
/* 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;
}
Exemplo n.º 10
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);
}
Exemplo n.º 11
0
/*
 * 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;
}
Exemplo n.º 12
0
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;
}
Exemplo n.º 13
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;
}
Exemplo n.º 14
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;
}
Exemplo n.º 15
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);
}
Exemplo n.º 16
0
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;
}
Exemplo n.º 17
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);
	}
}
Exemplo n.º 18
0
Arquivo: vfat.c Projeto: Artox/fstools
/* 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;
}