cloudreve / cloudreve/cloudreve

File Move/Rename APIs Lack Source File Identity Preconditions, Potentially Causing a Timed-Out Retry to Operate on a Newly Created File with the Same Name

Open
#3,565 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Go
Stars
28.7k
Forks
3.9k
PR merge metrics
No merged PRs in 30d

Description

### Self Checks

- [x] I have read the [Contributing Guide](https://docs.cloudreve.org/api/contributing) and [Language Policy](https://github.com/cloudreve/cloudreve/discussions/3335).
- [x] I have searched for existing issues [search for existing issues](https://github.com/cloudreve/cloudreve/issues), including closed ones.
- [x] I confirm that I am using English to submit this report, otherwise it will be closed. / 请使用英语提交,否则会被关闭。
- [x] Please do not modify this template :) and fill in all the required fields.

### 1. Is this request related to a challenge you're experiencing? Tell me about your story.

Problem Description

The REST file move and rename APIs use a URI to identify the source file, but the request parameters do not provide a condition such as “execute only if the source file ID or state matches the expected value.” [API source code](https://github.com/cloudreve/Cloudreve/blob/4.18.0/service/explorer/file.go)

A client-side request timeout does not mean that the server-side operation has been cancelled. If an old request is delayed and the original path is later reused by another file, the old request may operate on the new file instead of the file originally selected by the user.

Current status: this is a risk inferred from source code analysis and has not yet been reproduced through fault-injection testing.

Possible Trigger Sequence

Using a file move operation as an example:

File A is located at /normal/report.pdf.
The client sends request R1 to move it to /archive/. R1 is delayed before the source file is resolved, and the client eventually times out while waiting.
The client retries, and request R2 succeeds in moving File A into the archive directory.
The archived File A is renamed to report-old.pdf, freeing the original destination name.
A new File B is created at /normal/report.pdf.
If the old request R1 is still able to continue at this point and looks up the source file again by path, it may move File B into the archive directory.

The result is: an old operation originally intended for File A actually affects File B, which was created later.

This is not guaranteed to happen. The operation may fail if the source file no longer exists, if there is a destination name conflict, or if the old request has already terminated. The source code already contains locking and destination conflict handling, but these mechanisms are not equivalent to verifying that “the file currently located at the source path is still the same file originally specified by the client.” [Move implementation source code](https://github.com/cloudreve/Cloudreve/blob/4.18.0/pkg/filemanager/fs/dbfs/manage.go)

Expected Behavior

When the identity or state of the file at the source path has changed, the old request should return an explicit precondition failure instead of continuing to operate on the new file.

When the same logical operation is submitted more than once, the server should return the existing result or execution status rather than performing the file mutation again.

Suggested Solutions
1. Add Source File Precondition Validation — Recommended Priority

Add optional parameters to the move and rename APIs, for example:

expected_file_id: the expected file ID of the source file.
expected_revision: the expected state version of the source file; expected parent directory and filename conditions could also be used.

The server must perform the precondition validation and the actual mutation within the same protected transaction or atomic conditional update. If the condition does not match, the server should return an explicit error such as 412 Precondition Failed or an equivalent application-specific error code.

It is not sufficient for the client to first query the file ID and then send a path-based move request, because the file may still change between the two requests.

2. Add an Idempotency Identifier and Operation Result Query

Support Idempotency-Key or operation_id:

Same user, same operation identifier, same parameters: do not execute the operation again.
Same identifier but different parameters: reject the request and return a conflict.
Provide an operation status query that distinguishes between processing, success, and failure.
The operation record and file mutation need consistency guarantees to avoid cases where the file mutation succeeds but the process crashes before the result is recorded.

Idempotency prevents duplicate execution; source file preconditions prevent operating on the wrong object. Using both together is recommended.

3. Temporary Handling Before the API Is Improved

After a request times out, first verify the original file ID and destination state instead of blindly retrying the operation. If the result is still uncertain, temporarily avoid reusing the related source path and destination name.

Simply increasing the timeout, waiting a fixed number of seconds, or relying only on the client’s own database lock cannot guarantee that an already-sent old request will not continue executing later.

### 2. Additional context or comments

_No response_

### 3. Can you help us with this feature?

- [ ] I am interested in contributing to this feature.

Contributor guide

Open the contributing guide

Research direction

Start in service/explorer/file.go to trace the REST move and rename APIs, then read pkg/filemanager/fs/dbfs/manage.go for source resolution, locking, and mutation behavior. Compare the existing flow with the requested source identity preconditions and idempotency requirements. Done means the API rejects changed source identities and handles repeated operations without affecting a different file, with behavior covered by appropriate tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.