feat(proto/server)!: 升级至协议 v2(统一错误模型);全面替换 Request/Response 并移除 ret
- Breaking change: 协议不向后兼容,旧客户端需同步升级 - Proto: 新增 FsOp/ErrorCause/FsError/RpcStatus;为所有 FS 接口定义 <Op>Request/<Op>Response;删除历史 ret 字段 - Server: 所有 RPC 返回统一的 RpcStatus;成功 ok=true,失败填充 FsError(operation/paths/sys_msg 等) - Open/Read/Write 对齐新字段(fi/data/written);Readdir/Opendir/Releasedir 等返回类型调整 - Rename 传回 from/to;OpendirRequest 不再输入 fi,服务端生成并回传 - Docs: 新增 docs/protocol_v2.md;README 标注 v2 破坏性升级与用法 - Build: 主要面向 Windows(winapi)。Linux 环境类型检查可能失败 后续:完善 errno 抽取与 context 填充;可选引入流式 read/write 以优化大文件传输。
This commit is contained in:
13
README.md
13
README.md
@ -1,4 +1,13 @@
|
|||||||
# wls_vfs
|
# wls_vfs
|
||||||
|
|
||||||
windows linux文件共享,使用fuse + grpc + protobuffer
|
Windows/Linux 文件共享服务端(gRPC + Protobuf)。
|
||||||
语言:rust
|
|
||||||
|
本仓库已升级至协议 v2(统一错误模型):
|
||||||
|
|
||||||
|
- 所有 RPC 使用显式的 `Request`/`Response` 类型;
|
||||||
|
- 响应统一携带 `RpcStatus { ok, error }`;
|
||||||
|
- 删除历史 `ret` 字段;失败信息通过 `FsError` 完整承载(`code`/`sys_msg`/`operation`/`paths`/`context` 等)。
|
||||||
|
|
||||||
|
协议详情见:`proto/lws.proto` 与 `docs/protocol_v2.md`。
|
||||||
|
|
||||||
|
注意:本项目主要在 Windows 上构建运行(依赖 `winapi`)。在非 Windows 平台构建仅用于类型检查,可能失败。
|
||||||
|
14
docs/protocol_v2.md
Normal file
14
docs/protocol_v2.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# LWS 协议 v2(统一错误模型)
|
||||||
|
|
||||||
|
- RpcStatus { ok, error }
|
||||||
|
- FsError { code, message, sys_msg, grpc_code, grpc_message, operation(FsOp), paths[], context{...}, causes[], server, timestamp_ms, retriable }
|
||||||
|
|
||||||
|
成功:ok = true;失败:ok = false 且 error.code 为正数 POSIX errno。
|
||||||
|
|
||||||
|
常用 context keys: flags, offset, size, uid, gid, mode, fh, mask, xattr_name, xattr_size。
|
||||||
|
|
||||||
|
FsOp 与 RPC 映射:与客户端 docs 相同(参见 proto/lws.proto 中的 service 定义)。
|
||||||
|
|
||||||
|
服务端返回建议:
|
||||||
|
- 失败时设置 error.code 与 sys_msg,并补充 operation/paths/context。
|
||||||
|
- 传输层错误由客户端据 gRPC `Status` 统一映射。
|
290
proto/lws.proto
290
proto/lws.proto
@ -16,43 +16,62 @@ syntax = "proto3";
|
|||||||
|
|
||||||
package lws_vfs;
|
package lws_vfs;
|
||||||
|
|
||||||
service LwsVfs {
|
// Filesystem operation types for diagnostics and observability.
|
||||||
// Sends a greeting
|
enum FsOp {
|
||||||
rpc SayHello(HelloRequest) returns (HelloReply) {}
|
FSOP_UNKNOWN = 0;
|
||||||
rpc GetConfig(get_config) returns (get_config) {}
|
FSOP_GETATTR = 1;
|
||||||
|
FSOP_SETXATTR = 2;
|
||||||
rpc fgetattr(getattr) returns (getattr) {}
|
FSOP_ACCESS = 3;
|
||||||
rpc fsetxattr(setxattr) returns (setxattr) {}
|
FSOP_READDIR = 4;
|
||||||
rpc faccess(access) returns (access) {}
|
FSOP_READ = 5;
|
||||||
rpc freaddir(readdir) returns (readdir) {}
|
FSOP_OPEN = 6;
|
||||||
rpc fread(read) returns (read) {}
|
FSOP_WRITE = 7;
|
||||||
rpc fopen(open) returns (open) {}
|
FSOP_GETXATTR = 8;
|
||||||
rpc fwrite(write) returns (write) {}
|
FSOP_TRUNCATE = 9;
|
||||||
rpc fgetxattr(getxattr) returns (getxattr) {}
|
FSOP_UTIMENS = 10;
|
||||||
rpc ftruncate(truncate) returns (truncate) {}
|
FSOP_CHOWN = 11;
|
||||||
rpc futimens(utimens) returns (utimens) {}
|
FSOP_RELEASE = 12;
|
||||||
rpc fchown(chown) returns (chown) {}
|
FSOP_MKDIR = 13;
|
||||||
rpc frelease(release) returns (release) {}
|
FSOP_RMDIR = 14;
|
||||||
rpc fmkdir(mkdir) returns (mkdir) {}
|
FSOP_FLUSH = 15;
|
||||||
rpc frmdir(rmdir) returns (rmdir) {}
|
FSOP_OPENDIR = 16;
|
||||||
rpc fflush(flush) returns (flush) {}
|
FSOP_RELEASEDIR = 17;
|
||||||
rpc fopendir(opendir) returns (opendir) {}
|
FSOP_CREATE = 18;
|
||||||
rpc freleasedir(releasedir) returns (releasedir) {}
|
FSOP_UNLINK = 19;
|
||||||
rpc fcreate(create) returns (create) {}
|
FSOP_RENAME = 20;
|
||||||
rpc funlink(unlink) returns (unlink) {}
|
FSOP_HELLO = 21;
|
||||||
rpc frename(rename) returns (rename) {}
|
FSOP_GET_CONFIG = 22;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The request message containing the user's name.
|
// Optional chained cause for nested errors.
|
||||||
message HelloRequest { string name = 1; }
|
message ErrorCause {
|
||||||
|
int32 code = 1; // POSIX errno if available
|
||||||
// The response message containing the greetings
|
string message = 2; // human-readable message
|
||||||
message HelloReply { string message = 1; }
|
string type = 3; // error type/category from server implementation
|
||||||
|
|
||||||
message get_config {
|
|
||||||
string config = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rich error payload carried by all responses.
|
||||||
|
message FsError {
|
||||||
|
int32 code = 1; // POSIX errno (>0)
|
||||||
|
string message = 2; // application error message
|
||||||
|
string sys_msg = 3; // strerror(code) or OS message
|
||||||
|
uint32 grpc_code = 4; // gRPC status code (if applicable)
|
||||||
|
string grpc_message = 5; // gRPC status message
|
||||||
|
FsOp operation = 6; // which fs op failed
|
||||||
|
repeated string paths = 7; // involved paths (e.g. [from, to])
|
||||||
|
map<string, string> context = 8; // flags/size/offset/uid/gid ...
|
||||||
|
repeated ErrorCause causes = 9; // nested causes
|
||||||
|
string server = 10; // server id/hostname
|
||||||
|
uint64 timestamp_ms = 11; // server time when error occurred
|
||||||
|
bool retriable = 12; // whether retry may succeed
|
||||||
|
}
|
||||||
|
|
||||||
|
message RpcStatus {
|
||||||
|
bool ok = 1; // true if the call succeeded
|
||||||
|
FsError error = 2; // populated when ok == false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common structs
|
||||||
message file_info {
|
message file_info {
|
||||||
uint32 flags = 1;
|
uint32 flags = 1;
|
||||||
uint32 fh_old = 2;
|
uint32 fh_old = 2;
|
||||||
@ -77,34 +96,9 @@ message fstat {
|
|||||||
uint64 fst_blocks = 13;
|
uint64 fst_blocks = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fuse api function message define:
|
message timespec {
|
||||||
message getattr {
|
int32 tv_sec = 1;
|
||||||
string path = 1;
|
int64 tv_nsec = 2;
|
||||||
fstat stat = 2;
|
|
||||||
file_info fi = 3;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message setxattr {
|
|
||||||
string path = 1;
|
|
||||||
string name = 2;
|
|
||||||
bytes value = 3;
|
|
||||||
int64 size = 4;
|
|
||||||
uint32 flags = 5;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
message getxattr {
|
|
||||||
string path = 1;
|
|
||||||
string name = 2;
|
|
||||||
bytes value = 3;
|
|
||||||
int64 size = 4;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message access {
|
|
||||||
string path = 1;
|
|
||||||
uint32 mask = 2;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message direntry {
|
message direntry {
|
||||||
@ -112,112 +106,100 @@ message direntry {
|
|||||||
uint32 kind = 2;
|
uint32 kind = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message readdir {
|
// Service and RPCs
|
||||||
string path = 1;
|
service LwsVfs {
|
||||||
repeated direntry dirs = 2;
|
rpc SayHello(HelloRequest) returns (HelloReply) {}
|
||||||
uint32 offset = 3;
|
rpc GetConfig(GetConfigRequest) returns (GetConfigResponse) {}
|
||||||
file_info fi = 4;
|
|
||||||
int32 ret = 15;
|
rpc fgetattr(GetattrRequest) returns (GetattrResponse) {}
|
||||||
|
rpc fsetxattr(SetxattrRequest) returns (EmptyResponse) {}
|
||||||
|
rpc faccess(AccessRequest) returns (EmptyResponse) {}
|
||||||
|
rpc freaddir(ReaddirRequest) returns (ReaddirResponse) {}
|
||||||
|
rpc fread(ReadRequest) returns (ReadResponse) {}
|
||||||
|
rpc fopen(OpenRequest) returns (OpenResponse) {}
|
||||||
|
rpc fwrite(WriteRequest) returns (WriteResponse) {}
|
||||||
|
rpc fgetxattr(GetxattrRequest) returns (GetxattrResponse) {}
|
||||||
|
rpc ftruncate(TruncateRequest) returns (EmptyResponse) {}
|
||||||
|
rpc futimens(UtimensRequest) returns (EmptyResponse) {}
|
||||||
|
rpc fchown(ChownRequest) returns (EmptyResponse) {}
|
||||||
|
rpc frelease(ReleaseRequest) returns (EmptyResponse) {}
|
||||||
|
rpc fmkdir(MkdirRequest) returns (EmptyResponse) {}
|
||||||
|
rpc frmdir(RmdirRequest) returns (EmptyResponse) {}
|
||||||
|
rpc fflush(FlushRequest) returns (EmptyResponse) {}
|
||||||
|
rpc fopendir(OpendirRequest) returns (OpendirResponse) {}
|
||||||
|
rpc freleasedir(ReleasedirRequest) returns (EmptyResponse) {}
|
||||||
|
rpc fcreate(CreateRequest) returns (CreateResponse) {}
|
||||||
|
rpc funlink(UnlinkRequest) returns (EmptyResponse) {}
|
||||||
|
rpc frename(RenameRequest) returns (EmptyResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message open {
|
// Hello
|
||||||
string path = 1;
|
message HelloRequest { string name = 1; }
|
||||||
file_info fi = 2;
|
message HelloReply { string message = 1; RpcStatus status = 2; }
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message read {
|
// Config
|
||||||
string path = 1;
|
message GetConfigRequest {}
|
||||||
bytes buff = 2;
|
message GetConfigResponse { string config = 1; RpcStatus status = 2; }
|
||||||
int64 size = 3;
|
|
||||||
uint64 offset = 4;
|
|
||||||
file_info fi = 5;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message write {
|
// getattr
|
||||||
string path = 1;
|
message GetattrRequest { string path = 1; file_info fi = 2; }
|
||||||
bytes buff = 2;
|
message GetattrResponse { fstat stat = 1; RpcStatus status = 2; }
|
||||||
uint64 size = 3;
|
|
||||||
uint64 offset = 4;
|
|
||||||
file_info fi = 5;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message truncate {
|
// xattr
|
||||||
string path = 1;
|
message SetxattrRequest { string path = 1; string name = 2; bytes value = 3; int64 size = 4; uint32 flags = 5; }
|
||||||
int64 size = 3;
|
message GetxattrRequest { string path = 1; string name = 2; int64 size = 3; }
|
||||||
int32 ret = 15;
|
message GetxattrResponse { bytes value = 1; RpcStatus status = 2; }
|
||||||
}
|
|
||||||
|
|
||||||
message timespec {
|
// access
|
||||||
int32 tv_sec = 1;
|
message AccessRequest { string path = 1; uint32 mask = 2; }
|
||||||
int64 tv_nsec = 2;
|
|
||||||
}
|
|
||||||
message utimens {
|
|
||||||
string path = 1;
|
|
||||||
repeated timespec ts = 2; // const struct timespec ts[2]
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message chown {
|
// readdir
|
||||||
string path = 1;
|
message ReaddirRequest { string path = 1; uint32 offset = 2; file_info fi = 3; }
|
||||||
int32 uid = 2;
|
message ReaddirResponse { repeated direntry dirs = 1; RpcStatus status = 2; }
|
||||||
int32 gid = 3;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message release {
|
// open
|
||||||
string path = 1;
|
message OpenRequest { string path = 1; file_info fi = 2; }
|
||||||
file_info fi = 2;
|
message OpenResponse { file_info fi = 1; RpcStatus status = 2; }
|
||||||
uint32 flush = 3;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message mkdir {
|
// read
|
||||||
string path = 1;
|
message ReadRequest { string path = 1; uint64 offset = 2; int64 size = 3; file_info fi = 4; }
|
||||||
file_info fi = 2;
|
message ReadResponse { bytes data = 1; RpcStatus status = 2; }
|
||||||
uint32 mode = 3;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message rmdir {
|
// write
|
||||||
string path = 1;
|
message WriteRequest { string path = 1; uint64 offset = 2; bytes data = 3; file_info fi = 4; }
|
||||||
int32 ret = 15;
|
message WriteResponse { uint64 written = 1; RpcStatus status = 2; }
|
||||||
}
|
|
||||||
|
|
||||||
message flush {
|
// truncate
|
||||||
string path = 1;
|
message TruncateRequest { string path = 1; int64 size = 2; }
|
||||||
file_info fi = 2;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message opendir {
|
// utimens
|
||||||
string path = 1;
|
message UtimensRequest { string path = 1; repeated timespec ts = 2; }
|
||||||
file_info fi = 2;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message releasedir {
|
// chown
|
||||||
string path = 1;
|
message ChownRequest { string path = 1; int32 uid = 2; int32 gid = 3; }
|
||||||
file_info fi = 2;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message create {
|
// release/flush
|
||||||
string path = 1;
|
message ReleaseRequest { string path = 1; file_info fi = 2; uint32 flush = 3; }
|
||||||
uint32 mode = 2;
|
message FlushRequest { string path = 1; file_info fi = 2; }
|
||||||
file_info fi = 3;
|
|
||||||
int32 ret = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message unlink {
|
// mkdir/rmdir
|
||||||
string path = 1;
|
message MkdirRequest { string path = 1; uint32 mode = 2; }
|
||||||
int32 ret = 15;
|
message RmdirRequest { string path = 1; }
|
||||||
}
|
|
||||||
|
|
||||||
message rename {
|
// opendir/releasedir
|
||||||
string path = 1;
|
message OpendirRequest { string path = 1; }
|
||||||
string new = 2;
|
message OpendirResponse { file_info fi = 1; RpcStatus status = 2; }
|
||||||
int32 ret = 15;
|
message ReleasedirRequest { string path = 1; file_info fi = 2; }
|
||||||
}
|
|
||||||
|
// create
|
||||||
|
message CreateRequest { string path = 1; uint32 mode = 2; }
|
||||||
|
message CreateResponse { file_info fi = 1; RpcStatus status = 2; }
|
||||||
|
|
||||||
|
// unlink
|
||||||
|
message UnlinkRequest { string path = 1; }
|
||||||
|
|
||||||
|
// rename
|
||||||
|
message RenameRequest { string path = 1; string new = 2; }
|
||||||
|
|
||||||
|
// Empty response with status only
|
||||||
|
message EmptyResponse { RpcStatus status = 1; }
|
||||||
|
1051
src/lib.rs
1051
src/lib.rs
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user