in Sources/Foundation/NSURL.swift [1174:1459]
func read(_ keys: [URLResourceKey], for url: NSURL) throws -> [URLResourceKey: Any?] {
var result: [URLResourceKey: Any?] = [:]
let fm = FileManager.default
let path = url.path ?? ""
// Memoized access to attributes:
var fileAttributesStorage: [FileAttributeKey: Any]? = nil
func attributes() throws -> [FileAttributeKey: Any] {
if let storage = fileAttributesStorage {
return storage
} else {
let storage = try fm._attributesOfItem(atPath: path, includingPrivateAttributes: true)
fileAttributesStorage = storage
return storage
}
}
func attribute(_ fileAttributeKey: FileAttributeKey) throws -> Any? {
let attributeValues = try attributes()
return attributeValues[fileAttributeKey]
}
// Memoized access to lstat:
var urlStatStorage: stat?
func urlStat() throws -> stat {
if let storage = urlStatStorage {
return storage
} else {
let storage = try fm._lstatFile(atPath: path)
urlStatStorage = storage
return storage
}
}
// Memoized access to volume URLs:
var volumeURLsStorage: [URL]?
var volumeURLs: [URL] {
if let storage = volumeURLsStorage {
return storage
} else {
let storage = fm.mountedVolumeURLs(includingResourceValuesForKeys: nil) ?? []
volumeURLsStorage = storage
return storage
}
}
var volumeAttributesStorage: [FileAttributeKey: Any]?
var blockSizeStorage: UInt64?
func volumeAttributes() throws -> [FileAttributeKey: Any] {
if let storage = volumeAttributesStorage {
return storage
} else {
let (storage, block) = try fm._attributesOfFileSystemIncludingBlockSize(forPath: path)
volumeAttributesStorage = storage
blockSizeStorage = block
return storage
}
}
func blockSize() throws -> UInt64? {
_ = try volumeAttributes()
return blockSizeStorage
}
func volumeAttribute(_ fileAttributeKey: FileAttributeKey) throws -> Any? {
let attributeValues = try volumeAttributes()
return attributeValues[fileAttributeKey]
}
var volumeURLStorage: (searched: Bool, url: URL?)?
func volumeURL() throws -> URL? {
if let url = volumeURLStorage {
return url.url
}
var foundURL: URL?
for volumeURL in volumeURLs {
var relationship: FileManager.URLRelationship = .other
try fm.getRelationship(&relationship, ofDirectoryAt: volumeURL, toItemAt: url._swiftObject)
if relationship == .same || relationship == .contains {
foundURL = volumeURL
break
}
}
volumeURLStorage = (searched: true, url: foundURL)
return foundURL
}
for key in keys {
switch key {
case .nameKey:
result[key] = url.lastPathComponent
case .localizedNameKey:
result[key] = fm.displayName(atPath: path)
case .isRegularFileKey:
result[key] = try attribute(.type) as? FileAttributeType == FileAttributeType.typeRegular
case .isDirectoryKey:
result[key] = try attribute(.type) as? FileAttributeType == FileAttributeType.typeDirectory
case .isSymbolicLinkKey:
result[key] = try attribute(.type) as? FileAttributeType == FileAttributeType.typeSymbolicLink
case .isVolumeKey:
result[key] = volumeURLs.contains(url._swiftObject)
case .isPackageKey:
result[key] = try attribute(.type) as? FileAttributeType == FileAttributeType.typeDirectory && url.pathExtension != nil && url.pathExtension != ""
case .isApplicationKey:
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
result[key] = try attribute(.type) as? FileAttributeType == FileAttributeType.typeDirectory && url.pathExtension == "app"
#else
result[key] = false
#endif
case .applicationIsScriptableKey:
// Not supported.
break
case .isSystemImmutableKey:
result[key] = try attribute(._systemImmutable) as? Bool == true
case .isUserImmutableKey:
result[key] = try attribute(._userImmutable) as? Bool == true
case .isHiddenKey:
result[key] = try attribute(._hidden) as? Bool == true
case .hasHiddenExtensionKey:
result[key] = false // Most OSes do not have a way to record this.
case .creationDateKey:
result[key] = try attribute(.creationDate)
case .contentAccessDateKey:
result[key] = try attribute(._accessDate)
case .contentModificationDateKey:
result[key] = try attribute(.modificationDate)
case .attributeModificationDateKey:
// We do not support this in a cross-platform manner.
break
case .linkCountKey:
result[key] = Int(try urlStat().st_nlink)
case .parentDirectoryURLKey:
result[key] = url.deletingLastPathComponent
case .volumeURLKey:
result[key] = try volumeURL()
case .fileResourceIdentifierKey:
result[key] = _URLFileResourceIdentifier(path: path, inode: Int(try urlStat().st_ino), volumeIdentifier: Int(try urlStat().st_dev))
case .volumeIdentifierKey:
result[key] = try volumeAttribute(.systemNumber)
case .preferredIOBlockSizeKey:
result[key] = try blockSize()
case .isReadableKey:
result[key] = fm.isReadableFile(atPath: path)
case .isWritableKey:
result[key] = fm.isWritableFile(atPath: path)
case .isExecutableKey:
result[key] = fm.isExecutableFile(atPath: path)
case .pathKey:
result[key] = url.path
case .canonicalPathKey:
result[key] = try fm._canonicalizedPath(toFileAtPath: path)
case .fileResourceTypeKey:
result[key] = try attribute(.type)
case .totalFileSizeKey: fallthrough // FIXME: This should add the size of any metadata.
case .fileSizeKey:
result[key] = try attribute(.size)
case .totalFileAllocatedSizeKey: fallthrough // FIXME: This should add the size of any metadata.
case .fileAllocatedSizeKey:
#if !os(Windows)
let stat = try urlStat()
result[key] = Int(stat.st_blocks) * Int(stat.st_blksize)
#endif
case .isAliasFileKey:
// swift-corelibs-foundation does not support aliases and bookmarks.
break
case .volumeLocalizedFormatDescriptionKey:
// FIXME: This should have different names for different kinds of volumes, and be localized.
result[key] = "Volume"
case .volumeTotalCapacityKey:
result[key] = try volumeAttribute(.systemSize)
case .volumeAvailableCapacityKey:
result[key] = try volumeAttribute(.systemFreeSize)
case .volumeResourceCountKey:
result[key] = try volumeAttribute(.systemFileNumber)
// FIXME: swift-corelibs-foundation does not currently support querying this kind of filesystem information. We return reasonable assumptions for now, with the understanding that by noting support we are encouraging the application to try performing corresponding I/O operations (and handle those errors, which they already must) instead. Where those keys would inform I/O decisions that are not single operations, we assume conservatively.
case .volumeSupportsPersistentIDsKey:
result[key] = false
case .volumeSupportsSymbolicLinksKey:
result[key] = true
case .volumeSupportsHardLinksKey:
result[key] = true
case .volumeSupportsJournalingKey:
result[key] = false
case .volumeIsJournalingKey:
result[key] = false
case .volumeSupportsSparseFilesKey:
result[key] = false
case .volumeSupportsZeroRunsKey:
result[key] = false
case .volumeSupportsRootDirectoryDatesKey:
result[key] = true
case .volumeSupportsVolumeSizesKey:
result[key] = true
case .volumeSupportsRenamingKey:
result[key] = true
case .volumeSupportsAdvisoryFileLockingKey:
result[key] = false
case .volumeSupportsExtendedSecurityKey:
result[key] = false
case .volumeIsBrowsableKey:
result[key] = true
case .volumeIsReadOnlyKey:
result[key] = false
case .volumeCreationDateKey:
result[key] = try volumeAttribute(.creationDate)
case .volumeURLForRemountingKey:
result[key] = nil
case .volumeMaximumFileSizeKey: fallthrough
case .volumeIsEjectableKey: fallthrough
case .volumeIsRemovableKey: fallthrough
case .volumeIsInternalKey: fallthrough
case .volumeIsAutomountedKey: fallthrough
case .volumeIsLocalKey: fallthrough
case .volumeSupportsCaseSensitiveNamesKey: fallthrough
case .volumeUUIDStringKey: fallthrough
case .volumeIsEncryptedKey: fallthrough
case .volumeSupportsCompressionKey: fallthrough
case .volumeSupportsFileCloningKey: fallthrough
case .volumeSupportsSwapRenamingKey: fallthrough
case .volumeSupportsExclusiveRenamingKey: fallthrough
case .volumeSupportsCasePreservedNamesKey:
// Whatever we assume here, we may make problems for the implementation that relies on them; we just don't answer for now.
break
case .volumeNameKey:
if let url = try volumeURL() {
result[key] = url.lastPathComponent
}
case .volumeLocalizedNameKey:
if let url = try volumeURL() {
result[key] = fm.displayName(atPath: url.path)
}
case .volumeIsRootFileSystemKey:
#if !os(Windows)
if let url = try volumeURL() {
result[key] = url.path == "/"
}
#endif
case .isUbiquitousItemKey: fallthrough
case .ubiquitousItemHasUnresolvedConflictsKey: fallthrough
case .ubiquitousItemIsDownloadingKey: fallthrough
case .ubiquitousItemIsUploadedKey: fallthrough
case .ubiquitousItemIsUploadingKey: fallthrough
case .ubiquitousItemDownloadingStatusKey: fallthrough
case .ubiquitousItemDownloadingErrorKey: fallthrough
case .ubiquitousItemUploadingErrorKey: fallthrough
case .ubiquitousItemDownloadRequestedKey: fallthrough
case .ubiquitousItemContainerDisplayNameKey: fallthrough
case .fileSecurityKey: fallthrough
case .isExcludedFromBackupKey: fallthrough
case .tagNamesKey: fallthrough
case .typeIdentifierKey: fallthrough
case .localizedTypeDescriptionKey: fallthrough
case .labelNumberKey: fallthrough
case .labelColorKey: fallthrough
case .localizedLabelKey: fallthrough
case .effectiveIconKey: fallthrough
case .isMountTriggerKey: fallthrough
case .generationIdentifierKey: fallthrough
case .documentIdentifierKey: fallthrough
case .addedToDirectoryDateKey: fallthrough
case .quarantinePropertiesKey: fallthrough
case .thumbnailDictionaryKey: fallthrough
case .thumbnailKey: fallthrough
case .customIconKey:
// Not supported outside of Apple OSes.
break
default:
break
}
}
return result
}