//===--- FileSystem.cpp - Extra helpers for manipulating files ------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/Basic/FileSystem.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Process.h" using namespace swift; namespace { class OpenFileRAII { static const int INVALID_FD = -1; public: int fd = INVALID_FD; ~OpenFileRAII() { if (fd != INVALID_FD) llvm::sys::Process::SafelyCloseFileDescriptor(fd); } }; } std::error_code swift::moveFileIfDifferent(const llvm::Twine &source, const llvm::Twine &destination) { namespace fs = llvm::sys::fs; // First check for a self-move. if (fs::equivalent(source, destination)) return std::error_code(); OpenFileRAII sourceFile; fs::file_status sourceStatus; if (std::error_code error = fs::openFileForRead(source, sourceFile.fd)) { // If we can't open the source file, fail. return error; } if (std::error_code error = fs::status(sourceFile.fd, sourceStatus)) { // If we can't stat the source file, fail. return error; } OpenFileRAII destFile; fs::file_status destStatus; bool couldReadDest = !fs::openFileForRead(destination, destFile.fd); if (couldReadDest) couldReadDest = !fs::status(destFile.fd, destStatus); // If we could read the destination file, and it matches the source file in // size, they may be the same. Do an actual comparison of the contents. if (couldReadDest && sourceStatus.getSize() == destStatus.getSize()) { uint64_t size = sourceStatus.getSize(); bool same = false; if (size == 0) { same = true; } else { std::error_code sourceRegionErr; fs::mapped_file_region sourceRegion(sourceFile.fd, fs::mapped_file_region::readonly, size, 0, sourceRegionErr); if (sourceRegionErr) return sourceRegionErr; std::error_code destRegionErr; fs::mapped_file_region destRegion(destFile.fd, fs::mapped_file_region::readonly, size, 0, destRegionErr); if (!destRegionErr) { same = (0 == memcmp(sourceRegion.const_data(), destRegion.const_data(), size)); } } // If the file contents are the same, we are done. Just delete the source. if (same) return fs::remove(source); } // If we get here, we weren't able to prove that the files are the same. return fs::rename(source, destination); }