• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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