xref: /third_party/gn/src/gn/file_writer.cc (revision 6d528ed9)
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