]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - binutils/rename.c
PR27725, better objcopy -p times
[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 #if defined HAVE_UTIMES
26 #include <sys/time.h>
27 #elif defined HAVE_GOOD_UTIME_H
28 #include <utime.h>
29 #endif
30
31 /* The number of bytes to copy at once. */
32 #define COPY_BUF 8192
33
34 /* Copy file FROMFD to file TO, performing no translations.
35 Return 0 if ok, -1 if error. */
36
37 static int
38 simple_copy (int fromfd, const char *to,
39 struct stat *target_stat ATTRIBUTE_UNUSED)
40 {
41 int tofd, nread;
42 int saved;
43 char buf[COPY_BUF];
44
45 if (fromfd < 0
46 || lseek (fromfd, 0, SEEK_SET) != 0)
47 return -1;
48
49 tofd = open (to, O_WRONLY | O_TRUNC | O_BINARY);
50 if (tofd < 0)
51 {
52 saved = errno;
53 close (fromfd);
54 errno = saved;
55 return -1;
56 }
57
58 while ((nread = read (fromfd, buf, sizeof buf)) > 0)
59 {
60 if (write (tofd, buf, nread) != nread)
61 {
62 saved = errno;
63 close (fromfd);
64 close (tofd);
65 errno = saved;
66 return -1;
67 }
68 }
69
70 saved = errno;
71
72 #if !defined (_WIN32) || defined (__CYGWIN32__)
73 /* Writing to a setuid/setgid file may clear S_ISUID and S_ISGID.
74 Try to restore them, ignoring failure. */
75 if (target_stat != NULL)
76 fchmod (tofd, target_stat->st_mode);
77 #endif
78
79 close (fromfd);
80 close (tofd);
81 if (nread < 0)
82 {
83 errno = saved;
84 return -1;
85 }
86 return 0;
87 }
88
89 /* The following defines and inline functions are copied from gnulib.
90 FIXME: Use a gnulib import and stat-time.h instead. */
91 #if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
92 # if defined TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC
93 # define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim)
94 # else
95 # define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.tv_nsec)
96 # endif
97 #elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
98 # define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim##espec)
99 #elif defined HAVE_STRUCT_STAT_ST_ATIMENSEC
100 # define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim##ensec)
101 #elif defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC
102 # define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.st__tim.tv_nsec)
103 #endif
104
105 /* Return the nanosecond component of *ST's access time. */
106 inline long int
107 get_stat_atime_ns (struct stat const *st)
108 {
109 # if defined STAT_TIMESPEC
110 return STAT_TIMESPEC (st, st_atim).tv_nsec;
111 # elif defined STAT_TIMESPEC_NS
112 return STAT_TIMESPEC_NS (st, st_atim);
113 # else
114 return 0;
115 # endif
116 }
117
118 /* Return the nanosecond component of *ST's data modification time. */
119 inline long int
120 get_stat_mtime_ns (struct stat const *st)
121 {
122 # if defined STAT_TIMESPEC
123 return STAT_TIMESPEC (st, st_mtim).tv_nsec;
124 # elif defined STAT_TIMESPEC_NS
125 return STAT_TIMESPEC_NS (st, st_mtim);
126 # else
127 return 0;
128 # endif
129 }
130
131 /* Return *ST's access time. */
132 inline struct timespec
133 get_stat_atime (struct stat const *st)
134 {
135 #ifdef STAT_TIMESPEC
136 return STAT_TIMESPEC (st, st_atim);
137 #else
138 struct timespec t;
139 t.tv_sec = st->st_atime;
140 t.tv_nsec = get_stat_atime_ns (st);
141 return t;
142 #endif
143 }
144
145 /* Return *ST's data modification time. */
146 inline struct timespec
147 get_stat_mtime (struct stat const *st)
148 {
149 #ifdef STAT_TIMESPEC
150 return STAT_TIMESPEC (st, st_mtim);
151 #else
152 struct timespec t;
153 t.tv_sec = st->st_mtime;
154 t.tv_nsec = get_stat_mtime_ns (st);
155 return t;
156 #endif
157 }
158 /* End FIXME. */
159
160 /* Set the times of the file DESTINATION to be the same as those in
161 STATBUF. */
162
163 void
164 set_times (const char *destination, const struct stat *statbuf)
165 {
166 int result;
167 #if defined HAVE_UTIMENSAT
168 struct timespec times[2];
169 times[0] = get_stat_atime (statbuf);
170 times[1] = get_stat_mtime (statbuf);
171 result = utimensat (AT_FDCWD, destination, times, 0);
172 #elif defined HAVE_UTIMES
173 struct timeval tv[2];
174
175 tv[0].tv_sec = statbuf->st_atime;
176 tv[0].tv_usec = get_stat_atime_ns (statbuf) / 1000;
177 tv[1].tv_sec = statbuf->st_mtime;
178 tv[1].tv_usec = get_stat_mtime_ns (statbuf) / 1000;
179 result = utimes (destination, tv);
180 #elif defined HAVE_GOOD_UTIME_H
181 struct utimbuf tb;
182
183 tb.actime = statbuf->st_atime;
184 tb.modtime = statbuf->st_mtime;
185 result = utime (destination, &tb);
186 #else
187 long tb[2];
188
189 tb[0] = statbuf->st_atime;
190 tb[1] = statbuf->st_mtime;
191 result = utime (destination, tb);
192 #endif
193
194 if (result != 0)
195 non_fatal (_("%s: cannot set time: %s"), destination, strerror (errno));
196 }
197
198 /* Copy FROM to TO. TARGET_STAT has the file status that, if non-NULL,
199 is used to fix up timestamps. Return 0 if ok, -1 if error.
200 At one time this function renamed files, but file permissions are
201 tricky to update given the number of different schemes used by
202 various systems. So now we just copy. */
203
204 int
205 smart_rename (const char *from, const char *to, int fromfd,
206 struct stat *target_stat, bool preserve_dates)
207 {
208 int ret = 0;
209
210 if (to != from)
211 {
212 ret = simple_copy (fromfd, to, target_stat);
213 if (ret != 0)
214 non_fatal (_("unable to copy file '%s'; reason: %s"),
215 to, strerror (errno));
216 unlink (from);
217 }
218
219 if (preserve_dates)
220 set_times (to, target_stat);
221
222 return ret;
223 }