1 // Copyright (c) 2010 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/file_util.h"
6
7 #if defined(OS_WIN)
8 #include <io.h>
9 #endif
10 #include <stdio.h>
11
12 #include <fstream>
13
14 #include "base/file_path.h"
15 #include "base/logging.h"
16 #include "base/string_piece.h"
17 #include "base/string_util.h"
18 #include "base/utf_string_conversions.h"
19
20 namespace {
21
22 const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
23
24 } // namespace
25
26 namespace file_util {
27
EndsWithSeparator(const FilePath & path)28 bool EndsWithSeparator(const FilePath& path) {
29 FilePath::StringType value = path.value();
30 if (value.empty())
31 return false;
32
33 return FilePath::IsSeparator(value[value.size() - 1]);
34 }
35
EnsureEndsWithSeparator(FilePath * path)36 bool EnsureEndsWithSeparator(FilePath* path) {
37 if (!DirectoryExists(*path))
38 return false;
39
40 if (EndsWithSeparator(*path))
41 return true;
42
43 FilePath::StringType& path_str =
44 const_cast<FilePath::StringType&>(path->value());
45 path_str.append(&FilePath::kSeparators[0], 1);
46
47 return true;
48 }
49
GetFileExtensionFromPath(const FilePath & path)50 FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
51 FilePath::StringType file_name = path.BaseName().value();
52 const FilePath::StringType::size_type last_dot =
53 file_name.rfind(kExtensionSeparator);
54 return FilePath::StringType(last_dot == FilePath::StringType::npos ?
55 FILE_PATH_LITERAL("") :
56 file_name, last_dot+1);
57 }
58
InsertBeforeExtension(FilePath * path,const FilePath::StringType & suffix)59 void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
60 FilePath::StringType& value =
61 const_cast<FilePath::StringType&>(path->value());
62
63 const FilePath::StringType::size_type last_dot =
64 value.rfind(kExtensionSeparator);
65 const FilePath::StringType::size_type last_separator =
66 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
67
68 if (last_dot == FilePath::StringType::npos ||
69 (last_separator != std::wstring::npos && last_dot < last_separator)) {
70 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
71 // We should just append the suffix to the entire path.
72 value.append(suffix);
73 return;
74 }
75
76 value.insert(last_dot, suffix);
77 }
78
ContentsEqual(const FilePath & filename1,const FilePath & filename2)79 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
80 // We open the file in binary format even if they are text files because
81 // we are just comparing that bytes are exactly same in both files and not
82 // doing anything smart with text formatting.
83 std::ifstream file1(filename1.value().c_str(),
84 std::ios::in | std::ios::binary);
85 std::ifstream file2(filename2.value().c_str(),
86 std::ios::in | std::ios::binary);
87
88 // Even if both files aren't openable (and thus, in some sense, "equal"),
89 // any unusable file yields a result of "false".
90 if (!file1.is_open() || !file2.is_open())
91 return false;
92
93 const int BUFFER_SIZE = 2056;
94 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
95 do {
96 file1.read(buffer1, BUFFER_SIZE);
97 file2.read(buffer2, BUFFER_SIZE);
98
99 if ((file1.eof() != file2.eof()) ||
100 (file1.gcount() != file2.gcount()) ||
101 (memcmp(buffer1, buffer2, file1.gcount()))) {
102 file1.close();
103 file2.close();
104 return false;
105 }
106 } while (!file1.eof() || !file2.eof());
107
108 file1.close();
109 file2.close();
110 return true;
111 }
112
TextContentsEqual(const FilePath & filename1,const FilePath & filename2)113 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
114 std::ifstream file1(filename1.value().c_str(), std::ios::in);
115 std::ifstream file2(filename2.value().c_str(), std::ios::in);
116
117 // Even if both files aren't openable (and thus, in some sense, "equal"),
118 // any unusable file yields a result of "false".
119 if (!file1.is_open() || !file2.is_open())
120 return false;
121
122 do {
123 std::string line1, line2;
124 getline(file1, line1);
125 getline(file2, line2);
126
127 // Check for mismatched EOF states, or any error state.
128 if ((file1.eof() != file2.eof()) ||
129 file1.bad() || file2.bad()) {
130 return false;
131 }
132
133 // Trim all '\r' and '\n' characters from the end of the line.
134 std::string::size_type end1 = line1.find_last_not_of("\r\n");
135 if (end1 == std::string::npos)
136 line1.clear();
137 else if (end1 + 1 < line1.length())
138 line1.erase(end1 + 1);
139
140 std::string::size_type end2 = line2.find_last_not_of("\r\n");
141 if (end2 == std::string::npos)
142 line2.clear();
143 else if (end2 + 1 < line2.length())
144 line2.erase(end2 + 1);
145
146 if (line1 != line2)
147 return false;
148 } while (!file1.eof() || !file2.eof());
149
150 return true;
151 }
152
ReadFileToString(const FilePath & path,std::string * contents)153 bool ReadFileToString(const FilePath& path, std::string* contents) {
154 FILE* file = OpenFile(path, "rb");
155 if (!file) {
156 return false;
157 }
158
159 char buf[1 << 16];
160 size_t len;
161 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
162 if (contents)
163 contents->append(buf, len);
164 }
165 CloseFile(file);
166
167 return true;
168 }
169
IsDirectoryEmpty(const FilePath & dir_path)170 bool IsDirectoryEmpty(const FilePath& dir_path) {
171 FileEnumerator files(dir_path, false,
172 static_cast<FileEnumerator::FILE_TYPE>(
173 FileEnumerator::FILES | FileEnumerator::DIRECTORIES));
174 if (files.Next().value().empty())
175 return true;
176 return false;
177 }
178
CreateAndOpenTemporaryFile(FilePath * path)179 FILE* CreateAndOpenTemporaryFile(FilePath* path) {
180 FilePath directory;
181 if (!GetTempDir(&directory))
182 return NULL;
183
184 return CreateAndOpenTemporaryFileInDir(directory, path);
185 }
186
GetFileSize(const FilePath & file_path,int64 * file_size)187 bool GetFileSize(const FilePath& file_path, int64* file_size) {
188 base::PlatformFileInfo info;
189 if (!GetFileInfo(file_path, &info))
190 return false;
191 *file_size = info.size;
192 return true;
193 }
194
IsDot(const FilePath & path)195 bool IsDot(const FilePath& path) {
196 return FILE_PATH_LITERAL(".") == path.BaseName().value();
197 }
198
IsDotDot(const FilePath & path)199 bool IsDotDot(const FilePath& path) {
200 return FILE_PATH_LITERAL("..") == path.BaseName().value();
201 }
202
TouchFile(const FilePath & path,const base::Time & last_accessed,const base::Time & last_modified)203 bool TouchFile(const FilePath& path,
204 const base::Time& last_accessed,
205 const base::Time& last_modified) {
206 base::PlatformFile file =
207 base::CreatePlatformFile(path,
208 base::PLATFORM_FILE_OPEN |
209 base::PLATFORM_FILE_WRITE_ATTRIBUTES,
210 NULL, NULL);
211 if (file != base::kInvalidPlatformFileValue) {
212 bool result = base::TouchPlatformFile(file, last_accessed, last_modified);
213 base::ClosePlatformFile(file);
214 return result;
215 }
216
217 return false;
218 }
219
SetLastModifiedTime(const FilePath & path,const base::Time & last_modified)220 bool SetLastModifiedTime(const FilePath& path,
221 const base::Time& last_modified) {
222 return TouchFile(path, last_modified, last_modified);
223 }
224
CloseFile(FILE * file)225 bool CloseFile(FILE* file) {
226 if (file == NULL)
227 return true;
228 return fclose(file) == 0;
229 }
230
TruncateFile(FILE * file)231 bool TruncateFile(FILE* file) {
232 if (file == NULL)
233 return false;
234 long current_offset = ftell(file);
235 if (current_offset == -1)
236 return false;
237 #if defined(OS_WIN)
238 int fd = _fileno(file);
239 if (_chsize(fd, current_offset) != 0)
240 return false;
241 #else
242 int fd = fileno(file);
243 if (ftruncate(fd, current_offset) != 0)
244 return false;
245 #endif
246 return true;
247 }
248
ContainsPath(const FilePath & parent,const FilePath & child)249 bool ContainsPath(const FilePath &parent, const FilePath& child) {
250 FilePath abs_parent = FilePath(parent);
251 FilePath abs_child = FilePath(child);
252
253 if (!file_util::AbsolutePath(&abs_parent) ||
254 !file_util::AbsolutePath(&abs_child))
255 return false;
256
257 #if defined(OS_WIN)
258 // file_util::AbsolutePath() does not flatten case on Windows, so we must do
259 // a case-insensitive compare.
260 if (!StartsWith(abs_child.value(), abs_parent.value(), false))
261 #else
262 if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
263 #endif
264 return false;
265
266 // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
267 // to check kSeparators[0].
268 if (abs_child.value().length() <= abs_parent.value().length() ||
269 abs_child.value()[abs_parent.value().length()] !=
270 FilePath::kSeparators[0])
271 return false;
272
273 return true;
274 }
275
ComputeDirectorySize(const FilePath & root_path)276 int64 ComputeDirectorySize(const FilePath& root_path) {
277 int64 running_size = 0;
278 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
279 for (FilePath current = file_iter.Next(); !current.empty();
280 current = file_iter.Next()) {
281 FileEnumerator::FindInfo info;
282 file_iter.GetFindInfo(&info);
283 #if defined(OS_WIN)
284 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
285 running_size += li.QuadPart;
286 #else
287 running_size += info.stat.st_size;
288 #endif
289 }
290 return running_size;
291 }
292
ComputeFilesSize(const FilePath & directory,const FilePath::StringType & pattern)293 int64 ComputeFilesSize(const FilePath& directory,
294 const FilePath::StringType& pattern) {
295 int64 running_size = 0;
296 FileEnumerator file_iter(directory, false, FileEnumerator::FILES, pattern);
297 for (FilePath current = file_iter.Next(); !current.empty();
298 current = file_iter.Next()) {
299 FileEnumerator::FindInfo info;
300 file_iter.GetFindInfo(&info);
301 #if defined(OS_WIN)
302 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
303 running_size += li.QuadPart;
304 #else
305 running_size += info.stat.st_size;
306 #endif
307 }
308 return running_size;
309 }
310
311 ///////////////////////////////////////////////
312 // MemoryMappedFile
313
~MemoryMappedFile()314 MemoryMappedFile::~MemoryMappedFile() {
315 CloseHandles();
316 }
317
Initialize(const FilePath & file_name)318 bool MemoryMappedFile::Initialize(const FilePath& file_name) {
319 if (IsValid())
320 return false;
321
322 if (!MapFileToMemory(file_name)) {
323 CloseHandles();
324 return false;
325 }
326
327 return true;
328 }
329
Initialize(base::PlatformFile file)330 bool MemoryMappedFile::Initialize(base::PlatformFile file) {
331 if (IsValid())
332 return false;
333
334 file_ = file;
335
336 if (!MapFileToMemoryInternal()) {
337 CloseHandles();
338 return false;
339 }
340
341 return true;
342 }
343
IsValid() const344 bool MemoryMappedFile::IsValid() const {
345 return data_ != NULL;
346 }
347
MapFileToMemory(const FilePath & file_name)348 bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
349 file_ = base::CreatePlatformFile(
350 file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
351 NULL, NULL);
352
353 if (file_ == base::kInvalidPlatformFileValue) {
354 LOG(ERROR) << "Couldn't open " << file_name.value();
355 return false;
356 }
357
358 return MapFileToMemoryInternal();
359 }
360
361 // Deprecated functions ----------------------------------------------------
362
363 #if defined(OS_WIN)
AppendToPath(std::wstring * path,const std::wstring & new_ending)364 void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
365 if (!path) {
366 NOTREACHED();
367 return; // Don't crash in this function in release builds.
368 }
369
370 if (!EndsWithSeparator(FilePath(*path)))
371 path->push_back(FilePath::kSeparators[0]);
372 path->append(new_ending);
373 }
374
CopyDirectory(const std::wstring & from_path,const std::wstring & to_path,bool recursive)375 bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
376 bool recursive) {
377 return CopyDirectory(FilePath::FromWStringHack(from_path),
378 FilePath::FromWStringHack(to_path),
379 recursive);
380 }
Delete(const std::wstring & path,bool recursive)381 bool Delete(const std::wstring& path, bool recursive) {
382 return Delete(FilePath::FromWStringHack(path), recursive);
383 }
GetFileExtensionFromPath(const std::wstring & path)384 std::wstring GetFileExtensionFromPath(const std::wstring& path) {
385 FilePath::StringType extension =
386 GetFileExtensionFromPath(FilePath::FromWStringHack(path));
387 return extension;
388 }
OpenFile(const std::wstring & filename,const char * mode)389 FILE* OpenFile(const std::wstring& filename, const char* mode) {
390 return OpenFile(FilePath::FromWStringHack(filename), mode);
391 }
ReadFile(const std::wstring & filename,char * data,int size)392 int ReadFile(const std::wstring& filename, char* data, int size) {
393 return ReadFile(FilePath::FromWStringHack(filename), data, size);
394 }
WriteFile(const std::wstring & filename,const char * data,int size)395 int WriteFile(const std::wstring& filename, const char* data, int size) {
396 return WriteFile(FilePath::FromWStringHack(filename), data, size);
397 }
398 #endif // OS_WIN
399
400 ///////////////////////////////////////////////
401 // FileEnumerator
402 //
403 // Note: the main logic is in file_util_<platform>.cc
404
ShouldSkip(const FilePath & path)405 bool FileEnumerator::ShouldSkip(const FilePath& path) {
406 FilePath::StringType basename = path.BaseName().value();
407 return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
408 }
409
410 } // namespace
411