]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - binutils/rename.c
Update year range in copyright notice of binutils files
[thirdparty/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 bfd_boolean exists = target_stat != NULL;
183
184 #if defined (_WIN32) && !defined (__CYGWIN32__)
185 /* Win32, unlike unix, will not erase `to' in `rename(from, to)' but
186 fail instead. Also, chown is not present. */
187
188 if (exists)
189 remove (to);
190
191 ret = rename (from, to);
192 if (ret != 0)
193 {
194 /* We have to clean up here. */
195 non_fatal (_("unable to rename '%s'; reason: %s"), to, strerror (errno));
196 unlink (from);
197 }
198 #else
199 /* Avoid a full copy and use rename if we can fix up permissions of the
200 file after renaming, i.e.:
201
202 - TO is not a symbolic link
203 - TO is a regular file with only one hard link
204 - We have permission to write to TO
205 - FD is available to safely fix up permissions to be the same as the file
206 we overwrote with the rename.
207
208 Note though that the actual file on disk that TARGET_STAT describes may
209 have changed and we're only trying to preserve the status we know about.
210 At no point do we try to interact with the new file changes, so there can
211 only be two outcomes, i.e. either the external file change survives
212 without knowledge of our change (if it happens after the rename syscall)
213 or our rename and permissions fixup survive without any knowledge of the
214 external change. */
215 if (! exists
216 || (fd >= 0
217 && !S_ISLNK (target_stat->st_mode)
218 && S_ISREG (target_stat->st_mode)
219 && (target_stat->st_mode & S_IWUSR)
220 && target_stat->st_nlink == 1)
221 )
222 {
223 ret = rename (from, to);
224 if (ret == 0)
225 {
226 if (exists)
227 try_preserve_permissions (fd, target_stat);
228 }
229 else
230 {
231 /* We have to clean up here. */
232 non_fatal (_("unable to rename '%s'; reason: %s"), to, strerror (errno));
233 unlink (from);
234 }
235 }
236 else
237 {
238 ret = simple_copy (from, to);
239 if (ret != 0)
240 non_fatal (_("unable to copy file '%s'; reason: %s"), to, strerror (errno));
241
242 if (preserve_dates)
243 set_times (to, target_stat);
244 unlink (from);
245 }
246 if (fd >= 0)
247 close (fd);
248 #endif /* _WIN32 && !__CYGWIN32__ */
249
250 return ret;
251 }