xref: /netbsd-src/external/gpl3/binutils.old/dist/binutils/rename.c (revision e992f068c547fd6e84b3f104dc2340adcc955732)
1 /* rename.c -- rename a file, preserving symlinks.
2    Copyright (C) 1999-2022 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
simple_copy(int fromfd,const char * to,struct stat * target_stat ATTRIBUTE_UNUSED)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 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 
108 /* Return the nanosecond component of *ST's access time.  */
109 static inline long int
get_stat_atime_ns(struct stat const * st ATTRIBUTE_UNUSED)110 get_stat_atime_ns (struct stat const *st ATTRIBUTE_UNUSED)
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.  */
122 static inline long int
get_stat_mtime_ns(struct stat const * st ATTRIBUTE_UNUSED)123 get_stat_mtime_ns (struct stat const *st ATTRIBUTE_UNUSED)
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 #if defined HAVE_UTIMENSAT
135 /* Return *ST's access time.  */
136 static inline struct timespec
get_stat_atime(struct stat const * st)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.  */
150 static inline struct timespec
get_stat_mtime(struct stat const * st)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 }
162 #endif
163 /* End FIXME.  */
164 
165 /* Set the times of the file DESTINATION to be the same as those in
166    STATBUF.  */
167 
168 void
set_times(const char * destination,const struct stat * statbuf)169 set_times (const char *destination, const struct stat *statbuf)
170 {
171   int result;
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);
177 #elif defined HAVE_UTIMES
178   struct timeval tv[2];
179 
180   tv[0].tv_sec = statbuf->st_atime;
181   tv[0].tv_usec = get_stat_atime_ns (statbuf) / 1000;
182   tv[1].tv_sec = statbuf->st_mtime;
183   tv[1].tv_usec = get_stat_mtime_ns (statbuf) / 1000;
184   result = utimes (destination, tv);
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);
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
198 
199   if (result != 0)
200     non_fatal (_("%s: cannot set time: %s"), destination, strerror (errno));
201 }
202 
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.  */
208 
209 int
smart_rename(const char * from,const char * to,int fromfd,struct stat * target_stat,bool preserve_dates)210 smart_rename (const char *from, const char *to, int fromfd,
211 	      struct stat *target_stat, bool preserve_dates)
212 {
213   int ret = 0;
214 
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     }
223 
224   if (preserve_dates)
225     set_times (to, target_stat);
226 
227   return ret;
228 }
229