1 /* Emulation of copy_file_range.
2 Copyright (C) 2017-2018 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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 <http://www.gnu.org/licenses/>. */
19 /* The following macros should be defined before including this
22 COPY_FILE_RANGE_DECL Declaration specifiers for the function below.
23 COPY_FILE_RANGE Name of the function to define. */
30 #include <sys/types.h>
35 COPY_FILE_RANGE (int infd
, __off64_t
*pinoff
,
36 int outfd
, __off64_t
*poutoff
,
37 size_t length
, unsigned int flags
)
47 struct stat64 outstat
;
48 if (fstat64 (infd
, &instat
) != 0 || fstat64 (outfd
, &outstat
) != 0)
50 if (S_ISDIR (instat
.st_mode
) || S_ISDIR (outstat
.st_mode
))
55 if (!S_ISREG (instat
.st_mode
) || !S_ISREG (outstat
.st_mode
))
57 /* We need a regular input file so that the we can seek
58 backwards in case of a write failure. */
62 if (instat
.st_dev
!= outstat
.st_dev
)
64 /* Cross-device copies are not supported. */
70 /* The output descriptor must not have O_APPEND set. */
72 int flags
= __fcntl (outfd
, F_GETFL
);
80 /* Avoid an overflow in the result. */
81 if (length
> SSIZE_MAX
)
84 /* Main copying loop. The buffer size is arbitrary and is a
85 trade-off between stack size consumption, cache usage, and
86 amortization of system call overhead. */
91 size_t to_read
= length
;
92 if (to_read
> sizeof (buf
))
93 to_read
= sizeof (buf
);
95 /* Fill the buffer. */
98 read_count
= read (infd
, buf
, to_read
);
100 read_count
= __libc_pread64 (infd
, buf
, to_read
, *pinoff
);
102 /* End of file reached prematurely. */
107 /* Report the number of bytes copied so far. */
112 *pinoff
+= read_count
;
114 /* Write the buffer part which was read to the destination. */
115 char *end
= buf
+ read_count
;
116 for (char *p
= buf
; p
< end
; )
120 write_count
= write (outfd
, p
, end
- p
);
122 write_count
= __libc_pwrite64 (outfd
, p
, end
- p
, *poutoff
);
125 /* Adjust the input read position to match what we have
126 written, so that the caller can pick up after the
128 size_t written
= p
- buf
;
129 /* NB: This needs to be signed so that we can form the
130 negative value below. */
131 ssize_t overread
= read_count
- written
;
136 /* We are on an error recovery path, so we
137 cannot deal with failure here. */
138 int save_errno
= errno
;
139 (void) __libc_lseek64 (infd
, -overread
, SEEK_CUR
);
140 __set_errno (save_errno
);
143 else /* pinoff != NULL */
146 if (copied
+ written
> 0)
147 /* Report the number of bytes copied so far. */
148 return copied
+ written
;
153 *poutoff
+= write_count
;
156 copied
+= read_count
;
157 length
-= read_count
;