Skip to content

Upload: deterministic per-filename .tmp path is unsafe for concurrent same-name uploads #3135

Description

@vpetersson

Spun off from review on #3133.

Problem

The file_asset upload endpoint (api/views/mixins.py) stages each upload at a deterministic temp path — uuid5(NAMESPACE_URL, filename).hex + '.tmp'. Two uploads of files with the same name therefore share one .tmp, with no per-upload isolation. Consequences:

  • Concurrency race: two chunk requests for the same filename can both observe "file doesn't exist" and open wb, clobbering each other's bytes. Even with a create-exclusive (x) guard on the first write, two different concurrent uploads of the same name would still interleave into one file and produce a corrupted asset.
  • Cross-attempt bleed: a stale .tmp from an earlier, interrupted upload of the same name is reused by a later one. fix(server): handle disk-full uploads gracefully #3133 mitigates the trailing-bytes case (truncate-to-total on the final chunk), but the underlying shared-path design is the root cause.

Why this is a separate issue

Making concurrent same-name uploads correct needs per-upload session isolation — e.g. a per-request random temp name committed to the final path only on completion, or an explicit upload-session id. That's a change to the resumable-upload protocol (today, resume relies on the deterministic path to find the in-progress file), so it's a deliberate design decision rather than something to fold into the ENOSPC fix in #3133.

Suggested direction

  • Mint a per-request temp name (random UUID, not derived from the filename); return an opaque upload id the client echoes on subsequent chunks.
  • Commit (rename into the asset dir) only once all bytes 0..total-1 are present.
  • Reap orphaned temp files on a timer (a stale-.tmp sweep).

Low real-world impact today (the single-file browser uploader sends chunks sequentially and same-name concurrent uploads are rare), so not urgent — but worth fixing properly rather than with a partial guard that implies more safety than it provides.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions