Skip to content

[Bug] 115网盘 Copy 后 Rename 误操作源文件 #2506

@tide-wait

Description

@tide-wait

Please confirm the following

  • I have read and agree to AGPL-3.0 Section 15 .
    The program is provided "as is" without any warranties; you bear all risks of using it.

  • I have read and agree to AGPL-3.0 Section 16 .
    The copyright holders and distributors are not liable for any damages resulting from the use or inability to use the program.

  • I confirm my description is clear, polite, helps developers quickly locate the issue, and complies with community rules.

  • I have read the OpenList documentation.

  • I confirm there are no duplicate issues or discussions.

  • I confirm this is an OpenList issue, not caused by other reasons (such as network, dependencies, or operation).

  • I believe this issue must be handled by OpenList and not by a third party.

  • I confirm this issue is not fixed in the latest version.

  • I have not read these checkboxes and therefore I just ticked them all, Please close this issue.

OpenList Version (required)

v4.2.1

Storage Driver Used (required)

115_open

Bug Description (required)

问题描述

在 115 网盘(Open API 驱动)上连续执行 Copy → Rename 操作时,有一定概率出现重命名了源文件而非复制后的目标文件的问题。同时 POST /api/fs/mkdir 在 115 路径下创建文件夹也会失败。

环境信息

  • 驱动类型:115_open(Open API 令牌登录)
  • 挂载路径:/115

问题一:Copy → Rename 误操作源文件

复现步骤

  1. /115/整理/待验/二次元/文件A 执行 Copy 到 /115/整理/待验/二次元/已验/
  2. Copy 成功后立即对 /115/整理/待验/二次元/已验/文件A 执行 Rename
  3. 有时重命名的是源文件 /115/整理/待验/二次元/文件A 而非复制后的文件

根因

drivers/115_open/driver.goCopy 方法(第 247 行)调用 115 Open API 复制文件后,API 不返回新文件的信息,驱动返回的是源文件对象(包含源文件的 Fid)。
internal/op/fs.goCopy 缓存更新逻辑中(第 551-559 行),CopyResult 返回非 nil 对象时不会打 Temp 标记,导致目标目录缓存里存入了源文件的 Fid 而非新复制文件的 Fid。
后续 Rename 操作在 internal/op/fs.go 第 456 行通过 Get(..., excludeTempObj=true) 查找源文件,由于缓存条目没有 Temp 标记,excludeTempObj 保护失效,直接命中了带源文件 Fid 的缓存条目,最终通过该 Fid 调用 Rename API,错误地重命名了源文件。

影响范围

  • CopyResult 接口的实现驱动中,返回源对象而非新对象的驱动均受影响
  • Move 操作存在同类风险

问题二:Mkdir 失败(GetFolderInfo 反序列化错误)

复现步骤

curl 'http://127.0.0.1:5244/api/fs/mkdir' \
  -H 'Content-Type: application/json;charset=UTF-8' \
  --data-raw '{"path":"/115/整理/待验/二次元/电影"}'

返回:

{"code":500,"message":"failed to check if dir exists: failed to get obj: json: cannot unmarshal array into Go value of type sdk.GetFolderInfoResp"}

根因
115 Open API 的 folder/get_info 接口返回 data 字段的格式从单个对象 {...} 变更为 JSON 数组 [{...}]。但 SDK(115-sdk-go v0.2.3)的 GetFolderInfoResp 类型仍是单一结构体,导致 json.Unmarshal 失败。
SDK 中已有 json_types/StructOrArray[T] 类型可以处理这种场景,但 GetFolderInfoByPath 方法未使用。
Mkdir 在 op.MakeDir 中会先调用 Get 检查目录是否存在,Get 调用驱动的 GetFolderInfoByPath → SDK 解析失败 → 错误向上传播。

Logs (required)

问题一

2026-05-24 18:05:09 failed make dir /115/整理/待验/二次元/剧集:
json: cannot unmarshal array into Go value of type sdk.GetFolderInfoResp
2026-05-24 18:07:22 failed make dir /115/整理/待验/二次元/电影:
json: cannot unmarshal array into Go value of type sdk.GetFolderInfoResp

问题二:Mkdir 失败

2026-05-25 09:28:43 failed rename
百合熊风暴 - S01E05 - 第 05 集.cht.ass
→ 百合熊风暴 (2015) S01E05 想要独占你.ass:
code: 20004, message: 很抱歉,该目录名称已存在。

Configuration File Content (required)

{
  "force": false,
  "site_url": "",
  "cdn": "",
  "jwt_secret": "UtUClEwpuZhhi10Y",
  "token_expires_in": 48,
  "database": {
    "type": "sqlite3",
    "host": "",
    "port": 0,
    "user": "",
    "password": "",
    "name": "",
    "db_file": "data/data.db",
    "table_prefix": "x_",
    "ssl_mode": "",
    "dsn": ""
  },
  "meilisearch": {
    "host": "http://localhost:7700",
    "api_key": "",
    "index": "openlist"
  },
  "scheme": {
    "address": "0.0.0.0",
    "http_port": 5244,
    "https_port": -1,
    "force_https": false,
    "cert_file": "",
    "key_file": "",
    "unix_file": "",
    "unix_file_perm": "",
    "enable_h2c": false,
    "enable_h3": false
  },
  "temp_dir": "data/temp",
  "bleve_dir": "data/bleve",
  "dist_dir": "",
  "log": {
    "enable": true,
    "name": "data/log/log.log",
    "max_size": 50,
    "max_backups": 30,
    "max_age": 28,
    "compress": false
  },
  "delayed_start": 0,
  "auto_memory_limit": 4,
  "min_free_memory": 0,
  "max_block_limit": 0,
  "max_connections": 0,
  "max_concurrency": 64,
  "tls_insecure_skip_verify": false,
  "tasks": {
    "download": { "workers": 5, "max_retry": 1 },
    "transfer": { "workers": 5, "max_retry": 2 },
    "upload":   { "workers": 5, "max_retry": 0 },
    "copy":     { "workers": 5, "max_retry": 2 },
    "move":     { "workers": 5, "max_retry": 2 },
    "decompress":        { "workers": 5, "max_retry": 2 },
    "decompress_upload": { "workers": 5, "max_retry": 2 },
    "allow_retry_canceled": false
  },
  "cors": {
    "allow_origins": ["*"],
    "allow_methods": ["*"],
    "allow_headers": ["*"]
  },
  "s3":   { "enable": false, "port": 5246, "ssl": false },
  "ftp":  { "enable": false, "listen": ":5221" },
  "sftp": { "enable": false, "listen": ":5222" },
  "last_launched_version": "v4.2.1",
  "proxy_address": ""
}

Reproduction Link (optional)

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions