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.
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(" *** \(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) } }