| // Copyright 2020 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "gn/file_writer.h" | |
| #include "base/files/file_path.h" | |
| #include "base/logging.h" | |
| #include "gn/filesystem_utils.h" | |
| #if defined(OS_WIN) | |
| #include <windows.h> | |
| #include "base/strings/utf_string_conversions.h" | |
| #include "util/sys_info.h" | |
| #else | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| #include "base/posix/eintr_wrapper.h" | |
| #endif | |
| FileWriter::~FileWriter() = default; | |
| #if defined(OS_WIN) | |
| bool FileWriter::Create(const base::FilePath& file_path) { | |
| // On Windows, provide a custom implementation of base::WriteFile. Sometimes | |
| // the base version fails, especially on the bots. The guess is that Windows | |
| // Defender or other antivirus programs still have the file open (after | |
| // checking for the read) when the write happens immediately after. This | |
| // version opens with FILE_SHARE_READ (normally not what you want when | |
| // replacing the entire contents of the file) which lets us continue even if | |
| // another program has the file open for reading. See | |
| // http://crbug.com/468437 | |
| const std::u16string& path = file_path.value(); | |
| file_path_ = base::UTF16ToUTF8(path); | |
| file_ = base::win::ScopedHandle( | |
| ::CreateFile(reinterpret_cast<LPCWSTR>(path.c_str()), GENERIC_WRITE, | |
| FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL)); | |
| valid_ = file_.IsValid(); | |
| if (!valid_) { | |
| PLOG(ERROR) << "CreateFile failed for path " << file_path_; | |
| // Determine whether the path need long path support. | |
| if (path.size() >= MAX_PATH && !IsLongPathsSupportEnabled()) { | |
| LOG(ERROR) | |
| << "You might need to enable Long Path Support on Windows: " | |
| << "https://learn.microsoft.com/en-us/windows/win32/fileio/" | |
| "maximum-file-path-limitation?tabs=registry#enable-long-paths" | |
| "-in-windows-10-version-1607-and-later"; | |
| } | |
| } | |
| return valid_; | |
| } | |
| bool FileWriter::Write(std::string_view str) { | |
| if (!valid_) | |
| return false; | |
| DWORD written; | |
| BOOL result = | |
| ::WriteFile(file_.Get(), str.data(), str.size(), &written, nullptr); | |
| if (!result) { | |
| PLOG(ERROR) << "writing file " << file_path_ << " failed"; | |
| valid_ = false; | |
| return false; | |
| } | |
| if (static_cast<size_t>(written) != str.size()) { | |
| PLOG(ERROR) << "wrote " << written << " bytes to " << file_path_ | |
| << " expected " << str.size(); | |
| valid_ = false; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool FileWriter::Close() { | |
| // NOTE: file_.Close() is not used here because it cannot return an error. | |
| HANDLE handle = file_.Take(); | |
| if (handle && !::CloseHandle(handle)) | |
| return false; | |
| return valid_; | |
| } | |
| #else // !OS_WIN | |
| bool FileWriter::Create(const base::FilePath& file_path) { | |
| fd_.reset(HANDLE_EINTR(::creat(file_path.value().c_str(), 0666))); | |
| valid_ = fd_.is_valid(); | |
| if (!valid_) { | |
| PLOG(ERROR) << "creat() failed for path " << file_path.value(); | |
| } | |
| return valid_; | |
| } | |
| bool FileWriter::Write(std::string_view str) { | |
| if (!valid_) | |
| return false; | |
| while (!str.empty()) { | |
| ssize_t written = HANDLE_EINTR(::write(fd_.get(), str.data(), str.size())); | |
| if (written <= 0) { | |
| valid_ = false; | |
| return false; | |
| } | |
| str.remove_prefix(static_cast<size_t>(written)); | |
| } | |
| return true; | |
| } | |
| bool FileWriter::Close() { | |
| // The ScopedFD reset() method will crash on EBADF and ignore other errors | |
| // intentionally, so no need to check anything here. | |
| fd_.reset(); | |
| return valid_; | |
| } | |
| #endif // !OS_WIN |