From d5798b371ceabfe6a7912472edd32da1ebd7dcb7 Mon Sep 17 00:00:00 2001 From: Ken Brown Date: Thu, 17 Aug 2017 09:12:15 -0400 Subject: [PATCH] cygwin: Implement renameat2 Define the RENAME_NOREPLACE flag in as defined on Linux in . The other RENAME_* flags defined on Linux are not supported. --- newlib/libc/include/stdio.h | 3 ++ winsup/cygwin/common.din | 1 + winsup/cygwin/include/cygwin/fs.h | 6 +++ winsup/cygwin/include/cygwin/version.h | 3 +- winsup/cygwin/syscalls.cc | 67 +++++++++++++++++++++++++++++++--- 5 files changed, 73 insertions(+), 7 deletions(-) diff --git a/newlib/libc/include/stdio.h b/newlib/libc/include/stdio.h index 5d8cb1092..331a1cf07 100644 --- a/newlib/libc/include/stdio.h +++ b/newlib/libc/include/stdio.h @@ -384,6 +384,9 @@ int _EXFUN(vdprintf, (int, const char *__restrict, __VALIST) #endif #if __ATFILE_VISIBLE int _EXFUN(renameat, (int, const char *, int, const char *)); +# ifdef __CYGWIN__ +int _EXFUN(renameat2, (int, const char *, int, const char *, unsigned int)); +# endif #endif /* diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index 8da432b8a..ca6ff3cf9 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -1168,6 +1168,7 @@ remquof NOSIGFE remquol NOSIGFE rename SIGFE renameat SIGFE +renameat2 SIGFE res_close = __res_close SIGFE res_init = __res_init SIGFE res_mkquery = __res_mkquery SIGFE diff --git a/winsup/cygwin/include/cygwin/fs.h b/winsup/cygwin/include/cygwin/fs.h index f606ffc39..48b0cca45 100644 --- a/winsup/cygwin/include/cygwin/fs.h +++ b/winsup/cygwin/include/cygwin/fs.h @@ -19,4 +19,10 @@ details. */ #define BLKPBSZGET 0x0000127b #define BLKGETSIZE64 0x00041268 +/* Flags for renameat2, from /usr/include/linux/fs.h. For now we + support only RENAME_NOREPLACE. */ +#define RENAME_NOREPLACE (1 << 0) +/* #define RENAME_EXCHANGE (1 << 1) */ +/* #define RENAME_WHITEOUT (1 << 2) */ + #endif diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index efd4ac017..7686a6865 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -481,12 +481,13 @@ details. */ 314: Export explicit_bzero. 315: Export pthread_mutex_timedlock. 316: Export pthread_rwlock_timedrdlock, pthread_rwlock_timedwrlock. + 317: Export renameat2. Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 316 +#define CYGWIN_VERSION_API_MINOR 317 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible changes are made to the shared diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 885931632..2758bb776 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -60,6 +60,7 @@ details. */ #include "tls_pbuf.h" #include "sync.h" #include "child_info.h" +#include /* needed for RENAME_NOREPLACE */ #undef _close #undef _lseek @@ -2048,14 +2049,19 @@ nt_path_has_executable_suffix (PUNICODE_STRING upath) return false; } -extern "C" int -rename (const char *oldpath, const char *newpath) +/* If newpath names an existing file and the RENAME_NOREPLACE flag is + specified, fail with EEXIST. Exception: Don't fail if the purpose + of the rename is just to change the case of oldpath on a + case-insensitive file system. */ +static int +rename2 (const char *oldpath, const char *newpath, unsigned int flags) { tmp_pathbuf tp; int res = -1; path_conv oldpc, newpc, new2pc, *dstpc, *removepc = NULL; bool old_dir_requested = false, new_dir_requested = false; bool old_explicit_suffix = false, new_explicit_suffix = false; + bool noreplace = flags & RENAME_NOREPLACE; size_t olen, nlen; bool equal_path; NTSTATUS status = STATUS_SUCCESS; @@ -2068,6 +2074,12 @@ rename (const char *oldpath, const char *newpath) __try { + if (flags & ~RENAME_NOREPLACE) + /* RENAME_NOREPLACE is the only flag currently supported. */ + { + set_errno (EINVAL); + __leave; + } if (!*oldpath || !*newpath) { /* Reject rename("","x"), rename("x",""). */ @@ -2337,6 +2349,17 @@ rename (const char *oldpath, const char *newpath) __leave; } + /* If a removepc exists and RENAME_NOREPLACE has been specified, + then we want to fail with EEXIST. But dstpc points to a + non-existing file, so the setting of ReplaceIfExists below + will not cause the failure. We must therefore do it + explicitly. */ + if (removepc && noreplace) + { + set_errno (EEXIST); + __leave; + } + /* Opening the file must be part of the transaction. It's not sufficient to call only NtSetInformationFile under the transaction. Therefore we have to start the transaction here, if necessary. */ @@ -2410,6 +2433,11 @@ rename (const char *oldpath, const char *newpath) unlink_nt returns with STATUS_DIRECTORY_NOT_EMPTY. */ if (dstpc->isdir ()) { + if (noreplace) + { + set_errno (EEXIST); + __leave; + } status = unlink_nt (*dstpc); if (!NT_SUCCESS (status)) { @@ -2423,6 +2451,11 @@ rename (const char *oldpath, const char *newpath) due to a mangled suffix. */ else if (!removepc && dstpc->has_attribute (FILE_ATTRIBUTE_READONLY)) { + if (noreplace) + { + set_errno (EEXIST); + __leave; + } status = NtOpenFile (&nfh, FILE_WRITE_ATTRIBUTES, dstpc->get_object_attr (attr, sec_none_nih), &io, FILE_SHARE_VALID_FLAGS, @@ -2491,11 +2524,15 @@ rename (const char *oldpath, const char *newpath) __leave; } pfri = (PFILE_RENAME_INFORMATION) tp.w_get (); - pfri->ReplaceIfExists = TRUE; + pfri->ReplaceIfExists = !noreplace; pfri->RootDirectory = NULL; pfri->FileNameLength = dstpc->get_nt_native_path ()->Length; memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer, pfri->FileNameLength); + /* If dstpc points to an existing file and RENAME_NOREPLACE has + been specified, then we should get NT error + STATUS_OBJECT_NAME_COLLISION ==> Win32 error + ERROR_ALREADY_EXISTS ==> Cygwin error EEXIST. */ status = NtSetInformationFile (fh, &io, pfri, sizeof *pfri + pfri->FileNameLength, FileRenameInformation); @@ -2509,6 +2546,11 @@ rename (const char *oldpath, const char *newpath) if (status == STATUS_ACCESS_DENIED && dstpc->exists () && !dstpc->isdir ()) { + if (noremove) + { + set_errno (EEXIST); + __leave; + } bool need_open = false; if ((dstpc->fs_flags () & FILE_SUPPORTS_TRANSACTIONS) && !trans) @@ -2578,6 +2620,12 @@ rename (const char *oldpath, const char *newpath) return res; } +extern "C" int +rename (const char *oldpath, const char *newpath) +{ + return rename2 (oldpath, newpath, 0); +} + extern "C" int system (const char *cmdstring) { @@ -4719,8 +4767,8 @@ readlinkat (int dirfd, const char *__restrict pathname, char *__restrict buf, } extern "C" int -renameat (int olddirfd, const char *oldpathname, - int newdirfd, const char *newpathname) +renameat2 (int olddirfd, const char *oldpathname, + int newdirfd, const char *newpathname, unsigned int flags) { tmp_pathbuf tp; __try @@ -4731,13 +4779,20 @@ renameat (int olddirfd, const char *oldpathname, char *newpath = tp.c_get (); if (gen_full_path_at (newpath, newdirfd, newpathname)) __leave; - return rename (oldpath, newpath); + return rename2 (oldpath, newpath, flags); } __except (EFAULT) {} __endtry return -1; } +extern "C" int +renameat (int olddirfd, const char *oldpathname, + int newdirfd, const char *newpathname) +{ + return renameat2 (olddirfd, oldpathname, newdirfd, newpathname, 0); +} + extern "C" int scandirat (int dirfd, const char *pathname, struct dirent ***namelist, int (*select) (const struct dirent *), -- 2.14.1