internal/diskutil/mojave.go (62 lines of code) (raw):
package diskutil
import (
"context"
"fmt"
"regexp"
"github.com/aws/ec2-macos-utils/internal/diskutil/types"
"github.com/aws/ec2-macos-utils/internal/util"
)
// updatePhysicalStores provides separate functionality for fetching APFS physical stores for SystemPartitions.
func updatePhysicalStores(ctx context.Context, partitions *types.SystemPartitions) error {
// Independently update all APFS disks' physical stores
for i, part := range partitions.AllDisksAndPartitions {
// Only do the update if the disk/partition is APFS
if isAPFSVolume(part) {
// Fetch the physical store for the disk/partition
physicalStoreDeviceID, err := fetchPhysicalStore(ctx, part.DeviceIdentifier)
if err != nil {
return err
}
// Create a new physical store from the output
physicalStoreElement := types.APFSPhysicalStoreID{
DeviceIdentifier: physicalStoreDeviceID,
}
// Add the physical store to the DiskInfo
partitions.AllDisksAndPartitions[i].APFSPhysicalStores = append(part.APFSPhysicalStores, physicalStoreElement)
}
}
return nil
}
// isAPFSVolume checks if a given DiskPart is an APFS container.
func isAPFSVolume(part types.DiskPart) bool {
return part.APFSVolumes != nil
}
// fetchPhysicalStore parses the human-readable output of the list verb for the given ID in order to fetch its
// physical store. This function is limited to returning only one physical store so the behavior might cause problems
// for fusion devices that have more than one APFS physical store.
func fetchPhysicalStore(ctx context.Context, id string) (string, error) {
// Create the command for running diskutil and parsing the output to retrieve the desired info (physical store)
// * list - specifies the diskutil 'list' verb for a specific device ID and returns the human-readable output
cmdPhysicalStore := []string{"diskutil", "list", id}
// Execute the command to parse output from diskutil list
out, err := util.ExecuteCommand(ctx, cmdPhysicalStore, "", nil, nil)
if err != nil {
return "", fmt.Errorf("%s: %w", out.Stderr, err)
}
return parsePhysicalStoreId(out.Stdout)
}
var (
physicalStoreFieldTokenRegexp = regexp.MustCompile(`\s*Physical Store disk[0-9]+(s[0-9]+)*`)
physicalStoreValueDiskIDRegexp = regexp.MustCompile("disk[0-9]+(s[0-9]+)*")
)
// parsePhysicalStoreId searches a raw string for the string "Physical Store disk[0-9]+(s[0-9]+)*". The regular
// expression "disk[0-9]+(s[0-9]+)*" matches any disk ID without the "/dev/" prefix.
func parsePhysicalStoreId(raw string) (string, error) {
physicalStore := physicalStoreFieldTokenRegexp.FindString(raw)
diskId := physicalStoreValueDiskIDRegexp.FindString(physicalStore)
if diskId == "" {
return "", fmt.Errorf("physical store not found")
}
return diskId, nil
}
// updatePhysicalStore provides separate functionality for fetching APFS physical stores for DiskInfo.
func updatePhysicalStore(ctx context.Context, disk *types.DiskInfo) error {
if isAPFSMedia(disk) {
physicalStoreDeviceID, err := fetchPhysicalStore(ctx, disk.DeviceIdentifier)
if err != nil {
return err
}
physicalStoreElement := types.APFSPhysicalStore{
DeviceIdentifier: physicalStoreDeviceID,
}
disk.APFSPhysicalStores = append(disk.APFSPhysicalStores, physicalStoreElement)
}
return nil
}
// isAPFSMedia checks if the given DiskInfo is an APFS container or volume.
func isAPFSMedia(disk *types.DiskInfo) bool {
return disk.FilesystemType == "apfs" || disk.IORegistryEntryName == "AppleAPFSMedia"
}