6 Replies
      Latest reply on Feb 13, 2020 3:12 AM by exfat
      exfat Level 1 Level 1 (0 points)

        Hello!

        I have flash storage with exFAT filesystem and I need to get it filesystem UUID. How can I do it programmatically?

        I attempt to get it via ioreg, and I think that Filesystem UUID is "Content" property of partition. Below we can think that filesystem UUID is EBD0A0A2-B9E5-4433-87C0-68B6B72699C7.

         

        +-o IOBlockStorageServices  <class IORegistryEntry:IOService:IOBlockStorageDevice:IOBlockStorageServices, id 0x10000129c, registered, matched, active, busy 0 (73 ms), retain 6>

          | {

          |   "IOMinimumSegmentAlignmentByteCount" = 4

          |   "device-type" = "Generic"

          |   "Protocol Characteristics" = {"Physical Interconnect"="USB","Read Time Out Duration"=30000,"SCSI Logical Unit Number"=0,"Physical Interconnect Location"="External","Write Time Out Duration"=30000,"Retry Count"=20}

          |   "Device Characteristics" = {"Vendor Name"="Kingston","Product Name"="DataTraveler 2.0","Product Revision Level"="1.00"}

          | }

          |

          +-o IOBlockStorageDriver  <class IORegistryEntry:IOService:IOStorage:IOBlockStorageDriver, id 0x10000129d, registered, matched, active, busy 0 (72 ms), retain 8>

            | {

            |   "IOPropertyMatch" = {"device-type"="Generic"}

            |   "CFBundleIdentifier" = "com.apple.iokit.IOStorageFamily"

            |   "IOProviderClass" = "IOBlockStorageDevice"

            |   "IOClass" = "IOBlockStorageDriver"

            |   "IOProbeScore" = 0

            |   "CFBundleIdentifierKernel" = "com.apple.iokit.IOStorageFamily"

            |   "Statistics" = {"Operations (Write)"=338,"Latency Time (Write)"=0,"Bytes (Read)"=532992,"Errors (Write)"=0,"Total Time (Read)"=147844763,"Latency Time (Read)"=0,"Retries (Read)"=0,"Errors (Read)"=0,"Total Time (Write)"=3660822621,"Bytes (Write)"=1882624,"Operations (Read)"=101,"Retries (Write)"=0}

            |   "IOMatchCategory" = "IODefaultMatchCategory"

            |   "IOGeneralInterest" = "IOCommand is not serializable"

            | }

            |

            +-o Kingston DataTraveler 2.0 Media  <class IORegistryEntry:IOService:IOStorage:IOMedia, id 0x1000012b3, registered, matched, active, busy 0 (72 ms), retain 12>

              | {

              |   "Content" = "GUID_partition_scheme"

              |   "Removable" = Yes

              |   "Whole" = Yes

              |   "Leaf" = No

              |   "BSD Name" = "disk2"

              |   "Ejectable" = Yes

              |   "Preferred Block Size" = 512

              |   "IOMediaIcon" = {"IOBundleResourceFile"="Removable.icns","CFBundleIdentifier"="com.apple.iokit.IOStorageFamily"}

              |   "BSD Minor" = 9

              |   "IOGeneralInterest" = "IOCommand is not serializable"

              |   "Writable" = Yes

              |   "BSD Major" = 1

              |   "Size" = 15500574720

              |   "IOBusyInterest" = "IOCommand is not serializable"

              |   "Open" = Yes

              |   "Content Hint" = ""

              |   "BSD Unit" = 2

              | }

              |

              +-o IOMediaBSDClient  <class IORegistryEntry:IOService:IOMediaBSDClient, id 0x1000012b8, registered, matched, active, busy 0 (0 ms), retain 6>

              |   {

              |     "IOProbeScore" = 30000

              |     "CFBundleIdentifier" = "com.apple.iokit.IOStorageFamily"

              |     "IOMatchCategory" = "IOMediaBSDClient"

              |     "IOClass" = "IOMediaBSDClient"

              |     "IOProviderClass" = "IOMedia"

              |     "CFBundleIdentifierKernel" = "com.apple.iokit.IOStorageFamily"

              |     "IOResourceMatch" = "IOBSD"

              |   }

              |

              +-o IOGUIDPartitionScheme  <class IORegistryEntry:IOService:IOStorage:IOPartitionScheme:IOGUIDPartitionScheme, id 0x1000012ba, !registered, !matched, active, busy 0 (0 ms), retain 6>

                | {

                |   "IOProbeScore" = 4000

                |   "IOPropertyMatch" = {"Whole"=Yes}

                |   "IOMatchCategory" = "IOStorage"

                |   "IOClass" = "IOGUIDPartitionScheme"

                |   "IOProviderClass" = "IOMedia"

                |   "CFBundleIdentifier" = "com.apple.iokit.IOStorageFamily"

                |   "CFBundleIdentifierKernel" = "com.apple.iokit.IOStorageFamily"

                |   "UUID" = "DB6A5DED-C145-4F28-B80E-5DA726D76AF2"

                |   "Content Mask" = "GUID_partition_scheme"

                | }

                |

                +-o Microsoft Basic Data@1  <class IORegistryEntry:IOService:IOStorage:IOMedia, id 0x1000012be, registered, matched, active, busy 0 (0 ms), retain 11>

                  | {

                  |   "Open" = Yes

                  |   "Preferred Block Size" = 512

                  |   "Base" = 1048576

                  |   "Writable" = Yes

                  |   "IOBusyInterest" = "IOCommand is not serializable"

                  |   "Size" = 15499509248

                  |   "Content" = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"

                  |   "BSD Minor" = 10

                  |   "Whole" = No

                  |   "Removable" = Yes

                  |  "UUID" = "E704A750-5562-4578-9F97-D4D0E0326C7A"

                  |   "BSD Unit" = 2

                  |   "BSD Major" = 1

                  |   "Ejectable" = Yes

                  |   "BSD Name" = "disk2s1"

                  |   "Partition ID" = 1

                  |   "IOGeneralInterest" = "IOCommand is not serializable"

                  |   "GPT Attributes" = 0

                  |   "Content Hint" = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"

                  |   "Leaf" = Yes

                  | }

                  |

                  +-o IOMediaBSDClient  <class IORegistryEntry:IOService:IOMediaBSDClient, id 0x1000012bf, registered, matched, active, busy 0 (0 ms), retain 7>

                      {

                        "IOProbeScore" = 30000

                        "CFBundleIdentifier" = "com.apple.iokit.IOStorageFamily"

                        "IOMatchCategory" = "IOMediaBSDClient"

                        "IOClass" = "IOMediaBSDClient"

                        "IOProviderClass" = "IOMedia"

                        "CFBundleIdentifierKernel" = "com.apple.iokit.IOStorageFamily"

                        "IOResourceMatch" = "IOBSD"

                      }

         

         

        BUT when I try to check it via diskutil, I've get two ID's with similiar names - volume, disk/partition:

        diskutil info disk2s1 | grep UUID

           Volume UUID:               C1E39943-2F19-369A-A22F-11027887EBC8

           Disk / Partition UUID:     E704A750-5562-4578-9F97-D4D0E0326C7A

         

        And one of these UUIDs is not present in ioreg output - C1E39943-2F19-369A-A22F-11027887EBC8. Very interesting. I'd check in Disk Utility (with GUI) and in information about partition I see that (Filesystem UUID: C1E39943-2F19-369A-A22F-11027887EBC8):

        https://i.ibb.co/pKrqs3r/Screenshot-2020-02-11-at-10-45-34.png

         

        And for more information here is output of blkid in Linux:

        sudo blkid /dev/sdf1

        /dev/sdf1: LABEL="exFAT" UUID="9CAD-0C6D" TYPE="exfat" PARTLABEL="Microsoft Basic Data" PARTUUID="e704a750-5562-4578-9f97-d4d0e0326c7a"

        We can see that partition UUID is equal, but filesystem UUID - is not.

         

        Someone can explain me:

        1. how I can get Filesystem UUID of volume programmatically?

        2. why results in Linux and macOS are different?

        3. why in some places C1E39943-2F19-369A-A22F-11027887EBC8 is Volume UUID, but in some places - Filesystem UUID? After all It really different things!

        Thanks!

        • Re: Get a filesystem UUID
          john daniel Level 4 Level 4 (580 points)

          I'm not sure what you mean by "filesystem UUID". Maybe you could start off by explaining what you are trying to do at a higher level. None of what you have mentioned is "programatically". You are calling and parsing system utilities. You can call IOKit directly, use DiskArbitration, or maybe even just get attributes from a filesystem URL.

           

          Linux and macOS are completely different operating systems. What you've highlighed from Linux isn't even a UUID at all.

           

          If I use Google, I see a handful of places where the phrase "filesystem uuid" occurs at apple.com. Is that a Linux thing or something? Apple has "DiskUUID" and "VolumeUUID" and a "Content" sometimes. That "File system UUID" you are seeing is just something from Disk Utility. Just ignore that whole app. Only use the command line tools. And even then, don't use the text versions if an xml/plist output format exists.

            • Re: Get a filesystem UUID
              exfat Level 1 Level 1 (0 points)

              I just want to build tree of block devices.

               

              Most filesystems have UUID. It can be used for identifying what filesystem should be mounted (for example). Usually this UUID stored in filesystem. So, this UUID of in case exFAT is not depends on OS.

               

              I understand that "9CAD-0C6D"  is not true UUID, but it is some ID, right? I thinks this ID should be the same in Linux, macOS, Unix

               

              When I write "programatically" I mean "via API" (preferably). I read IO Registry via IOKit framework and I post here output from ioreg utility just for inspecting results which I can take from IO Registry.

               

              I understand that I can ignore it, but maybe somebody know more about these diferrences

                • Re: Get a filesystem UUID
                  john daniel Level 4 Level 4 (580 points)

                  It would probably be better to use the device ID for that. Granted, those device IDs can change. The UUIDs would be good for tracking devices that aren't currently connected.

                   

                  I think it is just awkward to talk about a "filesystem UUID". The filesystem can be composed of multiple volumes and those volumes can change. You can use the Volume and Disk UUID to uniquely refer to these objects, but the filesystem is more of a dynamic structure.

                   

                  My app has a tree of devices. It takes a snapshot of the current status, so I don't care about UUIDs, just device IDs. Sometimes, you don't even have that.

                    • Re: Get a filesystem UUID
                      exfat Level 1 Level 1 (0 points)

                      All can be be changed - Volume UUID, Filesystem UUID. Maybe Disk UUID will always the same, I don't know - it may depends on OS.

                       

                      One thing is important - how this device tree will used and what elements of this tree most important. For me really important are filesystems, because volume (disk/partition) without filesystem can't present data as files and directories.

                       

                      If you need to collect only devices - OK, it is true, you can don't care about filesystems. But when filesystem is missing a disk will present in system, but you can't mount it (because really you can mount filesystem - not a volume) and user will can't get data from it (can, but only binary blob).

                      Yes, I know that exist some filesystems which use few volumes - BTRFS for example. In this case managing of filesystems is more logical right too, IMHO. Because finally you need filesystems to get data from volumes. Volumes are intended just for storing filesystems You can do "mount <filesystem UUID>" and you mount all volumes with this filesystem, but you can do "mount volume1_with_path_of_fs" and you mount only part filesystem which can be stored on few volumes (I didn't check it in macOS but it is true for Linux).

                       

                      So, I still think that knowledge about filesystems of volumes is important (at least for me)

                • Re: Get a filesystem UUID
                  eskimo Apple Staff Apple Staff (13,095 points)

                  To understand what’s going on here, you need to learn more about how GPT and exFAT work.  I can cover some basics here but most of this stuff is not Apple-specific.

                  Anyway, with regards the UUIDs in the I/O Registry:

                  • Content is the GPT partition type GUID.  The value EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 is for a basic data partition.  The will be the same for all FAT-like volumes.

                  • UUID is the GPT unique partition GUID.  As the name suggests, this should be globally unique.

                  The Volume UUID reported by diskutil matches the value returned by DiskArbitration for the kDADiskDescriptionVolumeUUIDKey property.  For example:

                  import DiskArbitration
                  
                  func uuidForDisk(_ bsdName: String) -> UUID? {
                      guard
                          let session = DASessionCreate(nil),
                          let disk = DADiskCreateFromBSDName(nil, session, bsdName),
                          let descCF = DADiskCopyDescription(disk),
                          let desc = descCF as? [String:Any],
                          let uuidCF = desc[kDADiskDescriptionVolumeUUIDKey as String] as CFTypeRef?,
                          CFGetTypeID(uuidCF) == CFUUIDGetTypeID(),
                          let uuidStr = CFUUIDCreateString(nil, (uuidCF as! CFUUID)) as String?,
                          let uuid = UUID(uuidString: uuidStr)
                      else {
                          return nil
                      }
                      return uuid
                  }

                  For Apple file systems (like APFS and HFS Plus) this UUID is stored on the volume.  The VFS plug-in makes it available to the rest of the system via ATTR_VOL_UUID (see the getattrlist man page).

                  I’m not an expert on exFAT, but I believe it has a similar affordance.  Specifically, it looks like there is a Volume GUID directory entry to store this.  If that entry is missing, I believe that our exFAT VFS plug-in will synthesise a UUID by hashing the volume serial number.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"

                    • Re: Get a filesystem UUID
                      exfat Level 1 Level 1 (0 points)

                      Yes, I know how GPT works

                       

                      The Volume UUID reported by diskutil matches the value returned by DiskArbitration for the kDADiskDescriptionVolumeUUIDKey property.  For example:

                      1. import DiskArbitration
                      2. func uuidForDisk(_ bsdName: String) -> UUID? {
                      3.     guard
                      4.         let session = DASessionCreate(nil),
                      5.         let disk = DADiskCreateFromBSDName(nil, session, bsdName),
                      6.         let descCF = DADiskCopyDescription(disk),
                      7.         let desc = descCF as? [String:Any],
                      8.         let uuidCF = desc[kDADiskDescriptionVolumeUUIDKey as String] as CFTypeRef?,
                      9.         CFGetTypeID(uuidCF) == CFUUIDGetTypeID(),
                      10.         let uuidStr = CFUUIDCreateString(nil, (uuidCF as! CFUUID)) as String?,
                      11.         let uuid = UUID(uuidString: uuidStr)
                      12.     else {
                      13.         return nil
                      14.     }
                      15.     return uuid
                      16. }

                      For Apple file systems (like APFS and HFS Plus) this UUID is stored on the volume.  The VFS plug-in makes it available to the rest of the system via ATTR_VOL_UUID (see the getattrlist man page).

                       

                      Thanks! I hope I also can make it in C

                       

                      If that entry is missing, I believe that our exFAT VFS plug-in will synthesise a UUID by hashing the volume serial number.

                      Make sense, I thought so too

                       

                      Thank you