packaging: add --disable-experimental-malloc
[platform/upstream/glibc.git] / support / support_copy_file_range.c
1 /* Simplified copy_file_range with cross-device copy.
2    Copyright (C) 2018-2023 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <limits.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <support/support.h>
27
28 ssize_t
29 support_copy_file_range (int infd, __off64_t *pinoff,
30                          int outfd, __off64_t *poutoff,
31                          size_t length, unsigned int flags)
32 {
33   if (flags != 0)
34     {
35       errno = EINVAL;
36       return -1;
37     }
38
39   struct stat64 instat;
40   struct stat64 outstat;
41   if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
42     return -1;
43   if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
44     {
45       errno = EISDIR;
46       return -1;
47     }
48   if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode))
49     {
50       /* We need a regular input file so that the we can seek
51          backwards in case of a write failure.  */
52       errno = EINVAL;
53       return -1;
54     }
55
56   /* The output descriptor must not have O_APPEND set.  */
57   if (fcntl (outfd, F_GETFL) & O_APPEND)
58     {
59       errno = EBADF;
60       return -1;
61     }
62
63   /* Avoid an overflow in the result.  */
64   if (length > SSIZE_MAX)
65     length = SSIZE_MAX;
66
67   /* Main copying loop.  The buffer size is arbitrary and is a
68      trade-off between stack size consumption, cache usage, and
69      amortization of system call overhead.  */
70   size_t copied = 0;
71   char buf[8192];
72   while (length > 0)
73     {
74       size_t to_read = length;
75       if (to_read > sizeof (buf))
76         to_read = sizeof (buf);
77
78       /* Fill the buffer.  */
79       ssize_t read_count;
80       if (pinoff == NULL)
81         read_count = read (infd, buf, to_read);
82       else
83         read_count = pread64 (infd, buf, to_read, *pinoff);
84       if (read_count == 0)
85         /* End of file reached prematurely.  */
86         return copied;
87       if (read_count < 0)
88         {
89           if (copied > 0)
90             /* Report the number of bytes copied so far.  */
91             return copied;
92           return -1;
93         }
94       if (pinoff != NULL)
95         *pinoff += read_count;
96
97       /* Write the buffer part which was read to the destination.  */
98       char *end = buf + read_count;
99       for (char *p = buf; p < end; )
100         {
101           ssize_t write_count;
102           if (poutoff == NULL)
103             write_count = write (outfd, p, end - p);
104           else
105             write_count = pwrite64 (outfd, p, end - p, *poutoff);
106           if (write_count < 0)
107             {
108               /* Adjust the input read position to match what we have
109                  written, so that the caller can pick up after the
110                  error.  */
111               size_t written = p - buf;
112               /* NB: This needs to be signed so that we can form the
113                  negative value below.  */
114               ssize_t overread = read_count - written;
115               if (pinoff == NULL)
116                 {
117                   if (overread > 0)
118                     {
119                       /* We are on an error recovery path, so we
120                          cannot deal with failure here.  */
121                       int save_errno = errno;
122                       (void) lseek64 (infd, -overread, SEEK_CUR);
123                       errno = save_errno;
124                     }
125                 }
126               else /* pinoff != NULL */
127                 *pinoff -= overread;
128
129               if (copied + written > 0)
130                 /* Report the number of bytes copied so far.  */
131                 return copied + written;
132               return -1;
133             }
134           p += write_count;
135           if (poutoff != NULL)
136             *poutoff += write_count;
137         } /* Write loop.  */
138
139       copied += read_count;
140       length -= read_count;
141     }
142   return copied;
143 }