@@ -131,55 +131,17 @@ set_times (const char *destination, const struct stat *statbuf)
131131#endif
132132#endif
133133
134- #if !defined (_WIN32 ) || defined (__CYGWIN32__ )
135- /* Try to preserve the permission bits and ownership of an existing file when
136- rename overwrites it. FD is the file being renamed and TARGET_STAT has the
137- status of the file that was overwritten. */
138- static void
139- try_preserve_permissions (int fd , struct stat * target_stat )
140- {
141- struct stat from_stat ;
142- int ret = 0 ;
143-
144- if (fstat (fd , & from_stat ) != 0 )
145- return ;
146-
147- int from_mode = from_stat .st_mode & 0777 ;
148- int to_mode = target_stat -> st_mode & 0777 ;
149-
150- /* Fix up permissions before we potentially lose ownership with fchown.
151- Clear the setxid bits because in case the fchown below fails then we don't
152- want to end up with a sxid file owned by the invoking user. If the user
153- hasn't changed or if fchown succeeded, we add back the sxid bits at the
154- end. */
155- if (from_mode != to_mode )
156- fchmod (fd , to_mode );
157-
158- /* Fix up ownership, this will clear the setxid bits. */
159- if (from_stat .st_uid != target_stat -> st_uid
160- || from_stat .st_gid != target_stat -> st_gid )
161- ret = fchown (fd , target_stat -> st_uid , target_stat -> st_gid );
162-
163- /* Fix up the sxid bits if either the fchown wasn't needed or it
164- succeeded. */
165- if (ret == 0 )
166- fchmod (fd , target_stat -> st_mode & 07777 );
167- }
168- #endif
169-
170- /* Rename FROM to TO, copying if TO is either a link or is not a regular file.
171- FD is an open file descriptor pointing to FROM that we can use to safely fix
172- up permissions of the file after renaming. TARGET_STAT has the file status
173- that is used to fix up permissions and timestamps after rename. Return 0 if
174- ok, -1 if error and FD is closed before returning. */
134+ /* Rename FROM to TO, copying if TO is a link.
135+ Return 0 if ok, -1 if error. */
175136
176137int
177- smart_rename (const char * from , const char * to , int fd ATTRIBUTE_UNUSED ,
178- struct stat * target_stat ATTRIBUTE_UNUSED ,
179- int preserve_dates ATTRIBUTE_UNUSED )
138+ smart_rename (const char * from , const char * to , int preserve_dates ATTRIBUTE_UNUSED )
180139{
140+ bfd_boolean exists ;
141+ struct stat s ;
181142 int ret = 0 ;
182- bfd_boolean exists = target_stat != NULL ;
143+
144+ exists = lstat (to , & s ) == 0 ;
183145
184146#if defined (_WIN32 ) && !defined (__CYGWIN32__ )
185147 /* Win32, unlike unix, will not erase `to' in `rename(from, to)' but
@@ -196,35 +158,36 @@ smart_rename (const char *from, const char *to, int fd ATTRIBUTE_UNUSED,
196158 unlink (from );
197159 }
198160#else
199- /* Avoid a full copy and use rename if we can fix up permissions of the
200- file after renaming, i.e.:
201-
202- - TO is not a symbolic link
203- - TO is a regular file with only one hard link
204- - We have permission to write to TO
205- - FD is available to safely fix up permissions to be the same as the file
206- we overwrote with the rename.
207-
208- Note though that the actual file on disk that TARGET_STAT describes may
209- have changed and we're only trying to preserve the status we know about.
210- At no point do we try to interact with the new file changes, so there can
211- only be two outcomes, i.e. either the external file change survives
212- without knowledge of our change (if it happens after the rename syscall)
213- or our rename and permissions fixup survive without any knowledge of the
214- external change. */
161+ /* Use rename only if TO is not a symbolic link and has
162+ only one hard link, and we have permission to write to it. */
215163 if (! exists
216- || (fd >= 0
217- && !S_ISLNK (target_stat -> st_mode )
218- && S_ISREG (target_stat -> st_mode )
219- && (target_stat -> st_mode & S_IWUSR )
220- && target_stat -> st_nlink == 1 )
164+ || (!S_ISLNK (s .st_mode )
165+ && S_ISREG (s .st_mode )
166+ && (s .st_mode & S_IWUSR )
167+ && s .st_nlink == 1 )
221168 )
222169 {
223170 ret = rename (from , to );
224171 if (ret == 0 )
225172 {
226173 if (exists )
227- try_preserve_permissions (fd , target_stat );
174+ {
175+ /* Try to preserve the permission bits and ownership of
176+ TO. First get the mode right except for the setuid
177+ bit. Then change the ownership. Then fix the setuid
178+ bit. We do the chmod before the chown because if the
179+ chown succeeds, and we are a normal user, we won't be
180+ able to do the chmod afterward. We don't bother to
181+ fix the setuid bit first because that might introduce
182+ a fleeting security problem, and because the chown
183+ will clear the setuid bit anyhow. We only fix the
184+ setuid bit if the chown succeeds, because we don't
185+ want to introduce an unexpected setuid file owned by
186+ the user running objcopy. */
187+ chmod (to , s .st_mode & 0777 );
188+ if (chown (to , s .st_uid , s .st_gid ) >= 0 )
189+ chmod (to , s .st_mode & 07777 );
190+ }
228191 }
229192 else
230193 {
@@ -240,11 +203,9 @@ smart_rename (const char *from, const char *to, int fd ATTRIBUTE_UNUSED,
240203 non_fatal (_ ("unable to copy file '%s'; reason: %s" ), to , strerror (errno ));
241204
242205 if (preserve_dates )
243- set_times (to , target_stat );
206+ set_times (to , & s );
244207 unlink (from );
245208 }
246- if (fd >= 0 )
247- close (fd );
248209#endif /* _WIN32 && !__CYGWIN32__ */
249210
250211 return ret ;
0 commit comments