GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-81340: Use `copy_file_range` in `shutil.copyfile` copy functions by illia-v · Pull Request #93152 · python/cpython

Conversation

@illia-v

@illia-v illia-v commented May 23, 2022 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

This makes shutil.copyfile prefer the copy_file_range system call on Linux.

copy_file_range gives filesystems an opportunity to implement the use of reflinks or server-side copy, but we cannot determine whether any of them are implemented. Therefore, I added an allow_reflink argument in case anyone wants to disable copy-on-write.

GNU Coreutils enables copy-on-write by default, that is why I set allow_reflink to true by default (unlike @vstinner proposed in #81338).

Note, there is a known copy_file_range bug for copying from special filesystems like procfs and sysfs. It seems not to be fixed yet, so we have to check for a silent copy_file_range fail as Coreutils and Go do.

  • Issue: Use copy_file_range() in shutil.copyfile() (server-side copy) #81340

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

danielzgtg, thesamesam, and nanonyme reacted with thumbs up emoji
All reactions
  • 👍 3 reactions
@bedevere-bot bedevere-bot added the awaiting review label May 23, 2022
illia-v added 2 commits May 23, 2022 22:25
@illia-v
Move determining a block size for copying to a separate function
985159a
@illia-v
Add copy-on-write support to shutil
e4fa33b
@illia-v
illia-v force-pushed the fix-issue-81340 branch from 97956b4 to e4fa33b Compare May 23, 2022 19:25
@illia-v

illia-v commented May 23, 2022

Copy link
Copy Markdown
Contributor Author

@giampaolo I saw that you are marked as a code owner for shutil, but GitHub did not request your review for some reason.

cpython/.github/CODEOWNERS

Line 138 in 2176898

**/*shutil @giampaolo

Would you like to take a look?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@giampaolo

giampaolo commented May 23, 2022

Copy link
Copy Markdown
Contributor

@illia-v to my understanding copy_file_range() on Linux enables the so called "server side copy", not "reflink" / "copy on write". I posted a patch some time ago, but I should update it, see:
#81340

As for reflink / CoW copy, I also submitted a patch some time ago, but I wanted to add Windows support before merging it.
#81338

To my understanding reflink / CoW copy on Linux is achieved by using FICLONE, not copy_file_range().

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@illia-v

illia-v commented May 23, 2022

Copy link
Copy Markdown
Contributor Author

@giampaolo copy_file_range does try cloning. Take a look at https://stackoverflow.com/a/65518879 and https://github.com/torvalds/linux/blob/1e57930e9f4083ad5854ab6eadffe790a8167fb4/fs/read_write.c#L1491-L1507.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented May 23, 2022

Copy link
Copy Markdown
Member

cc @pablogsal

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

vstinner
vstinner reviewed May 23, 2022
View reviewed changes
Comment thread Doc/library/shutil.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/library/shutil.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/shutil.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/shutil.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/library/shutil.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented May 23, 2022

Copy link
Copy Markdown
Member

@giampaolo copy_file_range does try cloning. Take a look at https://stackoverflow.com/a/65518879 and https://github.com/torvalds/linux/blob/1e57930e9f4083ad5854ab6eadffe790a8167fb4/fs/read_write.c#L1491-L1507.

Would you mind to create a first PR to mention it in copy_file_range() documentation? https://docs.python.org/dev/library/os.html#os.copy_file_range

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented May 23, 2022

Copy link
Copy Markdown
Member

copyfile(src, dst, *, follow_symlinks=True, allow_reflink=True)

It terms of API, I would prefer to change the parameter name to just reflink=True. IMO it's clear enough that reflink=True will not use CoW if it's not supported or cannot be used. Python has a long habit of trying to use the most efficient method, but falls back on the most compatible code if it doesn't work.

For example, socket.sendfile() tries to use sendfile() but falls back on send() if sendfile() doesn't work or is not available.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented May 23, 2022

Copy link
Copy Markdown
Member

The Unix cp command has 3 modes for --reflink=WHEN:

  • always: "perform a lightweight copy (...) if this is not possible the copy fails"
  • never (False in your PR)
  • auto (True in your PR)

Here you only propose 2 modes. Do we need to implement the 3rd "always" mode?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

vstinner
vstinner reviewed May 23, 2022
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Your PR is quite big. Would it be possible to extract the tests refactoring and start with a first PR just for that?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner

vstinner commented May 23, 2022

Copy link
Copy Markdown
Member

I see a lot of confusion between which function/syscall is used, server-side copy, and copy of write. There are expectections, hopes, and ... disillusions :-) IMO the doc should be completed to better explain differences between server-side copy and copy-on-write, and attempt to document when and how one method is attempted, when it can fail, etc.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@illia-v

illia-v commented May 23, 2022 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

I am glad the issue has received this attention.

@vstinner thanks for valid points, especially one about the three modes. I think it will be nice to support all of them in Python too.

But copy_file_range has no way to specify that reflink is required.

  • cp --reflink=always command seems to use ioctl(dest_fd, FICLONE, src_fd) (1 & 2) as the only copying method or fail if the call is not possible;
  • cp --reflink=auto tries ioctl(dest_fd, FICLONE, src_fd) firstly, then copy_file_range, then others;
  • cp --reflink=never skips both ioctl(dest_fd, FICLONE, src_fd) and copy_file_range.

I will create a PR to extend os.copy_file_range documentation. Then I will need to experiment a bit with FICLONE.

Do you think it is possible to launch the three-mode reflink functionality for Linux firstly and add support for other OSes in later pull requests?
I am most interested in supporting this on Linux, but I may consider writing some code for other platforms later.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented May 23, 2022

Copy link
Copy Markdown
Member

Do you think it is possible to launch the three-mode reflink functionality for Linux firstly and add support for other OSes in later pull requests?

If possible, the API should be the same and should be usable on all platform. For example, "as an user, I would like to call shutil.copyfile() with the same arguments on all platforms and get a similar behavior on all platforms". For example, the API should attempt to use server-side copy and/or use Copy-on-Write, but silently switch to regular read+write copy if no modern copy function is available on the OS or on the source/destination filesystems.

For the os module, it's fine to have different functions depending on the OS. But the shutil module is more a high-level module with a (mostly) portable API.

illia-v reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

illia-v and others added 2 commits May 24, 2022 20:02
@illia-v @vstinner
Update comments in _determine_linux_fastcopy_blocksize
47b0834
Co-authored-by: Victor Stinner <[email protected]>
@illia-v
Update docs to link to copy_file_range as a Python function
474859c
@illia-v illia-v mentioned this pull request May 24, 2022
Update os.copy_file_range documentation #93180
Closed
@illia-v

illia-v commented May 24, 2022

Copy link
Copy Markdown
Contributor Author

@giampaolo copy_file_range does try cloning. Take a look at https://stackoverflow.com/a/65518879 and https://github.com/torvalds/linux/blob/1e57930e9f4083ad5854ab6eadffe790a8167fb4/fs/read_write.c#L1491-L1507.

Would you mind to create a first PR to mention it in copy_file_range() documentation? https://docs.python.org/dev/library/os.html#os.copy_file_range

Done in #93182, please review it.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@illia-v
illia-v marked this pull request as draft May 26, 2022 10:21
@illia-v

illia-v commented Jun 3, 2022

Copy link
Copy Markdown
Contributor Author

I am swaying away from the idea in favor of a separate reflink function #81338 (comment).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@thesamesam

thesamesam commented Jul 6, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

I am swaying away from the idea in favor of a separate reflink function #81338 (comment).

The problem with that is you don't get the automatic free win, which is contrary to what others are doing. e.g. GNOME's glib (not glibc), KDE's kio, and even GCC's libstdc++ upgrade automatically. GCC 14 will automatically use copy_file_range if available for filesystem::copy_file.

There doesn't appear to be much precedent for carving this out into its own opt-in functionality?

EDIT: It's been pointed out to me that Emacs, of all things, unconditionally uses copy_file_range if the system supports it for copy-file.

Jturnerusa reacted with thumbs up emoji Jturnerusa reacted with eyes emoji
All reactions
  • 👍 1 reaction
  • 👀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@eskultety eskultety mentioned this pull request Mar 7, 2024
Replace reflink dependency with an implementation using Python stdlib primitives hermetoproject/hermeto#483
Closed
@thesamesam thesamesam mentioned this pull request May 16, 2024
[WIP] GH-73991: Add os.copy() and friends #119079
Closed
@barneygale

barneygale commented Jun 3, 2024

Copy link
Copy Markdown
Contributor

@illia-v are you still interested in working on this? I wonder if we even need the allow_reflinks argument - perhaps we could enable use of copy_file_range() (where available) without adding any new arguments?

thesamesam and skshetry reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@illia-v

illia-v commented Jun 3, 2024

Copy link
Copy Markdown
Contributor Author

@barneygale yes, I am interested and can revise the changes this week

barneygale and thesamesam reacted with heart emoji
All reactions
  • ❤️ 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@barneygale barneygale mentioned this pull request Jun 3, 2024
GH-73991: Add pathlib.Path.copy() #119058
Merged
illia-v added 2 commits June 5, 2024 18:38
@illia-v
Merge branch 'main' into fix-issue-81340
1846895
@illia-v
Drop the allow_reflink argument
41d48d9
@illia-v
illia-v marked this pull request as ready for review June 5, 2024 16:10
@illia-v

illia-v commented Jun 5, 2024

Copy link
Copy Markdown
Contributor Author

@barneygale I dropped allow_reflinks, please take a look

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

barneygale
barneygale reviewed Jun 5, 2024
View reviewed changes
Comment thread Doc/library/shutil.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/shutil.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@barneygale
barneygale requested a review from vstinner June 5, 2024 19:16
@illia-v
Remove duplicate change entries from docs
e8feaca
@illia-v
illia-v requested a review from barneygale June 5, 2024 19:32
barneygale
barneygale approved these changes Jun 6, 2024
View reviewed changes

@barneygale barneygale left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

LGTM

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting review labels Jun 6, 2024
@barneygale
barneygale requested a review from zooba June 6, 2024 20:39
@giampaolo

giampaolo commented Jun 7, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

As I've previously stated in #81338 (comment), personally I consider copy and CoW / clone 2 distinct operations. E.g. one may want to occupy disk space right now instead of later. For this reason I think shutil.copyfile should give the possibility to disable this behavior. cp command defaults to --reflink=auto, but it allows to disable CoW with --reflink=never. The problem with copy_file_range() is that it doesn't allow you to opt-out from CoW, which is why back in 2020 I was more keen on providing a separate shutil.reflink function using FICLONE. But then again, copy_file_range() does provide fast server-side copy in some circumstances, which is something you always want. What a mess...

It appears to me that the tool which solved all these controversies is cp via --reflink=auto/always/never. I have the feeling that shutil.copyfile (and possibly also pathlib.Path.copy / #73991) should try to follow its lead and replicate that. E.g. given the signature:

def copyfile(..., reflink=None)

We may do:

  • reflink=None == cp --reflink=auto == use copy_file_range() - on failure use sendfile() or read/write
  • reflink=True == cp --reflink=always == use FICLONE and fail on error
  • reflink=False == cp --reflink=never == use sendfile() or read/write

But if we do this, I think it would make sense to expose shutil.reflink() first, which would also support macOS and Windows.

My 2 cents and sorry for not replying earlier on this topic.

barneygale reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@barneygale

barneygale commented Jun 7, 2024

Copy link
Copy Markdown
Contributor

There are some compelling arguments and examples from other languages in #81338. Personally I'm persuaded that we should enable it (without an opt-out), rather than second-guessing what the OS provides and what other language runtimes are doing. We have plenty of time til 3.14 ships if we need to back it out. But this is by no means my area of expertise, so I'm happy to be overruled.

thesamesam, zooba, illia-v, and nanonyme reacted with thumbs up emoji
All reactions
  • 👍 4 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

barneygale added a commit that referenced this pull request Jun 14, 2024
@barneygale @eryksun
GH-73991: Add pathlib.Path.copy() (#119058)
7c38097
Add a `Path.copy()` method that copies the content of one file to another.

This method is similar to `shutil.copyfile()` but differs in the following ways:

- Uses `fcntl.FICLONE` where available (see GH-81338)
- Uses `os.copy_file_range` where available (see GH-81340)
- Uses `_winapi.CopyFile2` where available, even though this copies more metadata than the other implementations. This makes `WindowsPath.copy()` more similar to `shutil.copy2()`.

The method is presently _less_ specified than the `shutil` functions to allow OS-specific optimizations that might copy more or less metadata.

Incorporates code from GH-81338 and GH-93152.

Co-authored-by: Eryk Sun <[email protected]>
@zooba

zooba commented Jun 17, 2024

Copy link
Copy Markdown
Member

Personally I'm persuaded that we should enable it (without an opt-out), rather than second-guessing what the OS provides and what other language runtimes are doing.

This seems alright to me, too. We're expecting/hoping Windows will start doing transparent CoW when doing file copies, so that ought to give an idea pretty quickly if there's a genuine need to avoid it, or if we'd be adding the opt-out without justification.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

mrahtz pushed a commit to mrahtz/cpython that referenced this pull request Jun 30, 2024
@barneygale @eryksun @mrahtz
pythonGH-73991: Add pathlib.Path.copy() (python#119058)
7e37fbe
Add a `Path.copy()` method that copies the content of one file to another.

This method is similar to `shutil.copyfile()` but differs in the following ways:

- Uses `fcntl.FICLONE` where available (see pythonGH-81338)
- Uses `os.copy_file_range` where available (see pythonGH-81340)
- Uses `_winapi.CopyFile2` where available, even though this copies more metadata than the other implementations. This makes `WindowsPath.copy()` more similar to `shutil.copy2()`.

The method is presently _less_ specified than the `shutil` functions to allow OS-specific optimizations that might copy more or less metadata.

Incorporates code from pythonGH-81338 and pythonGH-93152.

Co-authored-by: Eryk Sun <[email protected]>
estyxx pushed a commit to estyxx/cpython that referenced this pull request Jul 17, 2024
@barneygale @eryksun @estyxx
pythonGH-73991: Add pathlib.Path.copy() (python#119058)
595b213
Add a `Path.copy()` method that copies the content of one file to another.

This method is similar to `shutil.copyfile()` but differs in the following ways:

- Uses `fcntl.FICLONE` where available (see pythonGH-81338)
- Uses `os.copy_file_range` where available (see pythonGH-81340)
- Uses `_winapi.CopyFile2` where available, even though this copies more metadata than the other implementations. This makes `WindowsPath.copy()` more similar to `shutil.copy2()`.

The method is presently _less_ specified than the `shutil` functions to allow OS-specific optimizations that might copy more or less metadata.

Incorporates code from pythonGH-81338 and pythonGH-93152.

Co-authored-by: Eryk Sun <[email protected]>
@illia-v

illia-v commented Jul 31, 2024

Copy link
Copy Markdown
Contributor Author

@barneygale based on your experience of having #119058 present in main for more than a month, can we merge this too?

nanonyme reacted with rocket emoji
All reactions
  • 🚀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@barneygale

barneygale commented Aug 4, 2024

Copy link
Copy Markdown
Contributor

There have been no releases from main in that time, and the first alpha of 3.14 isn't due until October, so I wouldn't read too much into the lack of pathlib.Path.copy() bug reports.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@illia-v

illia-v commented Aug 5, 2024

Copy link
Copy Markdown
Contributor Author

@barneygale thank you for the response. Can we risk merging this PR before the alpha to be able to discover issues (if any) in early stages of 3.14?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cburroughs

cburroughs commented Dec 5, 2024

Copy link
Copy Markdown
Contributor

Sorry I'm not familiar with the details of CPython development. I see this PR has the awaiting merge, is it waiting on anything else before said merge?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zooba

zooba commented Dec 28, 2024

Copy link
Copy Markdown
Member

The merge conflicts need addressing, and it sounds like we want someone knowledgeable about the Linux API to chime in on whether this is using it properly.

But I agree, we ought to get it in asap, now that 3.14 is shipping.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@illia-v
Merge branch 'main' into fix-issue-81340
dc07a54
@illia-v

illia-v commented Dec 29, 2024

Copy link
Copy Markdown
Contributor Author

The merge conflicts need addressing, and it sounds like we want someone knowledgeable about the Linux API to chime in on whether this is using it properly.

I resolved the conflicts, thanks
The PR has an approval from @barneygale who integrated copy_file_range in pathlib #119058

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zooba
Merge branch 'main' into fix-issue-81340
cb1dae8
@zooba
zooba merged commit a33dcb9 into python:main Feb 3, 2025
@bedevere-app bedevere-app Bot removed the awaiting merge label Feb 3, 2025
@zooba

zooba commented Feb 3, 2025

Copy link
Copy Markdown
Member

Apologies for being slow. Despite the lack of an explicit endorsement from someone (else) claiming to understand the API well, I think the best option here is to have it in the 3.14 alphas.

I likely won't get tagged on any issues that are reported, but anyone who is should feel free to modify or revert the code if needed to fix users. I'm merging as a point of process, not because this is "my" code - I don't need to be consulted on changes.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

srinivasreddy pushed a commit to srinivasreddy/cpython that referenced this pull request Feb 7, 2025
@illia-v @srinivasreddy
pythongh-81340: Use copy_file_range in shutil.copyfile copy functions (…
e43fa23
…pythonGH-93152)

This allows the underlying file system an opportunity to optimise or avoid the actual copy.
@9001 9001 mentioned this pull request Jul 28, 2025
Use BTRFS reflink for safer deduplication 9001/copyparty#201
Closed
@cburroughs cburroughs mentioned this pull request Oct 22, 2025
optimize package installation for space and speed by using copy-on-write file clones ("reflinks") and storing wheel cache unpacked pypa/pip#11092
Open
1 task done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@barneygale barneygale barneygale approved these changes
@vstinner vstinner Awaiting requested review from vstinner
@zooba zooba Awaiting requested review from zooba

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

8 participants

@illia-v @giampaolo @vstinner @thesamesam @barneygale @zooba @cburroughs @bedevere-bot
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.