Please make sure of the following things
AList Version / AList 版本
v3.63.0
Driver used / 使用的存储驱动
本机存储
Describe the bug / 问题描述
当 WebDAV 用户或角色的“基本路径(Base Path)”设置为非根目录(例如设置为 /webdav,且存储根目录下已存在 /webdav 目录)时:
客户端连接 https://domain/dav/ 并尝试在根目录下创建子文件夹或上传文件(例如发送 MKCOL /dav/cc-switch-sync/)时,AList 会报错返回 409 Conflict (请确认上级目录存在)。
经排查,该报错是因为服务端代码在处理请求时把 BasePath 拼接了两次,导致 AList 试图去底层查找物理上不存在的 /webdav/webdav/cc-switch-sync 路径,引发父目录判定不存在错误。
Reproduction / 复现链接
【复现步骤】:
- 在 AList 存储根目录下确保存在一个名为
/webdav 的文件夹。
- 创建一个 WebDAV 用户(或为其绑定的角色),设置“基本路径”为
/webdav。
- 使用 WebDAV 客户端(如 CC-Switch / Rclone / Joplin)连接
https://domain/dav/。
- 客户端在根目录下尝试创建子目录(例如
cc-switch-sync)。
- 客户端收到报错:
409 Conflict (WebDAV MKCOL failed: 409 Conflict: Please ensure the parent directory exists)。
对比验证:如果把用户的基本路径改回 /,客户端连接 URL 设为 https://domain/dav/webdav/,则一切正常。
【源码级 Bug 根源分析】:
问题发生在 server/webdav/path.go 中的 ResolvePath 函数与 internal/model/user.go 中的 user.JoinPath() 的叠加作用:
- 在
server/webdav/path.go 中:
func ResolvePath(user *model.User, raw string) (string, error) {
cleaned := utils.FixAndCleanPath(raw)
basePath := utils.FixAndCleanPath(user.BasePath)
if cleaned != "/" && basePath != "/" && !utils.IsSubPath(basePath, cleaned) {
cleaned = path.Join(basePath, strings.TrimPrefix(cleaned, "/"))
}
return user.JoinPath(cleaned)
}
当客户端请求 MKCOL /dav/cc-switch-sync(raw = "/cc-switch-sync")时:
ResolvePath 判断 !utils.IsSubPath("/webdav", "/cc-switch-sync") 为 true,执行 path.Join("/webdav", "cc-switch-sync"),得到 cleaned = "/webdav/cc-switch-sync"。
- 随后
ResolvePath 将 "/webdav/cc-switch-sync" 传递给 user.JoinPath(cleaned):
func (u *User) JoinPath(reqPath string) (string, error) {
if reqPath == "/" {
return utils.FixAndCleanPath(u.BasePath), nil
}
path, err := utils.JoinBasePath(u.BasePath, reqPath) // <--- 此处又重复拼接了一次 u.BasePath !
...
}
user.JoinPath 内部调用 utils.JoinBasePath("/webdav", "/webdav/cc-switch-sync"),再次无条件拼接 u.BasePath,最终算出的目标路径为:
"/webdav/webdav/cc-switch-sync"。
由于存储根目录下只有 /webdav,并不存在 /webdav/webdav,因此底层 Stat 判定父目录不存在,向上抛出 409 Conflict。
【修复建议】:
建议修补 server/webdav/path.go 中的逻辑,避免在传递给 user.JoinPath 前重复拼接 basePath:
func ResolvePath(user *model.User, raw string) (string, error) {
cleaned := utils.FixAndCleanPath(raw)
basePath := utils.FixAndCleanPath(user.BasePath)
- if cleaned != "/" && basePath != "/" && !utils.IsSubPath(basePath, cleaned) {
- cleaned = path.Join(basePath, strings.TrimPrefix(cleaned, "/"))
- }
+ if basePath != "/" && utils.IsSubPath(basePath, cleaned) {
+ cleaned = utils.GetSubPath(basePath, cleaned)
+ }
return user.JoinPath(cleaned)
}
Config / 配置
与配置无关,需说明的配置在上面均已说明
Logs / 日志
代码层面已查明,暂且懒得提供日志
Please make sure of the following things
I have read the documentation.
我已经阅读了文档。
I'm sure there are no duplicate issues or discussions.
我确定没有重复的issue或讨论。
I'm sure it's due to
AListand not something else(such as Network ,DependenciesorOperational).我确定是
AList的问题,而不是其他原因(例如网络,依赖或操作)。I'm sure this issue is not fixed in the latest version.
我确定这个问题在最新版本中没有被修复。
AList Version / AList 版本
v3.63.0
Driver used / 使用的存储驱动
本机存储
Describe the bug / 问题描述
当 WebDAV 用户或角色的“基本路径(Base Path)”设置为非根目录(例如设置为
/webdav,且存储根目录下已存在/webdav目录)时:客户端连接
https://domain/dav/并尝试在根目录下创建子文件夹或上传文件(例如发送MKCOL /dav/cc-switch-sync/)时,AList 会报错返回409 Conflict (请确认上级目录存在)。经排查,该报错是因为服务端代码在处理请求时把
BasePath拼接了两次,导致 AList 试图去底层查找物理上不存在的/webdav/webdav/cc-switch-sync路径,引发父目录判定不存在错误。Reproduction / 复现链接
【复现步骤】:
/webdav的文件夹。/webdav。https://domain/dav/。cc-switch-sync)。409 Conflict (WebDAV MKCOL failed: 409 Conflict: Please ensure the parent directory exists)。对比验证:如果把用户的基本路径改回
/,客户端连接 URL 设为https://domain/dav/webdav/,则一切正常。【源码级 Bug 根源分析】:
问题发生在
server/webdav/path.go中的ResolvePath函数与internal/model/user.go中的user.JoinPath()的叠加作用:server/webdav/path.go中:当客户端请求
MKCOL /dav/cc-switch-sync(raw = "/cc-switch-sync")时:ResolvePath判断!utils.IsSubPath("/webdav", "/cc-switch-sync")为 true,执行path.Join("/webdav", "cc-switch-sync"),得到cleaned = "/webdav/cc-switch-sync"。ResolvePath将"/webdav/cc-switch-sync"传递给user.JoinPath(cleaned):user.JoinPath内部调用utils.JoinBasePath("/webdav", "/webdav/cc-switch-sync"),再次无条件拼接u.BasePath,最终算出的目标路径为:"/webdav/webdav/cc-switch-sync"。由于存储根目录下只有
/webdav,并不存在/webdav/webdav,因此底层Stat判定父目录不存在,向上抛出409 Conflict。【修复建议】:
建议修补
server/webdav/path.go中的逻辑,避免在传递给user.JoinPath前重复拼接basePath:func ResolvePath(user *model.User, raw string) (string, error) { cleaned := utils.FixAndCleanPath(raw) basePath := utils.FixAndCleanPath(user.BasePath) - if cleaned != "/" && basePath != "/" && !utils.IsSubPath(basePath, cleaned) { - cleaned = path.Join(basePath, strings.TrimPrefix(cleaned, "/")) - } + if basePath != "/" && utils.IsSubPath(basePath, cleaned) { + cleaned = utils.GetSubPath(basePath, cleaned) + } return user.JoinPath(cleaned) }Config / 配置
与配置无关,需说明的配置在上面均已说明
Logs / 日志
代码层面已查明,暂且懒得提供日志