8000 Fix: resolve issue #4478 by using a temporary file for non-append writes by onporat · Pull Request #4624 · distribution/distribution · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fix: resolve issue #4478 by using a temporary file for non-append writes #4624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

onporat
Copy link
@onporat onporat commented Apr 16, 2025

To address the issue where empty files are created when the write process is interrupted, the solution involves writing to a temporary file first and then atomically renaming it to the target file. This ensures that the target file is only updated if the write completes successfully, preventing empty or partially written files.

Explanation:

  1. Temporary File Creation: The content is first written to a temporary file (appending .tmp to the original path). This ensures that the original file remains intact until the write is complete.

  2. Write to Temporary File: Using the existing Writer with truncation (false), the content is written to the temporary file. If the write fails, the temporary file is closed and deleted.

  3. Commit and Rename: After successfully writing to the temporary file, it is committed. Then, the temporary file is atomically renamed to the target path using Move, which is handled by the filesystem's rename operation (atomic on most systems).

  4. Cleanup on Failure: If any step fails, the temporary file 8000 is cleaned up to avoid leaving orphaned files.

@milosgajdos
Copy link
Member

Please sign your DCO @onporat

…n-append writes

To address the issue where a failed write operation results in an empty file, we can use a temporary file for non-append writes. This ensures that the original file is only replaced once the new content is fully written and committed.

**Key Changes:**

1. **Temporary File Handling:**
   - For non-append writes, a temporary file is created in the same directory as the target file.
   - All write operations are performed on the temporary file first.

2. **Atomic Commit:**
   - The temporary file is only renamed to the target path during `Commit()`, ensuring atomic replacement.
   - If `Commit()` fails, the temporary file is cleaned up.

3. **Error Handling:**
   - `Cancel()` properly removes temporary files if the operation is aborted.
   - `Close()` is made idempotent to handle multiple calls safely.

4. **Data Integrity:**
   - Directory sync after rename ensures metadata persistence.
   - Proper file flushing and syncing before rename operations.

Signed-off-by: Oded Porat <onporat@gmail.com>
@onporat
Copy link
Author
onporat commented Apr 17, 2025

@milosgajdos Could you please approve the workflows? Thank you.

…cess is interrupted, the solution involves writing to a temporary file first and then atomically renaming it to the target file. This ensures that the target file is only updated if the write completes successfully, preventing empty or partially written files.

**Explanation:**

1. **Temporary File Creation:** The content is first written to a temporary file (appending `.tmp` to the original path). This ensures that the original file remains intact until the write is complete.

2. **Write to Temporary File:** Using the existing `Writer` with truncation (`false`), the content is written to the temporary file. If the write fails, the temporary file is closed and deleted.

3. **Commit and Rename:** After successfully writing to the temporary file, it is committed. Then, the temporary file is atomically renamed to the target path using `Move`, which is handled by the filesystem's rename operation (atomic on most systems).

4. **Cleanup on Failure:** If any step fails, the temporary file is cleaned up to avoid leaving orphaned files.

Signed-off-by: Oded Porat <onporat@gmail.com>
@onporat
Copy link
Author
onporat commented Apr 23, 2025

@milosgajdos I updated the PR, could you please approve the workflows? Thank you.

@onporat
Copy link
Author
onporat commented Apr 29, 2025

@milosgajdos Hello, could you please let me know whom I should ask to review this PR? Thanks.

@milosgajdos milosgajdos requested a review from Copilot May 3, 2025 16:35
Copy link
@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request addresses issue #4478 by introducing a safer file write mechanism that writes data to a temporary file before atomically replacing the target file to prevent empty or partially written files.

  • Use a temporary file (subPath + ".tmp") for data writes
  • Atomically rename the temporary file to the final target upon successful write
  • Clean up the temporary file if any write or rename errors occur

Append a UUID to ensure uniqueness
Join delete error

Signed-off-by: Oded Porat <onporat@gmail.com>
@milosgajdos milosgajdos requested a review from thaJeztah May 5, 2025 02:09
Copy link
Member
@milosgajdos milosgajdos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. @thaJeztah PTAL

Comment on lines +140 to +141
// Write to a temporary file to prevent partial writes.
writer, err := d.Writer(ctx, tempPath, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything in the Writer and/or driver that's created that handles concurrency? (Mostly considering here that before this code, the destination would be a deterministic path, which (could be? need to check the implementation) used to prevent concurrent writes to the same file.

With this change, the content is written to a temporary file (no means to check concurrency), then a os.Rename()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0