[go: nahoru, domu]

blob: c84190d4369fcf3601b8ae12d012b325ed40029a [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
brettw@chromium.orge3177dd52014-08-13 20:22:145#include "base/files/file_util.h"
initial.commitd7cae122008-07-26 21:49:386
Sean Maher7d0e8052022-12-09 01:46:327#include "base/task/sequenced_task_runner.h"
Xiaohan Wangb705a64a2022-01-15 18:31:138#include "build/build_config.h"
9
10#if BUILDFLAG(IS_WIN)
jeremy@chromium.orgc2c998c2009-01-27 19:08:3911#include <io.h>
12#endif
mark@chromium.org836f1342008-10-01 17:40:1313#include <stdio.h>
14
initial.commitd7cae122008-07-26 21:49:3815#include <fstream>
kaliamoorthi@chromium.org0710fdd2014-02-18 16:31:5116#include <limits>
Victor Costanb5a0a97002019-09-08 04:55:1517#include <memory>
Xiaohan Wang12bd22202022-05-19 15:38:0218#include <utility>
Victor Hugo Vianna Silva05f14542021-08-05 16:08:4819#include <vector>
initial.commitd7cae122008-07-26 21:49:3820
Fabrice de Gans074355d62022-08-30 17:11:2321#include "base/bit_cast.h"
Hans Wennborgc3cffa62020-04-27 10:09:1222#include "base/check_op.h"
Fabrice de Gans074355d62022-08-30 17:11:2323#include "base/containers/span.h"
brettw@chromium.org25a4c1c2013-06-08 04:53:3624#include "base/files/file_enumerator.h"
brettw@chromium.org57999812013-02-24 05:40:5225#include "base/files/file_path.h"
Fabrice de Gans074355d62022-08-30 17:11:2326#include "base/functional/function_ref.h"
David Sanders83f8ae42022-04-04 23:15:3927#include "base/notreached.h"
Greg Thompson3f7e20f42020-05-02 19:01:1128#include "base/posix/eintr_wrapper.h"
tfarina@chromium.orgeb62f7262013-03-30 14:29:0029#include "base/strings/string_piece.h"
avi@chromium.org251cd6e52013-06-11 13:36:3730#include "base/strings/string_util.h"
31#include "base/strings/stringprintf.h"
avi@chromium.orga4ea1f12013-06-07 18:37:0732#include "base/strings/utf_string_conversions.h"
Xiaohan Wang12bd22202022-05-19 15:38:0233#include "base/task/bind_post_task.h"
Etienne Pierre-Doray1da58592018-09-21 14:47:2034#include "base/threading/scoped_blocking_call.h"
estade@chromium.orgb9e04f02008-11-27 04:03:5735
Xiaohan Wangb705a64a2022-01-15 18:31:1336#if BUILDFLAG(IS_WIN)
Bruce Dawson1ad438d2021-07-09 20:10:0337#include <windows.h>
38#endif
39
brettw@chromium.org0408c752013-06-22 22:15:4640namespace base {
brettw@chromium.org04af979a2013-02-16 04:12:2641
Xiaohan Wang12bd22202022-05-19 15:38:0242namespace {
43
Fabrice de Gans074355d62022-08-30 17:11:2344#if !BUILDFLAG(IS_WIN)
45
Xiaohan Wang12bd22202022-05-19 15:38:0246void RunAndReply(OnceCallback<bool()> action_callback,
47 OnceCallback<void(bool)> reply_callback) {
48 bool result = std::move(action_callback).Run();
49 if (!reply_callback.is_null())
50 std::move(reply_callback).Run(result);
51}
52
Fabrice de Gans074355d62022-08-30 17:11:2353#endif // !BUILDFLAG(IS_WIN)
54
55bool ReadStreamToSpanWithMaxSize(
56 FILE* stream,
57 size_t max_size,
58 FunctionRef<span<uint8_t>(size_t)> resize_span) {
59 if (!stream) {
60 return false;
61 }
62
63 // Seeking to the beginning is best-effort -- it is expected to fail for
64 // certain non-file stream (e.g., pipes).
65 HANDLE_EINTR(fseek(stream, 0, SEEK_SET));
66
67 // Many files have incorrect size (proc files etc). Hence, the file is read
68 // sequentially as opposed to a one-shot read, using file size as a hint for
69 // chunk size if available.
70 constexpr size_t kDefaultChunkSize = 1 << 16;
71 size_t chunk_size = kDefaultChunkSize - 1;
72 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
73#if BUILDFLAG(IS_WIN)
74 BY_HANDLE_FILE_INFORMATION file_info = {};
75 if (::GetFileInformationByHandle(
76 reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stream))),
77 &file_info)) {
78 LARGE_INTEGER size;
79 size.HighPart = static_cast<LONG>(file_info.nFileSizeHigh);
80 size.LowPart = file_info.nFileSizeLow;
81 if (size.QuadPart > 0)
82 chunk_size = static_cast<size_t>(size.QuadPart);
83 }
84#else // BUILDFLAG(IS_WIN)
85 // In cases where the reported file size is 0, use a smaller chunk size to
86 // minimize memory allocated and cost of string::resize() in case the read
87 // size is small (i.e. proc files). If the file is larger than this, the read
88 // loop will reset |chunk_size| to kDefaultChunkSize.
89 constexpr size_t kSmallChunkSize = 4096;
90 chunk_size = kSmallChunkSize - 1;
91 stat_wrapper_t file_info = {};
92 if (!File::Fstat(fileno(stream), &file_info) && file_info.st_size > 0)
93 chunk_size = static_cast<size_t>(file_info.st_size);
94#endif // BUILDFLAG(IS_WIN)
95
96 // We need to attempt to read at EOF for feof flag to be set so here we use
97 // |chunk_size| + 1.
98 chunk_size = std::min(chunk_size, max_size) + 1;
99 size_t bytes_read_this_pass;
100 size_t bytes_read_so_far = 0;
101 bool read_status = true;
102 span<uint8_t> bytes_span = resize_span(chunk_size);
103 DCHECK_EQ(bytes_span.size(), chunk_size);
104
105 while ((bytes_read_this_pass = fread(bytes_span.data() + bytes_read_so_far, 1,
106 chunk_size, stream)) > 0) {
107 if ((max_size - bytes_read_so_far) < bytes_read_this_pass) {
108 // Read more than max_size bytes, bail out.
109 bytes_read_so_far = max_size;
110 read_status = false;
111 break;
112 }
113 // In case EOF was not reached, iterate again but revert to the default
114 // chunk size.
115 if (bytes_read_so_far == 0)
116 chunk_size = kDefaultChunkSize;
117
118 bytes_read_so_far += bytes_read_this_pass;
119 // Last fread syscall (after EOF) can be avoided via feof, which is just a
120 // flag check.
121 if (feof(stream))
122 break;
123 bytes_span = resize_span(bytes_read_so_far + chunk_size);
124 DCHECK_EQ(bytes_span.size(), bytes_read_so_far + chunk_size);
125 }
126 read_status = read_status && !ferror(stream);
127
128 // Trim the container down to the number of bytes that were actually read.
129 bytes_span = resize_span(bytes_read_so_far);
130 DCHECK_EQ(bytes_span.size(), bytes_read_so_far);
131
132 return read_status;
133}
134
Xiaohan Wang12bd22202022-05-19 15:38:02135} // namespace
136
Fabrice de Gans074355d62022-08-30 17:11:23137#if !BUILDFLAG(IS_WIN)
138
Xiaohan Wang12bd22202022-05-19 15:38:02139OnceClosure GetDeleteFileCallback(const FilePath& path,
140 OnceCallback<void(bool)> reply_callback) {
141 return BindOnce(&RunAndReply, BindOnce(&DeleteFile, path),
142 reply_callback.is_null()
143 ? std::move(reply_callback)
Sean Maher7d0e8052022-12-09 01:46:32144 : BindPostTask(SequencedTaskRunner::GetCurrentDefault(),
Xiaohan Wang12bd22202022-05-19 15:38:02145 std::move(reply_callback)));
Lei Zhange3aa0b9a2020-06-11 08:59:23146}
147
Xiaohan Wang12bd22202022-05-19 15:38:02148OnceClosure GetDeletePathRecursivelyCallback(
149 const FilePath& path,
150 OnceCallback<void(bool)> reply_callback) {
151 return BindOnce(&RunAndReply, BindOnce(&DeletePathRecursively, path),
152 reply_callback.is_null()
153 ? std::move(reply_callback)
Sean Maher7d0e8052022-12-09 01:46:32154 : BindPostTask(SequencedTaskRunner::GetCurrentDefault(),
Xiaohan Wang12bd22202022-05-19 15:38:02155 std::move(reply_callback)));
Lei Zhang3190449f2020-06-12 05:14:04156}
157
Xiaohan Wang5b7ea512022-05-20 14:56:29158#endif // !BUILDFLAG(IS_WIN)
159
avi543540e2015-12-24 05:15:32160int64_t ComputeDirectorySize(const FilePath& root_path) {
161 int64_t running_size = 0;
brettw@chromium.org0408c752013-06-22 22:15:46162 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
163 while (!file_iter.Next().empty())
164 running_size += file_iter.GetInfo().GetSize();
165 return running_size;
166}
167
brettw@chromium.org5553d5b2013-07-01 23:07:36168bool Move(const FilePath& from_path, const FilePath& to_path) {
169 if (from_path.ReferencesParent() || to_path.ReferencesParent())
170 return false;
brettw@chromium.orgf0ff2ad2013-07-09 17:42:26171 return internal::MoveUnsafe(from_path, to_path);
172}
173
Alexander Bolodurin53bfc89c22020-11-25 22:43:29174bool CopyFileContents(File& infile, File& outfile) {
Xiaohan Wangb705a64a2022-01-15 18:31:13175#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
Brian Geffon591e8f22021-04-08 18:33:23176 bool retry_slow = false;
177 bool res =
178 internal::CopyFileContentsWithSendfile(infile, outfile, retry_slow);
179 if (res || !retry_slow) {
180 return res;
181 }
182 // Any failures which allow retrying using read/write will not have modified
183 // either file offset or size.
184#endif
185
Alexander Bolodurin53bfc89c22020-11-25 22:43:29186 static constexpr size_t kBufferSize = 32768;
187 std::vector<char> buffer(kBufferSize);
188
189 for (;;) {
Peter Kasting2d40b0f2022-06-07 18:55:25190 int bytes_read =
191 infile.ReadAtCurrentPos(buffer.data(), static_cast<int>(buffer.size()));
Alexander Bolodurin53bfc89c22020-11-25 22:43:29192 if (bytes_read < 0) {
193 return false;
194 }
195 if (bytes_read == 0) {
196 return true;
197 }
198 // Allow for partial writes
199 int bytes_written_per_read = 0;
200 do {
201 int bytes_written_partial = outfile.WriteAtCurrentPos(
Peter Kasting2d40b0f2022-06-07 18:55:25202 &buffer[static_cast<size_t>(bytes_written_per_read)],
203 bytes_read - bytes_written_per_read);
Alexander Bolodurin53bfc89c22020-11-25 22:43:29204 if (bytes_written_partial < 0) {
205 return false;
206 }
207
208 bytes_written_per_read += bytes_written_partial;
209 } while (bytes_written_per_read < bytes_read);
210 }
211
212 NOTREACHED();
213 return false;
214}
215
evanm@google.com640517f2008-10-30 23:54:04216bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
initial.commitd7cae122008-07-26 21:49:38217 // We open the file in binary format even if they are text files because
218 // we are just comparing that bytes are exactly same in both files and not
219 // doing anything smart with text formatting.
Xiaohan Wangb705a64a2022-01-15 18:31:13220#if BUILDFLAG(IS_WIN)
Jan Wilken Dörrie9bb441d2019-11-01 17:48:40221 std::ifstream file1(filename1.value().c_str(),
erikkay@google.com5af2edb92008-08-08 20:16:08222 std::ios::in | std::ios::binary);
Jan Wilken Dörrie9bb441d2019-11-01 17:48:40223 std::ifstream file2(filename2.value().c_str(),
erikkay@google.com5af2edb92008-08-08 20:16:08224 std::ios::in | std::ios::binary);
Xiaohan Wangb705a64a2022-01-15 18:31:13225#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
jdoerrie983968a2019-01-29 12:54:41226 std::ifstream file1(filename1.value(), std::ios::in | std::ios::binary);
227 std::ifstream file2(filename2.value(), std::ios::in | std::ios::binary);
Xiaohan Wangb705a64a2022-01-15 18:31:13228#endif // BUILDFLAG(IS_WIN)
estade@chromium.orgb9e04f02008-11-27 04:03:57229
initial.commitd7cae122008-07-26 21:49:38230 // Even if both files aren't openable (and thus, in some sense, "equal"),
231 // any unusable file yields a result of "false".
232 if (!file1.is_open() || !file2.is_open())
233 return false;
234
235 const int BUFFER_SIZE = 2056;
236 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
237 do {
238 file1.read(buffer1, BUFFER_SIZE);
239 file2.read(buffer2, BUFFER_SIZE);
240
mark@chromium.orgb81637c32009-06-26 21:17:24241 if ((file1.eof() != file2.eof()) ||
initial.commitd7cae122008-07-26 21:49:38242 (file1.gcount() != file2.gcount()) ||
pkasting9cf9b942014-10-01 22:18:43243 (memcmp(buffer1, buffer2, static_cast<size_t>(file1.gcount())))) {
initial.commitd7cae122008-07-26 21:49:38244 file1.close();
245 file2.close();
246 return false;
247 }
mark@chromium.orgb81637c32009-06-26 21:17:24248 } while (!file1.eof() || !file2.eof());
initial.commitd7cae122008-07-26 21:49:38249
250 file1.close();
251 file2.close();
252 return true;
253}
254
mark@chromium.orgb81637c32009-06-26 21:17:24255bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
Xiaohan Wangb705a64a2022-01-15 18:31:13256#if BUILDFLAG(IS_WIN)
Jan Wilken Dörrie9bb441d2019-11-01 17:48:40257 std::ifstream file1(filename1.value().c_str(), std::ios::in);
258 std::ifstream file2(filename2.value().c_str(), std::ios::in);
Xiaohan Wangb705a64a2022-01-15 18:31:13259#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
jdoerrie983968a2019-01-29 12:54:41260 std::ifstream file1(filename1.value(), std::ios::in);
261 std::ifstream file2(filename2.value(), std::ios::in);
Xiaohan Wangb705a64a2022-01-15 18:31:13262#endif // BUILDFLAG(IS_WIN)
mark@chromium.orgb81637c32009-06-26 21:17:24263
264 // Even if both files aren't openable (and thus, in some sense, "equal"),
265 // any unusable file yields a result of "false".
266 if (!file1.is_open() || !file2.is_open())
267 return false;
268
269 do {
270 std::string line1, line2;
271 getline(file1, line1);
272 getline(file2, line2);
273
274 // Check for mismatched EOF states, or any error state.
275 if ((file1.eof() != file2.eof()) ||
276 file1.bad() || file2.bad()) {
277 return false;
278 }
279
280 // Trim all '\r' and '\n' characters from the end of the line.
281 std::string::size_type end1 = line1.find_last_not_of("\r\n");
282 if (end1 == std::string::npos)
283 line1.clear();
284 else if (end1 + 1 < line1.length())
285 line1.erase(end1 + 1);
286
287 std::string::size_type end2 = line2.find_last_not_of("\r\n");
288 if (end2 == std::string::npos)
289 line2.clear();
290 else if (end2 + 1 < line2.length())
291 line2.erase(end2 + 1);
292
293 if (line1 != line2)
294 return false;
295 } while (!file1.eof() || !file2.eof());
296
297 return true;
298}
299
Greg Thompson3f7e20f42020-05-02 19:01:11300bool ReadStreamToString(FILE* stream, std::string* contents) {
301 return ReadStreamToStringWithMaxSize(
302 stream, std::numeric_limits<size_t>::max(), contents);
303}
304
305bool ReadStreamToStringWithMaxSize(FILE* stream,
306 size_t max_size,
307 std::string* contents) {
Mikhail Istomin1ede1552018-05-16 17:40:24308 if (contents) {
Fabrice de Gans074355d62022-08-30 17:11:23309 contents->clear();
Mikhail Istomin1ede1552018-05-16 17:40:24310 }
initial.commitd7cae122008-07-26 21:49:38311
Fabrice de Gans074355d62022-08-30 17:11:23312 std::string content_string;
313 bool read_successs = ReadStreamToSpanWithMaxSize(
314 stream, max_size, [&content_string](size_t size) {
315 content_string.resize(size);
316 return as_writable_bytes(make_span(content_string));
317 });
318
319 if (contents) {
320 contents->swap(content_string);
321 }
322 return read_successs;
323}
324
325absl::optional<std::vector<uint8_t>> ReadFileToBytes(const FilePath& path) {
326 if (path.ReferencesParent()) {
327 return absl::nullopt;
328 }
329
330 ScopedFILE file_stream(OpenFile(path, "rb"));
331 if (!file_stream) {
332 return absl::nullopt;
333 }
334
335 std::vector<uint8_t> bytes;
336 if (!ReadStreamToSpanWithMaxSize(file_stream.get(),
337 std::numeric_limits<size_t>::max(),
338 [&bytes](size_t size) {
339 bytes.resize(size);
340 return make_span(bytes);
341 })) {
342 return absl::nullopt;
343 }
344 return bytes;
kaliamoorthi@chromium.org0710fdd2014-02-18 16:31:51345}
346
347bool ReadFileToString(const FilePath& path, std::string* contents) {
hashimoto6da2fef2016-02-24 03:39:58348 return ReadFileToStringWithMaxSize(path, contents,
349 std::numeric_limits<size_t>::max());
initial.commitd7cae122008-07-26 21:49:38350}
351
Greg Thompson3f7e20f42020-05-02 19:01:11352bool ReadFileToStringWithMaxSize(const FilePath& path,
353 std::string* contents,
354 size_t max_size) {
355 if (contents)
356 contents->clear();
357 if (path.ReferencesParent())
358 return false;
359 ScopedFILE file_stream(OpenFile(path, "rb"));
360 if (!file_stream)
361 return false;
362 return ReadStreamToStringWithMaxSize(file_stream.get(), max_size, contents);
363}
364
brettw@chromium.orgfb4bcfa32013-12-02 18:55:49365bool IsDirectoryEmpty(const FilePath& dir_path) {
366 FileEnumerator files(dir_path, false,
367 FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
368 if (files.Next().empty())
369 return true;
370 return false;
371}
372
Greg Thompson3f7e20f42020-05-02 19:01:11373bool CreateTemporaryFile(FilePath* path) {
374 FilePath temp_dir;
375 return GetTempDir(&temp_dir) && CreateTemporaryFileInDir(temp_dir, path);
376}
377
Greg Thompsonf75f5fb2020-04-30 08:13:25378ScopedFILE CreateAndOpenTemporaryStream(FilePath* path) {
brettw@chromium.org03d9afc02013-12-03 17:55:52379 FilePath directory;
380 if (!GetTempDir(&directory))
Ivan Kotenkova16212a52017-11-08 12:37:33381 return nullptr;
brettw@chromium.org03d9afc02013-12-03 17:55:52382
Greg Thompsonf75f5fb2020-04-30 08:13:25383 return CreateAndOpenTemporaryStreamInDir(directory, path);
brettw@chromium.org03d9afc02013-12-03 17:55:52384}
385
brettw@chromium.org426d1c92013-12-03 20:08:54386bool CreateDirectory(const FilePath& full_path) {
Ivan Kotenkova16212a52017-11-08 12:37:33387 return CreateDirectoryAndGetError(full_path, nullptr);
brettw@chromium.org426d1c92013-12-03 20:08:54388}
389
avi543540e2015-12-24 05:15:32390bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
rvargas@chromium.org54124ed02014-01-07 10:06:58391 File::Info info;
brettw@chromium.org9eae4e62013-12-04 20:56:49392 if (!GetFileInfo(file_path, &info))
brettw@chromium.org56285702013-12-04 18:22:49393 return false;
394 *file_size = info.size;
395 return true;
396}
397
brettw@chromium.orgc0d508162013-12-04 22:49:00398bool TouchFile(const FilePath& path,
399 const Time& last_accessed,
400 const Time& last_modified) {
Peter Kasting2d40b0f2022-06-07 18:55:25401 uint32_t flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES;
brettw@chromium.orgc0d508162013-12-04 22:49:00402
Xiaohan Wangb705a64a2022-01-15 18:31:13403#if BUILDFLAG(IS_WIN)
brettw@chromium.orgc0d508162013-12-04 22:49:00404 // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
405 if (DirectoryExists(path))
Alexei Svitkinec417b2cd2021-10-21 14:22:40406 flags |= File::FLAG_WIN_BACKUP_SEMANTICS;
Xiaohan Wangb705a64a2022-01-15 18:31:13407#elif BUILDFLAG(IS_FUCHSIA)
Wez432ea8d892019-03-31 01:17:08408 // On Fuchsia, we need O_RDONLY for directories, or O_WRONLY for files.
409 // TODO(https://crbug.com/947802): Find a cleaner workaround for this.
410 flags |= (DirectoryExists(path) ? File::FLAG_READ : File::FLAG_WRITE);
411#endif
brettw@chromium.orgc0d508162013-12-04 22:49:00412
rvargas@chromium.org54124ed02014-01-07 10:06:58413 File file(path, flags);
414 if (!file.IsValid())
415 return false;
brettw@chromium.orgc0d508162013-12-04 22:49:00416
rvargas@chromium.org54124ed02014-01-07 10:06:58417 return file.SetTimes(last_accessed, last_modified);
brettw@chromium.orgc0d508162013-12-04 22:49:00418}
419
mark@chromium.org836f1342008-10-01 17:40:13420bool CloseFile(FILE* file) {
Ivan Kotenkova16212a52017-11-08 12:37:33421 if (file == nullptr)
sidchat@google.coma1a19502008-10-21 17:14:45422 return true;
mark@chromium.org836f1342008-10-01 17:40:13423 return fclose(file) == 0;
424}
425
jeremy@chromium.orgc2c998c2009-01-27 19:08:39426bool TruncateFile(FILE* file) {
Ivan Kotenkova16212a52017-11-08 12:37:33427 if (file == nullptr)
jeremy@chromium.orgc2c998c2009-01-27 19:08:39428 return false;
429 long current_offset = ftell(file);
430 if (current_offset == -1)
431 return false;
Xiaohan Wangb705a64a2022-01-15 18:31:13432#if BUILDFLAG(IS_WIN)
jeremy@chromium.orgc2c998c2009-01-27 19:08:39433 int fd = _fileno(file);
434 if (_chsize(fd, current_offset) != 0)
435 return false;
436#else
437 int fd = fileno(file);
438 if (ftruncate(fd, current_offset) != 0)
439 return false;
440#endif
441 return true;
442}
443
Austin Sullivan0ca3bded42024-01-08 18:48:08444int ReadFile(const FilePath& filename, char* data, int max_size) {
445 if (max_size < 0) {
446 return -1;
447 }
448 std::optional<uint64_t> result =
449 ReadFile(filename, make_span(data, static_cast<uint32_t>(max_size)));
450 if (!result) {
451 return -1;
452 }
453 return checked_cast<int>(result.value());
454}
455
Lei Zhangeebbef62020-05-05 20:16:05456bool WriteFile(const FilePath& filename, span<const uint8_t> data) {
457 int size = checked_cast<int>(data.size());
458 return WriteFile(filename, reinterpret_cast<const char*>(data.data()),
459 size) == size;
460}
461
462bool WriteFile(const FilePath& filename, StringPiece data) {
463 int size = checked_cast<int>(data.size());
464 return WriteFile(filename, data.data(), size) == size;
465}
466
Greg Thompsonb4abcb42019-08-23 01:42:56467int GetUniquePathNumber(const FilePath& path) {
468 DCHECK(!path.empty());
469 if (!PathExists(path))
Greg Thompson42373544a2019-08-14 10:11:10470 return 0;
471
472 std::string number;
473 for (int count = 1; count <= kMaxUniqueFiles; ++count) {
474 StringAppendF(&number, " (%d)", count);
Greg Thompsonb4abcb42019-08-23 01:42:56475 if (!PathExists(path.InsertBeforeExtensionASCII(number)))
Greg Thompson42373544a2019-08-14 10:11:10476 return count;
477 number.clear();
jam@chromium.orge285afa2012-01-31 23:16:39478 }
479
480 return -1;
481}
Bruce Longd096e482019-02-28 17:50:00482
483FilePath GetUniquePath(const FilePath& path) {
Greg Thompsonb4abcb42019-08-23 01:42:56484 DCHECK(!path.empty());
485 const int uniquifier = GetUniquePathNumber(path);
486 if (uniquifier > 0)
487 return path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", uniquifier));
Xiaohan Wang12bd22202022-05-19 15:38:02488 return uniquifier == 0 ? path : FilePath();
Bruce Longd096e482019-02-28 17:50:00489}
Victor Costanb5a0a97002019-09-08 04:55:15490
491namespace internal {
492
493bool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes) {
494 DCHECK_GE(max_bytes, 0);
495
496 File file(file_path, File::FLAG_OPEN | File::FLAG_READ |
Alexei Svitkinec417b2cd2021-10-21 14:22:40497 File::FLAG_WIN_SEQUENTIAL_SCAN |
498 File::FLAG_WIN_SHARE_DELETE);
Victor Costanb5a0a97002019-09-08 04:55:15499 if (!file.IsValid())
500 return false;
501
502 constexpr int kBufferSize = 1024 * 1024;
503 // Ensures the buffer is deallocated at function exit.
504 std::unique_ptr<char[]> buffer_deleter(new char[kBufferSize]);
505 char* const buffer = buffer_deleter.get();
506
507 while (max_bytes > 0) {
508 // The static_cast<int> is safe because kBufferSize is int, and both values
509 // are non-negative. So, the minimum is guaranteed to fit in int.
510 const int read_size =
511 static_cast<int>(std::min<int64_t>(max_bytes, kBufferSize));
512 DCHECK_GE(read_size, 0);
513 DCHECK_LE(read_size, kBufferSize);
514
515 const int read_bytes = file.ReadAtCurrentPos(buffer, read_size);
516 if (read_bytes < 0)
517 return false;
518 if (read_bytes == 0)
519 break;
520
521 max_bytes -= read_bytes;
522 }
523
524 return true;
525}
526
527} // namespace internal
528
brettw@chromium.orga26f4ae2014-03-13 17:26:21529} // namespace base