2019-11-06 23:58:10 +01:00
|
|
|
//
|
|
|
|
// Disks.swift
|
|
|
|
// Clover
|
|
|
|
//
|
|
|
|
// Created by vector sigma on 19/10/2019.
|
|
|
|
// Copyright © 2019 CloverHackyColor. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
import IOKit
|
|
|
|
import IOKit.serial
|
|
|
|
import IOKit.kext
|
|
|
|
import CoreFoundation
|
|
|
|
import DiskArbitration
|
|
|
|
import SystemConfiguration
|
|
|
|
|
|
|
|
let kNotAvailable : String = "N/A"
|
|
|
|
|
|
|
|
/// Get DADisk dictionary from DiskArbitration from the given disk name or mount point.
|
|
|
|
func getDAdiskDescription(from diskOrMtp: String) -> NSDictionary? {
|
|
|
|
var dict : NSDictionary? = nil
|
|
|
|
if let session = DASessionCreate(kCFAllocatorDefault) {
|
|
|
|
if diskOrMtp.hasPrefix("/") &&
|
|
|
|
!diskOrMtp.hasPrefix("/dev/disk") &&
|
|
|
|
FileManager.default.fileExists(atPath: diskOrMtp) {
|
|
|
|
let volume : CFURL = URL(fileURLWithPath: diskOrMtp) as CFURL
|
|
|
|
if let disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, volume) {
|
|
|
|
dict = DADiskCopyDescription(disk)
|
|
|
|
}
|
|
|
|
} else if diskOrMtp.hasPrefix("/dev/disk") ||
|
|
|
|
diskOrMtp.hasPrefix("disk") {
|
|
|
|
var ndisk = diskOrMtp
|
|
|
|
if ndisk.hasPrefix("/dev/disk") {
|
|
|
|
ndisk = (ndisk as NSString).replacingOccurrences(of: "/dev/", with: "")
|
|
|
|
}
|
|
|
|
if let disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, ndisk) {
|
|
|
|
dict = DADiskCopyDescription(disk)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dict
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check disk or mount point is writable (kDADiskDescriptionMediaWritableKey).
|
|
|
|
func isWritable(diskOrMtp: String) -> Bool {
|
|
|
|
var isWritable : Bool = false
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: diskOrMtp) {
|
|
|
|
if let val = dict.object(forKey: kDADiskDescriptionMediaWritableKey) as? NSNumber {
|
|
|
|
isWritable = val.boolValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return isWritable
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the media content name (kDADiskDescriptionMediaContentKey).
|
|
|
|
func getMediaContent(from diskOrMtp: String) -> String? {
|
|
|
|
var content : String? = nil
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: diskOrMtp) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionMediaContentKey) != nil) {
|
|
|
|
content = dict.object(forKey: kDADiskDescriptionMediaContentKey) as? String
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return content
|
|
|
|
}
|
|
|
|
|
|
|
|
/// get the Partition Scheme Map from the parent disk (kDADiskDescriptionMediaContentKey).
|
|
|
|
func getPartitionSchemeMap(from diskOrMtp: String) -> String? {
|
|
|
|
var sheme : String? = nil
|
|
|
|
if let dadisk = getBSDParent(of: diskOrMtp) {
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: dadisk) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionMediaContentKey) != nil) {
|
|
|
|
sheme = dict.object(forKey: kDADiskDescriptionMediaContentKey) as? String
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sheme
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Find the mountpoint for the given disk object. You can pass also a mount point to se if it is valid.
|
|
|
|
func getMountPoint(from diskOrMtp: String) -> String? {
|
|
|
|
// kDADiskDescriptionVolumePathKey
|
|
|
|
var mountPoint : String? = nil
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: diskOrMtp) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionVolumePathKey) != nil) {
|
|
|
|
let temp : AnyObject = dict.object(forKey: kDADiskDescriptionVolumePathKey) as AnyObject
|
|
|
|
if temp is NSURL {
|
|
|
|
mountPoint = (dict.object(forKey: kDADiskDescriptionVolumePathKey) as? URL)?.path
|
|
|
|
} else if temp is NSString {
|
|
|
|
mountPoint = dict.object(forKey: kDADiskDescriptionVolumePathKey) as? String
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mountPoint
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Find the Volume name: be aware that this is not the mount point name.
|
|
|
|
func getVolumeName(from diskOrMtp: String) -> String? {
|
|
|
|
// kDADiskDescriptionVolumeNameKey
|
|
|
|
var name : String? = nil
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: diskOrMtp) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionVolumeNameKey) != nil) {
|
|
|
|
name = dict.object(forKey: kDADiskDescriptionVolumeNameKey) as? String
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get partition UUID for the given volume: This is not a disk UUID.
|
|
|
|
func getVolumeUUID(from diskOrMtp: String) -> String? {
|
|
|
|
// kDADiskDescriptionVolumeUUIDKey
|
|
|
|
var uuid : String? = nil
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: diskOrMtp) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionVolumeUUIDKey) != nil) {
|
|
|
|
|
|
|
|
let cfuuid :CFUUID = dict.object(forKey: kDADiskDescriptionVolumeUUIDKey) as! CFUUID
|
|
|
|
uuid = CFUUIDCreateString(kCFAllocatorDefault, cfuuid)! as String
|
|
|
|
//print(uuid!)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return uuid
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get disk uuid for the given diskx. This is not a Volume UUID but a media UUID.
|
|
|
|
func getDiskUUID(from diskOrMtp: String) -> String? {
|
|
|
|
// kDADiskDescriptionVolumeUUIDKey
|
|
|
|
var uuid : String? = nil
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: getBSDParent(of: diskOrMtp)!) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionMediaUUIDKey) != nil) {
|
|
|
|
let temp : AnyObject = dict.object(forKey: kDADiskDescriptionMediaUUIDKey) as AnyObject
|
|
|
|
if temp is NSUUID {
|
|
|
|
uuid = (dict.object(forKey: kDADiskDescriptionMediaUUIDKey) as? UUID)?.uuidString
|
|
|
|
} else if temp is NSString {
|
|
|
|
uuid = dict.object(forKey: kDADiskDescriptionMediaUUIDKey) as? String
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return uuid
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get Media Name (kDADiskDescriptionMediaNameKey).
|
2019-11-09 18:03:22 +01:00
|
|
|
func getMediaName(from diskOrMtp: String) -> String? {
|
|
|
|
var name : String? = nil
|
2019-11-06 23:58:10 +01:00
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: diskOrMtp) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionMediaNameKey) != nil) {
|
|
|
|
name = (dict.object(forKey: kDADiskDescriptionMediaNameKey) as? String)!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
/// Get Media Name (kDADiskDescriptionDeviceProtocolKey).
|
|
|
|
func getDeviceProtocol(from diskOrMtp: String) -> String {
|
|
|
|
var prot : String = kNotAvailable
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: diskOrMtp) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionDeviceProtocolKey) != nil) {
|
|
|
|
prot = (dict.object(forKey: kDADiskDescriptionDeviceProtocolKey) as? String)!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return prot
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get all BSDName in the System.
|
|
|
|
func getAlldisks() -> NSDictionary {
|
|
|
|
let match_dictionary: CFMutableDictionary = IOServiceMatching("IOMedia")
|
|
|
|
var entry_iterator: io_iterator_t = 0
|
|
|
|
let allDisks = NSMutableDictionary()
|
|
|
|
if IOServiceGetMatchingServices(kIOMasterPortDefault, match_dictionary, &entry_iterator) == kIOReturnSuccess {
|
|
|
|
//let serviceObject: io_registry_entry_t
|
|
|
|
//var serviceObject : UnsafeMutablePointer<io_registry_entry_t>
|
|
|
|
var serviceObject : io_registry_entry_t = 0
|
|
|
|
|
|
|
|
repeat {
|
|
|
|
serviceObject = IOIteratorNext(entry_iterator)
|
|
|
|
if serviceObject != 0 {
|
|
|
|
var serviceDictionary : Unmanaged<CFMutableDictionary>?
|
|
|
|
if (IORegistryEntryCreateCFProperties(serviceObject,
|
|
|
|
&serviceDictionary,
|
|
|
|
kCFAllocatorDefault,
|
|
|
|
0) != kIOReturnSuccess) {
|
|
|
|
IOObjectRelease(serviceObject)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
let d : NSDictionary = (serviceDictionary?.takeRetainedValue())!
|
|
|
|
|
|
|
|
if (d.object(forKey: kIOBSDNameKey) != nil) {
|
|
|
|
allDisks.setValue(d, forKey: (d.object(forKey: kIOBSDNameKey) as! String))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while serviceObject != 0
|
|
|
|
IOObjectRelease(entry_iterator)
|
|
|
|
}
|
|
|
|
|
|
|
|
return allDisks
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get FileSystem for the given disk or mount point.
|
|
|
|
func getFS(from diskOrMtp: String) -> String? {
|
|
|
|
var fs : String? = nil
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: diskOrMtp) {
|
|
|
|
var temp : String = ""
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionVolumeKindKey) != nil) {
|
|
|
|
temp = dict.object(forKey: kDADiskDescriptionVolumeKindKey) as! String
|
|
|
|
|
|
|
|
// if msdos we would know if is fat32, exfat etc..
|
|
|
|
if temp.lowercased() == "msdos" {
|
|
|
|
// Since last few OSes (..on 10.13) the DAVolumeType
|
|
|
|
// contains a string like ""MS-DOS (FAT32)" so that we can identify the real fs
|
|
|
|
// w/o using statfs.
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionVolumeTypeKey) != nil) {
|
|
|
|
let volType : String = dict.object(forKey: kDADiskDescriptionVolumeTypeKey) as! String
|
|
|
|
|
|
|
|
if (volType.lowercased().range(of: "exfat") != nil) {
|
|
|
|
temp = "exFAT"
|
|
|
|
} else if (volType.lowercased().range(of: "fat16") != nil) {
|
|
|
|
temp = "FAT16"
|
|
|
|
} else if (volType.lowercased().range(of: "fat32") != nil) {
|
|
|
|
temp = "FAT32"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fs = temp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fs
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get all ESP (EFI System Partition) in the System.
|
|
|
|
func getAllESPs() -> [String] {
|
|
|
|
var allEsps : [String] = [String]()
|
|
|
|
for disk in getAlldisks().allKeys {
|
|
|
|
let mediaContent = getMediaContent(from: disk as! String) ?? ""
|
|
|
|
if getMediaName(from: disk as! String) == "EFI System Partition" &&
|
|
|
|
mediaContent == "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"{
|
|
|
|
if !allEsps.contains(disk as! String) {
|
|
|
|
allEsps.append(disk as! String)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return allEsps
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a list of ESP that have a mount point.
|
|
|
|
func getListOfMountedEsp() -> [String] {
|
|
|
|
var mounted : [String] = [String]()
|
|
|
|
for bsdName in getAllESPs() {
|
|
|
|
if isMountPoint(path: bsdName) {
|
|
|
|
mounted.append(bsdName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mounted
|
|
|
|
}
|
|
|
|
|
2019-11-09 18:03:22 +01:00
|
|
|
/// get and array of currently mounted volumes
|
|
|
|
func getVolumes() -> [String] {
|
|
|
|
var mounted : [String] = [String]()
|
|
|
|
let all = getAlldisks().allKeys
|
|
|
|
for b in all {
|
|
|
|
let bsd : String = b as! String
|
|
|
|
if let mp = getMountPoint(from: bsd) {
|
|
|
|
mounted.append(mp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return mounted
|
|
|
|
}
|
|
|
|
|
2019-11-06 23:58:10 +01:00
|
|
|
/// Find the BSDName of the given mount point.
|
|
|
|
func getBSDName(of mountpoint: String) -> String? {
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: mountpoint) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionMediaBSDNameKey) != nil) {
|
|
|
|
return dict.object(forKey: kDADiskDescriptionMediaBSDNameKey) as? String
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Find the BSDName of the given parent disk.
|
|
|
|
func getBSDParent(of mountpointORDevDisk: String) -> String? {
|
|
|
|
// DAMediaBSDUnit
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: mountpointORDevDisk) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionMediaBSDUnitKey) != nil) {
|
|
|
|
return "disk" + ((dict.object(forKey: kDADiskDescriptionMediaBSDUnitKey) as? NSNumber)?.stringValue)!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the partition slice number (as string) for the given disk or mount point.
|
|
|
|
func getPartitionSlice(of mountpointORDevDisk: String) -> String? {
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: mountpointORDevDisk) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionMediaBSDNameKey) != nil) {
|
|
|
|
let disk = (dict.object(forKey: kDADiskDescriptionMediaBSDNameKey) as? String)!
|
|
|
|
if (disk.range(of: "s") != nil) {
|
|
|
|
let arr : [String] = disk.components(separatedBy: "s")
|
|
|
|
if arr.count == 3 { /* dis k0s 1 */
|
|
|
|
return arr.last
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the image (NSImage) for the given mount point or disk object.
|
|
|
|
func getIconFor(volume mountpointORDevDisk: String) -> NSImage? {
|
|
|
|
var image : NSImage? = nil
|
|
|
|
// get a customized icon.. if any
|
|
|
|
if isMountPoint(path: mountpointORDevDisk) {
|
|
|
|
let mtp : String = getMountPoint(from: mountpointORDevDisk)!
|
|
|
|
if FileManager.default.fileExists(atPath: mtp + "/.VolumeIcon.icns") {
|
|
|
|
image = NSImage(byReferencingFile: mtp + "/.VolumeIcon.icns")
|
|
|
|
return image
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// .. otherwise get a System icon
|
|
|
|
if let daDict : NSDictionary = getDAdiskDescription(from: mountpointORDevDisk) {
|
|
|
|
if let iconDict = daDict.object(forKey: kDADiskDescriptionMediaIconKey) as? NSDictionary,
|
|
|
|
let iconName = iconDict.object(forKey: kIOBundleResourceFileKey ) as? String {
|
|
|
|
let identifier = iconDict.object(forKey: kCFBundleIdentifierKey as String) as! CFString
|
|
|
|
|
|
|
|
let url : CFURL = Unmanaged.takeRetainedValue(KextManagerCreateURLForBundleIdentifier(kCFAllocatorDefault, identifier))()
|
|
|
|
//print(url)
|
|
|
|
if let kb = Bundle(url: url as URL) {
|
|
|
|
image = NSImage(byReferencingFile:
|
|
|
|
kb.path(forResource: iconName.deletingFileExtension, ofType: iconName.fileExtension) ?? "")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return image
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Boolean value indicanting if the given mount point or disk is internal.
|
|
|
|
func isInternalDevice(diskOrMtp: String) -> Bool {
|
|
|
|
if let dict : NSDictionary = getDAdiskDescription(from: diskOrMtp) {
|
|
|
|
if (dict.object(forKey: kDADiskDescriptionDeviceInternalKey) != nil) {
|
|
|
|
return ((dict.object(forKey: kDADiskDescriptionDeviceInternalKey) as? NSNumber)?.boolValue)!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Boolean value indicanting if the given path is a valid mount point for a disk object.
|
|
|
|
func isMountPoint(path: String) -> Bool {
|
|
|
|
let mtp : String? = getMountPoint(from: path)
|
|
|
|
return (mtp != nil)
|
|
|
|
}
|