How can a script reliably identify an encrypted disk image and its mounted volume?

Hello,

I am working on a backup process on macOS. Before copying files, the script needs to check that it is looking at the correct mounted backup volume.

A fictional example

A user opens an encrypted disk image called ExampleBackup.sparsebundle and enters the password themselves. The volume then appears in Finder as ExampleBackup.

Before copying anything, the script needs to confirm:

  • This mounted volume actually belongs to the intended disk image.
  • The volume is mounted read/write.
  • File ownership is being enforced.
  • The destination has not been replaced or changed since the previous check.

Simply checking the volume name or its path does not seem sufficient: another volume could have the same name, or a device name could be reused after ejecting and reconnecting an image.

My questions

  1. What supported macOS API or command should a script use to establish this relationship reliably?
  2. Can we rely on specific fields from hdiutil info -plist and diskutil info -plist <device>? If so, where are their meanings and limitations documented?
  3. How should the script distinguish “no” from “unknown” when information is missing or a check fails?
  4. What should it check again immediately before copying, since the volume could change between separate checks?

The checks themselves should only read information. They should not unlock or mount images, change permissions, or disable any security protections. If the script cannot confirm the destination safely, it should stop.

A recommended approach or small example would be very helpful.

Thank you.

Answered by DTS Engineer in 912019022

Python can call many Apple APIs via PyObjC. However, it’s not obvious whether that’s actually a better option than writing your own tool, per the outline you suggested. Regardless, let’s assume you have access to native APIs and look at your specific issues:

Written by RFAGermany in 852828021

This mounted volume actually belongs to the intended disk image.

This is tricky. I’ll come back to it below.

Written by RFAGermany in 852828021

The volume is mounted read/write.

This is easy. The low-level statfs system call has a MNT_RDONLY flag that tells you whether this is a read-only mount. This is also exposed by higher-level APIs, most notably the volumeIsReadOnly URL property.

Written by RFAGermany in 852828021

File ownership is being enforced.

This is similar, in that statfs has a MNT_IGNORE_OWNERSHIP flag. I don’t think this is exposed as a URL resource value, so you’ll probably have to call statfs directly.

Written by RFAGermany in 852828021

The destination has not been replaced or changed since the previous check.

Most volumes support a volume UUID. You can get this through low-level APIs [1] but in this case it’s much easier to use the volumeUUIDString URL property.


Coming back to your first question about mapping a volume to its underlying disk image, I don’t think there’s a great way to do this. You can certainly do it by invoking hdiutil and diskutil. That’s not terrible, and the -plist property helps, but it’s not great.

It’s likely that you can also get this info from the I/O registry, but I’m not sure if you can do that by only using supported properties.

I’m gonna ping Kevin about this to see if he has an suggestions.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Specifically, the ATTR_VOL_UUID attribute returned by getattrlist. See the man page for details.

This mounted volume actually belongs to the intended disk image.

If these are your own images, you can create your own hidden files with reliable, unique identifiers.

What supported macOS API or command should a script use to establish this relationship reliably?

There are a number of APIs that may prove useful:

  1. Disk Arbitrarion
  2. URL resource values
  3. statfs
  4. Maybe IOKit

From what you're describing, #1 and #2 should be sufficient.

Can we rely on specific fields from hdiutil info -plist and diskutil info -plist <device>? If so, where are their meanings and limitations documented?

hdiutil is deprecated, so there's that. While diskutil does have a machine-readable "-plist" option, the details of what those values mean is often not well documented. Plus it's an external tool you'd have to run. Disk Arbitration would be preferable. I sometimes use these tools, and "system_profiler", but normally that's to poke around in Apple's business. If these are disk images that your software is creating, you have much more liberty to just create your own metadata for them.

One important limitation is that a mount path, volume name, or BSD device name should not be treated as a durable identity. Device names can be reused after detach, and the path can be remounted. If you create the sparsebundle, put a random identifier in metadata inside it and compare that after the user mounts it. For an image you do not control, use Disk Arbitration to inspect the current disk and volume descriptions, correlate the mounted volume with the attached image, and treat missing or conflicting fields as unknown. I would not make a safety decision from a parsed display name alone.

