• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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