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