]>
Commit | Line | Data |
---|---|---|
252b5132 | 1 | /* rename.c -- rename a file, preserving symlinks. |
a2c58332 | 2 | Copyright (C) 1999-2022 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 | ||
47fcfcba | 134 | #if defined HAVE_UTIMENSAT |
985e0264 | 135 | /* Return *ST's access time. */ |
ad7c4616 | 136 | static inline struct timespec |
985e0264 AM |
137 | get_stat_atime (struct stat const *st) |
138 | { | |
139 | #ifdef STAT_TIMESPEC | |
140 | return STAT_TIMESPEC (st, st_atim); | |
141 | #else | |
142 | struct timespec t; | |
143 | t.tv_sec = st->st_atime; | |
144 | t.tv_nsec = get_stat_atime_ns (st); | |
145 | return t; | |
146 | #endif | |
147 | } | |
148 | ||
149 | /* Return *ST's data modification time. */ | |
ad7c4616 | 150 | static inline struct timespec |
985e0264 AM |
151 | get_stat_mtime (struct stat const *st) |
152 | { | |
153 | #ifdef STAT_TIMESPEC | |
154 | return STAT_TIMESPEC (st, st_mtim); | |
155 | #else | |
156 | struct timespec t; | |
157 | t.tv_sec = st->st_mtime; | |
158 | t.tv_nsec = get_stat_mtime_ns (st); | |
159 | return t; | |
160 | #endif | |
161 | } | |
47fcfcba | 162 | #endif |
985e0264 AM |
163 | /* End FIXME. */ |
164 | ||
252b5132 RH |
165 | /* Set the times of the file DESTINATION to be the same as those in |
166 | STATBUF. */ | |
167 | ||
168 | void | |
2da42df6 | 169 | set_times (const char *destination, const struct stat *statbuf) |
252b5132 RH |
170 | { |
171 | int result; | |
985e0264 AM |
172 | #if defined HAVE_UTIMENSAT |
173 | struct timespec times[2]; | |
174 | times[0] = get_stat_atime (statbuf); | |
175 | times[1] = get_stat_mtime (statbuf); | |
176 | result = utimensat (AT_FDCWD, destination, times, 0); | |
cca8873d AM |
177 | #elif defined HAVE_UTIMES |
178 | struct timeval tv[2]; | |
179 | ||
180 | tv[0].tv_sec = statbuf->st_atime; | |
985e0264 | 181 | tv[0].tv_usec = get_stat_atime_ns (statbuf) / 1000; |
cca8873d | 182 | tv[1].tv_sec = statbuf->st_mtime; |
985e0264 | 183 | tv[1].tv_usec = get_stat_mtime_ns (statbuf) / 1000; |
cca8873d | 184 | result = utimes (destination, tv); |
985e0264 AM |
185 | #elif defined HAVE_GOOD_UTIME_H |
186 | struct utimbuf tb; | |
187 | ||
188 | tb.actime = statbuf->st_atime; | |
189 | tb.modtime = statbuf->st_mtime; | |
190 | result = utime (destination, &tb); | |
cca8873d AM |
191 | #else |
192 | long tb[2]; | |
193 | ||
194 | tb[0] = statbuf->st_atime; | |
195 | tb[1] = statbuf->st_mtime; | |
196 | result = utime (destination, tb); | |
197 | #endif | |
252b5132 RH |
198 | |
199 | if (result != 0) | |
200 | non_fatal (_("%s: cannot set time: %s"), destination, strerror (errno)); | |
201 | } | |
202 | ||
cca8873d AM |
203 | /* Copy FROM to TO. TARGET_STAT has the file status that, if non-NULL, |
204 | is used to fix up timestamps. Return 0 if ok, -1 if error. | |
205 | At one time this function renamed files, but file permissions are | |
206 | tricky to update given the number of different schemes used by | |
207 | various systems. So now we just copy. */ | |
252b5132 RH |
208 | |
209 | int | |
c42c71a1 | 210 | smart_rename (const char *from, const char *to, int fromfd, |
015dc7e1 | 211 | struct stat *target_stat, bool preserve_dates) |
252b5132 | 212 | { |
d0ecdcdd | 213 | int ret = 0; |
252b5132 | 214 | |
d0ecdcdd AM |
215 | if (to != from) |
216 | { | |
217 | ret = simple_copy (fromfd, to, target_stat); | |
218 | if (ret != 0) | |
219 | non_fatal (_("unable to copy file '%s'; reason: %s"), | |
220 | to, strerror (errno)); | |
221 | unlink (from); | |
222 | } | |
252b5132 | 223 | |
c42c71a1 | 224 | if (preserve_dates) |
cca8873d | 225 | set_times (to, target_stat); |
252b5132 RH |
226 | |
227 | return ret; | |
228 | } |