summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2019-03-14 12:38:01 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2023-05-05 21:21:40 -0500
commit8ef0e484b6733fa07bfcfde8cd16dd87d4507c89 (patch)
treeec3e3129c4b08723d62cc6481413470f91a9be0a
parent1963fa800f4554d729f14f7f3a01a5b4af4dfbc1 (diff)
downloadmusl-8ef0e484b6733fa07bfcfde8cd16dd87d4507c89.tar.gz
musl-8ef0e484b6733fa07bfcfde8cd16dd87d4507c89.tar.bz2
musl-8ef0e484b6733fa07bfcfde8cd16dd87d4507c89.tar.xz
musl-8ef0e484b6733fa07bfcfde8cd16dd87d4507c89.zip
renameat: finish bringing interface to standard behaviour
-rw-r--r--src/unistd/renameat.c56
1 files changed, 39 insertions, 17 deletions
diff --git a/src/unistd/renameat.c b/src/unistd/renameat.c
index d0f31c21..e2f03d39 100644
--- a/src/unistd/renameat.c
+++ b/src/unistd/renameat.c
@@ -9,12 +9,31 @@
#include <unistd.h>
#include "syscall.h"
+static inline int test_symlink_dirness(int fd, const char *pathname)
+{
+ struct stat statbuf;
+ if (fstatat(fd, pathname, &statbuf, AT_SYMLINK_NOFOLLOW) == -1) {
+ return 1;
+ }
+ if (S_ISLNK(statbuf.st_mode)) {
+ if (fstatat(fd, pathname, &statbuf, 0) == -1) return 1;
+
+ if (S_ISDIR(statbuf.st_mode)) return 0;
+ else {
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+ return 1;
+}
+
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;
+ struct stat oldstat, newstat;
+ int r;
if ((old_size = strlen(old)) > PATH_MAX || \
(new_size = strlen(new)) > PATH_MAX) {
@@ -22,11 +41,18 @@ int renameat(int oldfd, const char *old, int newfd, const char *new)
return -1;
}
- if (strlen(old) == 0 || strlen(new) == 0) {
+ if (old_size == 0 || new_size == 0) {
errno = ENOENT;
return -1;
}
+ /* Test equality of old and new.
+ If they both resolve to the same dentry, we do nothing. */
+ if (fstatat(oldfd, old, &oldstat, 0) == 0 && \
+ fstatat(newfd, new, &newstat, 0) == 0 && \
+ oldstat.st_dev == newstat.st_dev && \
+ oldstat.st_ino == newstat.st_ino) return 0;
+
strcpy(old_copy, old);
strcpy(new_copy, new);
@@ -50,21 +76,17 @@ int renameat(int oldfd, const char *old, int newfd, const char *new)
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;
- }
- }
+ r = test_symlink_dirness(oldfd, old_copy);
+ if (r == 0) old = old_copy;
+ else if (r == -1) return -1;
+ }
+
+ if (new[new_size - 1] == '/') {
+ strcpy(new_copy, new);
+ new_copy[new_size - 1] = '\0';
+ r = test_symlink_dirness(newfd, new_copy);
+ if (r == 0) new = new_copy;
+ else if (r == -1) return -1;
}
return syscall(SYS_renameat, oldfd, old, newfd, new);