blob: a4bb5bee43636f1f93517527c329a9fccc1eb0c6 [file] [log] [blame]
// 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