For the read-only checks, collect the mounted volume URL and its volume UUID, read-only state, and ownership state, then verify those values again immediately before copying. A check followed by a later path-based copy still has a race. Where possible, open the destination directory once, retain its file descriptor, and perform traversal relative to that descriptor with no-follow checks, verifying the volume identity again if the mount can change. If the identity or ownership state cannot be established, fail closed and do not copy. The exact fields available vary by filesystem and OS version, so I would validate the behavior on the macOS versions and image types you support rather than assuming hdiutil or diskutil output is a stable API contract.

Written by RFAGermany in 852828021

the script needs to check that it is looking at the correct mounted backup volume.

What sort of script are we talking about here?

This matters because some scripting languages give you ready access to native APIs and some don’t, and the best answer for at least some of question question involves native APIs.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hello Quinn,
Thank you for taking the time to help and for asking for clarification. I appreciate your support.
The automation is written in Python. So far, the approach has been to call macOS command-line tools and interpret their output. However, I would prefer to use supported native APIs where these provide a more reliable answer.
Would a small Swift command-line helper, called by Python, be an appropriate approach? The helper could use Disk Arbitration and Foundation URL resource values, then return a small structured result to Python.
For example, after the user manually unlocks an encrypted sparsebundle, the helper would check:

  • Whether the mounted destination belongs to the intended disk image.
  • Whether it is mounted read/write and ownership is enforced.
  • Whether its identity is still consistent immediately before copying.
    The checks should be read-only. They should not unlock or mount images, change permissions, or alter security settings. Missing or conflicting information should produce an “unknown” result, and Python would stop rather than copy.
    Would you recommend this Python-plus-Swift arrangement? If so, which APIs would you suggest for establishing the image-to-volume relationship and reducing the risk of the destination changing between checking and copying?
    Thank you again for your guidance.

The automation is written in Python

You asked for an API, in the Apple Developer forum. So I answered.

However, those APIs really aren't suitable for Python. Maybe Python can call native methods. I really don't know. But that's a side issue.

My question to you is how would you implement this Python solution on a Linux system? What parts of that do you find missing on macOS? So far, you haven't given any indication of any blocker. I don't see any reason why you couldn't write the script on Linux, with minor changes for macOS to create and mount Mac disk images. I don't see any requirement for any kind of low-level, APFS/macOS-specific access to filesystem, device, or disk image details. Just create some hidden directories with some UUIDs and off you go.

If you've got a malicious user somehow making byte-for-byte copies to confuse your app, tell your users not to do that.

Python can call many Apple APIs via PyObjC. However, it’s not obvious whether that’s actually a better option than writing your own tool, per the outline you suggested. Regardless, let’s assume you have access to native APIs and look at your specific issues:

Written by RFAGermany in 852828021

This mounted volume actually belongs to the intended disk image.

This is tricky. I’ll come back to it below.

Written by RFAGermany in 852828021

The volume is mounted read/write.

This is easy. The low-level statfs system call has a MNT_RDONLY flag that tells you whether this is a read-only mount. This is also exposed by higher-level APIs, most notably the volumeIsReadOnly URL property.

Written by RFAGermany in 852828021

File ownership is being enforced.

This is similar, in that statfs has a MNT_IGNORE_OWNERSHIP flag. I don’t think this is exposed as a URL resource value, so you’ll probably have to call statfs directly.

Written by RFAGermany in 852828021

The destination has not been replaced or changed since the previous check.

Most volumes support a volume UUID. You can get this through low-level APIs [1] but in this case it’s much easier to use the volumeUUIDString URL property.


Coming back to your first question about mapping a volume to its underlying disk image, I don’t think there’s a great way to do this. You can certainly do it by invoking hdiutil and diskutil. That’s not terrible, and the -plist property helps, but it’s not great.

It’s likely that you can also get this info from the I/O registry, but I’m not sure if you can do that by only using supported properties.

I’m gonna ping Kevin about this to see if he has an suggestions.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Specifically, the ATTR_VOL_UUID attribute returned by getattrlist. See the man page for details.

The first and most important question is about this:

I am working on a backup process on macOS. Before copying files, the script needs to check that it is looking at the correct mounted backup volume.

Who is this "for"? Are you building this of personal use or for something you specifically control/manage? Or are you trying to build a commercial product people will pay for?

The problem here is that the range of file system edge cases is absolutely ENORMOUS, which means there is often a BIG gap between:

  1. It works really well on my machine.

  2. It works really well on ALL machines, all the time.

