Remove powerpc, sparc fdim inlines (bug 22987).
[glibc.git] / io / copy_file_range-compat.c
blob4ab22cad19146ca91c58a8dfc94c2bdd09d5a907
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
20 file:
22 COPY_FILE_RANGE_DECL Declaration specifiers for the function below.
23 COPY_FILE_RANGE Name of the function to define. */
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <inttypes.h>
28 #include <limits.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <unistd.h>
33 COPY_FILE_RANGE_DECL
34 ssize_t
35 COPY_FILE_RANGE (int infd, __off64_t *pinoff,
36 int outfd, __off64_t *poutoff,
37 size_t length, unsigned int flags)
39 if (flags != 0)
41 __set_errno (EINVAL);
42 return -1;
46 struct stat64 instat;
47 struct stat64 outstat;
48 if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
49 return -1;
50 if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
52 __set_errno (EISDIR);
53 return -1;
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. */
59 __set_errno (EINVAL);
60 return -1;
62 if (instat.st_dev != outstat.st_dev)
64 /* Cross-device copies are not supported. */
65 __set_errno (EXDEV);
66 return -1;
70 /* The output descriptor must not have O_APPEND set. */
72 int flags = __fcntl (outfd, F_GETFL);
73 if (flags & O_APPEND)
75 __set_errno (EBADF);
76 return -1;
80 /* Avoid an overflow in the result. */
81 if (length > SSIZE_MAX)
82 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. */
87 size_t copied = 0;
88 char buf[8192];
89 while (length > 0)
91 size_t to_read = length;
92 if (to_read > sizeof (buf))
93 to_read = sizeof (buf);
95 /* Fill the buffer. */
96 ssize_t read_count;
97 if (pinoff == NULL)
98 read_count = read (infd, buf, to_read);
99 else
100 read_count = __libc_pread64 (infd, buf, to_read, *pinoff);
101 if (read_count == 0)
102 /* End of file reached prematurely. */
103 return copied;
104 if (read_count < 0)
106 if (copied > 0)
107 /* Report the number of bytes copied so far. */
108 return copied;
109 return -1;
111 if (pinoff != NULL)
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; )
118 ssize_t write_count;
119 if (poutoff == NULL)
120 write_count = write (outfd, p, end - p);
121 else
122 write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
123 if (write_count < 0)
125 /* Adjust the input read position to match what we have
126 written, so that the caller can pick up after the
127 error. */
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;
132 if (pinoff == NULL)
134 if (overread > 0)
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 */
144 *pinoff -= overread;
146 if (copied + written > 0)
147 /* Report the number of bytes copied so far. */
148 return copied + written;
149 return -1;
151 p += write_count;
152 if (poutoff != NULL)
153 *poutoff += write_count;
154 } /* Write loop. */
156 copied += read_count;
157 length -= read_count;
159 return copied;