# WebDAV LOCK拦截机制详解 ## 核心拦截点 dav-server框架在HTTP请求处理流程中内置锁检查机制,无需手动拦截。 ### 1. PUT(写文件)拦截点 **文件位置**: `~/.cargo/registry/src/*/dav-server-0.11.0/src/handle_put.rs:131-139` ```rust // if locked check if we hold that lock. if let Some(ref locksystem) = self.ls { let principal = self.principal.as_deref(); if let Err(_l) = locksystem .check(&path, principal, false, false, &tokens) .await { return Err(DavError::StatusClose(SC::LOCKED)); // 423 LOCKED } } ``` **拦截流程**: ``` 客户端 PUT /webdav/file.txt ↓ DavHandler.handle_put() ↓ if_match_get_tokens() - 解析If头中的lock token ↓ locksystem.check() - 检查锁冲突 ↓ (失败) 返回 423 LOCKED (客户端无权限) ↓ (成功) LocalFs.write() - 执行实际写入 ``` ### 2. DELETE(删除)拦截点 **文件位置**: `~/.cargo/registry/src/*/dav-server-0.11.0/src/handle_delete.rs:123-132` ```rust // check locks. since we cancel the entire operation if there is // a conflicting lock, we do not return a 207 multistatus, but // just a simple status. if let Some(ref locksystem) = self.ls { let principal = self.principal.as_deref(); if let Err(_l) = locksystem .check(&path, principal, false, true, &tokens) // deep=true .await { return Err(DavError::Status(StatusCode::LOCKED)); } } ``` **关键参数**: `deep=true` 表示检查整个路径树(包括子目录) **拦截流程**: ``` 客户端 DELETE /webdav/folder/ ↓ DavHandler.handle_delete() ↓ locksystem.check(&path, principal, false, true, &tokens) ↓ (冲突) 返回 423 LOCKED ↓ (成功) locksystem.delete(&path) - 删除所有锁记录 ↓ LocalFs.remove_dir() - 执行删除 ``` ### 3. LOCK(加锁)拦截点 **文件位置**: `~/.cargo/registry/src/*/dav-server-0.11.0/src/handle_lock.rs` ```rust // 创建新锁 let lock = locksystem.lock( &path, principal, owner, timeout, shared, deep, ).await; // 刷新锁 let lock = locksystem.refresh(&path, &tokens[0], timeout).await; ``` **拦截流程**: ``` 客户端 LOCK /webdav/file.txt ↓ DavHandler.handle_lock() ↓ 检查If头中的token ↓ (有token) locksystem.refresh() - 刷新现有锁 ↓ (无token) locksystem.lock() - 创建新锁 ↓ 返回 200 OK + lock token ``` ### 4. UNLOCK(解锁)拦截点 **文件位置**: `~/.cargo/registry/src/*/dav-server-0.11.0/src/handle_lock.rs` ```rust locksystem.unlock(&path, &lock_token).await ``` **拦截流程**: ``` 客户端 UNLOCK /webdav/file.txt Header: Lock-Token: ↓ DavHandler.handle_unlock() ↓ 解析Lock-Token头 ↓ locksystem.unlock(&path, &token) ↓ (成功) 返回 204 No Content ↓ (失败) 返回 403 Forbidden (token无效) ``` ## DavLockSystem.check() 参数详解 ```rust fn check( path: &DavPath, // 文件路径 principal: Option<&str>, // 用户身份(来自Authorization头) ignore_principal: bool, // true=忽略用户身份检查 deep: bool, // true=检查子路径锁 submitted_tokens: &[String], // If头中的lock tokens ) -> LsFuture<'_, Result<(), DavLock>> ``` **返回值**: - `Ok(())` - 有权限(锁匹配或无锁) - `Err(DavLock)` - 冲突锁(返回423 LOCKED) ## if_match_get_tokens() 作用 **文件位置**: `~/.cargo/registry/src/*/dav-server-0.11.0/src/conditional.rs` 解析HTTP请求中的lock tokens: - `If: ` - 单个token - `If: ()` - 标准格式 - `If-Match: *` - 需要任意锁 - `If-None-Match: *` - 需要无锁 ## MarkBase实现位置 **LockManager**: `src/webdav/lock_manager.rs` ```rust impl DavLockSystem for LockManager { fn check( &'_ self, path: &DavPath, principal: Option<&str>, ignore_principal: bool, deep: bool, submitted_tokens: &[String], ) -> LsFuture<'_, Result<(), DavLock>> { // 1. 查询SQLite数据库中的锁 // 2. 清理过期锁(cleanup_expired_locks) // 3. 比对submitted_tokens(匹配则允许) // 4. 比对principal(同用户则允许) // 5. 检查deep锁(子路径冲突) // 6. 返回冲突锁(Err)或允许(Ok) } } ``` ## HTTP状态码对照 |状态码 |含义 |触发条件 | |-------|------|----------| | 200 OK | LOCK成功 | lock()返回Ok | | 204 No Content | UNLOCK/PUT成功 | unlock()或write()成功 | | 403 Forbidden | UNLOCK失败 | unlock()返回Err(token无效)| | 423 Locked | 操作被锁阻止 | check()返回Err | | 409 Conflict | 目标不存在 | 文件不存在且无法创建 | | 412 Precondition Failed | If条件不满足 | If-Match/If-None-Match失败 | ## macOS Finder行为 **典型请求序列**: ``` 1. PROPFIND /webdav/ - 获取文件列表 2. LOCK /webdav/file.txt - 加锁( exclusive) Header: If: () 3. PUT /webdav/file.txt - 写入(带If头) Header: If: () 4. UNLOCK /webdav/file.txt - 解锁 Header: Lock-Token: ``` **锁有效期**: macOS Finder默认60秒超时,需定期refresh ## 测试方法 ```bash # 手动测试锁机制 curl -X LOCK http://localhost:4919/webdav/test.txt \ -H "Content-Type: application/xml" \ -d '' # 查看锁token curl -X PROPFIND http://localhost:4919/webdav/test.txt \ -H "Depth: 0" \ -H "Content-Type: application/xml" \ -d '' # 尝试写入(无锁token) curl -X PUT http://localhost:4919/webdav/test.txt \ -d "test content" # 预期:423 Locked # 写入(带正确token) curl -X PUT http://localhost:4919/webdav/test.txt \ -H "If: ()" \ -d "test content" # 预期:204 No Content ``` --- **创建时间**: 2026-05-17 03:30 **版本**: 1.0(拦截点详解版)