Keep in mind that a lot of this isn't just about simple API behavior, but is also about the entire implementation "path". For example, the biggest issues in this are tied to uniquely identifying volumes and the risk that "confusion" will cause you to target the wrong volume. The simplest solution to this for #1 isn't to try and sort this out via API, it's to move the mount target to your own directory so that you no longer have to worry about the system colliding with "you".

Similarly, a lot of the issues around device removal are far less of a factor for #1, as you'll basically just "not do that". Most notably:

What should it check again immediately before copying, since the volume could change between separate checks?

The problem is that the file system is an inherently dynamic structure that can and will change at any time. More "personal" solutions get away with ignoring that reality because their designed around that particular users specific habits and usage patterns. General purpose/commercial products don't really have that luxury, which means the only safe approach is to dynamically monitor mount/device activity and adapt when something changes.

Jumping around a bit between other posts:

One important limitation is that a mount path, volume name, or BSD device name should not be treated as a durable identity.
...
I would not make a safety decision from a parsed display name alone.

This is excellent advice.

However, this:

If you create the sparsebundle, put a random identifier in metadata inside it and compare that after the user mounts it.

and this:

Most volumes support a volume UUID. You can get this through low-level APIs [1] but in this case it’s much easier to use the volumeUUIDString URL property.

...are a great example of the split between 1 & 2 above. They might work very well in a personal use product (#1) but they are a catastrophe in waiting in a commercial product (#2).

The problem here is that disk images can be trivially duplicated, so any verification that relies on the content of the volume can't necessarily be trusted. They'll work fine if you're using disk images in a very "controlled" way, but in a general purpose product they'll eventually cause data loss.

I'll also warn you that this extends all the way to security scoped bookmarks. More specifically, if you create a security scoped bookmark to an object inside a disk image and then duplicate that image, bookmark resolution can "jump" between volumes depending on which volume mounted first. Note that this isn't really a bug but an unavoidable limitation caused by the system inability to uniquely identify volume if/when those volumes are in fact "identical".

Coming back to your first question about mapping a volume to its underlying disk image, I don’t think there’s a great way to do this.

First, as a minor clarification, I'll mildly disagree with Quinn here in that hdiutil and diskutil exist in a slightly odd grey area between "API" and "random tool that could change at anytime". They're obviously not API, but the were specifically created to fulfill requirements common to many apps and, in general, their argument format and return data have been quite stable.

Ironically, the best evidence of that is that macOS 27 didn't simply remove hdiutil but clearly marked it as deprecated in it's man page and even included a helpful guide at the stop mapping hdiutil command to diskutil. This also isn't a sudden transition, as diskutil has supported disk images since macOS 13/14, the details of which it helpfully describes at the end of it's man page.

Having said that...

You can certainly do it by invoking hdiutil and diskutil. That’s not terrible, and the -plist property helps, but it’s not great.

You can do this with a mounted image, but the technique is not necessarily obvious. More specifically, diskutil info won't get you the source path, diskutil image info won't get you the mount/device path, and neither provide information you can correlate to the other.

However, you can do this with "attach", as running attach on a image file will return it's mount point and device node data it's already attached/mounted, attaching/mounting it first if is not:

diskutil image attach /Volumes/kevine/Downloads/Additional_Tools_for_Xcode_27.dmg
/dev/disk14  	GUID_partition_scheme
/dev/disk14s1	Apple_HFS            	/Volumes/Additional Tools

It’s likely that you can also get this info from the I/O registry,

You can do this through a property in the I/O Registry. If you look in the I/O Registry, the origin path of the disk image is a property off the base storage device of the image.

but I’m not sure if you can do that by only using supported properties.

...but the concept of "supported" is always tricky with the I/O Registry, as most of it's properties aren't actually declared. My own inclination would be to rely on diskutil, but I don't think it's necessarily "wrong" to use the I/O Registry key.

That then leads to here:

Can we rely on specific fields from hdiutil info -plist and diskutil info -plist <device>?

Yes. More specifically, diskutil can basically be treated as our "API layer" for advanced manipulation of mass storage device. In the context of disk images, it's role is similar to many of our APIs, in that it provides a standard information and command format so that the underlying implementation can change without disrupting "you".

If so, where are their meanings and limitations documented?

They aren't really formally documented but they should be straightforward enough to be self explanatory. Please let me know if you have a question about any of them.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware