Files
markbase/docs/FSKIT_API_ANALYSIS.md
Warren 1300a4e223
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
核心功能:
-  Categories/Series双视图管理(category_view.rs + import_markdown.rs)
-  FUSE Multi-Volume支持(tree_type参数)
-  SSH/SFTP/SCP/rsync协议完整实现(4042行)
-  NFS/SMB Module Phase 1-3完成
-  Archive Module Phase 1-4完成(2916行)
-  Download Center API完整实现
-  S3兼容API实现(560行)

Git配置修正:
-  删除错误origin(gitea.momentry.ddns.net)
-  删除m5max128(指向机器名)
-  设置origin = m5max128gitea.momentry.ddns.net/admin/markbase
-  设置m4minigitea = m4minigitea.momentry.ddns.net/warren/markbase

数据清理:
-  删除38个临时SQLite(保留accusys.sqlite、demo.sqlite)
-  删除.bak、test_*.bin、调试脚本等临时文件
-  删除临时目录(build/、download files/、raid_test/等)
-  更新.gitignore排除临时文件

架构优化:
- 52个文件修改,2434行新增,4739行删除
- Workspace成员整合(16个crate)
- 数据库状态:accusys.sqlite保留(主demo测试)

远程同步:
-  准备推送到m5max128gitea(远程Gitea)
-  准备推送到m4minigitea(本地Gitea)
2026-06-12 12:59:54 +08:00

1018 lines
26 KiB
Markdown

