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 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 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 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 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 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 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 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