static int disk_probe_slice(struct disk *dp, cdev_t dev, int slice, int reprobe) { struct disk_info *info = &dp->d_info; struct diskslice *sp = &dp->d_slice->dss_slices[slice]; disklabel_ops_t ops; struct partinfo part; const char *msg; char uuid_buf[128]; cdev_t ndev; int sno; u_int i; disk_debug(2, "disk_probe_slice (begin): %s (%s)\n", dev->si_name, dp->d_cdev->si_name); sno = slice ? slice - 1 : 0; ops = &disklabel32_ops; msg = ops->op_readdisklabel(dev, sp, &sp->ds_label, info); if (msg && !strcmp(msg, "no disk label")) { ops = &disklabel64_ops; msg = ops->op_readdisklabel(dev, sp, &sp->ds_label, info); } if (msg == NULL) { if (slice != WHOLE_DISK_SLICE) ops->op_adjust_label_reserved(dp->d_slice, slice, sp); else sp->ds_reserved = 0; sp->ds_ops = ops; for (i = 0; i < ops->op_getnumparts(sp->ds_label); i++) { ops->op_loadpartinfo(sp->ds_label, i, &part); if (part.fstype) { if (reprobe && (ndev = devfs_find_device_by_name("%s%c", dev->si_name, 'a' + i)) ) { /* * Device already exists and * is still valid. */ ndev->si_flags |= SI_REPROBE_TEST; /* * Destroy old UUID alias */ destroy_dev_alias(ndev, "part-by-uuid/*"); /* Create UUID alias */ if (!kuuid_is_nil(&part.storage_uuid)) { snprintf_uuid(uuid_buf, sizeof(uuid_buf), &part.storage_uuid); make_dev_alias(ndev, "part-by-uuid/%s", uuid_buf); udev_dict_set_cstr(ndev, "uuid", uuid_buf); } } else { ndev = make_dev_covering(&disk_ops, dp->d_rawdev->si_ops, dkmakeminor(dkunit(dp->d_cdev), slice, i), UID_ROOT, GID_OPERATOR, 0640, "%s%c", dev->si_name, 'a'+ i); ndev->si_parent = dev; ndev->si_iosize_max = dev->si_iosize_max; ndev->si_disk = dp; udev_dict_set_cstr(ndev, "subsystem", "disk"); /* Inherit parent's disk type */ if (dp->d_disktype) { udev_dict_set_cstr(ndev, "disk-type", __DECONST(char *, dp->d_disktype)); } /* Create serno alias */ if (dp->d_info.d_serialno) { make_dev_alias(ndev, "serno/%s.s%d%c", dp->d_info.d_serialno, sno, 'a' + i); } /* Create UUID alias */ if (!kuuid_is_nil(&part.storage_uuid)) { snprintf_uuid(uuid_buf, sizeof(uuid_buf), &part.storage_uuid); make_dev_alias(ndev, "part-by-uuid/%s", uuid_buf); udev_dict_set_cstr(ndev, "uuid", uuid_buf); } ndev->si_flags |= SI_REPROBE_TEST; } } } } else if (info->d_dsflags & DSO_COMPATLABEL) {
/* * Handle GPT on raw disk. Note that GPTs are not recursive. The MBR is * ignored once a GPT has been detected. * * GPTs always start at block #1, regardless of how the MBR has been set up. * In fact, the MBR's starting block might be pointing to the boot partition * in the GPT rather then to the start of the GPT. * * This routine is called from mbrinit() when a GPT has been detected. */ int gptinit(cdev_t dev, struct disk_info *info, struct diskslices **sspp) { struct buf *bp1 = NULL; struct buf *bp2 = NULL; struct gpt_hdr *gpt; struct gpt_ent *ent; struct diskslice *sp; struct diskslices *ssp; cdev_t wdev; int error; uint32_t len; uint32_t entries; uint32_t entsz; uint32_t crc; uint32_t table_lba; uint32_t table_blocks; int i = 0, j; const char *dname; /* * The GPT starts in sector 1. */ wdev = dev; dname = dev_dname(wdev); bp1 = geteblk((int)info->d_media_blksize); bp1->b_bio1.bio_offset = info->d_media_blksize; bp1->b_bio1.bio_done = biodone_sync; bp1->b_bio1.bio_flags |= BIO_SYNC; bp1->b_bcount = info->d_media_blksize; bp1->b_cmd = BUF_CMD_READ; dev_dstrategy(wdev, &bp1->b_bio1); if (biowait(&bp1->b_bio1, "gptrd") != 0) { kprintf("%s: reading GPT @ block 1: error %d\n", dname, bp1->b_error); error = EIO; goto done; } /* * Header sanity check */ gpt = (void *)bp1->b_data; len = le32toh(gpt->hdr_size); if (len < GPT_MIN_HDR_SIZE || len > info->d_media_blksize) { kprintf("%s: Illegal GPT header size %d\n", dname, len); error = EINVAL; goto done; } crc = le32toh(gpt->hdr_crc_self); gpt->hdr_crc_self = 0; if (crc32(gpt, len) != crc) { kprintf("%s: GPT CRC32 did not match\n", dname); error = EINVAL; goto done; } /* * Validate the partition table and its location, then read it * into a buffer. */ entries = le32toh(gpt->hdr_entries); entsz = le32toh(gpt->hdr_entsz); table_lba = le32toh(gpt->hdr_lba_table); table_blocks = (entries * entsz + info->d_media_blksize - 1) / info->d_media_blksize; if (entries < 1 || entries > 128 || entsz < 128 || (entsz & 7) || entsz > MAXBSIZE / entries || table_lba < 2 || table_lba + table_blocks > info->d_media_blocks) { kprintf("%s: GPT partition table is out of bounds\n", dname); error = EINVAL; goto done; } /* * XXX subject to device dma size limitations */ bp2 = geteblk((int)(table_blocks * info->d_media_blksize)); bp2->b_bio1.bio_offset = (off_t)table_lba * info->d_media_blksize; bp2->b_bio1.bio_done = biodone_sync; bp2->b_bio1.bio_flags |= BIO_SYNC; bp2->b_bcount = table_blocks * info->d_media_blksize; bp2->b_cmd = BUF_CMD_READ; dev_dstrategy(wdev, &bp2->b_bio1); if (biowait(&bp2->b_bio1, "gptrd") != 0) { kprintf("%s: reading GPT partition table @ %lld: error %d\n", dname, (long long)bp2->b_bio1.bio_offset, bp2->b_error); error = EIO; goto done; } /* * We are passed a pointer to a minimal slices struct. Replace * it with a maximal one (128 slices + special slices). Well, * really there is only one special slice (the WHOLE_DISK_SLICE) * since we use the compatibility slice for s0, but don't quibble. * */ kfree(*sspp, M_DEVBUF); ssp = *sspp = dsmakeslicestruct(BASE_SLICE+128, info); /* * Create a slice for each partition. */ for (i = 0; i < (int)entries && i < 128; ++i) { struct gpt_ent sent; char partname[2]; char *sname; ent = (void *)((char *)bp2->b_data + i * entsz); le_uuid_dec(&ent->ent_type, &sent.ent_type); le_uuid_dec(&ent->ent_uuid, &sent.ent_uuid); sent.ent_lba_start = le64toh(ent->ent_lba_start); sent.ent_lba_end = le64toh(ent->ent_lba_end); sent.ent_attr = le64toh(ent->ent_attr); for (j = 0; j < NELEM(ent->ent_name); ++j) sent.ent_name[j] = le16toh(ent->ent_name[j]); /* * The COMPATIBILITY_SLICE is actually slice 0 (s0). This * is a bit weird becaue the whole-disk slice is #1, so * slice 1 (s1) starts at BASE_SLICE. */ if (i == 0) sp = &ssp->dss_slices[COMPATIBILITY_SLICE]; else sp = &ssp->dss_slices[BASE_SLICE+i-1]; sname = dsname(dev, dkunit(dev), WHOLE_DISK_SLICE, WHOLE_SLICE_PART, partname); if (kuuid_is_nil(&sent.ent_type)) continue; if (sent.ent_lba_start < table_lba + table_blocks || sent.ent_lba_end >= info->d_media_blocks || sent.ent_lba_start >= sent.ent_lba_end) { kprintf("%s part %d: unavailable, bad start or " "ending lba\n", sname, i); } else { gpt_setslice(sname, info, sp, &sent); } } ssp->dss_nslices = BASE_SLICE + i; error = 0; done: if (bp1) { bp1->b_flags |= B_INVAL | B_AGE; brelse(bp1); } if (bp2) { bp2->b_flags |= B_INVAL | B_AGE; brelse(bp2); } if (error == EINVAL) error = 0; return (error); }