Date: Mon, 6 Feb 2017 08:26:45 +0000 (UTC) From: Toomas Soome <tsoome@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r313328 - in head/sys/boot: common usb/storage zfs Message-ID: <201702060826.v168QjD0056011@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: tsoome Date: Mon Feb 6 08:26:45 2017 New Revision: 313328 URL: https://svnweb.freebsd.org/changeset/base/313328 Log: 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 Modified: head/sys/boot/common/disk.c head/sys/boot/common/part.c head/sys/boot/common/part.h head/sys/boot/usb/storage/umass_loader.c head/sys/boot/zfs/zfs.c Modified: head/sys/boot/common/disk.c ============================================================================== --- head/sys/boot/common/disk.c Mon Feb 6 07:02:17 2017 (r313327) +++ head/sys/boot/common/disk.c Mon Feb 6 08:26:45 2017 (r313328) @@ -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, voi } 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, uint } 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, uint 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 @@ opened: 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 @@ opened: if (rc != 0) goto out; dev->d_offset += part.start; + od->entrysize = part.end - part.start + 1; } out: if (table != NULL) Modified: head/sys/boot/common/part.c ============================================================================== --- head/sys/boot/common/part.c Mon Feb 6 07:02:17 2017 (r313327) +++ head/sys/boot/common/part.c Mon Feb 6 08:26:45 2017 (r313328) @@ -310,10 +310,30 @@ ptable_gptread(struct ptable *table, voi 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; @@ -735,6 +755,19 @@ ptable_gettype(const struct ptable *tabl } 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) { struct pentry *entry; Modified: head/sys/boot/common/part.h ============================================================================== --- head/sys/boot/common/part.h Mon Feb 6 07:02:17 2017 (r313327) +++ head/sys/boot/common/part.h Mon Feb 6 08:26:45 2017 (r313328) @@ -70,6 +70,7 @@ struct ptable *ptable_open(void *dev, ui 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); Modified: head/sys/boot/usb/storage/umass_loader.c ============================================================================== --- head/sys/boot/usb/storage/umass_loader.c Mon Feb 6 07:02:17 2017 (r313327) +++ head/sys/boot/usb/storage/umass_loader.c Mon Feb 6 08:26:45 2017 (r313328) @@ -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: Modified: head/sys/boot/zfs/zfs.c ============================================================================== --- head/sys/boot/zfs/zfs.c Mon Feb 6 07:02:17 2017 (r313327) +++ head/sys/boot/zfs/zfs.c Mon Feb 6 08:26:45 2017 (r313328) @@ -483,7 +483,7 @@ zfs_probe_dev(const char *devname, uint6 { struct ptable *table; struct zfs_probe_args pa; - off_t mediasz; + uint64_t mediasz; int ret; pa.fd = open(devname, O_RDONLY);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201702060826.v168QjD0056011>