1 // Copyright 2012 The Chromium Authors
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 "base/files/file_util.h"
6
7 #include "base/task/sequenced_task_runner.h"
8 #include "build/build_config.h"
9
10 #if BUILDFLAG(IS_WIN)
11 #include <io.h>
12 #endif
13 #include <stdio.h>
14
15 #include <fstream>
16 #include <limits>
17 #include <memory>
18 #include <utility>
19 #include <vector>
20
21 #include "base/bit_cast.h"
22 #include "base/check_op.h"
23 #include "base/containers/span.h"
24 #include "base/files/file_enumerator.h"
25 #include "base/files/file_path.h"
26 #include "base/functional/function_ref.h"
27 #include "base/notreached.h"
28 #include "base/posix/eintr_wrapper.h"
29 #include "base/strings/string_piece.h"
30 #include "base/strings/string_util.h"
31 #include "base/strings/stringprintf.h"
32 #include "base/strings/utf_string_conversions.h"
33 #include "base/task/bind_post_task.h"
34 #include "base/threading/scoped_blocking_call.h"
35
36 #if BUILDFLAG(IS_WIN)
37 #include <windows.h>
38 #endif
39
40 namespace base {
41
42 namespace {
43
44 #if !BUILDFLAG(IS_WIN)
45
RunAndReply(OnceCallback<bool ()> action_callback,OnceCallback<void (bool)> reply_callback)46 void 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
53 #endif // !BUILDFLAG(IS_WIN)
54
ReadStreamToSpanWithMaxSize(FILE * stream,size_t max_size,FunctionRef<span<uint8_t> (size_t)> resize_span)55 bool 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
135 } // namespace
136
137 #if !BUILDFLAG(IS_WIN)
138
GetDeleteFileCallback(const FilePath & path,OnceCallback<void (bool)> reply_callback)139 OnceClosure 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)
144 : BindPostTask(SequencedTaskRunner::GetCurrentDefault(),
145 std::move(reply_callback)));
146 }
147
GetDeletePathRecursivelyCallback(const FilePath & path,OnceCallback<void (bool)> reply_callback)148 OnceClosure 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)
154 : BindPostTask(SequencedTaskRunner::GetCurrentDefault(),
155 std::move(reply_callback)));
156 }
157
158 #endif // !BUILDFLAG(IS_WIN)
159
ComputeDirectorySize(const FilePath & root_path)160 int64_t ComputeDirectorySize(const FilePath& root_path) {
161 int64_t running_size = 0;
162 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
Move(const FilePath & from_path,const FilePath & to_path)168 bool Move(const FilePath& from_path, const FilePath& to_path) {
169 if (from_path.ReferencesParent() || to_path.ReferencesParent())
170 return false;
171 return internal::MoveUnsafe(from_path, to_path);
172 }
173
CopyFileContents(File & infile,File & outfile)174 bool CopyFileContents(File& infile, File& outfile) {
175 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
176 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
186 static constexpr size_t kBufferSize = 32768;
187 std::vector<char> buffer(kBufferSize);
188
189 for (;;) {
190 int bytes_read =
191 infile.ReadAtCurrentPos(buffer.data(), static_cast<int>(buffer.size()));
192 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(
202 &buffer[static_cast<size_t>(bytes_written_per_read)],
203 bytes_read - bytes_written_per_read);
204 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
ContentsEqual(const FilePath & filename1,const FilePath & filename2)216 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
217 // 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.
220 #if BUILDFLAG(IS_WIN)
221 std::ifstream file1(filename1.value().c_str(),
222 std::ios::in | std::ios::binary);
223 std::ifstream file2(filename2.value().c_str(),
224 std::ios::in | std::ios::binary);
225 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
226 std::ifstream file1(filename1.value(), std::ios::in | std::ios::binary);
227 std::ifstream file2(filename2.value(), std::ios::in | std::ios::binary);
228 #endif // BUILDFLAG(IS_WIN)
229
230 // 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
241 if ((file1.eof() != file2.eof()) ||
242 (file1.gcount() != file2.gcount()) ||
243 (memcmp(buffer1, buffer2, static_cast<size_t>(file1.gcount())))) {
244 file1.close();
245 file2.close();
246 return false;
247 }
248 } while (!file1.eof() || !file2.eof());
249
250 file1.close();
251 file2.close();
252 return true;
253 }
254
TextContentsEqual(const FilePath & filename1,const FilePath & filename2)255 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
256 #if BUILDFLAG(IS_WIN)
257 std::ifstream file1(filename1.value().c_str(), std::ios::in);
258 std::ifstream file2(filename2.value().c_str(), std::ios::in);
259 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
260 std::ifstream file1(filename1.value(), std::ios::in);
261 std::ifstream file2(filename2.value(), std::ios::in);
262 #endif // BUILDFLAG(IS_WIN)
263
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
ReadStreamToString(FILE * stream,std::string * contents)300 bool ReadStreamToString(FILE* stream, std::string* contents) {
301 return ReadStreamToStringWithMaxSize(
302 stream, std::numeric_limits<size_t>::max(), contents);
303 }
304
ReadStreamToStringWithMaxSize(FILE * stream,size_t max_size,std::string * contents)305 bool ReadStreamToStringWithMaxSize(FILE* stream,
306 size_t max_size,
307 std::string* contents) {
308 if (contents) {
309 contents->clear();
310 }
311
312 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
ReadFileToBytes(const FilePath & path)325 absl::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;
345 }
346
ReadFileToString(const FilePath & path,std::string * contents)347 bool ReadFileToString(const FilePath& path, std::string* contents) {
348 return ReadFileToStringWithMaxSize(path, contents,
349 std::numeric_limits<size_t>::max());
350 }
351
ReadFileToStringWithMaxSize(const FilePath & path,std::string * contents,size_t max_size)352 bool 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
IsDirectoryEmpty(const FilePath & dir_path)365 bool 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
CreateTemporaryFile(FilePath * path)373 bool CreateTemporaryFile(FilePath* path) {
374 FilePath temp_dir;
375 return GetTempDir(&temp_dir) && CreateTemporaryFileInDir(temp_dir, path);
376 }
377
CreateAndOpenTemporaryStream(FilePath * path)378 ScopedFILE CreateAndOpenTemporaryStream(FilePath* path) {
379 FilePath directory;
380 if (!GetTempDir(&directory))
381 return nullptr;
382
383 return CreateAndOpenTemporaryStreamInDir(directory, path);
384 }
385
CreateDirectory(const FilePath & full_path)386 bool CreateDirectory(const FilePath& full_path) {
387 return CreateDirectoryAndGetError(full_path, nullptr);
388 }
389
GetFileSize(const FilePath & file_path,int64_t * file_size)390 bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
391 File::Info info;
392 if (!GetFileInfo(file_path, &info))
393 return false;
394 *file_size = info.size;
395 return true;
396 }
397
TouchFile(const FilePath & path,const Time & last_accessed,const Time & last_modified)398 bool TouchFile(const FilePath& path,
399 const Time& last_accessed,
400 const Time& last_modified) {
401 uint32_t flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES;
402
403 #if BUILDFLAG(IS_WIN)
404 // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
405 if (DirectoryExists(path))
406 flags |= File::FLAG_WIN_BACKUP_SEMANTICS;
407 #elif BUILDFLAG(IS_FUCHSIA)
408 // 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
412
413 File file(path, flags);
414 if (!file.IsValid())
415 return false;
416
417 return file.SetTimes(last_accessed, last_modified);
418 }
419
CloseFile(FILE * file)420 bool CloseFile(FILE* file) {
421 if (file == nullptr)
422 return true;
423 return fclose(file) == 0;
424 }
425
TruncateFile(FILE * file)426 bool TruncateFile(FILE* file) {
427 if (file == nullptr)
428 return false;
429 long current_offset = ftell(file);
430 if (current_offset == -1)
431 return false;
432 #if BUILDFLAG(IS_WIN)
433 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
WriteFile(const FilePath & filename,span<const uint8_t> data)444 bool WriteFile(const FilePath& filename, span<const uint8_t> data) {
445 int size = checked_cast<int>(data.size());
446 return WriteFile(filename, reinterpret_cast<const char*>(data.data()),
447 size) == size;
448 }
449
WriteFile(const FilePath & filename,StringPiece data)450 bool WriteFile(const FilePath& filename, StringPiece data) {
451 int size = checked_cast<int>(data.size());
452 return WriteFile(filename, data.data(), size) == size;
453 }
454
GetUniquePathNumber(const FilePath & path)455 int GetUniquePathNumber(const FilePath& path) {
456 DCHECK(!path.empty());
457 if (!PathExists(path))
458 return 0;
459
460 std::string number;
461 for (int count = 1; count <= kMaxUniqueFiles; ++count) {
462 StringAppendF(&number, " (%d)", count);
463 if (!PathExists(path.InsertBeforeExtensionASCII(number)))
464 return count;
465 number.clear();
466 }
467
468 return -1;
469 }
470
GetUniquePath(const FilePath & path)471 FilePath GetUniquePath(const FilePath& path) {
472 DCHECK(!path.empty());
473 const int uniquifier = GetUniquePathNumber(path);
474 if (uniquifier > 0)
475 return path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", uniquifier));
476 return uniquifier == 0 ? path : FilePath();
477 }
478
479 namespace internal {
480
PreReadFileSlow(const FilePath & file_path,int64_t max_bytes)481 bool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes) {
482 DCHECK_GE(max_bytes, 0);
483
484 File file(file_path, File::FLAG_OPEN | File::FLAG_READ |
485 File::FLAG_WIN_SEQUENTIAL_SCAN |
486 File::FLAG_WIN_SHARE_DELETE);
487 if (!file.IsValid())
488 return false;
489
490 constexpr int kBufferSize = 1024 * 1024;
491 // Ensures the buffer is deallocated at function exit.
492 std::unique_ptr<char[]> buffer_deleter(new char[kBufferSize]);
493 char* const buffer = buffer_deleter.get();
494
495 while (max_bytes > 0) {
496 // The static_cast<int> is safe because kBufferSize is int, and both values
497 // are non-negative. So, the minimum is guaranteed to fit in int.
498 const int read_size =
499 static_cast<int>(std::min<int64_t>(max_bytes, kBufferSize));
500 DCHECK_GE(read_size, 0);
501 DCHECK_LE(read_size, kBufferSize);
502
503 const int read_bytes = file.ReadAtCurrentPos(buffer, read_size);
504 if (read_bytes < 0)
505 return false;
506 if (read_bytes == 0)
507 break;
508
509 max_bytes -= read_bytes;
510 }
511
512 return true;
513 }
514
515 } // namespace internal
516
517 } // namespace base
518