-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Add copy_file_range syscall for Linux #2592
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
Conversation
std/os/linux.zig
Outdated
@@ -259,6 +259,10 @@ pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { | |||
return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); | |||
} | |||
|
|||
pub fn copy_file_range(fd_in: i32, off_in: *allowzero isize, fd_out: i32, off_out: *allowzero isize, len: usize, flags: u32) usize { |
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.
Why allowzero
? are you looking for ?*isize
to make it optional?
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.
Thank you @daurnimator, that is exactly what I was looking for.
std/os/linux.zig
Outdated
@@ -259,6 +259,10 @@ pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { | |||
return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); | |||
} | |||
|
|||
pub fn copy_file_range(fd_in: i32, off_in: ?*isize, fd_out: i32, off_out: ?*isize, len: usize, flags: u32) usize { |
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.
The use of isize
for the offsets is wrong, the kernel expects a loff_t
(a unsigned long
).
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.
note that unsigned long
in linux-land is equivalent to usize
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.
note that unsigned long in linux-land is equivalent to usize
Have you ever heard of the so-called 32bit systems?
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.
you mean like i386? I'm pretty sure sizeof(long) is 4 in that case, right? or is this logic incorrect:
Line 873 in 5954d52
return target_arch_pointer_bit_width(target->arch); |
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.
Oh well, I meant off_in
and off_out
and not isize
heh. The kernel definition indeed suggests that the size is a size_t
but the offsets are loff_t
(defined here) and are unsigned long long
. Again, I've misquoted the source code.
Sorry about that, I shouldn't write anything before my morning coffee.
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.
Yes, this is a off_t, so it is always 64-bit (except with legacy syscalls).
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.
If the kernel definition is unsigned long long
then off_in
and off_out
should be *u64
.
A test is needed (in It should also be used in |
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.
We don't have a 32-bit tier-one/tier-two architecture (we need one, for precisely this reason) so you can't really test this, but this patch is broken on 32-bit Linux, because the 64-bit arguments mean that this puts arguments in the wrong registers on 32-bit arches.
Also, it would be nice if you updated os/file.zig
's copyFile
to use this syscall (on Linux).
An ideal test for this would probably use memfd_create(), but alas, we don't have that syscall either..
std/os/linux.zig
Outdated
@@ -259,6 +259,10 @@ pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { | |||
return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); | |||
} | |||
|
|||
pub fn copy_file_range(fd_in: i32, off_in: ?*isize, fd_out: i32, off_out: ?*isize, len: usize, flags: u32) usize { |
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.
Yes, this is a off_t, so it is always 64-bit (except with legacy syscalls).
|
Thank you everyone for your feedback and help on this. I'm going to try to understand the signed/unsigned and 32-bit/64-bit situation better and prepare a more complete patch with tests and standard library integration. |
So I found out that sometimes copy_file_range fails because the filesystem does not support it, so you always need a backup code path. Maybe the syscall itsself should have compat code...that is how glibc does it. |
I’m closing this pull request because this system call had more implications than I had expected and I can’t commit to working through them all at the moment. I have updated the function signature to use Here is a summary of what is needed for the next person who wants to attempt working on this:
|
Thank you for your clear write-up. |
Here is the code I used to test this:
Closes #2563.