Bug #18388
closedIO.copy_stream incompatibility between Ruby 2 and Ruby 3
Description
Put test.rb, src and dst in the same directory:
test.rb file:
src = "src"
dst = "dst"
File.open(dst, "ab", 0644) do |dst|
File.open(src, 'rb', 0644) do |src|
puts "src size: #{src.size()}, dst size: #{dst.size()}"
count = IO.copy_stream(src, dst)
puts "count: #{count}"
end
end
src file (file size is 3):
dst file (file size is 1):
Run test.rb with Ruby 2.6, the content of the resulted dst is:
So the content of src is appended to dst with Ruby 2.6. In other words,
Open mode "a" is honored.
Run test.rb with Ruby 3, the content of the resulted dst is:
This behavior is different from Ruby 2.6.
The cause of the problem is IO.copy_stream uses fcopyfile function for Ruby 3.
If the following code from io.c is commented out, Ruby 3 has the same behavior
as Ruby 2.6.
#ifdef HAVE_FCOPYFILE
ret = nogvl_fcopyfile(stp);
if (ret != 0)
goto finish; /* error or success */
#endif
fcopyfile appends src to to and then truncates to to it's original size.
The following code shows this behavior:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <copyfile.h>
int main() {
copyfile_state_t state;
state = copyfile_state_alloc();
int to;
to = open("to", O_APPEND | O_WRONLY, 0644);
if (to < 0) {
perror("open to");
return 1;
}
int from;
from = open("from", O_RDONLY);
if (from < 0) {
perror("open from");
return 1;
}
int ret;
ret = fcopyfile(from, to, state, COPYFILE_DATA);
if (ret != 0) {
perror("fcopyfile");
return 1;
}
off_t copied;
ret = copyfile_state_get(state, COPYFILE_STATE_COPIED, &copied);
if (ret != 0) {
perror("copyfile_state_get");
return 1;
}
printf("copied: %lld\n", copied);
copyfile_state_free(state);
}
The following table summarizes the results after running the above code:
from |
to |
to after fcopyfile |
|---|---|---|
| 789 | 2 | 278 |
| 1 | 2 | 2 |
| 1 | 234 | 2 |
If this problem should be fixed, I am willing to do it provided with some
guidance from the community.
Here is the detailed version information:
- Ruby 2.6 version:
- Ruby 3 version
- OS: macOS 10.15.5