PR27349, ar breaks symlinks
[binutils-gdb.git] / binutils / rename.c
1 /* rename.c -- rename a file, preserving symlinks.
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 This file is part of GNU Binutils.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
19 02110-1301, USA. */
20
21 #include "sysdep.h"
22 #include "bfd.h"
23 #include "bucomm.h"
24
25 #ifdef HAVE_GOOD_UTIME_H
26 #include <utime.h>
27 #else /* ! HAVE_GOOD_UTIME_H */
28 #ifdef HAVE_UTIMES
29 #include <sys/time.h>
30 #endif /* HAVE_UTIMES */
31 #endif /* ! HAVE_GOOD_UTIME_H */
32
33 #if ! defined (_WIN32) || defined (__CYGWIN32__)
34 static int simple_copy (const char *, const char *);
35
36 /* The number of bytes to copy at once. */
37 #define COPY_BUF 8192
38
39 /* Copy file FROM to file TO, performing no translations.
40 Return 0 if ok, -1 if error. */
41
42 static int
43 simple_copy (const char *from, const char *to)
44 {
45 int fromfd, tofd, nread;
46 int saved;
47 char buf[COPY_BUF];
48
49 fromfd = open (from, O_RDONLY | O_BINARY);
50 if (fromfd < 0)
51 return -1;
52 #ifdef O_CREAT
53 tofd = open (to, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0777);
54 #else
55 tofd = creat (to, 0777);
56 #endif
57 if (tofd < 0)
58 {
59 saved = errno;
60 close (fromfd);
61 errno = saved;
62 return -1;
63 }
64 while ((nread = read (fromfd, buf, sizeof buf)) > 0)
65 {
66 if (write (tofd, buf, nread) != nread)
67 {
68 saved = errno;
69 close (fromfd);
70 close (tofd);
71 errno = saved;
72 return -1;
73 }
74 }
75 saved = errno;
76 close (fromfd);
77 close (tofd);
78 if (nread < 0)
79 {
80 errno = saved;
81 return -1;
82 }
83 return 0;
84 }
85 #endif /* __CYGWIN32__ or not _WIN32 */
86
87 /* Set the times of the file DESTINATION to be the same as those in
88 STATBUF. */
89
90 void
91 set_times (const char *destination, const struct stat *statbuf)
92 {
93 int result;
94
95 {
96 #ifdef HAVE_GOOD_UTIME_H
97 struct utimbuf tb;
98
99 tb.actime = statbuf->st_atime;
100 tb.modtime = statbuf->st_mtime;
101 result = utime (destination, &tb);
102 #else /* ! HAVE_GOOD_UTIME_H */
103 #ifndef HAVE_UTIMES
104 long tb[2];
105
106 tb[0] = statbuf->st_atime;
107 tb[1] = statbuf->st_mtime;
108 result = utime (destination, tb);
109 #else /* HAVE_UTIMES */
110 struct timeval tv[2];
111
112 tv[0].tv_sec = statbuf->st_atime;
113 tv[0].tv_usec = 0;
114 tv[1].tv_sec = statbuf->st_mtime;
115 tv[1].tv_usec = 0;
116 result = utimes (destination, tv);
117 #endif /* HAVE_UTIMES */
118 #endif /* ! HAVE_GOOD_UTIME_H */
119 }
120
121 if (result != 0)
122 non_fatal (_("%s: cannot set time: %s"), destination, strerror (errno));
123 }
124
125 #ifndef S_ISLNK
126 #ifdef S_IFLNK
127 #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
128 #else
129 #define S_ISLNK(m) 0
130 #define lstat stat
131 #endif
132 #endif
133
134 #if !defined (_WIN32) || defined (__CYGWIN32__)
135 /* Try to preserve the permission bits and ownership of an existing file when
136 rename overwrites it. FD is the file being renamed and TARGET_STAT has the
137 status of the file that was overwritten. */
138 static void
139 try_preserve_permissions (int fd, struct stat *target_stat)
140 {
141 struct stat from_stat;
142 int ret = 0;
143
144 if (fstat (fd, &from_stat) != 0)
145 return;
146
147 int from_mode = from_stat.st_mode & 0777;
148 int to_mode = target_stat->st_mode & 0777;
149
150 /* Fix up permissions before we potentially lose ownership with fchown.
151 Clear the setxid bits because in case the fchown below fails then we don't
152 want to end up with a sxid file owned by the invoking user. If the user
153 hasn't changed or if fchown succeeded, we add back the sxid bits at the
154 end. */
155 if (from_mode != to_mode)
156 fchmod (fd, to_mode);
157
158 /* Fix up ownership, this will clear the setxid bits. */
159 if (from_stat.st_uid != target_stat->st_uid
160 || from_stat.st_gid != target_stat->st_gid)
161 ret = fchown (fd, target_stat->st_uid, target_stat->st_gid);
162
163 /* Fix up the sxid bits if either the fchown wasn't needed or it
164 succeeded. */
165 if (ret == 0)
166 fchmod (fd, target_stat->st_mode & 07777);
167 }
168 #endif
169
170 /* Rename FROM to TO, copying if TO is either a link or is not a regular file.
171 FD is an open file descriptor pointing to FROM that we can use to safely fix
172 up permissions of the file after renaming. TARGET_STAT has the file status
173 that is used to fix up permissions and timestamps after rename. Return 0 if
174 ok, -1 if error and FD is closed before returning. */
175
176 int
177 smart_rename (const char *from, const char *to, int fd ATTRIBUTE_UNUSED,
178 struct stat *target_stat ATTRIBUTE_UNUSED,
179 int preserve_dates ATTRIBUTE_UNUSED)
180 {
181 int ret = 0;
182 struct stat to_stat;
183 bfd_boolean exists;
184
185 exists = lstat (to, &to_stat) == 0;
186
187 #if defined (_WIN32) && !defined (__CYGWIN32__)
188 /* Win32, unlike unix, will not erase `to' in `rename(from, to)' but
189 fail instead. Also, chown is not present. */
190
191 if (exists)
192 remove (to);
193
194 ret = rename (from, to);
195 if (ret != 0)
196 {
197 /* We have to clean up here. */
198 non_fatal (_("unable to rename '%s'; reason: %s"), to, strerror (errno));
199 unlink (from);
200 }
201 #else
202 /* Avoid a full copy and use rename if we can fix up permissions of the
203 file after renaming, i.e.:
204
205 - TO is not a symbolic link
206 - TO is a regular file with only one hard link
207 - We have permission to write to TO
208 - FD is available to safely fix up permissions to be the same as the file
209 we overwrote with the rename.
210
211 Note though that the actual file on disk that TARGET_STAT describes may
212 have changed and we're only trying to preserve the status we know about.
213 At no point do we try to interact with the new file changes, so there can
214 only be two outcomes, i.e. either the external file change survives
215 without knowledge of our change (if it happens after the rename syscall)
216 or our rename and permissions fixup survive without any knowledge of the
217 external change. */
218 if (! exists
219 || (fd >= 0
220 && !S_ISLNK (to_stat.st_mode)
221 && S_ISREG (to_stat.st_mode)
222 && (to_stat.st_mode & S_IWUSR)
223 && to_stat.st_nlink == 1)
224 )
225 {
226 ret = rename (from, to);
227 if (ret == 0)
228 {
229 if (exists && target_stat != NULL)
230 try_preserve_permissions (fd, target_stat);
231 }
232 else
233 {
234 /* We have to clean up here. */
235 non_fatal (_("unable to rename '%s'; reason: %s"), to, strerror (errno));
236 unlink (from);
237 }
238 }
239 else
240 {
241 ret = simple_copy (from, to);
242 if (ret != 0)
243 non_fatal (_("unable to copy file '%s'; reason: %s"), to, strerror (errno));
244
245 if (preserve_dates && target_stat != NULL)
246 set_times (to, target_stat);
247 unlink (from);
248 }
249 if (fd >= 0)
250 close (fd);
251 #endif /* _WIN32 && !__CYGWIN32__ */
252
253 return ret;
254 }