From 8ef0e484b6733fa07bfcfde8cd16dd87d4507c89 Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Thu, 14 Mar 2019 12:38:01 -0500 Subject: renameat: finish bringing interface to standard behaviour --- src/unistd/renameat.c | 56 +++++++++++++++++++++++++++++++++++---------------- 1 file 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 #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); -- cgit v1.2.3-60-g2f50