问题
I want to create my own volume id using the drive serial + partition offset + partition size, but I need to know how to get the partition information on OS X. I have (unsucceedingly) tried the following:
int fd;
if ((fd = open("/dev/disk0s1", O_RDONLY|O_NONBLOCK)) >= 0) {
struct hd_geometry geom;
if (ioctl(fd, 0x0301, &geom) == 0){ //0x0301 is HDIO_GETGEO
printf("Index = %u\n", geom.start);
}
close(fd);
}
But even if that were to succeed, it is a flawed solution since as this noted: hd_geometry.start is an unsigned long and "will not contain a meaningful value for disks over 219 Gb in size." Furthermore, I belive that it requires administrative rights, which is also bad. Is there any other way of doing this?
回答1:
Okay last point first. Requiring admin rights is necessary because you are trying to read a raw disk; you could for example potentially seek to a block where a private crypto key is written and read it as an unprivileged user and then where would we be?
Second, /dev/disk0s1 is just a partition and it's also the block-device version of it. You need to read the character device version of the disk, which would be /dev/rdisk0.
Third, HDIO_GETGEO is a linux kernel ioctl (especially consider the 0x0301 value of it) you are not going to get far on Darwin with this; Have a look at <sys/disk.h> for the related disk IOCTLs. I think DKIOCGETFEATURES / DKIOCGETPHYSICALBLOCKSIZE etc should get you going.
If you have trouble with these concepts I HIGHLY recommend doing this development in a virtual machine that you can clobber because you do NOT want to accidentally use an IOCTL which will screw up your disks.
Addendum (possibly the answer)
GUID Partition Table
So you are working on Mac OS X / Darwin; We'll assume GUID Partition Table
LBA == Logical Block Addressing ... ; 1 block = 512 bytes
LBA 0 - Master Boot Record (also contained old partition table)
LBA 1 - GUID Partition Table (standard for OS X)
LBA 2 - first 4 entries
LBA 3 - 33 - next 124 entries making for a total of 128 entries
LBA 34 - Partition 1
You can grab the second block and start tracing the information
Have a read at http://en.wikipedia.org/wiki/GUID_Partition_Table
It's quite well defined. GUID uses little-endian byte order for integer values (see examples at the bottom of the wikipedia page)
Suggestion for testing
Make a copy so that you are not screwing with the actual disks:
dd if=/dev/rdisk0 of=fakedisk count=33
this will create a copy of the first 33 blocks or a disk. Use fakedisk to test your program out.
MBR
In case your disk uses MBR use the same concepts as GPT
http://en.wikipedia.org/wiki/Master_Boot_Record
has excellent description of the sectors.
Using dtruss fdisk -d /dev/rdisk0 dump to get hints
dtrussing fdisk dump shows that fdisk uses the approach described above.
dtruss fdisk -d /dev/rdisk0
SYSCALL(args) = return
open("/dev/dtracehelper\0", 0x2, 0x7FFF5CFDD5C0) = 3 0
__sysctl(0x7FFF5CFDD084, 0x2, 0x7FFF5CFDD070) = 0 0
bsdthread_register(0x7FFF8BCA41D4, 0x7FFF8BCA41C4, 0x2000) = 0 0
[[ .... content edited ... ]]
open("/dev/rdisk0\0", 0x0, 0x7FFF5CFDDD7A) = 3 0
fstat64(0x3, 0x7FFF5CFDDA10, 0x0) = 0 0
fstat64(0x3, 0x7FFF5CFDDAC8, 0x0) = 0 0
ioctl(0x3, 0x40086419, 0x7FFF5CFDDB60) = 0 0
ioctl(0x3, 0x40046418, 0x7FFF5CFDDB5C) = 0 0
close(0x3) = 0 0
open("/dev/rdisk0\0", 0x0, 0x0) = 3 0
fstat64(0x3, 0x7FFF5CFDDAD0, 0x0) = 0 0
open("/dev/rdisk0\0", 0x0, 0x0) = 4 0
fstat64(0x4, 0x7FFF5CFDDA80, 0x0) = 0 0
lseek(0x4, 0x0, 0x0) = 0 0
issetugid(0x102C22000, 0x3, 0x7FFF5CFDDC00) = 0 0
geteuid(0x102C22000, 0x3, 0x0) = 0 0
[[ tracing data suppressed ]]
read(0x4, "\0", 0x200) = 512 0
close(0x4) = 0 0
getrlimit(0x1008, 0x7FFF5CFDCFA8, 0x7FFF8BD0D470) = 0 0
fstat64(0x1, 0x7FFF5CFDCEF8, 0x7FFF5CFDCFBC) = 0 0
ioctl(0x1, 0x4004667A, 0x7FFF5CFDCF94) = 0 0
write_nocancel(0x1, "1,625142447,0xEE,-,1023,254,63,1023,254,63\n\0", 0x2B) = 43 0
write_nocancel(0x1, "0,0,0x00,-,0,0,0,0,0,0\n\0", 0x17) = 23 0
write_nocancel(0x1, "0,0,0x00,-,0,0,0,0,0,0\n\0", 0x17) = 23 0
write_nocancel(0x1, "0,0,0x00,-,0,0,0,0,0,0\n\0", 0x17) = 23 0
close(0x3) = 0 0
deciphering ioctls
How did I figure out that it was these ioctls that are used.
dtruss dump is:
ioctl(0x3, 0x40086419, 0x7FFF5CFDDB60) = 0 0 ioctl(0x3, 0x40046418, 0x7FFF5CFDDB5C) = 0 0
and 0x40086518 corresponds to DKIOCGETBLOCKSIZE This is gleaned by tracing back disk.h (and noting that _IOR expands to _IOC in ioccom.h) and that the last 8 bits correspond to the second number in the IOCTL constant define.
#define DKIOCGETBLOCKSIZE _IOR('d', 24, uint32_t)
in 0x40086418 the trailing 18(hex) == 24(dec)
So now that we note that fdisk performs DKIOCGETBLOCKCOUNT and DKIOCGETBLOCKSIZE to get the physical extents because technically you should use the RESULT of that to figure out LBA offsets (see deciphering ioctls below)
The actual read in fdisk
This is how fdisk is doing it:
open("/dev/rdisk0\0", 0x0, 0x0) = 4 0 read(0x4, "\0", 0x200) = 512 0 close(0x4) = 0 0
You can follow suit make sure you replace the 0x200 with the actual block size.
Also, if you're going to use the dd command above to make a copy use the block size as it comes out here.
回答2:
Have you tried the DKIOCGETPHYSICALEXTENT ioctl? It fills in a dk_physical_extent_t structure that includes a 64-bit offset and a 64-bit length.
来源:https://stackoverflow.com/questions/16656218/how-do-i-get-the-partition-offset-in-os-x-with-c-c