|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/file_util.h" | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | #include <io.h> | 
|  | #endif | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include <fstream> | 
|  |  | 
|  | #include "base/file_path.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/stringprintf.h" | 
|  | #include "base/string_piece.h" | 
|  | #include "base/string_util.h" | 
|  | #include "base/utf_string_conversions.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.'); | 
|  |  | 
|  | // The maximum number of 'uniquified' files we will try to create. | 
|  | // This is used when the filename we're trying to download is already in use, | 
|  | // so we create a new unique filename by appending " (nnn)" before the | 
|  | // extension, where 1 <= nnn <= kMaxUniqueFiles. | 
|  | // Also used by code that cleans up said files. | 
|  | static const int kMaxUniqueFiles = 100; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace file_util { | 
|  |  | 
|  | bool g_bug108724_debug = false; | 
|  |  | 
|  | bool EndsWithSeparator(const FilePath& path) { | 
|  | FilePath::StringType value = path.value(); | 
|  | if (value.empty()) | 
|  | return false; | 
|  |  | 
|  | return FilePath::IsSeparator(value[value.size() - 1]); | 
|  | } | 
|  |  | 
|  | bool EnsureEndsWithSeparator(FilePath* path) { | 
|  | if (!DirectoryExists(*path)) | 
|  | return false; | 
|  |  | 
|  | if (EndsWithSeparator(*path)) | 
|  | return true; | 
|  |  | 
|  | FilePath::StringType& path_str = | 
|  | const_cast<FilePath::StringType&>(path->value()); | 
|  | path_str.append(&FilePath::kSeparators[0], 1); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) { | 
|  | FilePath::StringType& value = | 
|  | const_cast<FilePath::StringType&>(path->value()); | 
|  |  | 
|  | const FilePath::StringType::size_type last_dot = | 
|  | value.rfind(kExtensionSeparator); | 
|  | const FilePath::StringType::size_type last_separator = | 
|  | value.find_last_of(FilePath::StringType(FilePath::kSeparators)); | 
|  |  | 
|  | if (last_dot == FilePath::StringType::npos || | 
|  | (last_separator != std::wstring::npos && last_dot < last_separator)) { | 
|  | // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo". | 
|  | // We should just append the suffix to the entire path. | 
|  | value.append(suffix); | 
|  | return; | 
|  | } | 
|  |  | 
|  | value.insert(last_dot, suffix); | 
|  | } | 
|  |  | 
|  | bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { | 
|  | // We open the file in binary format even if they are text files because | 
|  | // we are just comparing that bytes are exactly same in both files and not | 
|  | // doing anything smart with text formatting. | 
|  | std::ifstream file1(filename1.value().c_str(), | 
|  | std::ios::in | std::ios::binary); | 
|  | std::ifstream file2(filename2.value().c_str(), | 
|  | std::ios::in | std::ios::binary); | 
|  |  | 
|  | // Even if both files aren't openable (and thus, in some sense, "equal"), | 
|  | // any unusable file yields a result of "false". | 
|  | if (!file1.is_open() || !file2.is_open()) | 
|  | return false; | 
|  |  | 
|  | const int BUFFER_SIZE = 2056; | 
|  | char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE]; | 
|  | do { | 
|  | file1.read(buffer1, BUFFER_SIZE); | 
|  | file2.read(buffer2, BUFFER_SIZE); | 
|  |  | 
|  | if ((file1.eof() != file2.eof()) || | 
|  | (file1.gcount() != file2.gcount()) || | 
|  | (memcmp(buffer1, buffer2, file1.gcount()))) { | 
|  | file1.close(); | 
|  | file2.close(); | 
|  | return false; | 
|  | } | 
|  | } while (!file1.eof() || !file2.eof()); | 
|  |  | 
|  | file1.close(); | 
|  | file2.close(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | #if !defined(__LB_SHELL__) && !defined(OS_STARBOARD) | 
|  | bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { | 
|  | std::ifstream file1(filename1.value().c_str(), std::ios::in); | 
|  | std::ifstream file2(filename2.value().c_str(), std::ios::in); | 
|  |  | 
|  | // Even if both files aren't openable (and thus, in some sense, "equal"), | 
|  | // any unusable file yields a result of "false". | 
|  | if (!file1.is_open() || !file2.is_open()) | 
|  | return false; | 
|  |  | 
|  | do { | 
|  | std::string line1, line2; | 
|  | getline(file1, line1); | 
|  | getline(file2, line2); | 
|  |  | 
|  | // Check for mismatched EOF states, or any error state. | 
|  | if ((file1.eof() != file2.eof()) || | 
|  | file1.bad() || file2.bad()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Trim all '\r' and '\n' characters from the end of the line. | 
|  | std::string::size_type end1 = line1.find_last_not_of("\r\n"); | 
|  | if (end1 == std::string::npos) | 
|  | line1.clear(); | 
|  | else if (end1 + 1 < line1.length()) | 
|  | line1.erase(end1 + 1); | 
|  |  | 
|  | std::string::size_type end2 = line2.find_last_not_of("\r\n"); | 
|  | if (end2 == std::string::npos) | 
|  | line2.clear(); | 
|  | else if (end2 + 1 < line2.length()) | 
|  | line2.erase(end2 + 1); | 
|  |  | 
|  | if (line1 != line2) | 
|  | return false; | 
|  | } while (!file1.eof() || !file2.eof()); | 
|  |  | 
|  | return true; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | bool ReadFileToString(const FilePath& path, std::string* contents) { | 
|  | if (path.ReferencesParent()) | 
|  | return false; | 
|  |  | 
|  | #if defined(COBALT) | 
|  | // Use a smaller buffer so we don't run out of stack space. | 
|  | const size_t kReadBufferSize = 1 << 12; | 
|  | #else | 
|  | const size_t kReadBufferSize = 1 << 16; | 
|  | #endif | 
|  |  | 
|  | #if defined(OS_STARBOARD) | 
|  | base::PlatformFile file = base::CreatePlatformFile( | 
|  | path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, NULL, NULL); | 
|  | if (file == base::kInvalidPlatformFileValue) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | char buf[kReadBufferSize]; | 
|  | size_t len; | 
|  | while ((len = base::ReadPlatformFileAtCurrentPos(file, buf, sizeof(buf))) > | 
|  | 0) { | 
|  | if (contents) { | 
|  | contents->append(buf, len); | 
|  | } | 
|  | } | 
|  | base::ClosePlatformFile(file); | 
|  |  | 
|  | return true; | 
|  | #else | 
|  | FILE* file = OpenFile(path, "rb"); | 
|  | if (!file) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | char buf[kReadBufferSize]; | 
|  | size_t len; | 
|  | while ((len = fread(buf, 1, sizeof(buf), file)) > 0) { | 
|  | if (contents) | 
|  | contents->append(buf, len); | 
|  | } | 
|  | CloseFile(file); | 
|  |  | 
|  | return true; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | bool IsDirectoryEmpty(const FilePath& dir_path) { | 
|  | FileEnumerator files(dir_path, false, | 
|  | FileEnumerator::FILES | FileEnumerator::DIRECTORIES); | 
|  | if (files.Next().value().empty()) | 
|  | return true; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #if !defined(OS_STARBOARD) | 
|  | FILE* CreateAndOpenTemporaryFile(FilePath* path) { | 
|  | FilePath directory; | 
|  | if (!GetTempDir(&directory)) | 
|  | return NULL; | 
|  |  | 
|  | return CreateAndOpenTemporaryFileInDir(directory, path); | 
|  | } | 
|  | #endif  // !defined(OS_STARBOARD) | 
|  |  | 
|  | bool GetFileSize(const FilePath& file_path, int64* file_size) { | 
|  | base::PlatformFileInfo info; | 
|  | if (!GetFileInfo(file_path, &info)) | 
|  | return false; | 
|  | *file_size = info.size; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool IsDot(const FilePath& path) { | 
|  | return FILE_PATH_LITERAL(".") == path.BaseName().value(); | 
|  | } | 
|  |  | 
|  | bool IsDotDot(const FilePath& path) { | 
|  | return FILE_PATH_LITERAL("..") == path.BaseName().value(); | 
|  | } | 
|  |  | 
|  | #if !defined(OS_STARBOARD) | 
|  | bool TouchFile(const FilePath& path, | 
|  | const base::Time& last_accessed, | 
|  | const base::Time& last_modified) { | 
|  | int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE_ATTRIBUTES; | 
|  |  | 
|  | #if defined(COBALT_WIN) | 
|  | // Ensure that this makes it through to the CRT. | 
|  | flags |= base::PLATFORM_FILE_WRITE; | 
|  | #endif // COBALT_WIN | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory. | 
|  | if (DirectoryExists(path)) | 
|  | flags |= base::PLATFORM_FILE_BACKUP_SEMANTICS; | 
|  | #endif  // OS_WIN | 
|  |  | 
|  | const base::PlatformFile file = | 
|  | base::CreatePlatformFile(path, flags, NULL, NULL); | 
|  | if (file != base::kInvalidPlatformFileValue) { | 
|  | bool result = base::TouchPlatformFile(file, last_accessed, last_modified); | 
|  | base::ClosePlatformFile(file); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool SetLastModifiedTime(const FilePath& path, | 
|  | const base::Time& last_modified) { | 
|  | return TouchFile(path, last_modified, last_modified); | 
|  | } | 
|  |  | 
|  | bool CloseFile(FILE* file) { | 
|  | if (file == NULL) | 
|  | return true; | 
|  | return fclose(file) == 0; | 
|  | } | 
|  |  | 
|  | bool TruncateFile(FILE* file) { | 
|  | if (file == NULL) | 
|  | return false; | 
|  | long current_offset = ftell(file); | 
|  | if (current_offset == -1) | 
|  | return false; | 
|  | #if defined(OS_WIN) | 
|  | int fd = _fileno(file); | 
|  | if (_chsize(fd, current_offset) != 0) | 
|  | return false; | 
|  | #else | 
|  | int fd = fileno(file); | 
|  | if (ftruncate(fd, current_offset) != 0) | 
|  | return false; | 
|  | #endif | 
|  | return true; | 
|  | } | 
|  | #endif  // !defined(OS_STARBOARD) | 
|  |  | 
|  | int GetUniquePathNumber( | 
|  | const FilePath& path, | 
|  | const FilePath::StringType& suffix) { | 
|  | bool have_suffix = !suffix.empty(); | 
|  | if (!PathExists(path) && | 
|  | (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | FilePath new_path; | 
|  | for (int count = 1; count <= kMaxUniqueFiles; ++count) { | 
|  | new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count)); | 
|  | if (!PathExists(new_path) && | 
|  | (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) { | 
|  | return count; | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | bool ContainsPath(const FilePath &parent, const FilePath& child) { | 
|  | FilePath abs_parent = FilePath(parent); | 
|  | FilePath abs_child = FilePath(child); | 
|  |  | 
|  | if (!file_util::AbsolutePath(&abs_parent) || | 
|  | !file_util::AbsolutePath(&abs_child)) | 
|  | return false; | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | // file_util::AbsolutePath() does not flatten case on Windows, so we must do | 
|  | // a case-insensitive compare. | 
|  | if (!StartsWith(abs_child.value(), abs_parent.value(), false)) | 
|  | #else | 
|  | if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true)) | 
|  | #endif | 
|  | return false; | 
|  |  | 
|  | // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need | 
|  | // to check kSeparators[0]. | 
|  | if (abs_child.value().length() <= abs_parent.value().length() || | 
|  | abs_child.value()[abs_parent.value().length()] != | 
|  | FilePath::kSeparators[0]) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | int64 ComputeDirectorySize(const FilePath& root_path) { | 
|  | int64 running_size = 0; | 
|  | FileEnumerator file_iter(root_path, true, FileEnumerator::FILES); | 
|  | for (FilePath current = file_iter.Next(); !current.empty(); | 
|  | current = file_iter.Next()) { | 
|  | FileEnumerator::FindInfo info; | 
|  | file_iter.GetFindInfo(&info); | 
|  | #if defined(OS_WIN) | 
|  | LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh }; | 
|  | running_size += li.QuadPart; | 
|  | #elif defined(OS_STARBOARD) | 
|  | running_size += info.sb_info.size; | 
|  | #else | 
|  | running_size += info.stat.st_size; | 
|  | #endif | 
|  | } | 
|  | return running_size; | 
|  | } | 
|  |  | 
|  | int64 ComputeFilesSize(const FilePath& directory, | 
|  | const FilePath::StringType& pattern) { | 
|  | int64 running_size = 0; | 
|  | FileEnumerator file_iter(directory, false, FileEnumerator::FILES, pattern); | 
|  | for (FilePath current = file_iter.Next(); !current.empty(); | 
|  | current = file_iter.Next()) { | 
|  | FileEnumerator::FindInfo info; | 
|  | file_iter.GetFindInfo(&info); | 
|  | #if defined(OS_WIN) | 
|  | LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh }; | 
|  | running_size += li.QuadPart; | 
|  | #elif defined(OS_STARBOARD) | 
|  | running_size += info.sb_info.size; | 
|  | #else | 
|  | running_size += info.stat.st_size; | 
|  | #endif | 
|  | } | 
|  | return running_size; | 
|  | } | 
|  |  | 
|  | #if !defined(__LB_SHELL__) && !defined(OS_STARBOARD) | 
|  | /////////////////////////////////////////////// | 
|  | // MemoryMappedFile | 
|  |  | 
|  | MemoryMappedFile::~MemoryMappedFile() { | 
|  | CloseHandles(); | 
|  | } | 
|  |  | 
|  | bool MemoryMappedFile::Initialize(const FilePath& file_name) { | 
|  | if (IsValid()) | 
|  | return false; | 
|  |  | 
|  | if (!MapFileToMemory(file_name)) { | 
|  | CloseHandles(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MemoryMappedFile::Initialize(base::PlatformFile file) { | 
|  | if (IsValid()) | 
|  | return false; | 
|  |  | 
|  | file_ = file; | 
|  |  | 
|  | if (!MapFileToMemoryInternal()) { | 
|  | CloseHandles(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MemoryMappedFile::IsValid() const { | 
|  | return data_ != NULL; | 
|  | } | 
|  |  | 
|  | bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) { | 
|  | file_ = base::CreatePlatformFile( | 
|  | file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, | 
|  | NULL, NULL); | 
|  |  | 
|  | if (file_ == base::kInvalidPlatformFileValue) { | 
|  | DLOG(ERROR) << "Couldn't open " << file_name.value(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return MapFileToMemoryInternal(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /////////////////////////////////////////////// | 
|  | // FileEnumerator | 
|  | // | 
|  | // Note: the main logic is in file_util_<platform>.cc | 
|  |  | 
|  | bool FileEnumerator::ShouldSkip(const FilePath& path) { | 
|  | FilePath::StringType basename = path.BaseName().value(); | 
|  | return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_)); | 
|  | } | 
|  |  | 
|  | }  // namespace |