2 Copyright (C) 2001-2003, 2006-2007, 2009-2020 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/>. */
22 #include "copy-file.h"
32 #include "ignore-value.h"
33 #include "safe-read.h"
34 #include "full-write.h"
35 #include "stat-time.h"
38 #include "binary-io.h"
42 #define _(str) gettext (str)
44 enum { IO_SIZE
= 32 * 1024 };
47 qcopy_file_preserving (const char *src_filename
, const char *dest_filename
)
55 src_fd
= open (src_filename
, O_RDONLY
| O_BINARY
| O_CLOEXEC
);
57 return GL_COPY_ERR_OPEN_READ
;
58 if (fstat (src_fd
, &statbuf
) < 0)
60 err
= GL_COPY_ERR_OPEN_READ
;
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
,
73 err
= GL_COPY_ERR_OPEN_BACKUP_WRITE
;
77 /* Copy the file contents. FIXME: Do not copy holes. */
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);
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
94 if (inbytes
!= 0 || empty_regular_file
)
97 int bufsize
= IO_SIZE
;
98 char *buf
= malloc (bufsize
);
100 buf
= smallbuf
, bufsize
= sizeof smallbuf
;
104 size_t n_read
= safe_read (src_fd
, buf
, bufsize
);
107 if (n_read
== SAFE_READ_ERROR
)
109 err
= GL_COPY_ERR_READ
;
112 if (full_write (dest_fd
, buf
, n_read
) < n_read
)
114 err
= GL_COPY_ERR_WRITE
;
126 if (close (dest_fd
) < 0)
128 err
= GL_COPY_ERR_WRITE
;
131 if (close (src_fd
) < 0)
132 return GL_COPY_ERR_AFTER_READ
;
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
);
145 /* Preserve the owner and group. */
146 ignore_value (chown (dest_filename
, statbuf
.st_uid
, statbuf
.st_gid
));
149 /* Preserve the access permissions. */
151 switch (qcopy_acl (src_filename
, src_fd
, dest_filename
, dest_fd
, mode
))
154 err
= GL_COPY_ERR_GET_ACL
;
157 err
= GL_COPY_ERR_SET_ACL
;
161 chmod (dest_filename
, mode
);
165 if (close (dest_fd
) < 0)
167 err
= GL_COPY_ERR_WRITE
;
170 if (close (src_fd
) < 0)
171 return GL_COPY_ERR_AFTER_READ
;
184 copy_file_preserving (const char *src_filename
, const char *dest_filename
)
186 switch (qcopy_file_preserving (src_filename
, dest_filename
))
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
));