Subject: bug#63850: cp fails for files > 2 GB if copy offload is unsupported

From: Pádraig Brady <[email-masked]>
Date: Sat, 3 Jun 2023 06:25:36 +0100

On 03/06/2023 02:02, Mike Gilbert wrote:
> On Fri, Jun 02, 2023 at 05:31:50PM +0100, Pádraig Brady wrote:
>> I'm not sure it was working correctly before 9.3 either.
>> Before 9.3 we would have switched from copy_file_range() to read()/write()
>> upon receiving the EINVAL, which might have worked, but also I'm not sure
>> the file offsets would be correct in that case. Could you show the output with:
>>
>> diff --git a/src/copy.c b/src/copy.c
>> index 0dd059d2e..35c54b905 100644
>> --- a/src/copy.c
>> +++ b/src/copy.c
>> @@ -363,7 +363,16 @@ sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
>>                   edge case where the file is made immutable after creating,
>>                   in which case the (more accurate) error is still shown.  */
>>                if (*total_n_read == 0 && is_CLONENOTSUP (errno))
>> -              break;
>> +              {
>> +                if (*total_n_read != 0)
>> +                  {
>> +                    off_t clone_read_offset = lseek (src_fd, 0, SEEK_CUR);
>> +                    off_t clone_write_offset = lseek (dest_fd, 0, SEEK_CUR);
>> +                    printf ("switching to standard copy at :%"PRIdMAX" read=%"PRIdMAX" write=%"PRIdMAX"\n",
>> +                            *total_n_read, clone_read_offset, clone_write_offset);
>> +                  }
>> +                break;
>> +              }
>>
>>                /* ENOENT was seen sometimes across CIFS shares, resulting in
>>                   no data being copied, but subsequent standard copies succeed.  */
> 
> I don't think this patch will do anything useful: *total_n_read cannot be 0
> and not 0 simultaneously, so the new block of code will never be
> executed. Maybe you meant to insert this block somewhere else?

Sorry was rushing.

Yes I meant to remove the first total_n_read check
as done in the following.

In any case an external check would be as useful.
I suppose we would have heard at this stage
but it would be good to have verification with md5sum or similar
on the source and destination files that the copy worked fine
on such a file >2G.

cheers,
Pádraig.

diff --git a/src/copy.c b/src/copy.c
index 0dd059d2e..296707c39 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -362,8 +362,17 @@ sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
                 also occur for immutable files, but that would only be in the
                 edge case where the file is made immutable after creating,
                 in which case the (more accurate) error is still shown.  */
-            if (*total_n_read == 0 && is_CLONENOTSUP (errno))
-              break;
+            if (is_CLONENOTSUP (errno))
+              {
+                if (*total_n_read != 0)
+                  {
+                    off_t clone_read_offset = lseek (src_fd, 0, SEEK_CUR);
+                    off_t clone_write_offset = lseek (dest_fd, 0, SEEK_CUR);
+                    printf ("switching to standard copy at :%"PRIdMAX" read=%"PRIdMAX" write=%"PRIdMAX"\n",
+                            *total_n_read, clone_read_offset, clone_write_offset);
+                  }
+                break;
+              }

              /* ENOENT was seen sometimes across CIFS shares, resulting in
                 no data being copied, but subsequent standard copies succeed.  */