diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2017-06-29 14:03:26 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2017-08-04 17:29:52 +0000 |
commit | 363dc9146e53e24172bb9c0ae68100a8543bd9ae (patch) | |
tree | 49374e22f5986e9a99e46e0f73a187f55ef67bc9 /src/corelib/io/qfilesystemengine_unix.cpp | |
parent | 74197140be68fd7fe54c3a346a0e769928d7a3ea (diff) |
QFSFileEngine: make rename() on Unix not overwrite
The rename(2) system call overwrites, so instead of using it, we try to
use the link/unlink pair. This works for regular cases, but can fail if
trying to change case in case-insensitive filesystems, if we're
operating on a non-Unix filesystem (FAT) or, on Linux, if the file
doesn't belong to the calling user (BSDs permit this). For those cases,
we fall back to rename(2).
That means there's a race condition if a new file is created there. But
we at least reduce the likelihood of that happening for regular files.
Change-Id: I1eba2b016de74620bfc8fffd14ccb38fd929e5aa
Reviewed-by: David Faure <david.faure@kdab.com>
Diffstat (limited to 'src/corelib/io/qfilesystemengine_unix.cpp')
-rw-r--r-- | src/corelib/io/qfilesystemengine_unix.cpp | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index b0c23e3f82..b77cea3f80 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -651,6 +651,50 @@ bool QFileSystemEngine::copyFile(const QFileSystemEntry &source, const QFileSyst //static bool QFileSystemEngine::renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) { + QFileSystemEntry::NativePath srcPath = source.nativeFilePath(); + QFileSystemEntry::NativePath tgtPath = target.nativeFilePath(); + if (::link(srcPath, tgtPath) == 0) { + if (::unlink(srcPath) == 0) + return true; + + // if we managed to link but can't unlink the source, it's likely + // it's in a directory we don't have write access to; fail the + // renaming instead + int savedErrno = errno; + + // this could fail too, but there's nothing we can do about it now + ::unlink(tgtPath); + + error = QSystemError(savedErrno, QSystemError::StandardLibraryError); + return false; + } + + switch (errno) { + case EACCES: + case EEXIST: + case ENAMETOOLONG: + case ENOENT: + case ENOTDIR: + case EROFS: + case EXDEV: + // accept the error from link(2) (especially EEXIST) and don't retry + break; + + default: + // fall back to rename() + // ### Race condition. If a file is moved in after this, it /will/ be + // overwritten. + if (::rename(srcPath, tgtPath) == 0) + return true; + } + + error = QSystemError(errno, QSystemError::StandardLibraryError); + return false; +} + +//static +bool QFileSystemEngine::renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error) +{ if (::rename(source.nativeFilePath().constData(), target.nativeFilePath().constData()) == 0) return true; error = QSystemError(errno, QSystemError::StandardLibraryError); |