How do I get the partition offset in OS X with C/C++?

筅森魡賤 提交于 2021-02-08 06:47:59

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!