cygwin: Implement renameat2

Define the RENAME_NOREPLACE flag in <cygwin/fs.h> as defined on Linux
in <linux/fs.h>.  The other RENAME_* flags defined on Linux are not
supported.
This commit is contained in:
Ken Brown 2017-08-17 09:12:15 -04:00 committed by Corinna Vinschen
parent c496cbb6bd
commit f665b1cef3
5 changed files with 54 additions and 7 deletions

View File

@ -384,6 +384,9 @@ int _EXFUN(vdprintf, (int, const char *__restrict, __VALIST)
#endif #endif
#if __ATFILE_VISIBLE #if __ATFILE_VISIBLE
int _EXFUN(renameat, (int, const char *, int, const char *)); int _EXFUN(renameat, (int, const char *, int, const char *));
# ifdef __CYGWIN__
int _EXFUN(renameat2, (int, const char *, int, const char *, unsigned int));
# endif
#endif #endif
/* /*

View File

@ -1168,6 +1168,7 @@ remquof NOSIGFE
remquol NOSIGFE remquol NOSIGFE
rename SIGFE rename SIGFE
renameat SIGFE renameat SIGFE
renameat2 SIGFE
res_close = __res_close SIGFE res_close = __res_close SIGFE
res_init = __res_init SIGFE res_init = __res_init SIGFE
res_mkquery = __res_mkquery SIGFE res_mkquery = __res_mkquery SIGFE

View File

@ -19,4 +19,10 @@ details. */
#define BLKPBSZGET 0x0000127b #define BLKPBSZGET 0x0000127b
#define BLKGETSIZE64 0x00041268 #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 #endif

View File

@ -481,12 +481,13 @@ details. */
314: Export explicit_bzero. 314: Export explicit_bzero.
315: Export pthread_mutex_timedlock. 315: Export pthread_mutex_timedlock.
316: Export pthread_rwlock_timedrdlock, pthread_rwlock_timedwrlock. 316: Export pthread_rwlock_timedrdlock, pthread_rwlock_timedwrlock.
317: Export renameat2.
Note that we forgot to bump the api for ualarm, strtoll, strtoull, Note that we forgot to bump the api for ualarm, strtoll, strtoull,
sigaltstack, sethostname. */ sigaltstack, sethostname. */
#define CYGWIN_VERSION_API_MAJOR 0 #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 /* There is also a compatibity version number associated with the shared memory
regions. It is incremented when incompatible changes are made to the shared regions. It is incremented when incompatible changes are made to the shared

View File

@ -60,6 +60,7 @@ details. */
#include "tls_pbuf.h" #include "tls_pbuf.h"
#include "sync.h" #include "sync.h"
#include "child_info.h" #include "child_info.h"
#include <cygwin/fs.h> /* needed for RENAME_NOREPLACE */
#undef _close #undef _close
#undef _lseek #undef _lseek
@ -2048,14 +2049,19 @@ nt_path_has_executable_suffix (PUNICODE_STRING upath)
return false; return false;
} }
extern "C" int /* If newpath names an existing file and the RENAME_NOREPLACE flag is
rename (const char *oldpath, const char *newpath) 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; tmp_pathbuf tp;
int res = -1; int res = -1;
path_conv oldpc, newpc, new2pc, *dstpc, *removepc = NULL; path_conv oldpc, newpc, new2pc, *dstpc, *removepc = NULL;
bool old_dir_requested = false, new_dir_requested = false; bool old_dir_requested = false, new_dir_requested = false;
bool old_explicit_suffix = false, new_explicit_suffix = false; bool old_explicit_suffix = false, new_explicit_suffix = false;
bool noreplace = flags & RENAME_NOREPLACE;
size_t olen, nlen; size_t olen, nlen;
bool equal_path; bool equal_path;
NTSTATUS status = STATUS_SUCCESS; NTSTATUS status = STATUS_SUCCESS;
@ -2068,6 +2074,12 @@ rename (const char *oldpath, const char *newpath)
__try __try
{ {
if (flags & ~RENAME_NOREPLACE)
/* RENAME_NOREPLACE is the only flag currently supported. */
{
set_errno (EINVAL);
__leave;
}
if (!*oldpath || !*newpath) if (!*oldpath || !*newpath)
{ {
/* Reject rename("","x"), rename("x",""). */ /* Reject rename("","x"), rename("x",""). */
@ -2337,6 +2349,13 @@ rename (const char *oldpath, const char *newpath)
__leave; __leave;
} }
/* Should we replace an existing file? */
if ((removepc || dstpc->exists ()) && noreplace)
{
set_errno (EEXIST);
__leave;
}
/* Opening the file must be part of the transaction. It's not sufficient /* Opening the file must be part of the transaction. It's not sufficient
to call only NtSetInformationFile under the transaction. Therefore we to call only NtSetInformationFile under the transaction. Therefore we
have to start the transaction here, if necessary. */ have to start the transaction here, if necessary. */
@ -2491,11 +2510,15 @@ rename (const char *oldpath, const char *newpath)
__leave; __leave;
} }
pfri = (PFILE_RENAME_INFORMATION) tp.w_get (); pfri = (PFILE_RENAME_INFORMATION) tp.w_get ();
pfri->ReplaceIfExists = TRUE; pfri->ReplaceIfExists = !noreplace;
pfri->RootDirectory = NULL; pfri->RootDirectory = NULL;
pfri->FileNameLength = dstpc->get_nt_native_path ()->Length; pfri->FileNameLength = dstpc->get_nt_native_path ()->Length;
memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer, memcpy (&pfri->FileName, dstpc->get_nt_native_path ()->Buffer,
pfri->FileNameLength); 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, status = NtSetInformationFile (fh, &io, pfri,
sizeof *pfri + pfri->FileNameLength, sizeof *pfri + pfri->FileNameLength,
FileRenameInformation); FileRenameInformation);
@ -2578,6 +2601,12 @@ rename (const char *oldpath, const char *newpath)
return res; return res;
} }
extern "C" int
rename (const char *oldpath, const char *newpath)
{
return rename2 (oldpath, newpath, 0);
}
extern "C" int extern "C" int
system (const char *cmdstring) system (const char *cmdstring)
{ {
@ -4719,8 +4748,8 @@ readlinkat (int dirfd, const char *__restrict pathname, char *__restrict buf,
} }
extern "C" int extern "C" int
renameat (int olddirfd, const char *oldpathname, renameat2 (int olddirfd, const char *oldpathname,
int newdirfd, const char *newpathname) int newdirfd, const char *newpathname, unsigned int flags)
{ {
tmp_pathbuf tp; tmp_pathbuf tp;
__try __try
@ -4731,13 +4760,20 @@ renameat (int olddirfd, const char *oldpathname,
char *newpath = tp.c_get (); char *newpath = tp.c_get ();
if (gen_full_path_at (newpath, newdirfd, newpathname)) if (gen_full_path_at (newpath, newdirfd, newpathname))
__leave; __leave;
return rename (oldpath, newpath); return rename2 (oldpath, newpath, flags);
} }
__except (EFAULT) {} __except (EFAULT) {}
__endtry __endtry
return -1; 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 extern "C" int
scandirat (int dirfd, const char *pathname, struct dirent ***namelist, scandirat (int dirfd, const char *pathname, struct dirent ***namelist,
int (*select) (const struct dirent *), int (*select) (const struct dirent *),