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