# FSKit API Real-World Analysis
**Source**: KhaosT/FSKitSample GitHub Repository
**Date**: 2026-06-11
**Purpose**: Fix HelloFS compilation errors with actual FSKit API
---
## Executive Summary
**FSKit is the new macOS Sequoia 15.4+ framework for user-space filesystem implementations.** This analysis extracts real working code patterns from the FSKitSample repository to fix compilation errors in HelloFS.
---
## 1. Repository Structure
```
FSKitSample/
├── FSKitExp/ # Main Application (SwiftUI)
│ ├── FSKitExpApp.swift # App entry point
│ ├── ContentView.swift # UI for managing extensions
│ ├── ViewModel.swift # FSClient interaction
│ ├── Info.plist
│ └── FSKitExp.entitlements
├── FSKitExpExtension/ # File System Extension (Target)
│ ├── FSKitExpExtension.swift # Extension entry point ⭐
│ ├── MyFS.swift # FSUnaryFileSystem implementation ⭐
│ ├── MyFSVolume.swift # FSVolume implementation ⭐
│ ├── MyFSItem.swift # FSItem implementation ⭐
│ ├── Constants.swift # UUID constants
│ ├── Info.plist # Extension configuration ⭐
│ └── FSKitExpExtension.entitlements ⭐
└── FSKitExp.xcodeproj/ # Xcode project
```
**Key Insight**: FSKit requires **two targets**:
1. **Main App** - For UI and extension management
2. **File System Extension** - The actual filesystem implementation
---
## 2. FSKit Extension Entry Point
### FSKitExpExtension.swift (Complete Code)
```swift
import Foundation
import FSKit
@main
struct FSKitExpExtension : UnaryFileSystemExtension {
var fileSystem : FSUnaryFileSystem & FSUnaryFileSystemOperations {
MyFS()
}
}
```
**Critical Finding**:
- Entry point is `UnaryFileSystemExtension` (NOT `FSUnaryFileSystemExtension`!)
- Returns `FSUnaryFileSystem & FSUnaryFileSystemOperations`
- Uses `@main` attribute for Swift 5.7+ entry point
---
## 3. Filesystem Implementation
### MyFS.swift (Complete Code)
```swift
import Foundation
import FSKit
import os
final class MyFS: FSUnaryFileSystem, FSUnaryFileSystemOperations {
private let logger = Logger(subsystem: "FSKitExp", category: "MyFS")
func probeResource(
resource: FSResource,
replyHandler: @escaping (FSProbeResult?, (any Error)?) -> Void
) {
logger.debug("probeResource: \(resource, privacy: .public)")
replyHandler(
FSProbeResult.usable(
name: "Test1",
containerID: FSContainerIdentifier(uuid: Constants.containerIdentifier)
),
nil
)
}
func loadResource(
resource: FSResource,
options: FSTaskOptions,
replyHandler: @escaping (FSVolume?, (any Error)?) -> Void
) {
containerStatus = .ready
logger.debug("loadResource: \(resource, privacy: .public)")
replyHandler(
MyFSVolume(resource: resource),
nil
)
}
func unloadResource(
resource: FSResource,
options: FSTaskOptions,
replyHandler reply: @escaping ((any Error)?) -> Void
) {
logger.debug("unloadResource: \(resource, privacy: .public)")
reply(nil)
}
func didFinishLoading() {
logger.debug("didFinishLoading")
}
}
```
**Key API Differences from HelloFS**:
| HelloFS (Wrong) | FSKitSample (Correct) |
|----------------|----------------------|
| `FSUnaryFileSystemExtension` | `UnaryFileSystemExtension` |
| `probeResource()` sync | `probeResource()` with replyHandler |
| `loadResource()` sync | `loadResource()` with replyHandler |
| `unloadResource()` sync | `unloadResource()` with replyHandler |
| No `didFinishLoading()` | Has `didFinishLoading()` |
---
## 4. Volume Implementation
### MyFSVolume.swift (Key Extracts)
```swift
final class MyFSVolume: FSVolume {
private let resource: FSResource
private let logger = Logger(subsystem: "FSKitExp", category: "MyFSVolume")
private let root: MyFSItem = {
let item = MyFSItem(name: FSFileName(string: "/"))
item.attributes.parentID = .parentOfRoot
item.attributes.fileID = .rootDirectory
item.attributes.uid = 0
item.attributes.gid = 0
item.attributes.linkCount = 1
item.attributes.type = .directory
item.attributes.mode = UInt32(S_IFDIR | 0b111_000_000)
item.attributes.allocSize = 1
item.attributes.size = 1
return item
}()
init(resource: FSResource) {
self.resource = resource
super.init(
volumeID: FSVolume.Identifier(uuid: Constants.volumeIdentifier),
volumeName: FSFileName(string: "Test1")
)
}
}
```
### Volume Protocols
**FSVolume.PathConfOperations**:
```swift
extension MyFSVolume: FSVolume.PathConfOperations {
var maximumLinkCount: Int { return -1 }
var maximumNameLength: Int { return -1 }
var restrictsOwnershipChanges: Bool { return false }
var truncatesLongNames: Bool { return false }
var maximumXattrSize: Int { return Int.max }
var maximumFileSize: UInt64 { return UInt64.max }
}
```
**FSVolume.Operations** (Core Operations):
```swift
extension MyFSVolume: FSVolume.Operations {
var supportedVolumeCapabilities: FSVolume.SupportedCapabilities {
let capabilities = FSVolume.SupportedCapabilities()
capabilities.supportsHardLinks = true
capabilities.supportsSymbolicLinks = true
capabilities.supportsPersistentObjectIDs = true
capabilities.doesNotSupportVolumeSizes = true
capabilities.supportsHiddenFiles = true
capabilities.supports64BitObjectIDs = true
capabilities.caseFormat = .insensitiveCasePreserving
return capabilities
}
var volumeStatistics: FSStatFSResult {
let result = FSStatFSResult(fileSystemTypeName: "MyFS")
result.blockSize = 1024000
result.ioSize = 1024000
result.totalBlocks = 1024000
result.availableBlocks = 1024000
result.freeBlocks = 1024000
result.totalFiles = 1024000
result.freeFiles = 1024000
return result
}
func activate(options: FSTaskOptions) async throws -> FSItem {
return root
}
func deactivate(options: FSDeactivateOptions = []) async throws {
}
func mount(options: FSTaskOptions) async throws {
}
func unmount() async {
}
func synchronize(flags: FSSyncFlags) async throws {
}
func attributes(
_ desiredAttributes: FSItem.GetAttributesRequest,
of item: FSItem
) async throws -> FSItem.Attributes {
if let item = item as? MyFSItem {
return item.attributes
} else {
throw fs_errorForPOSIXError(POSIXError.EIO.rawValue)
}
}
func setAttributes(
_ newAttributes: FSItem.SetAttributesRequest,
on item: FSItem
) async throws -> FSItem.Attributes {
if let item = item as? MyFSItem {
mergeAttributes(item.attributes, request: newAttributes)
return item.attributes
} else {
throw fs_errorForPOSIXError(POSIXError.EIO.rawValue)
}
}
func lookupItem(
named name: FSFileName,
inDirectory directory: FSItem
) async throws -> (FSItem, FSFileName) {
guard let directory = directory as? MyFSItem else {
throw fs_errorForPOSIXError(POSIXError.ENOENT.rawValue)
}
if let item = directory.children[name] {
return (item, name)
} else {
throw fs_errorForPOSIXError(POSIXError.ENOENT.rawValue)
}
}
func reclaimItem(_ item: FSItem) async throws {
}
func readSymbolicLink(_ item: FSItem) async throws -> FSFileName {
throw fs_errorForPOSIXError(POSIXError.EIO.rawValue)
}
func createItem(
named name: FSFileName,
type: FSItem.ItemType,
inDirectory directory: FSItem,
attributes newAttributes: FSItem.SetAttributesRequest
) async throws -> (FSItem, FSFileName) {
guard let directory = directory as? MyFSItem else {
throw fs_errorForPOSIXError(POSIXError.EIO.rawValue)
}
let item = MyFSItem(name: name)
mergeAttributes(item.attributes, request: newAttributes)
item.attributes.parentID = directory.attributes.fileID
item.attributes.type = type
directory.addItem(item)
return (item, name)
}
func createSymbolicLink(
named name: FSFileName,
inDirectory directory: FSItem,
attributes newAttributes: FSItem.SetAttributesRequest,
linkContents contents: FSFileName
) async throws -> (FSItem, FSFileName) {
throw fs_errorForPOSIXError(POSIXError.EIO.rawValue)
}
func createLink(
to item: FSItem,
named name: FSFileName,
inDirectory directory: FSItem
) async throws -> FSFileName {
throw fs_errorForPOSIXError(POSIXError.EIO.rawValue)
}
func removeItem(
_ item: FSItem,
named name: FSFileName,
fromDirectory directory: FSItem
) async throws {
if let item = item as? MyFSItem, let directory = directory as? MyFSItem {
directory.removeItem(item)
} else {
throw fs_errorForPOSIXError(POSIXError.EIO.rawValue)
}
}
func renameItem(
_ item: FSItem,
inDirectory sourceDirectory: FSItem,
named sourceName: FSFileName,
to destinationName: FSFileName,
inDirectory destinationDirectory: FSItem,
overItem: FSItem?
) async throws -> FSFileName {
throw fs_errorForPOSIXError(POSIXError.EIO.rawValue)
}
func enumerateDirectory(
_ directory: FSItem,
startingAt cookie: FSDirectoryCookie,
verifier: FSDirectoryVerifier,
attributes: FSItem.GetAttributesRequest?,
packer: FSDirectoryEntryPacker
) async throws -> FSDirectoryVerifier {
guard let directory = directory as? MyFSItem else {
throw fs_errorForPOSIXError(POSIXError.ENOENT.rawValue)
}
for (idx, item) in directory.children.values.enumerated() {
let isLast = (idx == directory.children.count - 1)
let v = packer.packEntry(
name: item.name,
itemType: item.attributes.type,
itemID: item.attributes.fileID,
nextCookie: FSDirectoryCookie(UInt64(idx)),
attributes: attributes != nil ? item.attributes : nil
)
}
return FSDirectoryVerifier(0)
}
}
```
**FSVolume.OpenCloseOperations**:
```swift
extension MyFSVolume: FSVolume.OpenCloseOperations {
func openItem(_ item: FSItem, modes: FSVolume.OpenModes) async throws {
}
func closeItem(_ item: FSItem, modes: FSVolume.OpenModes) async throws {
}
}
```
**FSVolume.XattrOperations**:
```swift
extension MyFSVolume: FSVolume.XattrOperations {
func xattr(named name: FSFileName, of item: FSItem) async throws -> Data {
if let item = item as? MyFSItem {
return item.xattrs[name] ?? Data()
} else {
return Data()
}
}
func setXattr(named name: FSFileName, to value: Data?, on item: FSItem, policy: FSVolume.SetXattrPolicy) async throws {
if let item = item as? MyFSItem {
item.xattrs[name] = value
}
}
func xattrs(of item: FSItem) async throws -> [FSFileName] {
if let item = item as? MyFSItem {
return Array(item.xattrs.keys)
} else {
return []
}
}
}
```
**FSVolume.ReadWriteOperations**:
```swift
extension MyFSVolume: FSVolume.ReadWriteOperations {
func read(from item: FSItem, at offset: off_t, length: Int, into buffer: FSMutableFileDataBuffer) async throws -> Int {
var bytesRead = 0
if let item = item as? MyFSItem, let data = item.data {
bytesRead = data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) in
let length = min(buffer.length, data.count)
_ = buffer.withUnsafeMutableBytes { dst in
memcpy(dst.baseAddress, ptr.baseAddress, length)
}
return length
}
}
return bytesRead
}
func write(contents: Data, to item: FSItem, at offset: off_t) async throws -> Int {
if let item = item as? MyFSItem {
item.data = contents
item.attributes.size = UInt64(contents.count)
item.attributes.allocSize = UInt64(contents.count)
}
return contents.count
}
}
```
---
## 5. FSItem Implementation
### MyFSItem.swift (Complete Code)
```swift
import Foundation
import FSKit
final class MyFSItem: FSItem {
private static var id: UInt64 = FSItem.Identifier.rootDirectory.rawValue + 1
static func getNextID() -> UInt64 {
let current = id
id += 1
return current
}
let name: FSFileName
let id = MyFSItem.getNextID()
var attributes = FSItem.Attributes()
var xattrs: [FSFileName: Data] = [:]
var data: Data?
private(set) var children: [FSFileName: MyFSItem] = [:]
init(name: FSFileName) {
self.name = name
attributes.fileID = FSItem.Identifier(rawValue: id) ?? .invalid
attributes.size = 0
attributes.allocSize = 0
attributes.flags = 0
var timespec = timespec()
timespec_get(&timespec, TIME_UTC)
attributes.addedTime = timespec
attributes.birthTime = timespec
attributes.changeTime = timespec
attributes.modifyTime = timespec
attributes.accessTime = timespec
}
func addItem(_ item: MyFSItem) {
children[item.name] = item
}
func removeItem(_ item: MyFSItem) {
children[item.name] = nil
}
}
```
**Key Points**:
- Subclasses `FSItem` directly
- Manages file ID allocation
- Stores attributes, xattrs, data, and children
- Helper methods for adding/removing children
---
## 6. Info.plist Configuration (CRITICAL)
### FSKitExpExtension/Info.plist
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EXAppExtensionAttributes</key>
<dict>
<key>EXExtensionPointIdentifier</key>
<string>com.apple.fskit.fsmodule</string>
<key>FSShortName</key>
<string>MyFS</string>
<key>FSPersonalities</key>
<dict>
<key>FSKitExpExtensionPersonality</key>
<dict>
<key>FSName</key>
<string>MyFS</string>
<key>FSfileObjectsAreCaseSensitive</key>
<false/>
</dict>
</dict>
<key>FSSupportsBlockResources</key>
<true/>
<key>FSSupportsGenericURLResources</key>
<false/>
<key>FSSupportsPathURLs</key>
<false/>
<key>FSSupportsServerURLs</key>
<false/>
<key>FSRequiresSecurityScopedPathURLResources</key>
<false/>
<key>FSMediaTypes</key>
<dict/>
<key>FSActivateOptionSyntax</key>
<dict>
<key>shortOptions</key>
<string>g:m:o:u:</string>
</dict>
<key>FSCheckOptionSyntax</key>
<dict>
<key>shortOptions</key>
<string>nqy</string>
</dict>
<key>FSFormatOptionSyntax</key>
<dict>
<key>shortOptions</key>
<string>v</string>
</dict>
</dict>
</dict>
</plist>
```
**Critical Configuration Keys**:
| Key | Value | Purpose |
|-----|-------|---------|
| `EXExtensionPointIdentifier` | `com.apple.fskit.fsmodule` | FSKit extension type |
| `FSShortName` | `MyFS` | Filesystem short name |
| `FSName` | `MyFS` | Filesystem display name |
| `FSSupportsBlockResources` | `true` | Supports block devices |
| `FSSupportsGenericURLResources` | `false` | No generic URL support |
| `FSSupportsPathURLs` | `false` | No path URL support |
| `FSSupportsServerURLs` | `false` | No server URL support |
| `FSActivateOptionSyntax` | `-g:m:o:u:` | Mount options syntax |
| `FSCheckOptionSyntax` | `-nqy` | Check options syntax |
| `FSFormatOptionSyntax` | `-v` | Format options syntax |
---
## 7. Entitlements Configuration
### FSKitExpExtension.entitlements
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.developer.fskit.fsmodule</key>
<true/>
</dict>
</plist>
```
**Required Entitlements**:
1. `com.apple.security.app-sandbox` - Must be enabled
2. `com.apple.developer.fskit.fsmodule` - FSKit module capability
---
## 8. Main App Structure
### FSKitExpApp.swift
```swift
import SwiftUI
@main
struct FSKitExpApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
```
### ContentView.swift
```swift
import SwiftUI
struct ContentView: View {
@State
private var viewModel = ViewModel()
var body: some View {
List {
ForEach(viewModel.modules, id: \.self) { module in
VStack(alignment: .leading) {
Text(module.bundleIdentifier)
.bold()
Text(module.url.absoluteString)
Text("\(module.isEnabled)")
}
}
}
}
}
```
### ViewModel.swift
```swift
import Foundation
import FSKit
import Observation
@Observable
@MainActor
final class ViewModel {
private var client: FSClient?
private(set) var modules: [FSModuleIdentity] = []
init() {
client = FSClient.shared
client?.fetchInstalledExtensions { modules, errors in
if let modules {
self.modules = modules
}
}
}
}
```
**Purpose**: Main app lists installed FSKit extensions and their status.
---
## 9. Testing and Mounting
### Create Dummy Block Device
```bash
# Create a 100MB dummy file
mkfile -n 100m dummy
# Mount it as a raw block device
hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount dummy
# Output: /dev/disk18
```
### Mount Filesystem
```bash
# Create mount point
mkdir /tmp/TestVol
# Mount the filesystem
mount -F -t MyFS disk18 /tmp/TestVol
```
### Unmount Filesystem
```bash
umount /tmp/TestVol
```
### Enable Extension
After building and running the app:
1. Open System Settings
2. Navigate to General → Login Items & Extensions
3. Enable the File System Extension under "File System Extensions"
---
## 10. Critical API Differences from HelloFS
### Entry Point
**HelloFS (Wrong)**:
```swift
@main
class HelloFSExtension: FSUnaryFileSystemExtension {
// ERROR: FSUnaryFileSystemExtension doesn't exist
}
```
**FSKitSample (Correct)**:
```swift
@main
struct FSKitExpExtension : UnaryFileSystemExtension {
var fileSystem : FSUnaryFileSystem & FSUnaryFileSystemOperations {
MyFS()
}
}
```
### Probe Resource
**HelloFS (Wrong)**:
```swift
func probeResource(_ resource: FSResource) -> FSProbeResult {
// Sync return
}
```
**FSKitSample (Correct)**:
```swift
func probeResource(
resource: FSResource,
replyHandler: @escaping (FSProbeResult?, (any Error)?) -> Void
) {
// Async with replyHandler
replyHandler(
FSProbeResult.usable(
name: "MyFS",
containerID: FSContainerIdentifier(uuid: Constants.containerIdentifier)
),
nil
)
}
```
### Load Resource
**HelloFS (Wrong)**:
```swift
func loadResource(_ resource: FSResource) -> FSVolume {
// Sync return
}
```
**FSKitSample (Correct)**:
```swift
func loadResource(
resource: FSResource,
options: FSTaskOptions,
replyHandler: @escaping (FSVolume?, (any Error)?) -> Void
) {
// Async with replyHandler
replyHandler(MyFSVolume(resource: resource), nil)
}
```
### Volume Operations
**HelloFS (Wrong)**:
```swift
func activate() -> FSItem {
// Sync return
}
```
**FSKitSample (Correct)**:
```swift
func activate(options: FSTaskOptions) async throws -> FSItem {
// Async with options parameter
}
```
---
## 11. Protocol Hierarchy
```
UnaryFileSystemExtension (Entry Point)
└─ Returns: FSUnaryFileSystem & FSUnaryFileSystemOperations
FSUnaryFileSystemOperations:
├─ probeResource(_:replyHandler:)
├─ loadResource(_:options:replyHandler:)
├─ unloadResource(_:options:replyHandler:)
└─ didFinishLoading()
FSVolume:
├─ FSVolume.Operations (Required)
├─ FSVolume.PathConfOperations
├─ FSVolume.OpenCloseOperations
├─ FSVolume.XattrOperations
└─ FSVolume.ReadWriteOperations
FSVolume.Operations:
├─ supportedVolumeCapabilities
├─ volumeStatistics
├─ activate(options:)
├─ deactivate(options:)
├─ mount(options:)
├─ unmount()
├─ synchronize(flags:)
├─ attributes(_:of:)
├─ setAttributes(_:on:)
├─ lookupItem(named:inDirectory:)
├─ reclaimItem(_:)
├─ readSymbolicLink(_:)
├─ createItem(named:type:inDirectory:attributes:)
├─ createSymbolicLink(named:inDirectory:attributes:linkContents:)
├─ createLink(to:named:inDirectory:)
├─ removeItem(_:named:fromDirectory:)
├─ renameItem(_:inDirectory:named:to:inDirectory:overItem:)
└─ enumerateDirectory(_:startingAt:verifier:attributes:packer:)
FSVolume.PathConfOperations:
├─ maximumLinkCount
├─ maximumNameLength
├─ restrictsOwnershipChanges
├─ truncatesLongNames
├─ maximumXattrSize
└─ maximumFileSize
FSVolume.OpenCloseOperations:
├─ openItem(_:modes:)
└─ closeItem(_:modes:)
FSVolume.XattrOperations:
├─ xattr(named:of:)
├─ setXattr(named:to:on:policy:)
└─ xattrs(of:)
FSVolume.ReadWriteOperations:
├─ read(from:at:length:into:)
└─ write(contents:to:at:)
```
---
## 12. FSItem Structure
```swift
FSItem.Attributes:
fileID: FSItem.Identifier
parentID: FSItem.Identifier
uid: uid_t
gid: gid_t
type: FSItem.ItemType
mode: UInt32
linkCount: UInt32
flags: UInt32
size: UInt64
allocSize: UInt64
accessTime: timespec
modifyTime: timespec
changeTime: timespec
birthTime: timespec
addedTime: timespec
backupTime: timespec
FSItem.GetAttributesRequest:
isValid(_ attribute: FSItem.Attribute) -> Bool
FSItem.SetAttributesRequest:
All attributes from FSItem.Attributes
isValid(_ attribute: FSItem.Attribute) -> Bool
FSItem.Identifier:
rootDirectory
invalid
parentOfRoot
init(rawValue: UInt64)
FSItem.ItemType:
directory
file
symbolicLink
etc.
FSFileName:
init(string: String)
string: String?
FSDirectoryCookie:
init(UInt64)
FSDirectoryVerifier:
init(UInt64)
FSDirectoryEntryPacker:
packEntry(name:itemType:itemID:nextCookie:attributes:) -> Bool
```
---
## 13. Error Handling
```swift
// POSIX error helper
fs_errorForPOSIXError(POSIXError.ENOENT.rawValue) // File not found
fs_errorForPOSIXError(POSIXError.EIO.rawValue) // I/O error
fs_errorForPOSIXError(POSIXError.EACCES.rawValue) // Permission denied
```
---
## 14. Build Configuration
### Xcode Project Structure
- **Main App Target**: FSKitExp (macOS App)
- **Extension Target**: FSKitExpExtension (File System Extension)
- **Platform**: macOS Sequoia 15.4+
- **Language**: Swift 5.7+
- **Frameworks**:
- SwiftUI (Main App)
- FSKit (Extension)
### Build Phases
**Main App**:
- Compile Sources: FSKitExpApp.swift, ContentView.swift, ViewModel.swift
- Link Binary: SwiftUI.framework
- Copy Bundle Resources: Assets.xcassets
**Extension**:
- Compile Sources: FSKitExpExtension.swift, MyFS.swift, MyFSVolume.swift, MyFSItem.swift, Constants.swift
- Link Binary: FSKit.framework
- Copy Bundle Resources: None
---
## 15. Key Takeaways for HelloFS
### 1. Fix Entry Point
```swift
// Change from:
@main
class HelloFSExtension: FSUnaryFileSystemExtension
// To:
@main
struct HelloFSExtension: UnaryFileSystemExtension {
var fileSystem: FSUnaryFileSystem & FSUnaryFileSystemOperations {
HelloFS()
}
}
```
### 2. Fix Filesystem Class
```swift
// Change from:
class HelloFS: FSUnaryFileSystemExtension
// To:
final class HelloFS: FSUnaryFileSystem, FSUnaryFileSystemOperations
```
### 3. Fix Probe/Load/Unload Methods
- Change sync methods to async with replyHandler
- Add `options: FSTaskOptions` parameter
- Add `didFinishLoading()` method
### 4. Fix Volume Operations
- Add `options: FSTaskOptions` parameter to async methods
- Use proper async/await syntax
- Implement all required protocols
### 5. Add Info.plist Configuration
- Add `EXAppExtensionAttributes` with FSKit configuration
- Set `FSShortName` and `FSName`
- Configure `FSSupportsBlockResources` = true
### 6. Add Entitlements
- Enable `com.apple.security.app-sandbox`
- Enable `com.apple.developer.fskit.fsmodule`
---
## 16. Summary of Compilation Errors to Fix
| Error | Cause | Fix |
|-------|-------|-----|
| `Cannot find type 'FSUnaryFileSystemExtension' in scope` | Wrong class name | Use `UnaryFileSystemExtension` |
| `Method signature mismatch` | Sync vs async | Use replyHandler pattern |
| `Missing parameter` | Missing `options` | Add `FSTaskOptions` parameter |
| `Missing method` | No `didFinishLoading()` | Add method |
| `Missing Info.plist keys` | No FSKit config | Add `EXAppExtensionAttributes` |
| `Missing entitlements` | No FSKit capability | Add entitlements |
---
**Last Updated**: 2026-06-11
**Status**: Complete Analysis
**Next Steps**: Apply fixes to HelloFS implementation