summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2010-08-13 17:27:44 -0700
committerH. Peter Anvin <hpa@linux.intel.com>2010-08-13 17:27:44 -0700
commit460675909dd059f5fa84985402fcd6490503c884 (patch)
treea95c93dfb1a233a89a40f798f2a9a93d95aaf894
parentea3e62637ad3f19c58f17b233fa7476a0e099889 (diff)
downloadsyslinux-460675909dd059f5fa84985402fcd6490503c884.tar.gz
extlinux: try to get the disk offset from sysfs
It is possible(?) that HDIO_GETGEO can't return the full offset, and in either case it is too small -- only 32 bits on 32-bit platforms. Thus query sysfs for the real value, if available. sysfs also contains information for slave devices -- this is going to really matter for the md/lvm issues. Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rwxr-xr-xextlinux/main.c43
1 files changed, 38 insertions, 5 deletions
diff --git a/extlinux/main.c b/extlinux/main.c
index 002cecd9..30422c2d 100755
--- a/extlinux/main.c
+++ b/extlinux/main.c
@@ -100,6 +100,32 @@ struct geometry_table {
struct hd_geometry g;
};
+static int sysfs_get_offset(int devfd, unsigned long *start)
+{
+ struct stat st;
+ char sysfs_name[128];
+ FILE *f;
+ int rv;
+
+ if (fstat(devfd, &st))
+ return -1;
+
+ if ((size_t)snprintf(sysfs_name, sizeof sysfs_name,
+ "/sys/dev/block/%u:%u/start",
+ major(st.st_dev), minor(st.st_dev))
+ >= sizeof sysfs_name)
+ return -1;
+
+ f = fopen(sysfs_name, "r");
+ if (!f)
+ return -1;
+
+ rv = fscanf(f, "%lu", start);
+ fclose(f);
+
+ return (rv == 1) ? 0 : -1;
+}
+
/* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
(x/64/32) is the final fallback. I don't know what LS-240 has
as its geometry, since I don't have one and don't know anyone that does,
@@ -123,24 +149,25 @@ int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
struct loop_info li;
struct loop_info64 li64;
const struct geometry_table *gp;
+ int rv = 0;
memset(geo, 0, sizeof *geo);
if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
- return 0;
+ goto ok;
} else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
geo->heads = fd_str.head;
geo->sectors = fd_str.sect;
geo->cylinders = fd_str.track;
geo->start = 0;
- return 0;
+ goto ok;
}
/* Didn't work. Let's see if this is one of the standard geometries */
for (gp = standard_geometries; gp->bytes; gp++) {
if (gp->bytes == totalbytes) {
memcpy(geo, &gp->g, sizeof *geo);
- return 0;
+ goto ok;
}
}
@@ -153,19 +180,25 @@ int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
geo->start = 0;
- if (!opt.sectors && !opt.heads)
+ if (!opt.sectors && !opt.heads) {
fprintf(stderr,
"Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
" (on hard disks, this is usually harmless.)\n",
geo->heads, geo->sectors);
+ rv = 1; /* Suboptimal result */
+ }
+ok:
/* If this is a loopback device, try to set the start */
if (!ioctl(devfd, LOOP_GET_STATUS64, &li64))
geo->start = li64.lo_offset >> SECTOR_SHIFT;
else if (!ioctl(devfd, LOOP_GET_STATUS, &li))
geo->start = (unsigned int)li.lo_offset >> SECTOR_SHIFT;
+ else if (!sysfs_get_offset(devfd, &geo->start)) {
+ /* OK */
+ }
- return 1;
+ return rv;
}
/*