void report(int fd) { uint8_t *vdit, *vdit2, *tmpvdit; size_t vditsize, vditsize2; struct vdm_label *dl; struct vdm_boot_info *bi; struct disklabel *lp; uint8_t sector[VDM_BLOCK_SIZE]; struct vdit_block_header *hdr; read_sector(fd, VDM_LABEL_SECTOR, sector); analyze_label_sector(sector, &dl, &bi); if (dl == NULL) return; printf("label version %04x\n", betoh16(dl->version)); if (bi != NULL) printf("disk boot info: start %08x size %08x version %08x\n", betoh32(bi->boot_start), betoh32(bi->boot_size), betoh32(bi->version)); read_sector(fd, VDIT_SECTOR, sector); hdr = (struct vdit_block_header *)sector; if (VDM_ID_KIND(&hdr->id) != VDIT_BLOCK_HEAD_BE) { lp = (struct disklabel *)(sector + LABELOFFSET); if (lp->d_magic == DISKMAGIC && lp->d_magic2 == DISKMAGIC) { if (verbose) printf("no VDIT but a native OpenBSD label\n"); return; } errx(3, "unexpected block kind on sector %08x: %02x", 1, VDM_ID_KIND(&hdr->id)); } vdit = read_vdit(fd, 1, &vditsize); if (vdit != NULL) { tmpvdit = vdit; while (tmpvdit != NULL) tmpvdit = print_vdit_entry(tmpvdit); vdit2 = read_vdit(fd, betoh32(hdr->secondary_vdit), &vditsize2); if (vdit2 == NULL) printf("can't read backup VDIT\n"); else { if (vditsize2 < vditsize) { printf("WARNING: backup VDIT is smaller " "than main VDIT!\n"); vditsize = vditsize2; } if (memcmp(vdit, vdit2, vditsize) != 0) printf("VDIT and backup VDIT differ!\n"); free(vdit2); } free(vdit); } }
uint8_t * append_vdit_portion(int fd, uint32_t secno, uint8_t *buf, uint8_t *sector, int kind) { struct vdit_block_header *hdr; u_int chunksz; hdr = (struct vdit_block_header *)sector; if (VDM_ID_KIND(&hdr->id) != kind) { printf("unexpected block kind on sector %08x: %02x\n", secno, VDM_ID_KIND(&hdr->id)); return NULL; } /* store first sector of the portion */ chunksz = betoh16(hdr->chunksz); buf = append_vdit_sector(secno, buf, sector, kind); chunksz--; secno++; /* do the others */ while (chunksz-- != 0) { read_sector(fd, secno, sector); buf = append_vdit_sector(secno, buf, sector, VDIT_BLOCK); if (buf == NULL) return NULL; secno++; } return buf; }
uint32_t get_vdit_size(int fd, uint32_t secno) { uint8_t sector[VDM_BLOCK_SIZE]; struct vdit_block_header *hdr; uint32_t cursize = 0; int kind = VDIT_BLOCK_HEAD_BE; for (;;) { read_sector(fd, secno, sector); hdr = (struct vdit_block_header *)sector; if (VDM_ID_KIND(&hdr->id) != kind) { printf("unexpected VDIT block kind " "on sector %08x: %02x\n", secno, VDM_ID_KIND(&hdr->id)); return 0; } if (verbose) printf("sector %08x: vdit frag type %02x, length %04x, " "next frag at %08x\n", secno, VDM_ID_KIND(&hdr->id), betoh16(hdr->chunksz), secno); cursize += betoh16(hdr->chunksz); if (betoh32(hdr->nextblk) == VDM_NO_BLK_NUMBER) break; secno = betoh32(hdr->nextblk); kind = VDIT_PORTION_HEADER_BLOCK; } return cursize; }
uint8_t * append_vdit_sector(uint32_t secno, uint8_t *buf, uint8_t *sector, int kind) { struct vdit_block_header *hdr; hdr = (struct vdit_block_header *)sector; if (VDM_ID_KIND(&hdr->id) != kind) { printf("unexpected block kind on sector %08x: %02x\n", secno, VDM_ID_KIND(&hdr->id)); return NULL; } #ifdef DEBUG printf("sector %08x: vdit block %08x\n", secno, VDM_ID_BLKNO(&hdr->id)); #endif memcpy(buf, sector + sizeof *hdr, VDM_BLOCK_SIZE - (sizeof *hdr)); return buf + VDM_BLOCK_SIZE - (sizeof *hdr); }
/* * Process a contiguous chunk of VDIT, verifying and removing each block header * as we go. */ char * extract_vdit_portion(char *dst, const char *src, unsigned int nsec, unsigned int vdit_blkno, int kind) { struct vdit_block_header *vbh; for (; nsec != 0; nsec--) { vbh = (struct vdit_block_header *)src; if (VDM_ID_KIND(&vbh->id) != kind || VDM_ID_BLKNO(&vbh->id) != vdit_blkno || vbh->id.node_number != VDM_NO_NODE_NUMBER) return NULL; kind = VDIT_BLOCK; memcpy(dst, src + sizeof *vbh, dbtob(1) - sizeof *vbh); dst += dbtob(1) - sizeof *vbh; src += dbtob(1); vdit_blkno++; } return dst; }
/* * Search for a VDIT volume information. If one is found, search for a * vdmpart instance of name "OpenBSD". If one is found, set the disklabel * bounds to the area it spans, and attempt to read a native label within * it. */ int readvditlabel(struct buf *bp, void (*strat)(struct buf *), struct disklabel *lp, daddr_t *partoffp, int spoofonly, struct vdm_boot_info *vbi) { struct buf *sbp = NULL; struct vdit_block_header *vbh; struct vdit_entry_header *veh; char *vdit_storage = NULL, *vdit_end; size_t vdit_size; unsigned int largest_chunk, vdit_blkno; int expected_kind; daddr_t blkno; int error = 0; vdit_id_t *vdmpart_id; struct vdit_vdmpart_instance *bsd_vdmpart; /* * Figure out the size of the first VDIT. */ vdit_size = largest_chunk = 0; expected_kind = VDIT_BLOCK_HEAD_BE; blkno = VDIT_SECTOR; for (;;) { error = readdisksector(bp, strat, lp, DL_BLKTOSEC(lp, blkno)); if (error) return (error); vbh = (struct vdit_block_header *)bp->b_data; if (VDM_ID_KIND(&vbh->id) != expected_kind || VDM_ID_BLKNO(&vbh->id) != vdit_size || vbh->id.node_number != VDM_NO_NODE_NUMBER) return EINVAL; if (vbi != NULL) { if ((blkno >= vbi->boot_start && blkno < vbi->boot_start + vbi->boot_size) || (blkno + vbh->chunksz - 1 >= vbi->boot_start && blkno + vbh->chunksz - 1 < vbi->boot_start + vbi->boot_size)) return EINVAL; } if (vbh->chunksz > largest_chunk) largest_chunk = vbh->chunksz; vdit_size += vbh->chunksz; if (vbh->nextblk == VDM_NO_BLK_NUMBER) break; blkno = vbh->nextblk; if (blkno >= DL_GETDSIZE(lp)) return EINVAL; expected_kind = VDIT_PORTION_HEADER_BLOCK; } /* * Now read the first VDIT. */ vdit_size *= dbtob(1) - sizeof(struct vdit_block_header); vdit_storage = malloc(vdit_size, M_DEVBUF, M_WAITOK); largest_chunk = dbtob(largest_chunk); sbp = geteblk(largest_chunk); sbp->b_dev = bp->b_dev; vdit_end = vdit_storage; expected_kind = VDIT_BLOCK_HEAD_BE; blkno = VDIT_SECTOR; vdit_blkno = 0; for (;;) { sbp->b_blkno = blkno; sbp->b_bcount = largest_chunk; CLR(sbp->b_flags, B_READ | B_WRITE | B_DONE); SET(sbp->b_flags, B_BUSY | B_READ | B_RAW); (*strat)(sbp); if ((error = biowait(sbp)) != 0) goto done; vbh = (struct vdit_block_header *)sbp->b_data; if (VDM_ID_KIND(&vbh->id) != expected_kind) { error = EINVAL; goto done; } vdit_end = extract_vdit_portion(vdit_end, sbp->b_data, vbh->chunksz, vdit_blkno, expected_kind); if (vdit_end == NULL) { error = EINVAL; goto done; } if (vbh->nextblk == VDM_NO_BLK_NUMBER) break; vdit_blkno += vbh->chunksz; blkno = vbh->nextblk; expected_kind = VDIT_PORTION_HEADER_BLOCK; } /* * Walk the VDIT entries. * * If we find an OpenBSD vdmpart, we'll set our disk area bounds to * its area, and will read a label from there. */ vdmpart_id = NULL; bsd_vdmpart = NULL; veh = (struct vdit_entry_header *)vdit_storage; while ((caddr_t)veh < vdit_end) { switch (veh->type) { case VDIT_ENTRY_SUBDRIVER_INFO: { struct vdit_subdriver_entry *vse; vse = (struct vdit_subdriver_entry *)(veh + 1); if (strcmp(vse->name, VDM_SUBDRIVER_VDMPART) == 0) vdmpart_id = &vse->subdriver_id; } break; case VDIT_ENTRY_INSTANCE: { struct vdit_instance_entry *vie; vie = (struct vdit_instance_entry *)(veh + 1); if (strcmp(vie->name, VDM_INSTANCE_OPENBSD) == 0) { if (vdmpart_id != NULL && memcmp(vdmpart_id, &vie->subdriver_id, sizeof(vdit_id_t)) == 0) { /* found it! */ if (bsd_vdmpart != NULL) { bsd_vdmpart = NULL; veh->type = VDIT_ENTRY_SENTINEL; } else bsd_vdmpart = (struct vdit_vdmpart_instance *)vie; } } } break; } if (veh->type == VDIT_ENTRY_SENTINEL) break; veh = (struct vdit_entry_header *)((char *)veh + veh->size); } if (bsd_vdmpart != NULL) { uint32_t start, size; memcpy(&start, &bsd_vdmpart->start_blkno, sizeof(uint32_t)); memcpy(&size, &bsd_vdmpart->size, sizeof(uint32_t)); if (start >= DL_GETDSIZE(lp) || start + size > DL_GETDSIZE(lp)) { error = EINVAL; goto done; } if (partoffp != NULL) { *partoffp = start; goto done; } else { DL_SETBSTART(lp, start); DL_SETBEND(lp, start + size); } /* * Now read the native label. */ if (spoofonly == 0) { error = readdisksector(bp, strat, lp, DL_BLKTOSEC(lp, start + LABELSECTOR)); if (error) goto done; error = checkdisklabel(bp->b_data + LABELOFFSET, lp, start, start + size); } } else { /* * VDM label, but no OpenBSD vdmpart partition found. * XXX is it worth registering the whole disk as a * XXX `don't touch' vendor partition in that case? */ error = ENOENT; goto done; } done: free(vdit_storage, M_DEVBUF, vdit_size); if (sbp != NULL) { sbp->b_flags |= B_INVAL; brelse(sbp); } return error; }