1. 新增支持对于驱动器显示名称的获取,通过显示名称的匹配更加直观
2. 优化rpc pool的使用。 3. 新增保存配置的功能给其他的组件使用
This commit is contained in:
parent
fa5b137db1
commit
acece94fae
197
Cargo.lock
generated
197
Cargo.lock
generated
|
@ -190,6 +190,12 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.2.25"
|
version = "3.2.25"
|
||||||
|
@ -214,6 +220,26 @@ dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
|
@ -526,36 +552,38 @@ version = "0.2.155"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.22"
|
version = "0.4.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lws_client"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"clap",
|
|
||||||
"env_logger",
|
|
||||||
"fuse_mt",
|
|
||||||
"hashbrown 0.9.1",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"prost",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"tokio",
|
|
||||||
"tonic",
|
|
||||||
"tonic-build",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchit"
|
name = "matchit"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
|
@ -600,6 +628,18 @@ version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
|
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
@ -641,6 +681,29 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
|
@ -812,6 +875,26 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"libredox",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.5"
|
version = "1.10.5"
|
||||||
|
@ -872,6 +955,12 @@ version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.204"
|
version = "1.0.204"
|
||||||
|
@ -903,6 +992,25 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
@ -928,6 +1036,28 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sstar_lws_vfs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"dirs",
|
||||||
|
"env_logger",
|
||||||
|
"fuse_mt",
|
||||||
|
"hashbrown 0.9.1",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"nix",
|
||||||
|
"parking_lot",
|
||||||
|
"prost",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"signal-hook",
|
||||||
|
"tokio",
|
||||||
|
"tonic",
|
||||||
|
"tonic-build",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -978,6 +1108,26 @@ version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
|
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "threadpool"
|
name = "threadpool"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
|
@ -1201,7 +1351,7 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1228,6 +1378,15 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "lws_client"
|
name = "sstar_lws_vfs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
@ -7,6 +7,10 @@ edition = "2021"
|
||||||
name = "lws_vfs_client"
|
name = "lws_vfs_client"
|
||||||
path = "src/client.rs"
|
path = "src/client.rs"
|
||||||
|
|
||||||
|
# [[bin]]
|
||||||
|
# name = "sscp"
|
||||||
|
# path = "src/sscp.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tonic = "0.11"
|
tonic = "0.11"
|
||||||
prost = "0.12"
|
prost = "0.12"
|
||||||
|
@ -21,6 +25,8 @@ env_logger = "0.8"
|
||||||
clap = "3.0"
|
clap = "3.0"
|
||||||
nix = { version = "0.29.0", features = ["process", "feature"]}
|
nix = { version = "0.29.0", features = ["process", "feature"]}
|
||||||
signal-hook = "0.3.17"
|
signal-hook = "0.3.17"
|
||||||
|
parking_lot = "0.12"
|
||||||
|
dirs = "4.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.11"
|
tonic-build = "0.11"
|
||||||
|
|
12
config.json
12
config.json
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"mount": {
|
"mount": {
|
||||||
"c:\\": "/l3c",
|
"系统": "/l3c",
|
||||||
"d:\\": "/l0d",
|
"ekko.bao_ubuntu (\\\\192.168.0.112)": "/l0d",
|
||||||
"f:\\": "/l0e"
|
"softerware": "/l0e",
|
||||||
|
"nas (\\\\192.168.0.117)": "/l0v"
|
||||||
},
|
},
|
||||||
"port": 7412,
|
"port": 33444
|
||||||
"addr": "192.168.0.111"
|
}
|
||||||
}
|
|
100
src/client.rs
100
src/client.rs
|
@ -1,12 +1,18 @@
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use lws_client::LwsVfsIns;
|
use sstar_lws_vfs::LwsVfsIns;
|
||||||
use std::{process, thread};
|
use std::{process, thread};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
extern crate log;
|
extern crate log;
|
||||||
use nix::unistd::{fork, ForkResult, getpid, getppid};
|
use nix::unistd::{fork, ForkResult, getpid, getppid};
|
||||||
use signal_hook::consts::{SIGCHLD, SIGINT};
|
// use signal_hook::consts::{SIGCHLD, SIGINT, SIGTERM};
|
||||||
use signal_hook::iterator::Signals;
|
// use signal_hook::iterator::Signals;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use libc::prctl;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::fs::{File, create_dir_all};
|
||||||
|
use std::io::Write;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use dirs;
|
||||||
|
|
||||||
const DEFAULT_PORT: u16 = 33444;
|
const DEFAULT_PORT: u16 = 33444;
|
||||||
const DEFAULT_CACHE_LIFE: u32 = 10_000;
|
const DEFAULT_CACHE_LIFE: u32 = 10_000;
|
||||||
|
@ -47,7 +53,7 @@ fn param_parser() -> (String, String, u32) {
|
||||||
.max_values(1)
|
.max_values(1)
|
||||||
.value_name("mount point")
|
.value_name("mount point")
|
||||||
.required(true)
|
.required(true)
|
||||||
.help("mount point, eg: ~/mnt"),
|
.help("mount point, eg: ~/mnt"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("c")
|
Arg::with_name("c")
|
||||||
|
@ -117,19 +123,87 @@ fn umount_point(mount: &String) {
|
||||||
}
|
}
|
||||||
fn set_cleanup(mount: &String) {
|
fn set_cleanup(mount: &String) {
|
||||||
// 捕获 SIGCHLD 信号
|
// 捕获 SIGCHLD 信号
|
||||||
log::info!("waiting for SIGCHLD SIGINT signal");
|
// log::info!("waiting for SIGCHLD SIGINT signal");
|
||||||
let mut signals = Signals::new(&[SIGCHLD, SIGINT]).expect("Error creating signals");
|
// let mut signals = Signals::new(&[SIGCHLD, SIGINT, SIGTERM]).expect("Error creating signals");
|
||||||
for _ in signals.forever() {
|
let mout_point = mount.clone();
|
||||||
log::info!("Catch SIGCHLD||SIGINT, exit....");
|
// 子线程监控父进程是否退出
|
||||||
umount_point(&mount);
|
let th: thread::JoinHandle<()> = thread::spawn( move || {
|
||||||
break;
|
let lws_client_pid = getppid();
|
||||||
}
|
loop {
|
||||||
|
let ppid = getppid();
|
||||||
|
if 1 == ppid.as_raw() {
|
||||||
|
log::info!("lws_client({}) has been exited! try cleanup", lws_client_pid);
|
||||||
|
umount_point(&mout_point);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
thread::sleep(Duration::from_micros(100));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// for sig in signals.forever() {
|
||||||
|
// match sig {
|
||||||
|
// SIGCHLD | SIGINT | SIGTERM=> {
|
||||||
|
// log::info!("Catch SIGCHLD|SIGINT|SIGTERM signal");
|
||||||
|
// umount_point(&mount);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// _ => {
|
||||||
|
// // 处理其他信号
|
||||||
|
// log::info!("Catch signal {} ....", sig);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
th.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct LwsConfig {
|
||||||
|
server: String,
|
||||||
|
mount_point: String,
|
||||||
|
cache_life: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_config(server: &str, mount_point: &str, cache_life: u32) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Get the user's home directory using dirs crate
|
||||||
|
let home_dir = match dirs::home_dir() {
|
||||||
|
Some(path) => path,
|
||||||
|
None => return Err("Unable to get the user's home directory".into()),
|
||||||
|
};
|
||||||
|
// Create the configuration directory
|
||||||
|
let config_dir = home_dir.join(".config").join("lws_client");
|
||||||
|
create_dir_all(&config_dir)?;
|
||||||
|
|
||||||
|
// Create the configuration file
|
||||||
|
let config_file = config_dir.join("config.json");
|
||||||
|
|
||||||
|
// Prepare the configuration data
|
||||||
|
let config = LwsConfig {
|
||||||
|
server: server.to_string(),
|
||||||
|
mount_point: mount_point.to_string(),
|
||||||
|
cache_life,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Serialize to JSON
|
||||||
|
let config_json = serde_json::to_string_pretty(&config)?;
|
||||||
|
|
||||||
|
// Write to the file
|
||||||
|
let mut file = File::create(config_file)?;
|
||||||
|
file.write_all(config_json.as_bytes())?;
|
||||||
|
|
||||||
|
log::info!("Configuration saved to ~/.config/lws_client/config.json");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let (server, mount_point, cache_life) = param_parser();
|
let (server, mount_point, cache_life) = param_parser();
|
||||||
|
|
||||||
|
// Save the configuration
|
||||||
|
if let Err(e) = save_config(&server, &mount_point, cache_life) {
|
||||||
|
log::warn!("Failed to save configuration: {:?}", e);
|
||||||
|
}
|
||||||
|
|
||||||
let lws_ins = match LwsVfsIns::new(&server, cache_life).await {
|
let lws_ins = match LwsVfsIns::new(&server, cache_life).await {
|
||||||
Ok(ins) => ins,
|
Ok(ins) => ins,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -178,7 +252,11 @@ fn child_process(mount: &String) {
|
||||||
let pid = getpid();
|
let pid = getpid();
|
||||||
let ppid = getppid();
|
let ppid = getppid();
|
||||||
log::info!("parent: {}, child: {}", ppid, pid);
|
log::info!("parent: {}, child: {}", ppid, pid);
|
||||||
|
unsafe {
|
||||||
|
prctl(libc::PR_SET_NAME, b"lws_monitor\0".as_ptr() as usize, 0, 0, 0);
|
||||||
|
}
|
||||||
set_cleanup(mount);
|
set_cleanup(mount);
|
||||||
|
process::exit(0);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("fork fail: {}", err);
|
log::error!("fork fail: {}", err);
|
||||||
|
|
259
src/lib.rs
259
src/lib.rs
|
@ -12,6 +12,8 @@ use std::thread::{self};
|
||||||
use std::{env, vec};
|
use std::{env, vec};
|
||||||
use tokio::runtime::{Builder, Runtime};
|
use tokio::runtime::{Builder, Runtime};
|
||||||
use tonic::transport::Channel as RpcChannel;
|
use tonic::transport::Channel as RpcChannel;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use parking_lot::{Mutex, RwLock, Condvar};
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
pub mod lws_client {
|
pub mod lws_client {
|
||||||
|
@ -54,10 +56,9 @@ impl Config {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
|
||||||
struct RpcPool {
|
struct RpcPool {
|
||||||
clients: Arc<Mutex<Vec<Arc<RwLock<Rpc>>>>>,
|
clients: Vec<Arc<RwLock<Rpc>>>, // 使用本地 Vec 而不是包裹在 Arc<Mutex<>> 中
|
||||||
index: Arc<RwLock<usize>>,
|
available: Mutex<Vec<usize>>, // 跟踪可用客户端的索引
|
||||||
wait: Arc<(Mutex<()>, Condvar)>,
|
wait: Arc<(Mutex<()>, Condvar)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,67 +73,75 @@ struct Rpc {
|
||||||
impl RpcPool {
|
impl RpcPool {
|
||||||
async fn new(size: usize, uri: String) -> Result<Self, Box<dyn std::error::Error>> {
|
async fn new(size: usize, uri: String) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
let mut clients = Vec::with_capacity(size);
|
let mut clients = Vec::with_capacity(size);
|
||||||
|
let mut available = Vec::with_capacity(size);
|
||||||
let uri = uri.to_ascii_lowercase();
|
let uri = uri.to_ascii_lowercase();
|
||||||
log::info!("create rpc pool size: {}", size);
|
log::info!("create rpc pool size: {}", size);
|
||||||
|
|
||||||
|
let shared_rt = Arc::new(
|
||||||
|
Builder::new_multi_thread()
|
||||||
|
.worker_threads(10)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
let channel = RpcChannel::from_shared(uri)?.connect().await?;
|
let channel = RpcChannel::from_shared(uri)?.connect().await?;
|
||||||
for _ in 0..size {
|
|
||||||
|
for i in 0..size {
|
||||||
let client = LwsVfsClient::new(channel.clone());
|
let client = LwsVfsClient::new(channel.clone());
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
clients.push(Arc::new(RwLock::new(Rpc {
|
clients.push(Arc::new(RwLock::new(Rpc {
|
||||||
client,
|
client,
|
||||||
in_use: false,
|
in_use: false,
|
||||||
rt: Arc::new(
|
rt: shared_rt.clone(),
|
||||||
Builder::new_multi_thread()
|
|
||||||
.worker_threads(1)
|
|
||||||
.build()
|
|
||||||
.unwrap(),
|
|
||||||
),
|
|
||||||
tx: Arc::new(tx),
|
tx: Arc::new(tx),
|
||||||
rx: Arc::new(Mutex::new(rx)),
|
rx: Arc::new(Mutex::new(rx)),
|
||||||
})));
|
})));
|
||||||
|
available.push(i); // 所有客户端初始时都是可用的
|
||||||
}
|
}
|
||||||
|
|
||||||
let wait = (Mutex::new(()), Condvar::new());
|
let wait = (Mutex::new(()), Condvar::new());
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
clients: Arc::new(Mutex::new(clients)),
|
clients,
|
||||||
index: Arc::new(RwLock::new(0)),
|
available: Mutex::new(available),
|
||||||
wait: Arc::new(wait),
|
wait: Arc::new(wait),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self) -> Result<Arc<RwLock<Rpc>>, Box<dyn Error>> {
|
fn get(&self) -> Result<Arc<RwLock<Rpc>>, Box<dyn Error>> {
|
||||||
// TODO: 优化遍历的逻辑
|
const MAX_RETRIES: usize = 30;
|
||||||
let find_idle = |rpc_pool: &Self| -> Result<Arc<RwLock<Rpc>>, Box<dyn Error>> {
|
|
||||||
let cs = self.clients.clone();
|
for _ in 0..MAX_RETRIES {
|
||||||
let clients = &(cs.lock().unwrap());
|
// 尝试获取可用客户端
|
||||||
let size = clients.len();
|
let client_opt = {
|
||||||
let index = *rpc_pool.index.read().unwrap();
|
let mut available = self.available.lock();
|
||||||
for i in 0..size {
|
if let Some(idx) = available.pop() {
|
||||||
let idx = (index + i) % size;
|
let client = self.clients[idx].clone();
|
||||||
let entry = clients[idx].read().unwrap();
|
{
|
||||||
if !entry.in_use {
|
// 在单独的作用域中借用 client
|
||||||
drop(entry);
|
let mut rpc = client.write();
|
||||||
let mut entry: std::sync::RwLockWriteGuard<'_, Rpc> =
|
rpc.in_use = true;
|
||||||
clients[idx].write().unwrap();
|
}
|
||||||
entry.in_use = true;
|
Some(client) // 现在可以安全地移动 client
|
||||||
return Ok(clients[idx].clone());
|
} else {
|
||||||
|
// 如果无法获取可用客户端,返回 None
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(client) = client_opt {
|
||||||
|
return Ok(client);
|
||||||
}
|
}
|
||||||
Err(Box::new(IoError::new(
|
|
||||||
ErrorKind::Other,
|
// 等待通知
|
||||||
"get free rpc client fail",
|
|
||||||
)))
|
|
||||||
};
|
|
||||||
loop {
|
|
||||||
if let Ok(rpc) = find_idle(self) {
|
|
||||||
return Ok(rpc);
|
|
||||||
}
|
|
||||||
let (mutex, cond) = &(*self.wait.clone());
|
let (mutex, cond) = &(*self.wait.clone());
|
||||||
let timeout = Duration::from_millis(100);
|
let mut guard = mutex.lock();
|
||||||
let (_unused, timeout_res) = cond.wait_timeout(mutex.lock().unwrap(), timeout).unwrap();
|
let timeout = Duration::from_millis(50);
|
||||||
if timeout_res.timed_out() {
|
let result = cond.wait_for(&mut guard, timeout);
|
||||||
break;
|
if !result.timed_out() {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log::warn!("get free rpc client time out");
|
log::warn!("get free rpc client time out");
|
||||||
Err(Box::new(IoError::new(
|
Err(Box::new(IoError::new(
|
||||||
ErrorKind::Other,
|
ErrorKind::Other,
|
||||||
|
@ -141,19 +150,27 @@ impl RpcPool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put(&self, client: Arc<RwLock<Rpc>>) {
|
fn put(&self, client: Arc<RwLock<Rpc>>) {
|
||||||
let mut i = 0;
|
// 找到客户端在数组中的索引
|
||||||
for entry in self.clients.lock().unwrap().iter_mut() {
|
for (idx, c) in self.clients.iter().enumerate() {
|
||||||
if Arc::ptr_eq(&entry, &client) {
|
if Arc::ptr_eq(c, &client) {
|
||||||
let mut item = entry.write().unwrap();
|
// 设置客户端为可用
|
||||||
item.in_use = false;
|
let mut entry = c.write();
|
||||||
*self.index.write().unwrap() = i;
|
entry.in_use = false;
|
||||||
let (mutex, cond) = &(*self.wait.clone());
|
|
||||||
let _unused = mutex.lock().unwrap();
|
// 将索引添加到可用列表
|
||||||
|
{
|
||||||
|
let mut available = self.available.lock();
|
||||||
|
available.push(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知等待的线程
|
||||||
|
let (_mutex, cond) = &(*self.wait.clone());
|
||||||
cond.notify_one();
|
cond.notify_one();
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::warn!("Attempted to return unknown RPC client to pool");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,10 +179,11 @@ struct VirFs {
|
||||||
stat: lws_client::Fstat,
|
stat: lws_client::Fstat,
|
||||||
sub: HashMap<String, VirFs>,
|
sub: HashMap<String, VirFs>,
|
||||||
is_root: bool,
|
is_root: bool,
|
||||||
|
is_link_remote: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VirFs {
|
impl VirFs {
|
||||||
fn create<T>(name: T, is_dir: bool) -> VirFs
|
fn create<T>(name: T, is_dir: bool, is_link_remote:bool) -> VirFs
|
||||||
where
|
where
|
||||||
T: ToString,
|
T: ToString,
|
||||||
{
|
{
|
||||||
|
@ -198,15 +216,17 @@ impl VirFs {
|
||||||
},
|
},
|
||||||
sub: HashMap::new(),
|
sub: HashMap::new(),
|
||||||
is_root: true,
|
is_root: true,
|
||||||
|
is_link_remote,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new() -> VirFs {
|
pub fn new() -> VirFs {
|
||||||
let mut node = VirFs::create("/", true);
|
let mut node = VirFs::create("/", true, false);
|
||||||
node.is_root = true;
|
node.is_root = true;
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add<T>(&mut self, name: T)
|
// 添加子目录
|
||||||
|
pub fn add<T>(&mut self, name: T, is_link_remote: bool)
|
||||||
where
|
where
|
||||||
T: ToString,
|
T: ToString,
|
||||||
{
|
{
|
||||||
|
@ -218,7 +238,7 @@ impl VirFs {
|
||||||
.collect::<Vec<&str>>();
|
.collect::<Vec<&str>>();
|
||||||
let name = ret[0];
|
let name = ret[0];
|
||||||
log::debug!("add fs {}", name);
|
log::debug!("add fs {}", name);
|
||||||
let mut node = VirFs::create(name.to_string(), true);
|
let mut node = VirFs::create(name.to_string(), true, is_link_remote);
|
||||||
node.is_root = false;
|
node.is_root = false;
|
||||||
self.sub.insert(name.to_string(), node);
|
self.sub.insert(name.to_string(), node);
|
||||||
}
|
}
|
||||||
|
@ -228,32 +248,20 @@ impl VirFs {
|
||||||
T: ToString,
|
T: ToString,
|
||||||
{
|
{
|
||||||
let name = name.to_string();
|
let name = name.to_string();
|
||||||
let mut vname = name.split('/').filter(|x| x.len() > 0);
|
let mut vname = name.split('/').filter(|x| !x.is_empty());
|
||||||
//log::info!("try match {}", name);
|
self._match_path(&mut vname)
|
||||||
if let Some(_curr) = vname.next() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(self)
|
|
||||||
//self.match_path(&mut vname)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _match_path<'a>(&self, name_list: &mut impl Iterator<Item = &'a str>) -> Option<&VirFs> {
|
pub fn _match_path<'a>(&self, name_list: &mut impl Iterator<Item = &'a str>) -> Option<&VirFs> {
|
||||||
if let Some(current) = name_list.next() {
|
if let Some(current) = name_list.next() {
|
||||||
match self.sub.get(current) {
|
self.sub.get(current).and_then(|v| v._match_path(name_list))
|
||||||
Some(v) => {
|
|
||||||
// try match sub dir
|
|
||||||
if let Some(sub) = v._match_path(name_list) {
|
|
||||||
Some(sub)
|
|
||||||
// sub dirs not match
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// find over
|
// 如果是链接到远程的虚拟目录,则不匹配,需要由远端获取属性
|
||||||
Some(self)
|
if self.is_link_remote {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,9 +290,12 @@ fn current_time_in_ms() -> u32 {
|
||||||
}
|
}
|
||||||
impl FileAttrCache {
|
impl FileAttrCache {
|
||||||
pub fn new(attr: FileAttrInfo, cache_life: u32) -> FileAttrCache {
|
pub fn new(attr: FileAttrInfo, cache_life: u32) -> FileAttrCache {
|
||||||
|
let now = current_time_in_ms();
|
||||||
|
let exp_time = now + cache_life;
|
||||||
|
log::trace!("Creating cache, current time: {}ms, expiration time: {}ms, lifetime: {}ms", now, exp_time, cache_life);
|
||||||
FileAttrCache {
|
FileAttrCache {
|
||||||
attr,
|
attr,
|
||||||
exp_time: current_time_in_ms() + cache_life,
|
exp_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,16 +332,16 @@ impl FileAttrCacheManager {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
let cache_ctx = FileAttrCacheCtx::new(rx);
|
let cache_ctx = FileAttrCacheCtx::new(rx);
|
||||||
let handle = thread::spawn(move || {
|
let handle = thread::spawn(move || {
|
||||||
Self::cache_manager(cache_ctx, cache_life);
|
Self::cache_manager_thread(cache_ctx, cache_life);
|
||||||
});
|
});
|
||||||
FileAttrCacheManager { _handle:handle, tx }
|
FileAttrCacheManager { _handle:handle, tx }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cache_manager(mut ctx: FileAttrCacheCtx, cache_life: u32) {
|
fn cache_manager_thread(mut ctx: FileAttrCacheCtx, cache_life: u32) {
|
||||||
let rx = ctx.rx.lock().unwrap();
|
let rx = ctx.rx.lock();
|
||||||
fn invalid_cache(cache: &mut HashMap<String, FileAttrCache>) -> u32 {
|
fn invalid_cache(cache: &mut HashMap<String, FileAttrCache>) -> u32 {
|
||||||
let mut cache_size = 0;
|
let mut cache_size = 0;
|
||||||
let curr = current_time_in_ms();
|
let curr: u32 = current_time_in_ms();
|
||||||
let del: Vec<String> = cache
|
let del: Vec<String> = cache
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&(_, val)| val.exp_time < curr)
|
.filter(|&(_, val)| val.exp_time < curr)
|
||||||
|
@ -346,6 +357,7 @@ impl FileAttrCacheManager {
|
||||||
loop {
|
loop {
|
||||||
match rx.recv_timeout(Duration::from_millis(200)) {
|
match rx.recv_timeout(Duration::from_millis(200)) {
|
||||||
Ok(req) => {
|
Ok(req) => {
|
||||||
|
// 只有当缓存大小超过阈值时才进行清理
|
||||||
if ctx.cache_size > 256 {
|
if ctx.cache_size > 256 {
|
||||||
ctx.cache_size -= invalid_cache(&mut ctx.cache);
|
ctx.cache_size -= invalid_cache(&mut ctx.cache);
|
||||||
}
|
}
|
||||||
|
@ -356,7 +368,16 @@ impl FileAttrCacheManager {
|
||||||
}
|
}
|
||||||
FileAttrCacheMsg::Get(path, chn) => match ctx.cache.get(&path) {
|
FileAttrCacheMsg::Get(path, chn) => match ctx.cache.get(&path) {
|
||||||
Some(attr) => {
|
Some(attr) => {
|
||||||
let _ = chn.send(FileAttrCacheMsg::GetReply(path, Some(attr.attr)));
|
// 检查缓存是否已过期
|
||||||
|
let curr = current_time_in_ms();
|
||||||
|
if attr.exp_time >= curr {
|
||||||
|
let _ = chn.send(FileAttrCacheMsg::GetReply(path, Some(attr.attr.clone())));
|
||||||
|
} else {
|
||||||
|
// 缓存已过期,移除并返回None
|
||||||
|
ctx.cache.remove(&path);
|
||||||
|
ctx.cache_size -= 1;
|
||||||
|
let _ = chn.send(FileAttrCacheMsg::GetReply(path, None));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let _ = chn.send(FileAttrCacheMsg::GetReply(path, None));
|
let _ = chn.send(FileAttrCacheMsg::GetReply(path, None));
|
||||||
|
@ -372,6 +393,7 @@ impl FileAttrCacheManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
// 定期清理过期缓存,而不是每次都清理
|
||||||
ctx.cache_size -= invalid_cache(&mut ctx.cache);
|
ctx.cache_size -= invalid_cache(&mut ctx.cache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,7 +410,8 @@ impl FileAttrCacheManager {
|
||||||
tx: &Arc<mpsc::Sender<FileAttrCacheMsg>>,
|
tx: &Arc<mpsc::Sender<FileAttrCacheMsg>>,
|
||||||
rx: &Arc<Mutex<mpsc::Receiver<FileAttrCacheMsg>>>,
|
rx: &Arc<Mutex<mpsc::Receiver<FileAttrCacheMsg>>>,
|
||||||
) -> Option<FileAttrInfo> {
|
) -> Option<FileAttrInfo> {
|
||||||
//send get gile attribute request
|
//send get file attribute request
|
||||||
|
log::trace!("Requesting cache: {}", path);
|
||||||
let _ = self
|
let _ = self
|
||||||
.tx
|
.tx
|
||||||
.send(FileAttrCacheMsg::Get(path.to_string(), tx.clone()));
|
.send(FileAttrCacheMsg::Get(path.to_string(), tx.clone()));
|
||||||
|
@ -397,26 +420,29 @@ impl FileAttrCacheManager {
|
||||||
loop {
|
loop {
|
||||||
let elapsed = start.elapsed();
|
let elapsed = start.elapsed();
|
||||||
if elapsed >= total_timeout {
|
if elapsed >= total_timeout {
|
||||||
|
log::debug!("Cache request timeout: {}", path);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let timeout = total_timeout - elapsed;
|
let timeout = total_timeout - elapsed;
|
||||||
let resp = {
|
let resp = {
|
||||||
match rx.lock().unwrap().recv_timeout(timeout) {
|
let rx = rx.lock();
|
||||||
Ok(resp) => resp,
|
rx.recv_timeout(timeout)
|
||||||
Err(_e) => return None,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
match resp {
|
match resp {
|
||||||
FileAttrCacheMsg::GetReply(rpath, resp) => {
|
Ok(msg) => {
|
||||||
if *path == rpath {
|
if let FileAttrCacheMsg::GetReply(_, Some(attr)) = msg {
|
||||||
//message is not match
|
log::trace!("Cache hit: {}", path);
|
||||||
return resp;
|
return Some(attr);
|
||||||
}
|
}
|
||||||
|
log::trace!("Cache miss: {}", path);
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
Err(_e) => {
|
||||||
|
log::debug!("Error receiving cache response: {}", path);
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
_ => break,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clr(&self, paths: Vec<String>) {
|
pub fn clr(&self, paths: Vec<String>) {
|
||||||
|
@ -512,7 +538,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
if let Some(attr) = self.cache.get(&path, &rpc.tx, &rpc.rx) {
|
if let Some(attr) = self.cache.get(&path, &rpc.tx, &rpc.rx) {
|
||||||
let d = client.clone();
|
let d = client.clone();
|
||||||
drop(rpc);
|
drop(rpc);
|
||||||
|
@ -600,7 +626,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.ftruncate(request));
|
let resp = rt.block_on(rpc.ftruncate(request));
|
||||||
|
@ -657,7 +683,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.futimens(request));
|
let resp = rt.block_on(rpc.futimens(request));
|
||||||
|
@ -719,7 +745,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.fmkdir(request));
|
let resp = rt.block_on(rpc.fmkdir(request));
|
||||||
|
@ -755,7 +781,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.funlink(request));
|
let resp = rt.block_on(rpc.funlink(request));
|
||||||
|
@ -792,7 +818,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.frmdir(request));
|
let resp = rt.block_on(rpc.frmdir(request));
|
||||||
|
@ -852,7 +878,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.frename(request));
|
let resp = rt.block_on(rpc.frename(request));
|
||||||
|
@ -907,7 +933,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.fopen(request));
|
let resp = rt.block_on(rpc.fopen(request));
|
||||||
|
@ -967,7 +993,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return callback(Err(libc::ENOMSG)),
|
Err(_) => return callback(Err(libc::ENOMSG)),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.fread(request));
|
let resp = rt.block_on(rpc.fread(request));
|
||||||
|
@ -1022,7 +1048,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.fwrite(request));
|
let resp = rt.block_on(rpc.fwrite(request));
|
||||||
|
@ -1068,7 +1094,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.fflush(request));
|
let resp = rt.block_on(rpc.fflush(request));
|
||||||
|
@ -1121,7 +1147,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.frelease(request));
|
let resp = rt.block_on(rpc.frelease(request));
|
||||||
|
@ -1175,7 +1201,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.fopendir(request));
|
let resp = rt.block_on(rpc.fopendir(request));
|
||||||
|
@ -1240,7 +1266,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.freaddir(request));
|
let resp = rt.block_on(rpc.freaddir(request));
|
||||||
|
@ -1294,7 +1320,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.freleasedir(request));
|
let resp = rt.block_on(rpc.freleasedir(request));
|
||||||
|
@ -1404,8 +1430,13 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
}
|
}
|
||||||
mode as u32
|
mode as u32
|
||||||
}
|
}
|
||||||
|
// log::info!("access path: {:?}, mask: {:?}", path, mask);
|
||||||
|
let path = path.to_string_lossy().into_owned();
|
||||||
|
if let Some(_) = self.vir_root.find(&path) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
let request = tonic::Request::new(Access {
|
let request = tonic::Request::new(Access {
|
||||||
path: path.to_string_lossy().into_owned(),
|
path: path,
|
||||||
mask: mask2mode(mask as i32),
|
mask: mask2mode(mask as i32),
|
||||||
..Access::default()
|
..Access::default()
|
||||||
});
|
});
|
||||||
|
@ -1414,7 +1445,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.faccess(request));
|
let resp = rt.block_on(rpc.faccess(request));
|
||||||
|
@ -1463,7 +1494,7 @@ impl FilesystemMT for LwsVfsIns {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Err(libc::EAGAIN),
|
Err(_) => return Err(libc::EAGAIN),
|
||||||
};
|
};
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.fcreate(request));
|
let resp = rt.block_on(rpc.fcreate(request));
|
||||||
|
@ -1505,7 +1536,7 @@ impl LwsVfsIns {
|
||||||
let config = LwsVfsIns::fetch_config(&rpcs).await?;
|
let config = LwsVfsIns::fetch_config(&rpcs).await?;
|
||||||
let mut vir_root = VirFs::new();
|
let mut vir_root = VirFs::new();
|
||||||
for mount in config.get_mount() {
|
for mount in config.get_mount() {
|
||||||
vir_root.add(mount);
|
vir_root.add(mount, true);
|
||||||
}
|
}
|
||||||
Ok(LwsVfsIns {
|
Ok(LwsVfsIns {
|
||||||
config,
|
config,
|
||||||
|
@ -1521,7 +1552,7 @@ impl LwsVfsIns {
|
||||||
..GetConfig::default()
|
..GetConfig::default()
|
||||||
});
|
});
|
||||||
let client = rpcs.get().unwrap();
|
let client = rpcs.get().unwrap();
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rpc.get_config(request).await;
|
let resp = rpc.get_config(request).await;
|
||||||
(client.clone(), resp)
|
(client.clone(), resp)
|
||||||
|
@ -1548,7 +1579,7 @@ impl LwsVfsIns {
|
||||||
});
|
});
|
||||||
let (rpc, resp) = {
|
let (rpc, resp) = {
|
||||||
let client = self.rpcs.get()?;
|
let client = self.rpcs.get()?;
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let rt = rpc.rt.clone();
|
let rt = rpc.rt.clone();
|
||||||
let rpc = &mut (rpc.client);
|
let rpc = &mut (rpc.client);
|
||||||
let resp = rt.block_on(rpc.say_hello(request));
|
let resp = rt.block_on(rpc.say_hello(request));
|
||||||
|
@ -1565,7 +1596,7 @@ impl LwsVfsIns {
|
||||||
});
|
});
|
||||||
let (rpc, resp) = {
|
let (rpc, resp) = {
|
||||||
let client = self.rpcs.get()?;
|
let client = self.rpcs.get()?;
|
||||||
let mut rpc = client.write().unwrap();
|
let mut rpc = client.write();
|
||||||
let resp = rpc.client.say_hello(request).await?;
|
let resp = rpc.client.say_hello(request).await?;
|
||||||
(client.clone(), resp)
|
(client.clone(), resp)
|
||||||
};
|
};
|
||||||
|
|
29
sscp_tool/setup.py
Normal file
29
sscp_tool/setup.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="sscp",
|
||||||
|
version="0.1.0",
|
||||||
|
description="从L3向L0传输文件的简化工具",
|
||||||
|
author="Your Name",
|
||||||
|
author_email="your.email@example.com",
|
||||||
|
packages=find_packages(),
|
||||||
|
py_modules=["sscp"],
|
||||||
|
entry_points={
|
||||||
|
"console_scripts": [
|
||||||
|
"sscp=sscp:main",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
classifiers=[
|
||||||
|
"Development Status :: 3 - Alpha",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Python :: 3.6",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.9",
|
||||||
|
],
|
||||||
|
python_requires=">=3.6",
|
||||||
|
)
|
203
sscp_tool/sscp.py
Normal file
203
sscp_tool/sscp.py
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
sscp - 简化从L3到L0的文件传输工具
|
||||||
|
用法: sscp <源文件/目录> [<源文件/目录>...] <目标路径>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def parse_arguments():
|
||||||
|
"""解析命令行参数"""
|
||||||
|
parser = argparse.ArgumentParser(description='从L3向L0传输文件的简化工具')
|
||||||
|
parser.add_argument('sources', nargs='+', help='源文件或目录路径')
|
||||||
|
parser.add_argument('target', help='目标路径')
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true', help='显示详细信息')
|
||||||
|
parser.add_argument('-r', '--recursive', action='store_true', help='递归处理目录')
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
def get_mount_points():
|
||||||
|
"""从配置文件获取挂载点信息"""
|
||||||
|
config_path = Path.home() / ".config" / "sscp" / "config.json"
|
||||||
|
|
||||||
|
if not config_path.exists():
|
||||||
|
print(f"警告: 找不到配置文件 {config_path},将使用默认挂载点 ~/mnt")
|
||||||
|
return {str(Path.home() / "mnt"): "/l0"}
|
||||||
|
|
||||||
|
with open(config_path, 'r') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
# 从配置中提取挂载点信息
|
||||||
|
mount_points = []
|
||||||
|
mount_points = config['mounts'].copy()
|
||||||
|
return mount_points
|
||||||
|
def encode_path(path):
|
||||||
|
"""
|
||||||
|
对L0路径进行编码
|
||||||
|
将 /l0x/path/to/file 编码为 l0x@path@to@file
|
||||||
|
同时处理路径中已有的@符号
|
||||||
|
"""
|
||||||
|
# 先将已有的@符号替换为特殊序列
|
||||||
|
path_escaped = path.replace('@', '@@')
|
||||||
|
|
||||||
|
# 将路径中的斜杠替换为@符号
|
||||||
|
encoded_path = path_escaped.replace('/', '@')
|
||||||
|
|
||||||
|
# 确保路径以l0开头
|
||||||
|
if not encoded_path.startswith('@l0'):
|
||||||
|
raise ValueError(f"无法识别的L0路径格式: {path}")
|
||||||
|
|
||||||
|
# 移除开头的@
|
||||||
|
return encoded_path[1:]
|
||||||
|
|
||||||
|
def create_symlink(source_path, encoded_path):
|
||||||
|
"""创建源文件或目录到临时位置的软链接"""
|
||||||
|
temp_dir = Path("/tmp/sscp")
|
||||||
|
temp_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
temp_link = temp_dir / encoded_path
|
||||||
|
# 确保父目录存在
|
||||||
|
temp_link.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# 如果已存在,先删除
|
||||||
|
if temp_link.exists():
|
||||||
|
if temp_link.is_dir() and not temp_link.is_symlink():
|
||||||
|
shutil.rmtree(temp_link)
|
||||||
|
else:
|
||||||
|
temp_link.unlink()
|
||||||
|
|
||||||
|
# 创建软链接
|
||||||
|
os.symlink(os.path.abspath(source_path), temp_link)
|
||||||
|
return temp_link
|
||||||
|
|
||||||
|
def call_ssbuild(encoded_path, verbose=False):
|
||||||
|
"""调用ssbuild工具发送文件或目录"""
|
||||||
|
try:
|
||||||
|
cmd = ["ssbuild", str(encoded_path)]
|
||||||
|
if verbose:
|
||||||
|
print(f"执行命令: {' '.join(cmd)}")
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
check=True,
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
return result.stdout
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"调用ssbuild失败: {e}")
|
||||||
|
print(f"错误输出: {e.stderr}")
|
||||||
|
sys.exit(1)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("错误: 找不到ssbuild命令,请确保它已安装并在PATH中")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def is_mount_path(path, mount_points):
|
||||||
|
"""检查路径是否在挂载点中"""
|
||||||
|
path_str = str(path)
|
||||||
|
for mount_point in mount_points:
|
||||||
|
if path_str.startswith(mount_point):
|
||||||
|
return mount_point
|
||||||
|
return None
|
||||||
|
|
||||||
|
def process_source(source_path, target_path, mount_points, verbose=False):
|
||||||
|
"""处理单个源文件或目录"""
|
||||||
|
source_path = Path(source_path).resolve()
|
||||||
|
target_path = Path(target_path)
|
||||||
|
|
||||||
|
# 检查源是否存在
|
||||||
|
if not source_path.exists():
|
||||||
|
print(f"错误: 源 '{source_path}' 不存在")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 如果目标是目录,添加源文件名
|
||||||
|
if target_path.is_dir() or (len(mount_points) > 0 and not target_path.suffix):
|
||||||
|
final_target = target_path / source_path.name
|
||||||
|
else:
|
||||||
|
final_target = target_path
|
||||||
|
|
||||||
|
# 检查目标路径是否在挂载点中
|
||||||
|
mount_point = is_mount_path(final_target, mount_points)
|
||||||
|
if not mount_point:
|
||||||
|
print(f"错误: 目标路径 '{final_target}' 不在已知的挂载目录中")
|
||||||
|
print(f"有效的挂载点: {', '.join(mount_points.keys())}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 从挂载路径提取L0路径部分
|
||||||
|
target_str = str(final_target)
|
||||||
|
relative_path = target_str[len(mount_point):]
|
||||||
|
|
||||||
|
# 构建完整的L0路径
|
||||||
|
l0_path = mount_points[mount_point] + relative_path
|
||||||
|
|
||||||
|
# 对L0路径进行编码
|
||||||
|
encoded_path = encode_path(l0_path)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print(f"源路径: {source_path}")
|
||||||
|
print(f"目标路径: {final_target}")
|
||||||
|
print(f"挂载点: {mount_point}")
|
||||||
|
print(f"L0路径: {l0_path}")
|
||||||
|
print(f"编码后的路径: {encoded_path}")
|
||||||
|
|
||||||
|
# 创建源的软链接
|
||||||
|
temp_link = create_symlink(source_path, encoded_path)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print(f"创建软链接: {source_path} -> {temp_link}")
|
||||||
|
|
||||||
|
# 调用ssbuild工具发送文件或目录
|
||||||
|
print(f"正在发送: {source_path} -> {final_target}")
|
||||||
|
output = call_ssbuild(temp_link, verbose)
|
||||||
|
|
||||||
|
if verbose and output:
|
||||||
|
print(f"ssbuild输出: {output}")
|
||||||
|
|
||||||
|
print(f"已成功传输到 {final_target}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"处理 {source_path} 时出错: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_arguments()
|
||||||
|
|
||||||
|
# 获取挂载点信息
|
||||||
|
mount_points = get_mount_points()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
print(f"已识别的挂载点: {mount_points}")
|
||||||
|
|
||||||
|
# 最后一个参数是目标路径
|
||||||
|
target = args.sources.pop()
|
||||||
|
sources = args.sources
|
||||||
|
|
||||||
|
if not sources:
|
||||||
|
print("错误: 至少需要一个源文件或目录")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 处理每个源文件或目录
|
||||||
|
success_count = 0
|
||||||
|
for source in sources:
|
||||||
|
if process_source(source, target, mount_points, args.verbose):
|
||||||
|
success_count += 1
|
||||||
|
|
||||||
|
# 报告结果
|
||||||
|
if success_count == len(sources):
|
||||||
|
print(f"所有 {len(sources)} 个文件/目录已成功传输")
|
||||||
|
else:
|
||||||
|
print(f"传输完成: {success_count}/{len(sources)} 个文件/目录成功")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user