Getting vender id from mount point

Hello,

I'd like to know if there is a way to get this properties(like the image below) of a USB thumb drive from a mount point(/Volumes/mydrive) in a generic kernel extension? Thanks!


https://www.dropbox.com/s/5ykusvrlz7apzzs/IORegistryExplorer.png?raw=1

Accepted Reply

I decide to check the volume type in user space.

Wise choice!

Could you please take a look at my code. Is it correct?

You can do this more easily by calling

statfs
and then passing
f_mntfromname
to
DADiskCreateFromBSDName
.

Also, it’s a lot easier to wrangle dictionaries from Objective-C (or Objective-C++, if you’re so inclined). For example:

struct statfs stfs;

int err = statfs("/", &stfs);
…  check error …

if ( !(stfs.f_flags & MNT_LOCAL) ) {
    … bail out …
}

DASessionRef session = DASessionCreate(NULL);
CFAutorelease(session);

DADiskRef disk = DADiskCreateFromBSDName(NULL, session, stfs.f_mntfromname);
CFAutorelease(disk);

NSDictionary * diskInfo = CFBridgingRelease( DADiskCopyDescription(disk) );
BOOL isRemovable = [diskInfo[(__bridge NSString *) kDADiskDescriptionMediaRemovableKey] boolValue];
NSLog(@"removable: %@", isRemovable ? @"YES" : @"NO");

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

I don't know anything about kernel extensions. This is just IOKit data. I assume you can get that from anywhere.

I can use DiskArbitration and IOKit frameworks to get vender id in user mode, but I cannot use the same code in kernel extension.

This is my code.


int GetVenderId(DADiskRef dsk, int *vid)
{
    io_service_t ioService;
    CFTypeRef vendorid;
   
    ioService = DADiskCopyIOMedia(dsk);
    
    if (0 != ioService)
    {
        vendorid = IORegistryEntrySearchCFProperty(ioService, kIOServicePlane, CFSTR("idVendor"), NULL, kIORegistryIterateParents | kIORegistryIterateRecursively);
        if(0 != vendorid)
        {
            if (CFNumberGetValue(vendorid, kCFNumberIntType, vid))
            {
                return 0;
            }
            
            CFRelease(vendorid);
        }
        
        IOObjectRelease(ioService);
    }
    
    return 1;
}

Clearly your KEXT doesn’t have a

DADiskRef
because DiskArb is a user-space-only API. So what data is your KEXT starting with?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

You're right. I'm using MAC framework in legacy code of a general kernel extension. I need to block some USB drives, but the only parameter I can use in callback function is a vnode pointer. Is it possible to get device vid starting from that? Or is there any kernel-mode IOKit API I can use to get that information? I have no idea whether it's safe to mix BSD and IOKit code together. I don't want to change the entire project from c to c++. Thanks!

I'm using MAC framework in legacy code of a general kernel extension.

Just so we’re clear, the kernel’s MAC framework is not considered a supported KPI. See QA1574 Kernel's MAC framework.

Having said that, your actual question (how to map a vnode to I/O Registry properties) should be possible with supported KPIs. The process runs something like this:

  1. Call

    vnode_mount
    to map the
    vnode_t
    to a
    mount_t
    .
  2. Call

    vfs_statfs
    to map the
    mount_t
    to a
    struct vfsstatfs *
    .
  3. Look at the

    f_mntfromname
    member. This is the name of the dev node that the volume is mounted on.

From there things proceed much like they would in user space:

  1. Use I/O Registry KPIs to find the

    IOMedia
    node for that volume.
  2. Walk up the I/O Registry service plane to find the node you care about.

  3. Extract properties from there.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks a lot. I have one more little question. Is there a way to differentiate among these these three types of mount point: removable drive(thumb drive, harddisk), CD/DVD drive and network share(SMB, AFP), regardless of the their connection types. My first thought is to check the file system name, but there are too many types out there. Is there a reliable way to get this information? Thanks!

removable drive (thumb drive, harddisk), CD/DVD drive and network share (SMB, AFP), regardless of the their connection types.

This is a tricky business. Part of the problem is one of definition. Let’s say I have a DVD mounted over iSCSI. Is that a DVD drive? Or a network drive? It kinda depends on how you look at it. It’s a DVD drive in terms of it drive access semantics but it’s a network drive in terms of latency and reliability.

Note This is not a theoretical example. For a long time macOS supported System Preferences > Sharing > DVD or CD Sharing.

Anyway, to start you can distinguish between network volumes and non-network volumes by looking at the

MNT_LOCAL
flag. In general, a volume with
MNT_LOCAL
set is expected to have a mount-from value (for example,
f_mntfromname
in
statfs
) that leads to a dev node.

Be warned:

  • Local volumes can actually be running over the network, as in the iSCSI example I gave above.

  • Non-local volumes are often used for completely virtual file systems, like the

    procfs
    example in Fuse for macOS.
  • Even for a local volume, the mount-from value may be virtual (for example, a disk image).

