• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/files/file_util.h"
11 
12 #include <algorithm>
13 #include <string_view>
14 
15 #include "base/task/sequenced_task_runner.h"
16 #include "build/build_config.h"
17 
18 #if BUILDFLAG(IS_WIN)
19 #include <io.h>
20 #endif
21 #include <stdio.h>
22 
23 #include <fstream>
24 #include <limits>
25 #include <memory>
26 #include <utility>
27 #include <vector>
28 
29 #include "base/bit_cast.h"
30 #include "base/check_op.h"
31 #include "base/containers/contains.h"
32 #include "base/containers/span.h"
33 #include "base/files/file_enumerator.h"
34 #include "base/files/file_path.h"
35 #include "base/functional/function_ref.h"
36 #include "base/notreached.h"
37 #include "base/posix/eintr_wrapper.h"
38 #include "base/ranges/algorithm.h"
39 #include "base/strings/string_util.h"
40 #include "base/strings/stringprintf.h"
41 #include "base/strings/utf_string_conversions.h"
42 #include "base/task/bind_post_task.h"
43 #include "base/threading/scoped_blocking_call.h"
44 
45 #if BUILDFLAG(IS_WIN)
46 #include <windows.h>
47 #endif
48 
49 namespace base {
50 
51 namespace {
52 
53 #if !BUILDFLAG(IS_WIN)
54 
RunAndReply(OnceCallback<bool ()> action_callback,OnceCallback<void (bool)> reply_callback)55 void RunAndReply(OnceCallback<bool()> action_callback,
56                  OnceCallback<void(bool)> reply_callback) {
57   bool result = std::move(action_callback).Run();
58   if (!reply_callback.is_null()) {
59     std::move(reply_callback).Run(result);
60   }
61 }
62 
63 #endif  // !BUILDFLAG(IS_WIN)
64 
ReadStreamToSpanWithMaxSize(FILE * stream,size_t max_size,FunctionRef<span<uint8_t> (size_t)> resize_span)65 bool ReadStreamToSpanWithMaxSize(
66     FILE* stream,
67     size_t max_size,
68     FunctionRef<span<uint8_t>(size_t)> resize_span) {
69   if (!stream) {
70     return false;
71   }
72 
73   // Seeking to the beginning is best-effort -- it is expected to fail for
74   // certain non-file stream (e.g., pipes).
75   HANDLE_EINTR(fseek(stream, 0, SEEK_SET));
76 
77   // Many files have incorrect size (proc files etc). Hence, the file is read
78   // sequentially as opposed to a one-shot read, using file size as a hint for
79   // chunk size if available.
80   constexpr size_t kDefaultChunkSize = 1 << 16;
81   size_t chunk_size = kDefaultChunkSize - 1;
82   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
83 #if BUILDFLAG(IS_WIN)
84   BY_HANDLE_FILE_INFORMATION file_info = {};
85   if (::GetFileInformationByHandle(
86           reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stream))),
87           &file_info)) {
88     LARGE_INTEGER size;
89     size.HighPart = static_cast<LONG>(file_info.nFileSizeHigh);
90     size.LowPart = file_info.nFileSizeLow;
91     if (size.QuadPart > 0) {
92       chunk_size = static_cast<size_t>(size.QuadPart);
93     }
94   }
95 #else   // BUILDFLAG(IS_WIN)
96   // In cases where the reported file size is 0, use a smaller chunk size to
97   // minimize memory allocated and cost of string::resize() in case the read
98   // size is small (i.e. proc files). If the file is larger than this, the read
99   // loop will reset |chunk_size| to kDefaultChunkSize.
100   constexpr size_t kSmallChunkSize = 4096;
101   chunk_size = kSmallChunkSize - 1;
102   stat_wrapper_t file_info = {};
103   if (!File::Fstat(fileno(stream), &file_info) && file_info.st_size > 0) {
104     chunk_size = static_cast<size_t>(file_info.st_size);
105   }
106 #endif  // BUILDFLAG(IS_WIN)
107 
108   // We need to attempt to read at EOF for feof flag to be set so here we use
109   // |chunk_size| + 1.
110   chunk_size = std::min(chunk_size, max_size) + 1;
111   size_t bytes_read_this_pass;
112   size_t bytes_read_so_far = 0;
113   bool read_status = true;
114   span<uint8_t> bytes_span = resize_span(chunk_size);
115   DCHECK_EQ(bytes_span.size(), chunk_size);
116 
117   while ((bytes_read_this_pass = fread(bytes_span.data() + bytes_read_so_far, 1,
118                                        chunk_size, stream)) > 0) {
119     if ((max_size - bytes_read_so_far) < bytes_read_this_pass) {
120       // Read more than max_size bytes, bail out.
121       bytes_read_so_far = max_size;
122       read_status = false;
123       break;
124     }
125     // In case EOF was not reached, iterate again but revert to the default
126     // chunk size.
127     if (bytes_read_so_far == 0) {
128       chunk_size = kDefaultChunkSize;
129     }
130 
131     bytes_read_so_far += bytes_read_this_pass;
132     // Last fread syscall (after EOF) can be avoided via feof, which is just a
133     // flag check.
134     if (feof(stream)) {
135       break;
136     }
137     bytes_span = resize_span(bytes_read_so_far + chunk_size);
138     DCHECK_EQ(bytes_span.size(), bytes_read_so_far + chunk_size);
139   }
140   read_status = read_status && !ferror(stream);
141 
142   // Trim the container down to the number of bytes that were actually read.
143   bytes_span = resize_span(bytes_read_so_far);
144   DCHECK_EQ(bytes_span.size(), bytes_read_so_far);
145 
146   return read_status;
147 }
148 
149 }  // namespace
150 
151 #if !BUILDFLAG(IS_WIN)
152 
GetDeleteFileCallback(const FilePath & path,OnceCallback<void (bool)> reply_callback)153 OnceClosure GetDeleteFileCallback(const FilePath& path,
154                                   OnceCallback<void(bool)> reply_callback) {
155   return BindOnce(&RunAndReply, BindOnce(&DeleteFile, path),
156                   reply_callback.is_null()
157                       ? std::move(reply_callback)
158                       : BindPostTask(SequencedTaskRunner::GetCurrentDefault(),
159                                      std::move(reply_callback)));
160 }
161 
GetDeletePathRecursivelyCallback(const FilePath & path,OnceCallback<void (bool)> reply_callback)162 OnceClosure GetDeletePathRecursivelyCallback(
163     const FilePath& path,
164     OnceCallback<void(bool)> reply_callback) {
165   return BindOnce(&RunAndReply, BindOnce(&DeletePathRecursively, path),
166                   reply_callback.is_null()
167                       ? std::move(reply_callback)
168                       : BindPostTask(SequencedTaskRunner::GetCurrentDefault(),
169                                      std::move(reply_callback)));
170 }
171 
172 #endif  // !BUILDFLAG(IS_WIN)
173 
ComputeDirectorySize(const FilePath & root_path)174 int64_t ComputeDirectorySize(const FilePath& root_path) {
175   int64_t running_size = 0;
176   FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
177   while (!file_iter.Next().empty()) {
178     running_size += file_iter.GetInfo().GetSize();
179   }
180   return running_size;
181 }
182 
Move(const FilePath & from_path,const FilePath & to_path)183 bool Move(const FilePath& from_path, const FilePath& to_path) {
184   if (from_path.ReferencesParent() || to_path.ReferencesParent()) {
185     return false;
186   }
187   return internal::MoveUnsafe(from_path, to_path);
188 }
189 
CopyFileContents(File & infile,File & outfile)190 bool CopyFileContents(File& infile, File& outfile) {
191 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
192   bool retry_slow = false;
193   bool res =
194       internal::CopyFileContentsWithSendfile(infile, outfile, retry_slow);
195   if (res || !retry_slow) {
196     return res;
197   }
198   // Any failures which allow retrying using read/write will not have modified
199   // either file offset or size.
200 #endif
201 
202   static constexpr size_t kBufferSize = 32768;
203   std::vector<char> buffer(kBufferSize);
204 
205   for (;;) {
206     int bytes_read =
207         infile.ReadAtCurrentPos(buffer.data(), static_cast<int>(buffer.size()));
208     if (bytes_read < 0) {
209       return false;
210     }
211     if (bytes_read == 0) {
212       return true;
213     }
214     // Allow for partial writes
215     int bytes_written_per_read = 0;
216     do {
217       int bytes_written_partial = outfile.WriteAtCurrentPos(
218           &buffer[static_cast<size_t>(bytes_written_per_read)],
219           bytes_read - bytes_written_per_read);
220       if (bytes_written_partial < 0) {
221         return false;
222       }
223 
224       bytes_written_per_read += bytes_written_partial;
225     } while (bytes_written_per_read < bytes_read);
226   }
227 
228   NOTREACHED();
229 }
230 
ContentsEqual(const FilePath & filename1,const FilePath & filename2)231 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
232   // We open the file in binary format even if they are text files because
233   // we are just comparing that bytes are exactly same in both files and not
234   // doing anything smart with text formatting.
235 #if BUILDFLAG(IS_WIN)
236   std::ifstream file1(filename1.value().c_str(),
237                       std::ios::in | std::ios::binary);
238   std::ifstream file2(filename2.value().c_str(),
239                       std::ios::in | std::ios::binary);
240 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
241   std::ifstream file1(filename1.value(), std::ios::in | std::ios::binary);
242   std::ifstream file2(filename2.value(), std::ios::in | std::ios::binary);
243 #endif  // BUILDFLAG(IS_WIN)
244 
245   // Even if both files aren't openable (and thus, in some sense, "equal"),
246   // any unusable file yields a result of "false".
247   if (!file1.is_open() || !file2.is_open()) {
248     return false;
249   }
250 
251   const int BUFFER_SIZE = 2056;
252   char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
253   do {
254     file1.read(buffer1, BUFFER_SIZE);
255     file2.read(buffer2, BUFFER_SIZE);
256 
257     if ((file1.eof() != file2.eof()) || (file1.gcount() != file2.gcount()) ||
258         (memcmp(buffer1, buffer2, static_cast<size_t>(file1.gcount())))) {
259       file1.close();
260       file2.close();
261       return false;
262     }
263   } while (!file1.eof() || !file2.eof());
264 
265   file1.close();
266   file2.close();
267   return true;
268 }
269 
TextContentsEqual(const FilePath & filename1,const FilePath & filename2)270 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
271 #if BUILDFLAG(IS_WIN)
272   std::ifstream file1(filename1.value().c_str(), std::ios::in);
273   std::ifstream file2(filename2.value().c_str(), std::ios::in);
274 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
275   std::ifstream file1(filename1.value(), std::ios::in);
276   std::ifstream file2(filename2.value(), std::ios::in);
277 #endif  // BUILDFLAG(IS_WIN)
278 
279   // Even if both files aren't openable (and thus, in some sense, "equal"),
280   // any unusable file yields a result of "false".
281   if (!file1.is_open() || !file2.is_open()) {
282     return false;
283   }
284 
285   do {
286     std::string line1, line2;
287     getline(file1, line1);
288     getline(file2, line2);
289 
290     // Check for mismatched EOF states, or any error state.
291     if ((file1.eof() != file2.eof()) || file1.bad() || file2.bad()) {
292       return false;
293     }
294 
295     // Trim all '\r' and '\n' characters from the end of the line.
296     std::string::size_type end1 = line1.find_last_not_of("\r\n");
297     if (end1 == std::string::npos) {
298       line1.clear();
299     } else if (end1 + 1 < line1.length()) {
300       line1.erase(end1 + 1);
301     }
302 
303     std::string::size_type end2 = line2.find_last_not_of("\r\n");
304     if (end2 == std::string::npos) {
305       line2.clear();
306     } else if (end2 + 1 < line2.length()) {
307       line2.erase(end2 + 1);
308     }
309 
310     if (line1 != line2) {
311       return false;
312     }
313   } while (!file1.eof() || !file2.eof());
314 
315   return true;
316 }
317 
ReadStreamToString(FILE * stream,std::string * contents)318 bool ReadStreamToString(FILE* stream, std::string* contents) {
319   return ReadStreamToStringWithMaxSize(
320       stream, std::numeric_limits<size_t>::max(), contents);
321 }
322 
ReadStreamToStringWithMaxSize(FILE * stream,size_t max_size,std::string * contents)323 bool ReadStreamToStringWithMaxSize(FILE* stream,
324                                    size_t max_size,
325                                    std::string* contents) {
326   if (contents) {
327     contents->clear();
328   }
329 
330   std::string content_string;
331   bool read_successs = ReadStreamToSpanWithMaxSize(
332       stream, max_size, [&content_string](size_t size) {
333         content_string.resize(size);
334         return as_writable_byte_span(content_string);
335       });
336 
337   if (contents) {
338     contents->swap(content_string);
339   }
340   return read_successs;
341 }
342 
ReadFileToBytes(const FilePath & path)343 std::optional<std::vector<uint8_t>> ReadFileToBytes(const FilePath& path) {
344   if (path.ReferencesParent()) {
345     return std::nullopt;
346   }
347 
348   ScopedFILE file_stream(OpenFile(path, "rb"));
349   if (!file_stream) {
350     return std::nullopt;
351   }
352 
353   std::vector<uint8_t> bytes;
354   if (!ReadStreamToSpanWithMaxSize(file_stream.get(),
355                                    std::numeric_limits<size_t>::max(),
356                                    [&bytes](size_t size) {
357                                      bytes.resize(size);
358                                      return span(bytes);
359                                    })) {
360     return std::nullopt;
361   }
362   return bytes;
363 }
364 
ReadFileToString(const FilePath & path,std::string * contents)365 bool ReadFileToString(const FilePath& path, std::string* contents) {
366   return ReadFileToStringWithMaxSize(path, contents,
367                                      std::numeric_limits<size_t>::max());
368 }
369 
ReadFileToStringWithMaxSize(const FilePath & path,std::string * contents,size_t max_size)370 bool ReadFileToStringWithMaxSize(const FilePath& path,
371                                  std::string* contents,
372                                  size_t max_size) {
373   if (contents) {
374     contents->clear();
375   }
376   if (path.ReferencesParent()) {
377     return false;
378   }
379   ScopedFILE file_stream(OpenFile(path, "rb"));
380   if (!file_stream) {
381     return false;
382   }
383   return ReadStreamToStringWithMaxSize(file_stream.get(), max_size, contents);
384 }
385 
IsDirectoryEmpty(const FilePath & dir_path)386 bool IsDirectoryEmpty(const FilePath& dir_path) {
387   FileEnumerator files(dir_path, false,
388                        FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
389   if (files.Next().empty()) {
390     return true;
391   }
392   return false;
393 }
394 
CreateTemporaryFile(FilePath * path)395 bool CreateTemporaryFile(FilePath* path) {
396   FilePath temp_dir;
397   return GetTempDir(&temp_dir) && CreateTemporaryFileInDir(temp_dir, path);
398 }
399 
CreateAndOpenTemporaryStream(FilePath * path)400 ScopedFILE CreateAndOpenTemporaryStream(FilePath* path) {
401   FilePath directory;
402   if (!GetTempDir(&directory)) {
403     return nullptr;
404   }
405 
406   return CreateAndOpenTemporaryStreamInDir(directory, path);
407 }
408 
CreateDirectory(const FilePath & full_path)409 bool CreateDirectory(const FilePath& full_path) {
410   return CreateDirectoryAndGetError(full_path, nullptr);
411 }
412 
GetFileSize(const FilePath & file_path)413 std::optional<int64_t> GetFileSize(const FilePath& file_path) {
414   File::Info info;
415   if (!GetFileInfo(file_path, &info)) {
416     return std::nullopt;
417   }
418   return info.size;
419 }
420 
GetFileSizeCallback(const FilePath & path)421 OnceCallback<std::optional<int64_t>()> GetFileSizeCallback(
422     const FilePath& path) {
423   return BindOnce([](const FilePath& path) { return GetFileSize(path); }, path);
424 }
425 
TouchFile(const FilePath & path,const Time & last_accessed,const Time & last_modified)426 bool TouchFile(const FilePath& path,
427                const Time& last_accessed,
428                const Time& last_modified) {
429   uint32_t flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES;
430 
431 #if BUILDFLAG(IS_WIN)
432   // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
433   if (DirectoryExists(path)) {
434     flags |= File::FLAG_WIN_BACKUP_SEMANTICS;
435   }
436 #elif BUILDFLAG(IS_FUCHSIA)
437   // On Fuchsia, we need O_RDONLY for directories, or O_WRONLY for files.
438   // TODO(crbug.com/40620916): Find a cleaner workaround for this.
439   flags |= (DirectoryExists(path) ? File::FLAG_READ : File::FLAG_WRITE);
440 #endif
441 
442   File file(path, flags);
443   if (!file.IsValid()) {
444     return false;
445   }
446 
447   return file.SetTimes(last_accessed, last_modified);
448 }
449 
CloseFile(FILE * file)450 bool CloseFile(FILE* file) {
451   if (file == nullptr) {
452     return true;
453   }
454   return fclose(file) == 0;
455 }
456 
TruncateFile(FILE * file)457 bool TruncateFile(FILE* file) {
458   if (file == nullptr) {
459     return false;
460   }
461   long current_offset = ftell(file);
462   if (current_offset == -1) {
463     return false;
464   }
465 #if BUILDFLAG(IS_WIN)
466   int fd = _fileno(file);
467   if (_chsize(fd, current_offset) != 0) {
468     return false;
469   }
470 #else
471   int fd = fileno(file);
472   if (ftruncate(fd, current_offset) != 0) {
473     return false;
474   }
475 #endif
476   return true;
477 }
478 
ReadFile(const FilePath & filename,span<uint8_t> buffer)479 std::optional<uint64_t> ReadFile(const FilePath& filename,
480                                  span<uint8_t> buffer) {
481   return ReadFile(filename, base::as_writable_chars(buffer));
482 }
483 
ReadFile(const FilePath & filename,char * data,int max_size)484 int ReadFile(const FilePath& filename, char* data, int max_size) {
485   if (max_size < 0) {
486     return -1;
487   }
488   std::optional<uint64_t> result =
489       ReadFile(filename, span(data, static_cast<uint32_t>(max_size)));
490   if (!result) {
491     return -1;
492   }
493   return checked_cast<int>(result.value());
494 }
495 
WriteFile(const FilePath & filename,std::string_view data)496 bool WriteFile(const FilePath& filename, std::string_view data) {
497   return WriteFile(filename, as_byte_span(data));
498 }
499 
GetUniquePath(const FilePath & path)500 FilePath GetUniquePath(const FilePath& path) {
501   return GetUniquePathWithSuffixFormat(path, " (%d)");
502 }
503 
GetUniquePathWithSuffixFormat(const FilePath & path,base::cstring_view suffix_format)504 FilePath GetUniquePathWithSuffixFormat(const FilePath& path,
505                                        base::cstring_view suffix_format) {
506   DCHECK(!path.empty());
507   DCHECK_EQ(base::ranges::count(suffix_format, '%'), 1);
508   DCHECK(base::Contains(suffix_format, "%d"));
509 
510   if (!PathExists(path)) {
511     return path;
512   }
513   for (int count = 1; count <= kMaxUniqueFiles; ++count) {
514     FilePath candidate_path = path.InsertBeforeExtensionASCII(
515         StringPrintfNonConstexpr(suffix_format.data(), count));
516     if (!PathExists(candidate_path)) {
517       return candidate_path;
518     }
519   }
520   return FilePath();
521 }
522 
523 }  // namespace base
524