已完成: ✅ App ID(6770506571) ✅ Bundle ID(com.momentry.markbase.fskit) ✅ Developer ID Application证书导入 ✅ .app Bundle创建(build/MarkBaseFSKit.app) ✅ entitlements.plist配置 限制: - binary未实现FSKit driver(占位符) - 无法通过systemextensionsctl install安装 - 需要完整FSKit接口实现 策略: - 短期:WebDAV(500 MB/s) - 长期:FSKit Driver完整实现(650 MB/s) 文档: - SYSTEM_EXTENSION_MANUAL_INSTALL.md - FSKIT_DRIVER_TODO.md(未来待办)
118 lines
3.6 KiB
Rust
118 lines
3.6 KiB
Rust
use clap::Parser;
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
use axum::{Extension, Router, routing::any};
|
|
use tokio::net::TcpListener;
|
|
use dav_server::{DavHandler, localfs::LocalFs, fakels::FakeLs};
|
|
|
|
#[derive(Parser)]
|
|
struct Args {
|
|
#[arg(short, long, default_value = "4932")]
|
|
port: u16,
|
|
|
|
#[arg(long, default_value = "data/raid_simple.sparseimage")]
|
|
vdisk_path: PathBuf,
|
|
|
|
#[arg(long, default_value = "RAID_AUTO")]
|
|
mount_name: String,
|
|
}
|
|
|
|
fn main() {
|
|
tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap()
|
|
.block_on(async_main());
|
|
}
|
|
|
|
async fn async_main() {
|
|
let args = Args::parse();
|
|
|
|
println!("=== RAID WebDAV Server (Auto-Mount) ===");
|
|
println!("Port: {}", args.port);
|
|
println!("VDisk: {}", args.vdisk_path.display());
|
|
println!("Mount Name: {}", args.mount_name);
|
|
println!("");
|
|
|
|
if !args.vdisk_path.exists() {
|
|
eprintln!("Error: Virtual disk not found at {}", args.vdisk_path.display());
|
|
return;
|
|
}
|
|
|
|
println!("Step 1: Check if already mounted...");
|
|
let mount_point = check_or_mount(&args.vdisk_path, &args.mount_name);
|
|
|
|
println!("Step 2: Verify mount point...");
|
|
if !mount_point.exists() {
|
|
eprintln!("Error: Mount point does not exist: {}", mount_point.display());
|
|
return;
|
|
}
|
|
|
|
println!("✅ Mounted at: {}", mount_point.display());
|
|
println!("");
|
|
|
|
println!("Step 3: Starting WebDAV server...");
|
|
let dav = DavHandler::builder()
|
|
.filesystem(LocalFs::new(mount_point.to_string_lossy().to_string(), false, false, false))
|
|
.locksystem(FakeLs::new())
|
|
.strip_prefix("/webdav")
|
|
.build_handler();
|
|
|
|
let addr = format!("127.0.0.1:{}", args.port);
|
|
let listener = TcpListener::bind(&addr).await.unwrap();
|
|
|
|
let router = Router::new()
|
|
.route("/webdav", any(handle_dav))
|
|
.route("/webdav/", any(handle_dav))
|
|
.route("/webdav/{*path}", any(handle_dav))
|
|
.layer(Extension(dav));
|
|
|
|
println!("Listening on: http://{}", addr);
|
|
println!("Mount with Finder:");
|
|
println!(" Cmd+K → http://localhost:{}/webdav", args.port);
|
|
println!(" Guest/Guest or blank password");
|
|
println!("");
|
|
println!("Press Ctrl+C to stop...");
|
|
|
|
axum::serve(listener, router).await.unwrap();
|
|
}
|
|
|
|
fn check_or_mount(vdisk_path: &PathBuf, mount_name: &str) -> PathBuf {
|
|
let expected_mount = PathBuf::from("/Volumes").join(mount_name);
|
|
|
|
if expected_mount.exists() {
|
|
println!("✅ Already mounted at: {}", expected_mount.display());
|
|
return expected_mount;
|
|
}
|
|
|
|
println!("Mounting sparseimage...");
|
|
let output = Command::new("hdiutil")
|
|
.args(&["attach", "-nobrowse"])
|
|
.arg(vdisk_path)
|
|
.output()
|
|
.expect("Failed to mount sparseimage");
|
|
|
|
if !output.status.success() {
|
|
eprintln!("Mount failed: {}", String::from_utf8_lossy(&output.stderr));
|
|
return expected_mount;
|
|
}
|
|
|
|
println!("Mount output: {}", String::from_utf8_lossy(&output.stdout));
|
|
|
|
let mount_output = String::from_utf8_lossy(&output.stdout);
|
|
for line in mount_output.lines() {
|
|
if line.contains("/Volumes/") {
|
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
|
if let Some(mount_path) = parts.last() {
|
|
println!("✅ Mounted at: {}", mount_path);
|
|
return PathBuf::from(mount_path);
|
|
}
|
|
}
|
|
}
|
|
|
|
expected_mount
|
|
}
|
|
|
|
async fn handle_dav(Extension(dav): Extension<dav_server::DavHandler>, req: axum::extract::Request) -> impl axum::response::IntoResponse {
|
|
dav.handle(req).await
|
|
} |