1 /* Rename a file relative to open directories.
2 Copyright (C) 2009-2020 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* written by Eric Blake and Paul Eggert */
21 #include "renameatu.h"
29 # include <sys/syscall.h>
49 # include "openat-priv.h"
52 rename_noreplace (char const *src
, char const *dst
)
54 /* This has a race between the call to lstat and the call to rename. */
56 return (lstat (dst
, &st
) == 0 || errno
== EOVERFLOW
? errno_fail (EEXIST
)
57 : errno
== ENOENT
? rename (src
, dst
)
64 /* Rename FILE1, in the directory open on descriptor FD1, to FILE2, in
65 the directory open on descriptor FD2. If possible, do it without
66 changing the working directory. Otherwise, resort to using
67 save_cwd/fchdir, then rename/restore_cwd. If either the save_cwd or
68 the restore_cwd fails, then give a diagnostic and exit nonzero.
70 Obey FLAGS when doing the renaming. If FLAGS is zero, this
71 function is equivalent to renameat (FD1, SRC, FD2, DST).
72 Otherwise, attempt to implement FLAGS even if the implementation is
73 not atomic; this differs from the GNU/Linux native renameat2,
74 which fails if it cannot guarantee atomicity. */
77 renameatu (int fd1
, char const *src
, int fd2
, char const *dst
,
84 ret_val
= renameat2 (fd1
, src
, fd2
, dst
, flags
);
86 #elif defined SYS_renameat2
87 ret_val
= syscall (SYS_renameat2
, fd1
, src
, fd2
, dst
, flags
);
89 #elif defined RENAME_EXCL
90 if (! (flags
& ~(RENAME_EXCHANGE
| RENAME_NOREPLACE
)))
92 ret_val
= renameatx_np (fd1
, src
, fd2
, dst
,
93 ((flags
& RENAME_EXCHANGE
? RENAME_SWAP
: 0)
94 | (flags
& RENAME_NOREPLACE
? RENAME_EXCL
: 0)));
99 if (! (ret_val
< 0 && (err
== EINVAL
|| err
== ENOSYS
|| err
== ENOTSUP
)))
106 char *src_temp
= (char *) src
;
107 char *dst_temp
= (char *) dst
;
110 int rename_errno
= ENOTDIR
;
113 bool dst_found_nonexistent
= false;
117 /* RENAME_NOREPLACE is the only flag currently supported. */
118 if (flags
& ~RENAME_NOREPLACE
)
119 return errno_fail (ENOTSUP
);
122 /* This has a race between the call to lstatat and the calls to
124 if (lstatat (fd2
, dst
, &dst_st
) == 0 || errno
== EOVERFLOW
)
125 return errno_fail (EEXIST
);
128 dst_found_nonexistent
= true;
132 /* Let strace see any ENOENT failure. */
133 src_len
= strlen (src
);
134 dst_len
= strlen (dst
);
135 if (!src_len
|| !dst_len
)
136 return renameat (fd1
, src
, fd2
, dst
);
138 src_slash
= src
[src_len
- 1] == '/';
139 dst_slash
= dst
[dst_len
- 1] == '/';
140 if (!src_slash
&& !dst_slash
)
141 return renameat (fd1
, src
, fd2
, dst
);
143 /* Presence of a trailing slash requires directory semantics. If
144 the source does not exist, or if the destination cannot be turned
145 into a directory, give up now. Otherwise, strip trailing slashes
146 before calling rename. */
147 if (lstatat (fd1
, src
, &src_st
))
149 if (dst_found_nonexistent
)
151 if (!S_ISDIR (src_st
.st_mode
))
152 return errno_fail (ENOENT
);
154 else if (lstatat (fd2
, dst
, &dst_st
))
156 if (errno
!= ENOENT
|| !S_ISDIR (src_st
.st_mode
))
159 else if (!S_ISDIR (dst_st
.st_mode
))
160 return errno_fail (ENOTDIR
);
161 else if (!S_ISDIR (src_st
.st_mode
))
162 return errno_fail (EISDIR
);
164 # if RENAME_TRAILING_SLASH_SOURCE_BUG
165 /* See the lengthy comment in rename.c why Solaris 9 is forced to
166 GNU behavior, while Solaris 10 is left with POSIX behavior,
167 regarding symlinks with trailing slash. */
171 src_temp
= strdup (src
);
174 /* Rather than rely on strdup-posix, we set errno ourselves. */
175 rename_errno
= ENOMEM
;
178 strip_trailing_slashes (src_temp
);
179 if (lstatat (fd1
, src_temp
, &src_st
))
181 rename_errno
= errno
;
184 if (S_ISLNK (src_st
.st_mode
))
189 dst_temp
= strdup (dst
);
192 rename_errno
= ENOMEM
;
195 strip_trailing_slashes (dst_temp
);
196 if (lstatat (fd2
, dst_temp
, &dst_st
))
200 rename_errno
= errno
;
204 else if (S_ISLNK (dst_st
.st_mode
))
207 # endif /* RENAME_TRAILING_SLASH_SOURCE_BUG */
209 /* renameat does not honor trailing / on Solaris 10. Solve it in a
210 similar manner to rename. No need to worry about bugs not present
211 on Solaris, since all other systems either lack renameat or honor
212 trailing slash correctly. */
214 ret_val
= renameat (fd1
, src_temp
, fd2
, dst_temp
);
215 rename_errno
= errno
;
222 errno
= rename_errno
;
225 #else /* !HAVE_RENAMEAT */
227 /* RENAME_NOREPLACE is the only flag currently supported. */
228 if (flags
& ~RENAME_NOREPLACE
)
229 return errno_fail (ENOTSUP
);
230 return at_func2 (fd1
, src
, fd2
, dst
, flags
? rename_noreplace
: rename
);
232 #endif /* !HAVE_RENAMEAT */