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 #else
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include "base/posix/eintr_wrapper.h"
18 #endif
19
20 FileWriter::~FileWriter() = default;
21
22 #if defined(OS_WIN)
23
Create(const base::FilePath & file_path)24 bool FileWriter::Create(const base::FilePath& file_path) {
25 // On Windows, provide a custom implementation of base::WriteFile. Sometimes
26 // the base version fails, especially on the bots. The guess is that Windows
27 // Defender or other antivirus programs still have the file open (after
28 // checking for the read) when the write happens immediately after. This
29 // version opens with FILE_SHARE_READ (normally not what you want when
30 // replacing the entire contents of the file) which lets us continue even if
31 // another program has the file open for reading. See
32 // http://crbug.com/468437
33 file_path_ = base::UTF16ToUTF8(file_path.value());
34 file_ = base::win::ScopedHandle(::CreateFile(
35 reinterpret_cast<LPCWSTR>(file_path.value().c_str()), GENERIC_WRITE,
36 FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL));
37
38 valid_ = file_.IsValid();
39 if (!valid_) {
40 PLOG(ERROR) << "CreateFile failed for path " << file_path_;
41 }
42 return valid_;
43 }
44
Write(std::string_view str)45 bool FileWriter::Write(std::string_view str) {
46 if (!valid_)
47 return false;
48
49 DWORD written;
50 BOOL result =
51 ::WriteFile(file_.Get(), str.data(), str.size(), &written, nullptr);
52 if (!result) {
53 PLOG(ERROR) << "writing file " << file_path_ << " failed";
54 valid_ = false;
55 return false;
56 }
57 if (static_cast<size_t>(written) != str.size()) {
58 PLOG(ERROR) << "wrote " << written << " bytes to "
59 << file_path_ << " expected " << str.size();
60 valid_ = false;
61 return false;
62 }
63 return true;
64 }
65
Close()66 bool FileWriter::Close() {
67 // NOTE: file_.Close() is not used here because it cannot return an error.
68 HANDLE handle = file_.Take();
69 if (handle && !::CloseHandle(handle))
70 return false;
71
72 return valid_;
73 }
74
75 #else // !OS_WIN
76
Create(const base::FilePath & file_path)77 bool FileWriter::Create(const base::FilePath& file_path) {
78 fd_.reset(HANDLE_EINTR(::creat(file_path.value().c_str(), 0666)));
79 valid_ = fd_.is_valid();
80 if (!valid_) {
81 PLOG(ERROR) << "creat() failed for path " << file_path.value();
82 }
83 return valid_;
84 }
85
Write(std::string_view str)86 bool FileWriter::Write(std::string_view str) {
87 if (!valid_)
88 return false;
89
90 while (!str.empty()) {
91 ssize_t written = HANDLE_EINTR(::write(fd_.get(), str.data(), str.size()));
92 if (written <= 0) {
93 valid_ = false;
94 return false;
95 }
96 str.remove_prefix(static_cast<size_t>(written));
97 }
98 return true;
99 }
100
Close()101 bool FileWriter::Close() {
102 // The ScopedFD reset() method will crash on EBADF and ignore other errors
103 // intentionally, so no need to check anything here.
104 fd_.reset();
105 return valid_;
106 }
107
108 #endif // !OS_WIN
109