summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2017-06-29 14:27:48 -0700
committerThiago Macieira <thiago.macieira@intel.com>2017-08-04 17:30:33 +0000
commit88c30618d512864adab0733b58dd5f48b7f98f74 (patch)
tree0c28e9d39de9bb0a2076fc83e9f3501c1b2c9587 /src/corelib
parent4934138be29868bdf848e2aeb6c6163819ab3a5c (diff)
QFileSystemEngine::renameFile: use calls that don't overwrite
The renameat2(2) Linux system call, new in 3.16, allows for the atomic renaming of a file if and only if it won't clobber an existing file. None of the Linux libcs have enabled this syscall as an API, so we use syscall(3) to place the call. If your libc has SYS_renameat2 but your kernel doesn't support it, we'll keep issuing the unknown syscall, every time. Users in that situation should upgrade (3.16 is from 2014). On Darwin, there's a similar renameatx_np (guessing "np" stands for "non-portable"). I haven't found anything similar on the other BSDs. Change-Id: I1eba2b016de74620bfc8fffd14ccb4e455a3ec9e Reviewed-by: David Faure <david.faure@kdab.com>
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/configure.json14
-rw-r--r--src/corelib/global/minimum-linux.S5
-rw-r--r--src/corelib/global/qconfig-bootstrapped.h1
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp38
4 files changed, 57 insertions, 1 deletions
diff --git a/src/corelib/configure.json b/src/corelib/configure.json
index 44d6ccfdf5..61fc2e16c9 100644
--- a/src/corelib/configure.json
+++ b/src/corelib/configure.json
@@ -379,6 +379,15 @@
]
}
},
+ "renameat2": {
+ "label": "renameat2()",
+ "type": "compile",
+ "test": {
+ "head": "#define _ATFILE_SOURCE 1",
+ "include": [ "fcntl.h", "stdio.h" ],
+ "main": "renameat2(AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_NOREPLACE | RENAME_WHITEOUT);"
+ }
+ },
"syslog": {
"label": "syslog",
"type": "compile",
@@ -576,6 +585,11 @@
"condition": "features.statemachine",
"output": [ "publicFeature" ]
},
+ "renameat2": {
+ "label": "renameat2()",
+ "condition": "config.linux && tests.renameat2",
+ "output": [ "privateFeature" ]
+ },
"slog2": {
"label": "slog2",
"condition": "libs.slog2",
diff --git a/src/corelib/global/minimum-linux.S b/src/corelib/global/minimum-linux.S
index 3d97ea7ec8..aa67be44ca 100644
--- a/src/corelib/global/minimum-linux.S
+++ b/src/corelib/global/minimum-linux.S
@@ -86,6 +86,7 @@
* - eventfd 2.6.23
* - pipe2 & dup3 2.6.27
* - accept4 2.6.28
+ * - renameat2 3.16 QT_CONFIG(renameat2)
* - getrandom 3.17 QT_CONFIG(getentropy)
*/
@@ -93,6 +94,10 @@
.long 3
.long 17
.long 0
+#elif QT_CONFIG(renameat2)
+ .long 3
+ .long 16
+ .long 0
#else
.long 2
.long 6
diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h
index 398da739e1..f1b99609ce 100644
--- a/src/corelib/global/qconfig-bootstrapped.h
+++ b/src/corelib/global/qconfig-bootstrapped.h
@@ -88,6 +88,7 @@
#define QT_NO_QOBJECT
#define QT_FEATURE_process -1
#define QT_NO_SYSTEMLOCALE
+#define QT_FEATURE_renameat2 -1
#define QT_FEATURE_slog2 -1
#define QT_FEATURE_syslog -1
#define QT_FEATURE_temporaryfile 1
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index b77cea3f80..79b015938c 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -54,7 +54,6 @@
#include <stdio.h>
#include <errno.h>
-
#if defined(Q_OS_MAC)
# include <QtCore/private/qcore_mac_p.h>
# include <CoreFoundation/CFBundle.h>
@@ -78,6 +77,16 @@ Q_FORWARD_DECLARE_OBJC_CLASS(NSString);
extern "C" NSString *NSTemporaryDirectory();
#endif
+#if defined(Q_OS_LINUX)
+# include <sys/syscall.h>
+# include <linux/fs.h>
+
+# if !QT_CONFIG(renameat2) && defined(SYS_renameat2)
+static int renameat2(int oldfd, const char *oldpath, int newfd, const char *newpath, unsigned flags)
+{ return syscall(SYS_renameat2, oldfd, oldpath, newfd, newpath, flags); }
+# endif
+#endif
+
QT_BEGIN_NAMESPACE
#if defined(Q_OS_DARWIN)
@@ -653,6 +662,33 @@ bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSy
{
QFileSystemEntry::NativePath srcPath = source.nativeFilePath();
QFileSystemEntry::NativePath tgtPath = target.nativeFilePath();
+
+#if defined(RENAME_NOREPLACE) && (QT_CONFIG(renameat2) || defined(SYS_renameat2))
+ if (renameat2(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_NOREPLACE) == 0)
+ return true;
+
+ // If we're using syscall(), check for ENOSYS;
+ // if renameat2 came from libc, we don't accept ENOSYS.
+ if (QT_CONFIG(renameat2) || errno != ENOSYS) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+#endif
+#if defined(Q_OS_DARWIN) && defined(RENAME_EXCL)
+ const auto current = QOperatingSystemVersion::current();
+ if (current >= QOperatingSystemVersion::MacOSSierra ||
+ current >= QOperatingSystemVersion(QOperatingSystemVersion::IOS, 10) ||
+ current >= QOperatingSystemVersion(QOperatingSystemVersion::TvOS, 10) ||
+ current >= QOperatingSystemVersion(QOperatingSystemVersion::WatchOS, 3)) {
+ if (renameatx_np(AT_FDCWD, srcPath, AT_FDCWD, tgtPath, RENAME_EXCL) == 0)
+ return true;
+ if (errno != ENOTSUP) {
+ error = QSystemError(errno, QSystemError::StandardLibraryError);
+ return false;
+ }
+ }
+#endif
+
if (::link(srcPath, tgtPath) == 0) {
if (::unlink(srcPath) == 0)
return true;