loader: Implement disk_ioctl() to support DIOCGSECTORSIZE and DIOCGMEDIASIZE.

Need interface to extract information about disk abstraction,
to read disk or partition size depending on the provided argument
and adjust disk size based on information in partition table.

The disk handle from disk_open() has d_offset field to point to
partition start. So we can use this fact to return either whole disk
size or partition size. For this we only need to record partition size
we get from disk_open() anyhow.

In addition, this will also make it possible to adjust the disk media size
based on information from partition table. The problem with disk size is
about some BIOS systems reporting bogus disk size for 2+TB disks, but
since such disks are using GPT partitioning, and GPT does have information
about disk size (alternate LBA + 1), we can use this fact to record disk
size based on partition table.

This patch does exactly this: implements DIOCGSECTORSIZE and DIOCGMEDIASIZE
ioctl, and DIOCGMEDIASIZE will report either disk media size or partition size.

Adds ptable_getsize() call to read partition size in bytes from ptable pointer.
Updates disk_open() to use ptable_getsize() to update mediasize value.

Implements GPT detection function to update ptable size (used by
ptable_getsize()) according to alternate lba (which is location of backup copy
of GPT header table).

Reviewed by:	allanjude
Approved by:	allanjude (mentor)
Differential Revision:	https://reviews.freebsd.org/D8594
This commit is contained in:
Toomas Soome 2017-02-06 08:26:45 +00:00
parent b8fe1f690c
commit c12dbfe608
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=313328
5 changed files with 80 additions and 7 deletions

View File

@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
struct open_disk {
struct ptable *table;
uint64_t mediasize;
uint64_t entrysize;
u_int sectorsize;
u_int flags;
int rcnt;
@ -264,13 +265,28 @@ disk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
}
int
disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *buf)
disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data)
{
struct open_disk *od = dev->d_opendata;
if (dev->d_dev->dv_ioctl)
return ((*dev->d_dev->dv_ioctl)(dev->d_opendata, cmd, buf));
if (od == NULL)
return (ENOTTY);
return (ENXIO);
switch (cmd) {
case DIOCGSECTORSIZE:
*(u_int *)data = od->sectorsize;
break;
case DIOCGMEDIASIZE:
if (dev->d_offset == 0)
*(uint64_t *)data = od->mediasize;
else
*(uint64_t *)data = od->entrysize * od->sectorsize;
break;
default:
return (ENOTTY);
}
return (0);
}
int
@ -315,6 +331,7 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize,
}
dev->d_opendata = od;
od->rcnt = 0;
od->entrysize = 0;
}
od->mediasize = mediasize;
od->sectorsize = sectorsize;
@ -330,14 +347,24 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize,
rc = ENXIO;
goto out;
}
if (ptable_getsize(od->table, &mediasize) != 0) {
rc = ENXIO;
goto out;
}
if (mediasize > od->mediasize) {
od->mediasize = mediasize;
}
opened:
rc = 0;
if (ptable_gettype(od->table) == PTABLE_BSD &&
partition >= 0) {
/* It doesn't matter what value has d_slice */
rc = ptable_getpart(od->table, &part, partition);
if (rc == 0)
if (rc == 0) {
dev->d_offset = part.start;
od->entrysize = part.end - part.start + 1;
}
} else if (slice >= 0) {
/* Try to get information about partition */
if (slice == 0)
@ -347,6 +374,7 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize,
if (rc != 0) /* Partition doesn't exist */
goto out;
dev->d_offset = part.start;
od->entrysize = part.end - part.start + 1;
slice = part.index;
if (ptable_gettype(od->table) == PTABLE_GPT) {
partition = 255;
@ -389,6 +417,7 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize,
if (rc != 0)
goto out;
dev->d_offset += part.start;
od->entrysize = part.end - part.start + 1;
}
out:
if (table != NULL)

View File

@ -310,10 +310,30 @@ ptable_gptread(struct ptable *table, void *dev, diskread_t dread)
DEBUG("GPT detected");
size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
MAXTBLSZ * table->sectorsize);
/*
* If the disk's sector count is smaller than the sector count recorded
* in the disk's GPT table header, set the table->sectors to the value
* recorded in GPT tables. This is done to work around buggy firmware
* that returns truncated disk sizes.
*
* Note, this is still not a foolproof way to get disk's size. For
* example, an image file can be truncated when copied to smaller media.
*/
if (hdr.hdr_lba_alt + 1 > table->sectors)
table->sectors = hdr.hdr_lba_alt + 1;
for (i = 0; i < size / hdr.hdr_entsz; i++) {
ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);
if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
continue;
/* Simple sanity checks. */
if (ent->ent_lba_start < hdr.hdr_lba_start ||
ent->ent_lba_end > hdr.hdr_lba_end ||
ent->ent_lba_start > ent->ent_lba_end)
continue;
entry = malloc(sizeof(*entry));
if (entry == NULL)
break;
@ -734,6 +754,19 @@ ptable_gettype(const struct ptable *table)
return (table->type);
}
int
ptable_getsize(const struct ptable *table, uint64_t *sizep)
{
uint64_t tmp = table->sectors * table->sectorsize;
if (tmp < table->sectors)
return (EOVERFLOW);
if (sizep != NULL)
*sizep = tmp;
return (0);
}
int
ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
{

View File

@ -70,6 +70,7 @@ struct ptable *ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize,
diskread_t *dread);
void ptable_close(struct ptable *table);
enum ptable_type ptable_gettype(const struct ptable *table);
int ptable_getsize(const struct ptable *table, uint64_t *sizep);
int ptable_getpart(const struct ptable *table, struct ptable_entry *part,
int index);

View File

@ -137,10 +137,20 @@ umass_disk_open(struct open_file *f,...)
}
static int
umass_disk_ioctl(struct open_file *f __unused, u_long cmd, void *buf)
umass_disk_ioctl(struct open_file *f, u_long cmd, void *buf)
{
struct disk_devdesc *dev;
uint32_t nblock;
uint32_t blocksize;
int rc;
dev = (struct disk_devdesc *)(f->f_devdata);
if (dev == NULL)
return (EINVAL);
rc = disk_ioctl(dev, cmd, buf);
if (rc != ENOTTY)
return (rc);
switch (cmd) {
case DIOCGSECTORSIZE:

View File

@ -483,7 +483,7 @@ zfs_probe_dev(const char *devname, uint64_t *pool_guid)
{
struct ptable *table;
struct zfs_probe_args pa;
off_t mediasz;
uint64_t mediasz;
int ret;
pa.fd = open(devname, O_RDONLY);