1 /* Rename a file relative to open directories.
2 Copyright (C) 2009-2017 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 <http://www.gnu.org/licenses/>. */
17 /* written by Eric Blake and Paul Eggert */
21 #include "renameat2.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). */
74 renameat2 (int fd1
, char const *src
, int fd2
, char const *dst
,
78 int r
= syscall (SYS_renameat2
, fd1
, src
, fd2
, dst
, flags
);
79 if (! (r
< 0 && (errno
== ENOSYS
|| errno
== EINVAL
)))
87 char *src_temp
= (char *) src
;
88 char *dst_temp
= (char *) dst
;
92 int rename_errno
= ENOTDIR
;
95 bool dst_found_nonexistent
= false;
99 /* RENAME_NOREPLACE is the only flag currently supported. */
100 if (flags
& ~RENAME_NOREPLACE
)
101 return errno_fail (ENOTSUP
);
104 /* This has a race between the call to lstatat and the calls to
106 if (lstatat (fd2
, dst
, &dst_st
) == 0 || errno
== EOVERFLOW
)
107 return errno_fail (EEXIST
);
110 dst_found_nonexistent
= true;
114 /* Let strace see any ENOENT failure. */
115 src_len
= strlen (src
);
116 dst_len
= strlen (dst
);
117 if (!src_len
|| !dst_len
)
118 return renameat (fd1
, src
, fd2
, dst
);
120 src_slash
= src
[src_len
- 1] == '/';
121 dst_slash
= dst
[dst_len
- 1] == '/';
122 if (!src_slash
&& !dst_slash
)
123 return renameat (fd1
, src
, fd2
, dst
);
125 /* Presence of a trailing slash requires directory semantics. If
126 the source does not exist, or if the destination cannot be turned
127 into a directory, give up now. Otherwise, strip trailing slashes
128 before calling rename. */
129 if (lstatat (fd1
, src
, &src_st
))
131 if (dst_found_nonexistent
)
133 if (!S_ISDIR (src_st
.st_mode
))
134 return errno_fail (ENOENT
);
136 else if (lstatat (fd2
, dst
, &dst_st
))
138 if (errno
!= ENOENT
|| !S_ISDIR (src_st
.st_mode
))
141 else if (!S_ISDIR (dst_st
.st_mode
))
142 return errno_fail (ENOTDIR
);
143 else if (!S_ISDIR (src_st
.st_mode
))
144 return errno_fail (EISDIR
);
146 # if RENAME_TRAILING_SLASH_SOURCE_BUG
147 /* See the lengthy comment in rename.c why Solaris 9 is forced to
148 GNU behavior, while Solaris 10 is left with POSIX behavior,
149 regarding symlinks with trailing slash. */
153 src_temp
= strdup (src
);
156 /* Rather than rely on strdup-posix, we set errno ourselves. */
157 rename_errno
= ENOMEM
;
160 strip_trailing_slashes (src_temp
);
161 if (lstatat (fd1
, src_temp
, &src_st
))
163 rename_errno
= errno
;
166 if (S_ISLNK (src_st
.st_mode
))
171 dst_temp
= strdup (dst
);
174 rename_errno
= ENOMEM
;
177 strip_trailing_slashes (dst_temp
);
178 if (lstatat (fd2
, dst_temp
, &dst_st
))
182 rename_errno
= errno
;
186 else if (S_ISLNK (dst_st
.st_mode
))
189 # endif /* RENAME_TRAILING_SLASH_SOURCE_BUG */
191 /* renameat does not honor trailing / on Solaris 10. Solve it in a
192 similar manner to rename. No need to worry about bugs not present
193 on Solaris, since all other systems either lack renameat or honor
194 trailing slash correctly. */
196 ret_val
= renameat (fd1
, src_temp
, fd2
, dst_temp
);
197 rename_errno
= errno
;
204 errno
= rename_errno
;
207 #else /* !HAVE_RENAMEAT */
209 /* RENAME_NOREPLACE is the only flag currently supported. */
210 if (flags
& ~RENAME_NOREPLACE
)
211 return errno_fail (ENOTSUP
);
212 return at_func2 (fd1
, src
, fd2
, dst
, flags
? rename_noreplace
: rename
);
214 #endif /* !HAVE_RENAMEAT */