Beyond that, things get even trickier. You can use the mount-from value on a local volume to get to an I/O Registry entry, and then monkey around in the I/O Registry looking for key information about the volume. You will end up with a bunch of special cases, and those special cases will have to be updated over time.

My general advice would be to bounce out to user space and ask DiskArbitration about the volume. It already calculates the info, so relying on it saves you a bunch of work and ensures you use the same values as the rest of the OS.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks a lot! I decide to check the volume type in user space. Could you please take a look at my code. Is it correct?


Utility::VOLUME_TYPE Utility::getVolumeType(const char *path)
{
    Utility::VOLUME_TYPE type = Utility::VOLUME_TYPE::UNKNOWN;
    
    DASessionRef session;
    CFURLRef url;
    DADiskRef disk;
    CFDictionaryRef dict;
    DADiskRef wholeDisk;
    io_service_t mediaService;
    
    session = DASessionCreate(NULL);
    if (session)
    {
        url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, strlen(path), TRUE);
        if (url)
        {
            disk = DADiskCreateFromVolumePath(NULL, session, url);
            CFRelease(url);
            
            if (disk)
            {
                dict = DADiskCopyDescription(disk);
                
                CFBooleanRef type_key = (CFBooleanRef)CFDictionaryGetValue(dict, kDADiskDescriptionVolumeNetworkKey);
                if (type_key && CFBooleanGetValue(type_key))
                {
                    type = Utility::VOLUME_TYPE::NETWORK;
                }
                else
                {
                    type_key = (CFBooleanRef)CFDictionaryGetValue(dict, kDADiskDescriptionMediaRemovableKey);
                    if (type_key)
                    {
                        type = CFBooleanGetValue(type_key) ? Utility::VOLUME_TYPE::REMOVABLE : Utility::VOLUME_TYPE::INTERNAL;
                        
                        wholeDisk = DADiskCopyWholeDisk(disk);
                        if (wholeDisk)
                        {
                            mediaService = DADiskCopyIOMedia(wholeDisk);
                            if (mediaService)
                            {
                                if (IOObjectConformsTo(mediaService, kIOCDMediaClass)
                                    || IOObjectConformsTo(mediaService, kIODVDMediaClass))
                                {
                                    type = Utility::VOLUME_TYPE::OPTICAL;
                                }
                                IOObjectRelease(mediaService);
                            }
                            CFRelease(wholeDisk);
                        }
                    }
                }
                CFRelease(dict);
                CFRelease(disk);
            }
        }
        CFRelease(session);
    }
    
    return type;
}

I decide to check the volume type in user space.

Wise choice!

Could you please take a look at my code. Is it correct?

You can do this more easily by calling

statfs
and then passing
f_mntfromname
to
DADiskCreateFromBSDName
.

Also, it’s a lot easier to wrangle dictionaries from Objective-C (or Objective-C++, if you’re so inclined). For example:

struct statfs stfs;

int err = statfs("/", &stfs);
…  check error …

if ( !(stfs.f_flags & MNT_LOCAL) ) {
    … bail out …
}

DASessionRef session = DASessionCreate(NULL);
CFAutorelease(session);

DADiskRef disk = DADiskCreateFromBSDName(NULL, session, stfs.f_mntfromname);
CFAutorelease(disk);

NSDictionary * diskInfo = CFBridgingRelease( DADiskCopyDescription(disk) );
BOOL isRemovable = [diskInfo[(__bridge NSString *) kDADiskDescriptionMediaRemovableKey] boolValue];
NSLog(@"removable: %@", isRemovable ? @"YES" : @"NO");

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks a lot!

I found a problem when testing my kext. I want to block sensitive files being copied to external drives. For removable drives and network shares, I can get the file path and from there get f_mntfromname and f_mnttoname. But for DVD drive, I cannot prevent burning sensitive information on it. When a blank disc in the drive, there is no file system on it, so there is no volume mounted for that drive, only a raw disk. When the burning process starts, the mpo_vnode_check_open callback function is not called, or I don't register the correct callback. Is it possible to capture this event(file writing) using MAC framework? Thanks!

Is it possible to capture this event (file writing) using MAC framework?

As I mentioned earlier, the kernel’s MAC framework is not a supported KPI.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Is it possible to block DVD burning using any public KPI, like Kauth? Regardless of the KPI I use, there seems to be no file writing operation I can hook and block.

Is it possible to block DVD burning using any public KPI, like Kauth?

Probably not.

Rather, I’d approach this by preventing the user from seeing the blank DVD in the first place. There’s two ways you might achieve that:

  • In kernel space, you might be able to use I/O Kit to match ‘harder’ on the I/O Registry nubs and thus prevent the built-in DVD infrastructure from matching.

  • In user space, you might be able to use DiskArbitration to claim the blank DVD and thus prevent the system from using it.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I will try the Disk Arbitration framework first. I just wonder if there is a specific background daemon, which is responsible for burning files in the .fpbf folder to DVD drive. If so, what's its name? Thanks!