1 /* Rename a file relative to open directories.
2 Copyright (C) 2009-2021 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
);
91 if (! (ret_val
< 0 && (err
== EINVAL
|| err
== ENOSYS
|| err
== ENOTSUP
)))
96 # if defined RENAME_EXCL /* macOS */
101 char *src_temp
= (char *) src
;
102 char *dst_temp
= (char *) dst
;
105 int rename_errno
= ENOTDIR
;
108 bool dst_found_nonexistent
= false;
110 /* Check the flags. */
111 # if defined RENAME_EXCL
112 /* We can support RENAME_EXCHANGE and RENAME_NOREPLACE. */
113 if (flags
& ~(RENAME_EXCHANGE
| RENAME_NOREPLACE
))
115 /* RENAME_NOREPLACE is the only flag currently supported. */
116 if (flags
& ~RENAME_NOREPLACE
)
118 return errno_fail (ENOTSUP
);
120 # if defined RENAME_EXCL
121 uflags
= ((flags
& RENAME_EXCHANGE
? RENAME_SWAP
: 0)
122 | (flags
& RENAME_NOREPLACE
? RENAME_EXCL
: 0));
125 # if !defined RENAME_EXCL
126 if ((flags
& RENAME_NOREPLACE
) != 0)
128 /* This has a race between the call to lstatat and the calls to
130 if (lstatat (fd2
, dst
, &dst_st
) == 0 || errno
== EOVERFLOW
)
131 return errno_fail (EEXIST
);
134 dst_found_nonexistent
= true;
138 /* Let strace see any ENOENT failure. */
139 src_len
= strlen (src
);
140 dst_len
= strlen (dst
);
141 if (!src_len
|| !dst_len
)
142 # if defined RENAME_EXCL
143 return renameatx_np (fd1
, src
, fd2
, dst
, uflags
);
145 return renameat (fd1
, src
, fd2
, dst
);
148 src_slash
= src
[src_len
- 1] == '/';
149 dst_slash
= dst
[dst_len
- 1] == '/';
150 if (!src_slash
&& !dst_slash
)
151 # if defined RENAME_EXCL
152 return renameatx_np (fd1
, src
, fd2
, dst
, uflags
);
154 return renameat (fd1
, src
, fd2
, dst
);
157 /* Presence of a trailing slash requires directory semantics. If
158 the source does not exist, or if the destination cannot be turned
159 into a directory, give up now. Otherwise, strip trailing slashes
160 before calling rename. */
161 if (lstatat (fd1
, src
, &src_st
))
163 if (dst_found_nonexistent
)
165 if (!S_ISDIR (src_st
.st_mode
))
166 return errno_fail (ENOENT
);
168 else if (lstatat (fd2
, dst
, &dst_st
))
170 if (errno
!= ENOENT
|| !S_ISDIR (src_st
.st_mode
))
173 else if (!S_ISDIR (dst_st
.st_mode
))
174 return errno_fail (ENOTDIR
);
175 else if (!S_ISDIR (src_st
.st_mode
))
176 return errno_fail (EISDIR
);
178 # if RENAME_TRAILING_SLASH_SOURCE_BUG
179 /* See the lengthy comment in rename.c why Solaris 9 is forced to
180 GNU behavior, while Solaris 10 is left with POSIX behavior,
181 regarding symlinks with trailing slash. */
185 src_temp
= strdup (src
);
188 /* Rather than rely on strdup-posix, we set errno ourselves. */
189 rename_errno
= ENOMEM
;
192 strip_trailing_slashes (src_temp
);
193 if (lstatat (fd1
, src_temp
, &src_st
))
195 rename_errno
= errno
;
198 if (S_ISLNK (src_st
.st_mode
))
203 dst_temp
= strdup (dst
);
206 rename_errno
= ENOMEM
;
209 strip_trailing_slashes (dst_temp
);
210 if (lstatat (fd2
, dst_temp
, &dst_st
))
214 rename_errno
= errno
;
218 else if (S_ISLNK (dst_st
.st_mode
))
221 # endif /* RENAME_TRAILING_SLASH_SOURCE_BUG */
223 /* renameat does not honor trailing / on Solaris 10. Solve it in a
224 similar manner to rename. No need to worry about bugs not present
225 on Solaris, since all other systems either lack renameat or honor
226 trailing slash correctly. */
228 # if defined RENAME_EXCL
229 ret_val
= renameatx_np (fd1
, src_temp
, fd2
, dst_temp
, uflags
);
231 ret_val
= renameat (fd1
, src_temp
, fd2
, dst_temp
);
233 rename_errno
= errno
;
240 errno
= rename_errno
;
243 #else /* !HAVE_RENAMEAT */
245 /* RENAME_NOREPLACE is the only flag currently supported. */
246 if (flags
& ~RENAME_NOREPLACE
)
247 return errno_fail (ENOTSUP
);
248 return at_func2 (fd1
, src
, fd2
, dst
, flags
? rename_noreplace
: rename
);
250 #endif /* !HAVE_RENAMEAT */