diff options
author | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2019-03-14 11:14:38 -0500 |
---|---|---|
committer | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2023-05-05 21:21:40 -0500 |
commit | 1963fa800f4554d729f14f7f3a01a5b4af4dfbc1 (patch) | |
tree | d4f941ec1ef023a168f8ea7b15c8f48b0d38df5b /src/unistd | |
parent | 9f619f70533967a5266e61aa8657c332f9fd0327 (diff) | |
download | musl-1963fa800f4554d729f14f7f3a01a5b4af4dfbc1.tar.gz musl-1963fa800f4554d729f14f7f3a01a5b4af4dfbc1.tar.bz2 musl-1963fa800f4554d729f14f7f3a01a5b4af4dfbc1.tar.xz musl-1963fa800f4554d729f14f7f3a01a5b4af4dfbc1.zip |
renameat: further bring towards standard behaviour
Diffstat (limited to 'src/unistd')
-rw-r--r-- | src/unistd/renameat.c | 35 |
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); } |