summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2019-03-14 11:14:38 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2023-05-05 21:21:40 -0500
commit1963fa800f4554d729f14f7f3a01a5b4af4dfbc1 (patch)
treed4f941ec1ef023a168f8ea7b15c8f48b0d38df5b
parent9f619f70533967a5266e61aa8657c332f9fd0327 (diff)
downloadmusl-1963fa800f4554d729f14f7f3a01a5b4af4dfbc1.tar.gz
musl-1963fa800f4554d729f14f7f3a01a5b4af4dfbc1.tar.bz2
musl-1963fa800f4554d729f14f7f3a01a5b4af4dfbc1.tar.xz
musl-1963fa800f4554d729f14f7f3a01a5b4af4dfbc1.zip
renameat: further bring towards standard behaviour
-rw-r--r--src/unistd/renameat.c35
1 files changed, 34 insertions, 1 deletions
diff --git a/src/unistd/renameat.c b/src/unistd/renameat.c
index 07386407..d0f31c21 100644
--- a/src/unistd/renameat.c
+++ b/src/unistd/renameat.c
@@ -1,16 +1,23 @@
#include <errno.h>
+#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "syscall.h"
int renameat(int oldfd, const char *old, int newfd, const char *new)
{
char old_copy[PATH_MAX+1], new_copy[PATH_MAX+1];
char *base;
+ size_t old_size, new_size;
+ struct stat statbuf;
- if (strlen(old) > PATH_MAX || strlen(new) > PATH_MAX) {
+ if ((old_size = strlen(old)) > PATH_MAX || \
+ (new_size = strlen(new)) > PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
@@ -34,5 +41,31 @@ int renameat(int oldfd, const char *old, int newfd, const char *new)
return -1;
}
+ /* The Linux kernel will fail when attempting to rename a symlink of a
+ directory with a trailing slash. We therefore have to handle this
+ case ourselves. */
+ if (old[old_size - 1] == '/') {
+ /* Calling stat(2) on a symlink to a dir with the trailing
+ slash causes stat(2) to return the actual directory instead
+ of the symlink itself. */
+ strcpy(old_copy, old);
+ old_copy[old_size - 1] = '\0';
+ if (fstatat(oldfd, old_copy, &statbuf, AT_SYMLINK_NOFOLLOW) == -1) {
+ return -1;
+ }
+ if (S_ISLNK(statbuf.st_mode)) {
+ if (fstatat(oldfd, old, &statbuf, 0) == -1) {
+ return -1;
+ }
+ if (S_ISDIR(statbuf.st_mode)) {
+ old = old_copy;
+ } else {
+ /* may as well not waste the syscall */
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+ }
+
return syscall(SYS_renameat, oldfd, old, newfd, new);
}