1 // Copyright (c) 2012 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 "base/files/file_util.h"
6 
7 #if defined(OS_WIN)
8 #include <io.h>
9 #endif
10 #include <stdio.h>
11 
12 #include <fstream>
13 #include <limits>
14 #include <memory>
15 
16 #include "base/check_op.h"
17 #include "base/files/file_enumerator.h"
18 #include "base/files/file_path.h"
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/strings/string_piece.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/threading/scoped_blocking_call.h"
25 #include "build/build_config.h"
26 
27 namespace base {
28 
29 #if !defined(OS_NACL_NONSFI)
GetDeleteFileCallback()30 OnceCallback<void(const FilePath&)> GetDeleteFileCallback() {
31   return BindOnce(IgnoreResult(&DeleteFile));
32 }
33 
GetDeletePathRecursivelyCallback()34 OnceCallback<void(const FilePath&)> GetDeletePathRecursivelyCallback() {
35   return BindOnce(IgnoreResult(&DeletePathRecursively));
36 }
37 
ComputeDirectorySize(const FilePath & root_path)38 int64_t ComputeDirectorySize(const FilePath& root_path) {
39   int64_t running_size = 0;
40   FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
41   while (!file_iter.Next().empty())
42     running_size += file_iter.GetInfo().GetSize();
43   return running_size;
44 }
45 
Move(const FilePath & from_path,const FilePath & to_path)46 bool Move(const FilePath& from_path, const FilePath& to_path) {
47   if (from_path.ReferencesParent() || to_path.ReferencesParent())
48     return false;
49   return internal::MoveUnsafe(from_path, to_path);
50 }
51 
ContentsEqual(const FilePath & filename1,const FilePath & filename2)52 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
53   // We open the file in binary format even if they are text files because
54   // we are just comparing that bytes are exactly same in both files and not
55   // doing anything smart with text formatting.
56 #if defined(OS_WIN)
57   std::ifstream file1(filename1.value().c_str(),
58                       std::ios::in | std::ios::binary);
59   std::ifstream file2(filename2.value().c_str(),
60                       std::ios::in | std::ios::binary);
61 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
62   std::ifstream file1(filename1.value(), std::ios::in | std::ios::binary);
63   std::ifstream file2(filename2.value(), std::ios::in | std::ios::binary);
64 #endif  // OS_WIN
65 
66   // Even if both files aren't openable (and thus, in some sense, "equal"),
67   // any unusable file yields a result of "false".
68   if (!file1.is_open() || !file2.is_open())
69     return false;
70 
71   const int BUFFER_SIZE = 2056;
72   char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
73   do {
74     file1.read(buffer1, BUFFER_SIZE);
75     file2.read(buffer2, BUFFER_SIZE);
76 
77     if ((file1.eof() != file2.eof()) ||
78         (file1.gcount() != file2.gcount()) ||
79         (memcmp(buffer1, buffer2, static_cast<size_t>(file1.gcount())))) {
80       file1.close();
81       file2.close();
82       return false;
83     }
84   } while (!file1.eof() || !file2.eof());
85 
86   file1.close();
87   file2.close();
88   return true;
89 }
90 
TextContentsEqual(const FilePath & filename1,const FilePath & filename2)91 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
92 #if defined(OS_WIN)
93   std::ifstream file1(filename1.value().c_str(), std::ios::in);
94   std::ifstream file2(filename2.value().c_str(), std::ios::in);
95 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
96   std::ifstream file1(filename1.value(), std::ios::in);
97   std::ifstream file2(filename2.value(), std::ios::in);
98 #endif  // OS_WIN
99 
100   // Even if both files aren't openable (and thus, in some sense, "equal"),
101   // any unusable file yields a result of "false".
102   if (!file1.is_open() || !file2.is_open())
103     return false;
104 
105   do {
106     std::string line1, line2;
107     getline(file1, line1);
108     getline(file2, line2);
109 
110     // Check for mismatched EOF states, or any error state.
111     if ((file1.eof() != file2.eof()) ||
112         file1.bad() || file2.bad()) {
113       return false;
114     }
115 
116     // Trim all '\r' and '\n' characters from the end of the line.
117     std::string::size_type end1 = line1.find_last_not_of("\r\n");
118     if (end1 == std::string::npos)
119       line1.clear();
120     else if (end1 + 1 < line1.length())
121       line1.erase(end1 + 1);
122 
123     std::string::size_type end2 = line2.find_last_not_of("\r\n");
124     if (end2 == std::string::npos)
125       line2.clear();
126     else if (end2 + 1 < line2.length())
127       line2.erase(end2 + 1);
128 
129     if (line1 != line2)
130       return false;
131   } while (!file1.eof() || !file2.eof());
132 
133   return true;
134 }
135 #endif  // !defined(OS_NACL_NONSFI)
136 
ReadStreamToString(FILE * stream,std::string * contents)137 bool ReadStreamToString(FILE* stream, std::string* contents) {
138   return ReadStreamToStringWithMaxSize(
139       stream, std::numeric_limits<size_t>::max(), contents);
140 }
141 
ReadStreamToStringWithMaxSize(FILE * stream,size_t max_size,std::string * contents)142 bool ReadStreamToStringWithMaxSize(FILE* stream,
143                                    size_t max_size,
144                                    std::string* contents) {
145   if (contents)
146     contents->clear();
147 
148   // Seeking to the beginning is best-effort -- it is expected to fail for
149   // certain non-file stream (e.g., pipes).
150   HANDLE_EINTR(fseek(stream, 0, SEEK_SET));
151 
152   // Many files have incorrect size (proc files etc). Hence, the file is read
153   // sequentially as opposed to a one-shot read, using file size as a hint for
154   // chunk size if available.
155   constexpr int64_t kDefaultChunkSize = 1 << 16;
156   int64_t chunk_size = kDefaultChunkSize - 1;
157 #if !defined(OS_NACL_NONSFI)
158   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
159 #if defined(OS_WIN)
160   BY_HANDLE_FILE_INFORMATION file_info = {};
161   if (::GetFileInformationByHandle(
162           reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stream))),
163           &file_info)) {
164     LARGE_INTEGER size;
165     size.HighPart = file_info.nFileSizeHigh;
166     size.LowPart = file_info.nFileSizeLow;
167     if (size.QuadPart > 0)
168       chunk_size = size.QuadPart;
169   }
170 #else   // defined(OS_WIN)
171   stat_wrapper_t file_info = {};
172   if (!File::Fstat(fileno(stream), &file_info) && file_info.st_size > 0)
173     chunk_size = file_info.st_size;
174 #endif  // defined(OS_WIN)
175   // We need to attempt to read at EOF for feof flag to be set so here we
176   // use |chunk_size| + 1.
177   chunk_size = std::min<uint64_t>(chunk_size, max_size) + 1;
178 #else   // !defined(OS_NACL_NONSFI)
179   chunk_size = kDefaultChunkSize;
180 #endif  // !defined(OS_NACL_NONSFI)
181   size_t bytes_read_this_pass;
182   size_t bytes_read_so_far = 0;
183   bool read_status = true;
184   std::string local_contents;
185   local_contents.resize(chunk_size);
186 
187   while ((bytes_read_this_pass = fread(&local_contents[bytes_read_so_far], 1,
188                                        chunk_size, stream)) > 0) {
189     if ((max_size - bytes_read_so_far) < bytes_read_this_pass) {
190       // Read more than max_size bytes, bail out.
191       bytes_read_so_far = max_size;
192       read_status = false;
193       break;
194     }
195     // In case EOF was not reached, iterate again but revert to the default
196     // chunk size.
197     if (bytes_read_so_far == 0)
198       chunk_size = kDefaultChunkSize;
199 
200     bytes_read_so_far += bytes_read_this_pass;
201     // Last fread syscall (after EOF) can be avoided via feof, which is just a
202     // flag check.
203     if (feof(stream))
204       break;
205     local_contents.resize(bytes_read_so_far + chunk_size);
206   }
207   read_status = read_status && !ferror(stream);
208   if (contents) {
209     contents->swap(local_contents);
210     contents->resize(bytes_read_so_far);
211   }
212 
213   return read_status;
214 }
215 
ReadFileToString(const FilePath & path,std::string * contents)216 bool ReadFileToString(const FilePath& path, std::string* contents) {
217   return ReadFileToStringWithMaxSize(path, contents,
218                                      std::numeric_limits<size_t>::max());
219 }
220 
ReadFileToStringWithMaxSize(const FilePath & path,std::string * contents,size_t max_size)221 bool ReadFileToStringWithMaxSize(const FilePath& path,
222                                  std::string* contents,
223                                  size_t max_size) {
224   if (contents)
225     contents->clear();
226   if (path.ReferencesParent())
227     return false;
228   ScopedFILE file_stream(OpenFile(path, "rb"));
229   if (!file_stream)
230     return false;
231   return ReadStreamToStringWithMaxSize(file_stream.get(), max_size, contents);
232 }
233 
234 #if !defined(OS_NACL_NONSFI)
IsDirectoryEmpty(const FilePath & dir_path)235 bool IsDirectoryEmpty(const FilePath& dir_path) {
236   FileEnumerator files(dir_path, false,
237       FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
238   if (files.Next().empty())
239     return true;
240   return false;
241 }
242 
CreateTemporaryFile(FilePath * path)243 bool CreateTemporaryFile(FilePath* path) {
244   FilePath temp_dir;
245   return GetTempDir(&temp_dir) && CreateTemporaryFileInDir(temp_dir, path);
246 }
247 
CreateAndOpenTemporaryStream(FilePath * path)248 ScopedFILE CreateAndOpenTemporaryStream(FilePath* path) {
249   FilePath directory;
250   if (!GetTempDir(&directory))
251     return nullptr;
252 
253   return CreateAndOpenTemporaryStreamInDir(directory, path);
254 }
255 
CreateDirectory(const FilePath & full_path)256 bool CreateDirectory(const FilePath& full_path) {
257   return CreateDirectoryAndGetError(full_path, nullptr);
258 }
259 
GetFileSize(const FilePath & file_path,int64_t * file_size)260 bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
261   File::Info info;
262   if (!GetFileInfo(file_path, &info))
263     return false;
264   *file_size = info.size;
265   return true;
266 }
267 
TouchFile(const FilePath & path,const Time & last_accessed,const Time & last_modified)268 bool TouchFile(const FilePath& path,
269                const Time& last_accessed,
270                const Time& last_modified) {
271   int flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES;
272 
273 #if defined(OS_WIN)
274   // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
275   if (DirectoryExists(path))
276     flags |= File::FLAG_BACKUP_SEMANTICS;
277 #elif defined(OS_FUCHSIA)
278   // On Fuchsia, we need O_RDONLY for directories, or O_WRONLY for files.
279   // TODO(https://crbug.com/947802): Find a cleaner workaround for this.
280   flags |= (DirectoryExists(path) ? File::FLAG_READ : File::FLAG_WRITE);
281 #endif
282 
283   File file(path, flags);
284   if (!file.IsValid())
285     return false;
286 
287   return file.SetTimes(last_accessed, last_modified);
288 }
289 #endif  // !defined(OS_NACL_NONSFI)
290 
CloseFile(FILE * file)291 bool CloseFile(FILE* file) {
292   if (file == nullptr)
293     return true;
294   return fclose(file) == 0;
295 }
296 
297 #if !defined(OS_NACL_NONSFI)
TruncateFile(FILE * file)298 bool TruncateFile(FILE* file) {
299   if (file == nullptr)
300     return false;
301   long current_offset = ftell(file);
302   if (current_offset == -1)
303     return false;
304 #if defined(OS_WIN)
305   int fd = _fileno(file);
306   if (_chsize(fd, current_offset) != 0)
307     return false;
308 #else
309   int fd = fileno(file);
310   if (ftruncate(fd, current_offset) != 0)
311     return false;
312 #endif
313   return true;
314 }
315 
WriteFile(const FilePath & filename,span<const uint8_t> data)316 bool WriteFile(const FilePath& filename, span<const uint8_t> data) {
317   int size = checked_cast<int>(data.size());
318   return WriteFile(filename, reinterpret_cast<const char*>(data.data()),
319                    size) == size;
320 }
321 
WriteFile(const FilePath & filename,StringPiece data)322 bool WriteFile(const FilePath& filename, StringPiece data) {
323   int size = checked_cast<int>(data.size());
324   return WriteFile(filename, data.data(), size) == size;
325 }
326 
GetUniquePathNumber(const FilePath & path)327 int GetUniquePathNumber(const FilePath& path) {
328   DCHECK(!path.empty());
329   if (!PathExists(path))
330     return 0;
331 
332   std::string number;
333   for (int count = 1; count <= kMaxUniqueFiles; ++count) {
334     StringAppendF(&number, " (%d)", count);
335     if (!PathExists(path.InsertBeforeExtensionASCII(number)))
336       return count;
337     number.clear();
338   }
339 
340   return -1;
341 }
342 
GetUniquePath(const FilePath & path)343 FilePath GetUniquePath(const FilePath& path) {
344   DCHECK(!path.empty());
345   const int uniquifier = GetUniquePathNumber(path);
346   if (uniquifier > 0)
347     return path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", uniquifier));
348   return uniquifier == 0 ? path : base::FilePath();
349 }
350 
351 namespace internal {
352 
PreReadFileSlow(const FilePath & file_path,int64_t max_bytes)353 bool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes) {
354   DCHECK_GE(max_bytes, 0);
355 
356   File file(file_path, File::FLAG_OPEN | File::FLAG_READ |
357                            File::FLAG_SEQUENTIAL_SCAN |
358                            File::FLAG_SHARE_DELETE);
359   if (!file.IsValid())
360     return false;
361 
362   constexpr int kBufferSize = 1024 * 1024;
363   // Ensures the buffer is deallocated at function exit.
364   std::unique_ptr<char[]> buffer_deleter(new char[kBufferSize]);
365   char* const buffer = buffer_deleter.get();
366 
367   while (max_bytes > 0) {
368     // The static_cast<int> is safe because kBufferSize is int, and both values
369     // are non-negative. So, the minimum is guaranteed to fit in int.
370     const int read_size =
371         static_cast<int>(std::min<int64_t>(max_bytes, kBufferSize));
372     DCHECK_GE(read_size, 0);
373     DCHECK_LE(read_size, kBufferSize);
374 
375     const int read_bytes = file.ReadAtCurrentPos(buffer, read_size);
376     if (read_bytes < 0)
377       return false;
378     if (read_bytes == 0)
379       break;
380 
381     max_bytes -= read_bytes;
382   }
383 
384   return true;
385 }
386 
387 }  // namespace internal
388 
389 #endif  // !defined(OS_NACL_NONSFI)
390 
391 }  // namespace base
392