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
21 FileWriter::~FileWriter() = default;
22
23 #if defined(OS_WIN)
24
Create(const base::FilePath & file_path)25 bool 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
Write(std::string_view str)56 bool 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
Close()77 bool 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
Create(const base::FilePath & file_path)88 bool 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
Write(std::string_view str)97 bool 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
Close()112 bool 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