Getting inode number from URL

I couldn't find any other way of getting the inode number without using FileManager.attributesOfItem(atPath: url.path)[.systemFileNumber]. I'm already using FileManager.enumerator(at:includingPropertiesForKeys:errorHandler:) for enumerating large directories and using that other FileManager method only for accessing the inode number doubles the scan time. I looked for a URLResourceKey but there doesn't seem to be any. I would be really grateful for any kind of help.
Answered by DTS Engineer in 626656022
Code Block
/// Adapts `getattrlistbulk` to use Swift-style errors.
func getattrlistbulk2(_ dirFD: CInt, _ attrListPtr: UnsafeMutablePointer<attrlist>, _ attrBuf: UnsafeMutableRawBufferPointer, _ options: UInt64) throws -> Int {
while true {
let result = getattrlistbulk(dirFD, attrListPtr, attrBuf.baseAddress!, attrBuf.count, 0)
if result >= 0 {
return Int(result)
}
let err = errno
if err != EINTR {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: nil)
}
// continue on `EINTR`
}
}
/// A copy of `vtype`.
///
/// This system enum is not available to Swift. In a real project you’d access it via a
/// bridging header.
enum vtype: fsobj_type_t {
/* 0 */
case VNON
/* 1 - 5 */
case VREG; case VDIR; case VBLK; case VCHR; case VLNK
/* 6 - 10 */
case VSOCK; case VFIFO; case VBAD; case VSTR; case VCPLX
};
/// Prints the attributes in the supplied buffer.
func printAttrs(_ itemCount: Int, _ attrBuf: UnsafeMutableRawBufferPointer) {
var entryStart = attrBuf.baseAddress!
for _ in 0..<itemCount {
var field = entryStart
let length = Int(field.load(as: UInt32.self))
field += MemoryLayout<UInt32>.size
entryStart += length
let returned = field.load(as: attribute_set_t.self)
field += MemoryLayout<attribute_set_t>.size
var error: UInt32 = 0
if (returned.commonattr & attrgroup_t(bitPattern: ATTR_CMN_ERROR)) != 0 {
error = field.load(as: UInt32.self)
field += MemoryLayout<UInt32>.size
}
var name: String = ""
if (returned.commonattr & attrgroup_t(bitPattern: ATTR_CMN_NAME)) != 0 {
let base = field
let nameInfo = field.load(as: attrreference_t.self)
field += MemoryLayout<attrreference_t>.size
name = String(cString: (base + Int(nameInfo.attr_dataoffset)).assumingMemoryBound(to: CChar.self))
}
if error != 0 {
print("Error in reading attributes for directory entry \(error)");
continue
}
var objectType: fsobj_type_t = vtype.VNON.rawValue
if (returned.commonattr & attrgroup_t(bitPattern: ATTR_CMN_OBJTYPE)) != 0 {
objectType = field.load(as: fsobj_type_t.self)
field += MemoryLayout<fsobj_type_t>.size
switch objectType {
case vtype.VREG.rawValue:
print("file \(name)")
case vtype.VDIR.rawValue:
print(" dir \(name)")
default:
print(" xxx \(name)")
}
}
}
}
func demo(_ dirPath: String) throws {
let dirFD = open(dirPath, O_RDONLY)
guard dirFD >= 0 else {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: nil)
}
defer {
let junk = close(dirFD)
assert(junk == 0)
}
var attrList = attrlist()
attrList.bitmapcount = u_short( ATTR_BIT_MAP_COUNT )
attrList.commonattr =
attrgroup_t(ATTR_CMN_RETURNED_ATTRS) |
attrgroup_t(bitPattern: ATTR_CMN_NAME) |
attrgroup_t(bitPattern: ATTR_CMN_ERROR) |
attrgroup_t(bitPattern: ATTR_CMN_OBJTYPE)
let attrBuf = UnsafeMutableRawBufferPointer.allocate(byteCount: 256, alignment: 16)
defer {
attrBuf.deallocate()
}
while true {
let itemCount = try getattrlistbulk2(dirFD, &attrList, attrBuf, 0)
guard itemCount > 0 else {
return
}
printAttrs(itemCount, attrBuf)
}
}

I just wanted to add that for some reason the order of imports inside the test files is important in some cases. I have to write:

@testable import MyApp
import XCTest

Inverting those two lines causes several compiler errors inside the bridging header file:

Redefinition of 'vtype'
Redefinition of enumerator 'VBAD'
Redefinition of enumerator 'VBLK'
...

I have no idea why this happens, as in another project with the exact same bridging header file the order of imports seems to be irrelevant and doesn't cause any compiler errors.

Getting inode number from URL
 
 
Q