Skip site navigation (1)Skip section navigation (2)
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>