Commit | Line | Data |
---|---|---|
252b5132 | 1 | /* rename.c -- rename a file, preserving symlinks. |
250d07de | 2 | Copyright (C) 1999-2021 Free Software Foundation, Inc. |
252b5132 RH |
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 | |
32866df7 | 8 | the Free Software Foundation; either version 3 of the License, or |
252b5132 RH |
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 | |
b43b5d5f NC |
18 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA |
19 | 02110-1301, USA. */ | |
252b5132 | 20 | |
3db64b00 | 21 | #include "sysdep.h" |
252b5132 RH |
22 | #include "bfd.h" |
23 | #include "bucomm.h" | |
24 | ||
985e0264 | 25 | #if defined HAVE_UTIMES |
252b5132 | 26 | #include <sys/time.h> |
985e0264 AM |
27 | #elif defined HAVE_GOOD_UTIME_H |
28 | #include <utime.h> | |
cca8873d | 29 | #endif |
252b5132 RH |
30 | |
31 | /* The number of bytes to copy at once. */ | |
32 | #define COPY_BUF 8192 | |
33 | ||
c42c71a1 | 34 | /* Copy file FROMFD to file TO, performing no translations. |
252b5132 RH |
35 | Return 0 if ok, -1 if error. */ |
36 | ||
37 | static int | |
40b02646 AM |
38 | simple_copy (int fromfd, const char *to, |
39 | struct stat *target_stat ATTRIBUTE_UNUSED) | |
252b5132 | 40 | { |
c42c71a1 | 41 | int tofd, nread; |
252b5132 RH |
42 | int saved; |
43 | char buf[COPY_BUF]; | |
44 | ||
c42c71a1 AM |
45 | if (fromfd < 0 |
46 | || lseek (fromfd, 0, SEEK_SET) != 0) | |
252b5132 | 47 | return -1; |
c42c71a1 AM |
48 | |
49 | tofd = open (to, O_WRONLY | O_TRUNC | O_BINARY); | |
252b5132 RH |
50 | if (tofd < 0) |
51 | { | |
52 | saved = errno; | |
53 | close (fromfd); | |
54 | errno = saved; | |
55 | return -1; | |
56 | } | |
c42c71a1 | 57 | |
252b5132 RH |
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 | } | |
c42c71a1 | 69 | |
252b5132 | 70 | saved = errno; |
c42c71a1 AM |
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 | ||
252b5132 RH |
79 | close (fromfd); |
80 | close (tofd); | |
81 | if (nread < 0) | |
82 | { | |
83 | errno = saved; | |
84 | return -1; | |
85 | } | |
86 | return 0; | |
87 | } | |
88 | ||
985e0264 AM |
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 | ||
4dee4f3e NC |
105 | static inline long int get_stat_atime_ns (struct stat const *) ATTRIBUTE_UNUSED; |
106 | static inline long int get_stat_mtime_ns (struct stat const *) ATTRIBUTE_UNUSED; | |
107 | ||
985e0264 | 108 | /* Return the nanosecond component of *ST's access time. */ |
ad7c4616 | 109 | static inline long int |
4c79248a | 110 | get_stat_atime_ns (struct stat const *st ATTRIBUTE_UNUSED) |
985e0264 AM |
111 | { |
112 | # if defined STAT_TIMESPEC | |
113 | return STAT_TIMESPEC (st, st_atim).tv_nsec; | |
114 | # elif defined STAT_TIMESPEC_NS | |
115 | return STAT_TIMESPEC_NS (st, st_atim); | |
116 | # else | |
117 | return 0; | |
118 | # endif | |
119 | } | |
120 | ||
121 | /* Return the nanosecond component of *ST's data modification time. */ | |
ad7c4616 | 122 | static inline long int |
4c79248a | 123 | get_stat_mtime_ns (struct stat const *st ATTRIBUTE_UNUSED) |
985e0264 AM |
124 | { |
125 | # if defined STAT_TIMESPEC | |
126 | return STAT_TIMESPEC (st, st_mtim).tv_nsec; | |
127 | # elif defined STAT_TIMESPEC_NS | |
128 | return STAT_TIMESPEC_NS (st, st_mtim); | |
129 | # else | |
130 | return 0; | |
131 | # endif | |
132 | } | |
133 | ||
134 | /* Return *ST's access time. */ | |
ad7c4616 | 135 | static inline struct timespec |
985e0264 AM |
136 | get_stat_atime (struct stat const *st) |
137 | { | |
138 | #ifdef STAT_TIMESPEC | |
139 | return STAT_TIMESPEC (st, st_atim); | |
140 | #else | |
141 | struct timespec t; | |
142 | t.tv_sec = st->st_atime; | |
143 | t.tv_nsec = get_stat_atime_ns (st); | |
144 | return t; | |
145 | #endif | |
146 | } | |
147 | ||
148 | /* Return *ST's data modification time. */ | |
ad7c4616 | 149 | static inline struct timespec |
985e0264 AM |
150 | get_stat_mtime (struct stat const *st) |
151 | { | |
152 | #ifdef STAT_TIMESPEC | |
153 | return STAT_TIMESPEC (st, st_mtim); | |
154 | #else | |
155 | struct timespec t; | |
156 | t.tv_sec = st->st_mtime; | |
157 | t.tv_nsec = get_stat_mtime_ns (st); | |
158 | return t; | |
159 | #endif | |
160 | } | |
161 | /* End FIXME. */ | |
162 | ||
252b5132 RH |
163 | /* Set the times of the file DESTINATION to be the same as those in |
164 | STATBUF. */ | |
165 | ||
166 | void | |
2da42df6 | 167 | set_times (const char *destination, const struct stat *statbuf) |
252b5132 RH |
168 | { |
169 | int result; | |
985e0264 AM |
170 | #if defined HAVE_UTIMENSAT |
171 | struct timespec times[2]; | |
172 | times[0] = get_stat_atime (statbuf); | |
173 | times[1] = get_stat_mtime (statbuf); | |
174 | result = utimensat (AT_FDCWD, destination, times, 0); | |
cca8873d AM |
175 | #elif defined HAVE_UTIMES |
176 | struct timeval tv[2]; | |
177 | ||
178 | tv[0].tv_sec = statbuf->st_atime; | |
985e0264 | 179 | tv[0].tv_usec = get_stat_atime_ns (statbuf) / 1000; |
cca8873d | 180 | tv[1].tv_sec = statbuf->st_mtime; |
985e0264 | 181 | tv[1].tv_usec = get_stat_mtime_ns (statbuf) / 1000; |
cca8873d | 182 | result = utimes (destination, tv); |
985e0264 AM |
183 | #elif defined HAVE_GOOD_UTIME_H |
184 | struct utimbuf tb; | |
185 | ||
186 | tb.actime = statbuf->st_atime; | |
187 | tb.modtime = statbuf->st_mtime; | |
188 | result = utime (destination, &tb); | |
cca8873d AM |
189 | #else |
190 | long tb[2]; | |
191 | ||
192 | tb[0] = statbuf->st_atime; | |
193 | tb[1] = statbuf->st_mtime; | |
194 | result = utime (destination, tb); | |
195 | #endif | |
252b5132 RH |
196 | |
197 | if (result != 0) | |
198 | non_fatal (_("%s: cannot set time: %s"), destination, strerror (errno)); | |
199 | } | |
200 | ||
cca8873d AM |
201 | /* Copy FROM to TO. TARGET_STAT has the file status that, if non-NULL, |
202 | is used to fix up timestamps. Return 0 if ok, -1 if error. | |
203 | At one time this function renamed files, but file permissions are | |
204 | tricky to update given the number of different schemes used by | |
205 | various systems. So now we just copy. */ | |
252b5132 RH |
206 | |
207 | int | |
c42c71a1 | 208 | smart_rename (const char *from, const char *to, int fromfd, |
015dc7e1 | 209 | struct stat *target_stat, bool preserve_dates) |
252b5132 | 210 | { |
d0ecdcdd | 211 | int ret = 0; |
252b5132 | 212 | |
d0ecdcdd AM |
213 | if (to != from) |
214 | { | |
215 | ret = simple_copy (fromfd, to, target_stat); | |
216 | if (ret != 0) | |
217 | non_fatal (_("unable to copy file '%s'; reason: %s"), | |
218 | to, strerror (errno)); | |
219 | unlink (from); | |
220 | } | |
252b5132 | 221 | |
c42c71a1 | 222 | if (preserve_dates) |
cca8873d | 223 | set_times (to, target_stat); |
252b5132 RH |
224 | |
225 | return ret; | |
226 | } |