exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / copy-file.c
blobd2588b82716dd4abb38af35214513425e6acfecf
1 /* Copying of files.
2 Copyright (C) 2001-2003, 2006-2007, 2009-2024 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program 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
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
19 #include <config.h>
21 /* Specification. */
22 #include "copy-file.h"
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
31 #include <error.h>
32 #include "ignore-value.h"
33 #include "safe-read.h"
34 #include "full-write.h"
35 #include "stat-time.h"
36 #include "utimens.h"
37 #include "acl.h"
38 #include "binary-io.h"
39 #include "quote.h"
40 #include "gettext.h"
42 #define _(str) gettext (str)
44 enum { IO_SIZE = 32 * 1024 };
46 int
47 qcopy_file_preserving (const char *src_filename, const char *dest_filename)
49 int err = 0;
50 int src_fd;
51 struct stat statbuf;
52 int mode;
53 int dest_fd;
55 src_fd = open (src_filename, O_RDONLY | O_BINARY | O_CLOEXEC);
56 if (src_fd < 0)
57 return GL_COPY_ERR_OPEN_READ;
58 if (fstat (src_fd, &statbuf) < 0)
60 err = GL_COPY_ERR_OPEN_READ;
61 goto error_src;
64 mode = statbuf.st_mode & 07777;
65 off_t inbytes = S_ISREG (statbuf.st_mode) ? statbuf.st_size : -1;
66 bool empty_regular_file = inbytes == 0;
68 dest_fd = open (dest_filename,
69 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC,
70 0600);
71 if (dest_fd < 0)
73 err = GL_COPY_ERR_OPEN_BACKUP_WRITE;
74 goto error_src;
77 /* Copy the file contents. FIXME: Do not copy holes. */
78 while (0 < inbytes)
80 size_t copy_max = -1;
81 copy_max -= copy_max % IO_SIZE;
82 size_t len = inbytes < copy_max ? inbytes : copy_max;
83 ssize_t copied = copy_file_range (src_fd, NULL, dest_fd, NULL, len, 0);
84 if (copied <= 0)
85 break;
86 inbytes -= copied;
89 /* Finish up with read/write, in case the file was not a regular
90 file, or the file shrank or had I/O errors (in which case find
91 whether it was a read or write error). Read empty regular files
92 since they might be in /proc with their true sizes unknown until
93 they are read. */
94 if (inbytes != 0 || empty_regular_file)
96 char smallbuf[1024];
97 int bufsize = IO_SIZE;
98 char *buf = malloc (bufsize);
99 if (!buf)
100 buf = smallbuf, bufsize = sizeof smallbuf;
102 while (true)
104 size_t n_read = safe_read (src_fd, buf, bufsize);
105 if (n_read == 0)
106 break;
107 if (n_read == SAFE_READ_ERROR)
109 err = GL_COPY_ERR_READ;
110 break;
112 if (full_write (dest_fd, buf, n_read) < n_read)
114 err = GL_COPY_ERR_WRITE;
115 break;
119 if (buf != smallbuf)
120 free (buf);
121 if (err)
122 goto error_src_dest;
125 #if !USE_ACL
126 if (close (dest_fd) < 0)
128 err = GL_COPY_ERR_WRITE;
129 goto error_src;
131 if (close (src_fd) < 0)
132 return GL_COPY_ERR_AFTER_READ;
133 #endif
135 /* Preserve the access and modification times. */
137 struct timespec ts[2];
139 ts[0] = get_stat_atime (&statbuf);
140 ts[1] = get_stat_mtime (&statbuf);
141 utimens (dest_filename, ts);
144 #if HAVE_CHOWN
145 /* Preserve the owner and group. */
146 ignore_value (chown (dest_filename, statbuf.st_uid, statbuf.st_gid));
147 #endif
149 /* Preserve the access permissions. */
150 #if USE_ACL
151 switch (qcopy_acl (src_filename, src_fd, dest_filename, dest_fd, mode))
153 case -2:
154 err = GL_COPY_ERR_GET_ACL;
155 goto error_src_dest;
156 case -1:
157 err = GL_COPY_ERR_SET_ACL;
158 goto error_src_dest;
160 #else
161 chmod (dest_filename, mode);
162 #endif
164 #if USE_ACL
165 if (close (dest_fd) < 0)
167 err = GL_COPY_ERR_WRITE;
168 goto error_src;
170 if (close (src_fd) < 0)
171 return GL_COPY_ERR_AFTER_READ;
172 #endif
174 return 0;
176 error_src_dest:
177 close (dest_fd);
178 error_src:
179 close (src_fd);
180 return err;
183 void
184 copy_file_preserving (const char *src_filename, const char *dest_filename)
186 switch (qcopy_file_preserving (src_filename, dest_filename))
188 case 0:
189 return;
191 case GL_COPY_ERR_OPEN_READ:
192 error (EXIT_FAILURE, errno, _("error while opening %s for reading"),
193 quote (src_filename));
195 case GL_COPY_ERR_OPEN_BACKUP_WRITE:
196 error (EXIT_FAILURE, errno, _("cannot open backup file %s for writing"),
197 quote (dest_filename));
199 case GL_COPY_ERR_READ:
200 error (EXIT_FAILURE, errno, _("error reading %s"),
201 quote (src_filename));
203 case GL_COPY_ERR_WRITE:
204 error (EXIT_FAILURE, errno, _("error writing %s"),
205 quote (dest_filename));
207 case GL_COPY_ERR_AFTER_READ:
208 error (EXIT_FAILURE, errno, _("error after reading %s"),
209 quote (src_filename));
211 case GL_COPY_ERR_GET_ACL:
212 error (EXIT_FAILURE, errno, "%s", quote (src_filename));
214 case GL_COPY_ERR_SET_ACL:
215 error (EXIT_FAILURE, errno, _("preserving permissions for %s"),
216 quote (dest_filename));
218 default:
219 abort ();