1// Copyright 2020 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "gn/file_writer.h" 6 7#include "base/files/file_path.h" 8#include "base/logging.h" 9#include "gn/filesystem_utils.h" 10 11#if defined(OS_WIN) 12#include <windows.h> 13#include "base/strings/utf_string_conversions.h" 14#include "util/sys_info.h" 15#else 16#include <fcntl.h> 17#include <unistd.h> 18#include "base/posix/eintr_wrapper.h" 19#endif 20 21FileWriter::~FileWriter() = default; 22 23#if defined(OS_WIN) 24 25bool FileWriter::Create(const base::FilePath& file_path) { 26 // On Windows, provide a custom implementation of base::WriteFile. Sometimes 27 // the base version fails, especially on the bots. The guess is that Windows 28 // Defender or other antivirus programs still have the file open (after 29 // checking for the read) when the write happens immediately after. This 30 // version opens with FILE_SHARE_READ (normally not what you want when 31 // replacing the entire contents of the file) which lets us continue even if 32 // another program has the file open for reading. See 33 // http://crbug.com/468437 34 const std::u16string& path = file_path.value(); 35 36 file_path_ = base::UTF16ToUTF8(path); 37 file_ = base::win::ScopedHandle(::CreateFile( 38 reinterpret_cast<LPCWSTR>(path.c_str()), GENERIC_WRITE, 39 FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL)); 40 41 valid_ = file_.IsValid(); 42 if (!valid_) { 43 PLOG(ERROR) << "CreateFile failed for path " << file_path_; 44 45 // Determine whether the path need long path support. 46 if (path.size() >= MAX_PATH && !IsLongPathsSupportEnabled()) { 47 LOG(ERROR) << "You might need to enable Long Path Support on Windows: " 48 << "https://learn.microsoft.com/en-us/windows/win32/fileio/" 49 "maximum-file-path-limitation?tabs=registry#enable-long-paths" 50 "-in-windows-10-version-1607-and-later"; 51 } 52 } 53 return valid_; 54} 55 56bool FileWriter::Write(std::string_view str) { 57 if (!valid_) 58 return false; 59 60 DWORD written; 61 BOOL result = 62 ::WriteFile(file_.Get(), str.data(), str.size(), &written, nullptr); 63 if (!result) { 64 PLOG(ERROR) << "writing file " << file_path_ << " failed"; 65 valid_ = false; 66 return false; 67 } 68 if (static_cast<size_t>(written) != str.size()) { 69 PLOG(ERROR) << "wrote " << written << " bytes to " << file_path_ 70 << " expected " << str.size(); 71 valid_ = false; 72 return false; 73 } 74 return true; 75} 76 77bool FileWriter::Close() { 78 // NOTE: file_.Close() is not used here because it cannot return an error. 79 HANDLE handle = file_.Take(); 80 if (handle && !::CloseHandle(handle)) 81 return false; 82 83 return valid_; 84} 85 86#else // !OS_WIN 87 88bool FileWriter::Create(const base::FilePath& file_path) { 89 fd_.reset(HANDLE_EINTR(::creat(file_path.value().c_str(), 0666))); 90 valid_ = fd_.is_valid(); 91 if (!valid_) { 92 PLOG(ERROR) << "creat() failed for path " << file_path.value(); 93 } 94 return valid_; 95} 96 97bool FileWriter::Write(std::string_view str) { 98 if (!valid_) 99 return false; 100 101 while (!str.empty()) { 102 ssize_t written = HANDLE_EINTR(::write(fd_.get(), str.data(), str.size())); 103 if (written <= 0) { 104 valid_ = false; 105 return false; 106 } 107 str.remove_prefix(static_cast<size_t>(written)); 108 } 109 return true; 110} 111 112bool FileWriter::Close() { 113 // The ScopedFD reset() method will crash on EBADF and ignore other errors 114 // intentionally, so no need to check anything here. 115 fd_.reset(); 116 return valid_; 117} 118 119#endif // !OS_WIN 120