| // 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" | 
 | #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 | 
 |   file_path_ = base::UTF16ToUTF8(file_path.value()); | 
 |   file_ = base::win::ScopedHandle(::CreateFile( | 
 |       reinterpret_cast<LPCWSTR>(file_path.value().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_; | 
 |   } | 
 |   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 |