• 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 #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