我正在尝试创建带有保险丝的内存文件系统。
我在Mac OS 10.14.4上安装了FUSE for macOS 3.10.2,
我使用xcode 10.2.1创建了swift项目并与OSXFUSE.framework
我给每个文件和目录随机生成一个数字,并在attributeOfItem回调函数中设置systemFileNumber属性。
我以为这是inode值,所以我对硬链接文件使用了相同的值,但是,
我的随机数根本没有用。
我错过了什么吗?
下面是我的代码。
谢谢。
import Foundation
let DIRECTORY_ITEM_SIZE = 32
let FILE_SYSTEM_BLOCK_SIZE = 512
var usedSystemFileNumbers: [UInt64] = []
class FileItemInfo {
var name: String = ""
var type: FileAttributeType = .typeUnknown
var dateModification: Date? = nil
var permission: UInt16? = nil
var ownerAccountId: UInt32? = nil
var groupOwnerAccountId: UInt32? = nil
var systemFileNumber: UInt64
var dateCreation: Date? = nil
var dateBackup: Date? = nil
var dateChange: Date? = nil
var dateAccess: Date? = nil
var flags: UInt32? = nil
var hardLinkTarget: [FileItemInfo] = []
private var _internalHardLinkSource: FileItemInfo? = nil
var hardLinkSource: FileItemInfo? {
get {
return _internalHardLinkSource
}
set(value) {
if nil != value {
if let index = usedSystemFileNumbers.firstIndex(of: self.systemFileNumber) {
usedSystemFileNumbers.remove(at: index)
}
self.systemFileNumber = 0
_internalHardLinkSource = value
}
}
}
private var _internalSize: UInt64 = 0
var size: UInt64 {
get {
return _internalSize
}
}
private var _internalData: Data? = nil
var data: Data? {
get {
return _internalData
}
set(value) {
if nil != value {
_internalData = value
_internalSize = UInt64(value!.count)
}
}
}
public init(_ name: String, type: FileAttributeType, dateModification: Date? = nil, permission: UInt16? = nil, ownerAccountId: UInt32? = nil, groupOwnerAccountId: UInt32? = nil, dateCreation: Date? = nil, dateBackup: Date? = nil, dateChange: Date? = nil, dateAccess: Date? = nil, flags: UInt32? = nil, data: Data? = nil, hardLinkSource: FileItemInfo? = nil, hardLinkTarget: [FileItemInfo] = []) {
self.name = name
self.type = type
self.dateModification = dateModification
self.permission = permission
self.ownerAccountId = ownerAccountId
self.groupOwnerAccountId = groupOwnerAccountId
self.dateCreation = dateCreation
self.dateBackup = dateBackup
self.dateChange = dateChange
self.dateAccess = dateAccess
self.flags = flags
var gen = SystemRandomNumberGenerator()
var number = gen.next()
while 0 == number || usedSystemFileNumbers.contains(number) {
number = gen.next()
}
usedSystemFileNumbers.append(number)
self.systemFileNumber = number
self.hardLinkSource = hardLinkSource
self.hardLinkTarget = hardLinkTarget
self.data = data
}
}
class FileTree {
var this: FileItemInfo
var children: [FileTree]
public init(_ item: FileItemInfo, children: [FileTree] = []) {
self.this = item
self.children = children
}
}
var root = FileItemInfo("/", type: .typeDirectory)
var root_subdir = FileItemInfo("subdir", type: .typeDirectory)
var root_file = FileItemInfo("file", type: .typeRegular)
var root_hardLinkToFile = FileItemInfo("hard_link_to_file", type: .typeRegular)
root_hardLinkToFile.hardLinkSource = root_file
root_file.hardLinkTarget.append(root_hardLinkToFile)
root_file.data = Data(repeating: 68, count: 2789)
var tree = FileTree(root)
tree.children.append(FileTree(root_subdir))
tree.children.append(FileTree(root_file))
tree.children.append(FileTree(root_hardLinkToFile))
func getFileTree(byPath: String) -> FileTree? {
var tokens = byPath.components(separatedBy: "/")
if let first = tokens.first {
if "" == first {
tokens = Array(tokens.dropFirst())
}
}
if let last = tokens.last {
if "" == last {
tokens = Array(tokens.dropLast())
}
}
var cursor: FileTree? = tree
var currentDir: String = "/"
if !tokens.isEmpty {
for token in tokens {
print("finding \"\(token)\" under \"\(currentDir)\"")
cursor = cursor?.children.first {$0.this.name == token}
guard let _ = cursor else {
print("failed.")
return nil
}
currentDir += "/\(token)"
}
}
return cursor
}
class FuseDelegator : NSObject {
override func willMount() {
print("\(Date().description) :: willMount called.")
}
override func willUnmount() {
print("\(Date().description) :: willUnmount called.")
}
override func contentsOfDirectory(atPath path: String!) throws -> [Any] {
guard let pathUnwrap = path else {
throw NSError(domain: #function, code: #line, userInfo: nil)
}
print("\n\n------------------------------------------------------------")
print("\(Date().description) :: contentsOfDirectory called. \"\(pathUnwrap)\"")
guard let found = getFileTree(byPath: pathUnwrap) else {
print("error.")
throw NSError(domain: #function, code: #line, userInfo: nil)
}
print("~~~~~~~~~~~ found ~~~~~~~~~~~~~")
let result = found.children.map {$0.this.name}
print("result = \(result)")
return result
}
override func attributesOfItem(atPath path: String!, userData: Any!) throws -> [AnyHashable : Any] {
guard let pathUnwrap = path else {
throw NSError(domain: #function, code: #line, userInfo: nil)
}
print("\(Date().description) :: attributesOfItem called. \"\(pathUnwrap)\" \"\(userData)\"")
guard let foundTree = getFileTree(byPath: pathUnwrap) else {
print("error.")
throw NSError(domain: #function, code: #line, userInfo: nil)
}
print("~~~~~~~~~~~ found ~~~~~~~~~~~~~")
var found = foundTree.this
while .typeDirectory != found.type && nil != found.hardLinkSource {
found = found.hardLinkSource!
print("hard link to \"\(found.name)\" filenumber \(found.systemFileNumber)")
}
var result: [AnyHashable : Any] = [FileAttributeKey.type : found.type]
print("type : \(found.type)")
var fileSize: UInt64 = 0
switch found.type {
case .typeDirectory:
fileSize = UInt64(2 + foundTree.children.count) * UInt64(DIRECTORY_ITEM_SIZE)
result.updateValue(fileSize, forKey: FileAttributeKey.size)
print("size : \(fileSize)")
default:
fileSize = found.size
result.updateValue(fileSize, forKey: FileAttributeKey.size)
print("size : \(fileSize)")
}
if let date = found.dateModification {
result.updateValue(date, forKey: FileAttributeKey.modificationDate)
print("mod date : \(date)")
}
switch found.type {
case .typeDirectory:
let count = 2 + foundTree.children.count
result.updateValue(count, forKey: FileAttributeKey.referenceCount)
print("reference count : \(count)")
default:
let count = 1 + found.hardLinkTarget.count
result.updateValue(count, forKey: FileAttributeKey.referenceCount)
print("reference count : \(count)")
}
if let permission = found.permission {
result.updateValue(permission, forKey: FileAttributeKey.posixPermissions)
print("posix permission : \(permission)")
}
if let ownerUid = found.ownerAccountId {
result.updateValue(ownerUid, forKey: FileAttributeKey.ownerAccountID)
print("owner account id \(ownerUid)")
}
if let ownerGid = found.groupOwnerAccountId {
result.updateValue(ownerGid, forKey: FileAttributeKey.groupOwnerAccountID)
print("group owner account id \(ownerGid)")
}
result.updateValue(found.systemFileNumber, forKey: FileAttributeKey.systemFileNumber)
print("system file number \(found.systemFileNumber)")
if let date = found.dateCreation {
result.updateValue(date, forKey: FileAttributeKey.creationDate)
print("create date : \(date)")
}
if let date = found.dateBackup {
result.updateValue(date, forKey: kGMUserFileSystemFileBackupDateKey)
print("backup date : \(date)")
}
if let date = found.dateChange {
result.updateValue(date, forKey: kGMUserFileSystemFileChangeDateKey)
print("change date : \(date)")
}
if let date = found.dateAccess {
result.updateValue(date, forKey: kGMUserFileSystemFileAccessDateKey)
print("access date : \(date)")
}
if let flags = found.flags {
result.updateValue(flags, forKey: kGMUserFileSystemFileFlagsKey)
print("flags : \(String(format: "%o", flags))")
}
var sizeInBlocks = fileSize / UInt64(FILE_SYSTEM_BLOCK_SIZE)
let rest = fileSize % UInt64(FILE_SYSTEM_BLOCK_SIZE)
if 0 < rest {
sizeInBlocks += 1
}
result.updateValue(sizeInBlocks, forKey: kGMUserFileSystemFileSizeInBlocksKey)
print("size in blocks : \(sizeInBlocks)")
return result
}
}
class Observer {
@objc func didMount(noti: Notification) {
print("============================================================")
print("\(Date().description) !!!! mounted !!!! \(noti) !!!!")
print("============================================================")
}
@objc func mountFailed(noti: Notification) {
print("============================================================")
print("\(Date().description) !!!! mount failed !!!! \(noti) !!!!")
print("============================================================")
}
@objc func unmounted(noti: Notification) {
print("============================================================")
print("\(Date().description) !!!!unmounted !!!! \(noti) !!!!")
print("============================================================")
}
}
let observer = Observer()
NotificationCenter.default.addObserver(observer, selector: #selector(Observer.didMount), name: NSNotification.Name(rawValue: kGMUserFileSystemDidMount), object: nil)
NotificationCenter.default.addObserver(observer, selector: #selector(Observer.mountFailed), name: NSNotification.Name(rawValue: kGMUserFileSystemMountFailed), object: nil)
NotificationCenter.default.addObserver(observer, selector: #selector(Observer.unmounted), name: NSNotification.Name(rawValue: kGMUserFileSystemDidUnmount), object: nil)
let delegator = FuseDelegator()
let test = GMUserFileSystem.init(delegate: delegator, isThreadSafe: false)
test?.mount(atPath: "/Volumes/inMemoryFuse", withOptions: ["nolocalcaches", "noubc"], shouldForeground: true, detachNewThread: false)
defer {
test?.unmount()
}
signal(SIGINT) { s in test?.unmount(); exit(0) }
while true {
sleep(1)
}
保险丝委托味精
------------------------------------------------------------
2019-08-01 08:18:02 +0000 :: contentsOfDirectory called. "/"
~~~~~~~~~~~ found ~~~~~~~~~~~~~
result = ["subdir", "file", "hard_link_to_file"]
2019-08-01 08:18:02 +0000 :: attributesOfItem called. "/subdir" "nil"
finding "subdir" under "/"
~~~~~~~~~~~ found ~~~~~~~~~~~~~
type : NSFileAttributeType(_rawValue: NSFileTypeDirectory)
size : 64
reference count : 2
system file number 1974617016092975998
size in blocks : 1
2019-08-01 08:18:02 +0000 :: attributesOfItem called. "/file" "nil"
finding "file" under "/"
~~~~~~~~~~~ found ~~~~~~~~~~~~~
type : NSFileAttributeType(_rawValue: NSFileTypeRegular)
size : 2789
reference count : 2
system file number 7083229264230220976
size in blocks : 6
2019-08-01 08:18:02 +0000 :: attributesOfItem called. "/._file" "nil"
finding "._file" under "/"
failed.
error.
2019-08-01 08:18:02 +0000 :: attributesOfItem called. "/hard_link_to_file" "nil"
finding "hard_link_to_file" under "/"
~~~~~~~~~~~ found ~~~~~~~~~~~~~
hard link to "file" filenumber 7083229264230220976
type : NSFileAttributeType(_rawValue: NSFileTypeRegular)
size : 2789
reference count : 2
system file number 7083229264230220976
size in blocks : 6
结果
$ ls -lhid /Volumes/inMemoryFuse
1 drwxrwxr-x@ 5 idid staff 160B 1 1 1970 /Volumes/inMemoryFuse
$ ls -lhi /Volumes/inMemoryFuse/
total 24
3 -rwxrwxr-x 2 idid staff 2.7K 1 1 1970 file
4 -rwxrwxr-x 2 idid staff 2.7K 1 1 1970 hard_link_to_file
2 drwxrwxr-x 2 idid staff 64B 1 1 1970 subdir