diff options
author | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2019-03-14 12:38:01 -0500 |
---|---|---|
committer | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2023-05-05 21:21:40 -0500 |
commit | 8ef0e484b6733fa07bfcfde8cd16dd87d4507c89 (patch) | |
tree | ec3e3129c4b08723d62cc6481413470f91a9be0a /src | |
parent | 1963fa800f4554d729f14f7f3a01a5b4af4dfbc1 (diff) | |
download | musl-8ef0e484b6733fa07bfcfde8cd16dd87d4507c89.tar.gz musl-8ef0e484b6733fa07bfcfde8cd16dd87d4507c89.tar.bz2 musl-8ef0e484b6733fa07bfcfde8cd16dd87d4507c89.tar.xz musl-8ef0e484b6733fa07bfcfde8cd16dd87d4507c89.zip |
renameat: finish bringing interface to standard behaviour
Diffstat (limited to 'src')
-rw-r--r-- | src/unistd/renameat.c | 56 |
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); |