michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "base/file_util.h" michael@0: michael@0: #if defined(OS_WIN) michael@0: #include michael@0: #endif michael@0: #include michael@0: michael@0: #include michael@0: michael@0: #include "base/files/file_enumerator.h" michael@0: #include "base/files/file_path.h" michael@0: #include "base/logging.h" michael@0: #include "base/strings/string_piece.h" michael@0: #include "base/strings/string_util.h" michael@0: #include "base/strings/stringprintf.h" michael@0: #include "base/strings/utf_string_conversions.h" michael@0: michael@0: namespace base { michael@0: michael@0: namespace { michael@0: michael@0: const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.'); michael@0: michael@0: // The maximum number of 'uniquified' files we will try to create. michael@0: // This is used when the filename we're trying to download is already in use, michael@0: // so we create a new unique filename by appending " (nnn)" before the michael@0: // extension, where 1 <= nnn <= kMaxUniqueFiles. michael@0: // Also used by code that cleans up said files. michael@0: static const int kMaxUniqueFiles = 100; michael@0: michael@0: } // namespace michael@0: michael@0: bool g_bug108724_debug = false; michael@0: michael@0: int64 ComputeDirectorySize(const FilePath& root_path) { michael@0: int64 running_size = 0; michael@0: FileEnumerator file_iter(root_path, true, FileEnumerator::FILES); michael@0: while (!file_iter.Next().empty()) michael@0: running_size += file_iter.GetInfo().GetSize(); michael@0: return running_size; michael@0: } michael@0: michael@0: bool Move(const FilePath& from_path, const FilePath& to_path) { michael@0: if (from_path.ReferencesParent() || to_path.ReferencesParent()) michael@0: return false; michael@0: return internal::MoveUnsafe(from_path, to_path); michael@0: } michael@0: michael@0: bool CopyFile(const FilePath& from_path, const FilePath& to_path) { michael@0: if (from_path.ReferencesParent() || to_path.ReferencesParent()) michael@0: return false; michael@0: return internal::CopyFileUnsafe(from_path, to_path); michael@0: } michael@0: michael@0: bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { michael@0: // We open the file in binary format even if they are text files because michael@0: // we are just comparing that bytes are exactly same in both files and not michael@0: // doing anything smart with text formatting. michael@0: std::ifstream file1(filename1.value().c_str(), michael@0: std::ios::in | std::ios::binary); michael@0: std::ifstream file2(filename2.value().c_str(), michael@0: std::ios::in | std::ios::binary); michael@0: michael@0: // Even if both files aren't openable (and thus, in some sense, "equal"), michael@0: // any unusable file yields a result of "false". michael@0: if (!file1.is_open() || !file2.is_open()) michael@0: return false; michael@0: michael@0: const int BUFFER_SIZE = 2056; michael@0: char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE]; michael@0: do { michael@0: file1.read(buffer1, BUFFER_SIZE); michael@0: file2.read(buffer2, BUFFER_SIZE); michael@0: michael@0: if ((file1.eof() != file2.eof()) || michael@0: (file1.gcount() != file2.gcount()) || michael@0: (memcmp(buffer1, buffer2, file1.gcount()))) { michael@0: file1.close(); michael@0: file2.close(); michael@0: return false; michael@0: } michael@0: } while (!file1.eof() || !file2.eof()); michael@0: michael@0: file1.close(); michael@0: file2.close(); michael@0: return true; michael@0: } michael@0: michael@0: bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { michael@0: std::ifstream file1(filename1.value().c_str(), std::ios::in); michael@0: std::ifstream file2(filename2.value().c_str(), std::ios::in); michael@0: michael@0: // Even if both files aren't openable (and thus, in some sense, "equal"), michael@0: // any unusable file yields a result of "false". michael@0: if (!file1.is_open() || !file2.is_open()) michael@0: return false; michael@0: michael@0: do { michael@0: std::string line1, line2; michael@0: getline(file1, line1); michael@0: getline(file2, line2); michael@0: michael@0: // Check for mismatched EOF states, or any error state. michael@0: if ((file1.eof() != file2.eof()) || michael@0: file1.bad() || file2.bad()) { michael@0: return false; michael@0: } michael@0: michael@0: // Trim all '\r' and '\n' characters from the end of the line. michael@0: std::string::size_type end1 = line1.find_last_not_of("\r\n"); michael@0: if (end1 == std::string::npos) michael@0: line1.clear(); michael@0: else if (end1 + 1 < line1.length()) michael@0: line1.erase(end1 + 1); michael@0: michael@0: std::string::size_type end2 = line2.find_last_not_of("\r\n"); michael@0: if (end2 == std::string::npos) michael@0: line2.clear(); michael@0: else if (end2 + 1 < line2.length()) michael@0: line2.erase(end2 + 1); michael@0: michael@0: if (line1 != line2) michael@0: return false; michael@0: } while (!file1.eof() || !file2.eof()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool ReadFileToString(const FilePath& path, std::string* contents) { michael@0: if (path.ReferencesParent()) michael@0: return false; michael@0: FILE* file = file_util::OpenFile(path, "rb"); michael@0: if (!file) { michael@0: return false; michael@0: } michael@0: michael@0: char buf[1 << 16]; michael@0: size_t len; michael@0: while ((len = fread(buf, 1, sizeof(buf), file)) > 0) { michael@0: if (contents) michael@0: contents->append(buf, len); michael@0: } michael@0: file_util::CloseFile(file); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // namespace base michael@0: michael@0: // ----------------------------------------------------------------------------- michael@0: michael@0: namespace file_util { michael@0: michael@0: using base::FileEnumerator; michael@0: using base::FilePath; michael@0: using base::kExtensionSeparator; michael@0: using base::kMaxUniqueFiles; michael@0: michael@0: bool IsDirectoryEmpty(const FilePath& dir_path) { michael@0: FileEnumerator files(dir_path, false, michael@0: FileEnumerator::FILES | FileEnumerator::DIRECTORIES); michael@0: if (files.Next().empty()) michael@0: return true; michael@0: return false; michael@0: } michael@0: michael@0: FILE* CreateAndOpenTemporaryFile(FilePath* path) { michael@0: FilePath directory; michael@0: if (!GetTempDir(&directory)) michael@0: return NULL; michael@0: michael@0: return CreateAndOpenTemporaryFileInDir(directory, path); michael@0: } michael@0: michael@0: bool CreateDirectory(const base::FilePath& full_path) { michael@0: return CreateDirectoryAndGetError(full_path, NULL); michael@0: } michael@0: michael@0: bool GetFileSize(const FilePath& file_path, int64* file_size) { michael@0: base::PlatformFileInfo info; michael@0: if (!GetFileInfo(file_path, &info)) michael@0: return false; michael@0: *file_size = info.size; michael@0: return true; michael@0: } michael@0: michael@0: bool TouchFile(const FilePath& path, michael@0: const base::Time& last_accessed, michael@0: const base::Time& last_modified) { michael@0: int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE_ATTRIBUTES; michael@0: michael@0: #if defined(OS_WIN) michael@0: // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory. michael@0: if (DirectoryExists(path)) michael@0: flags |= base::PLATFORM_FILE_BACKUP_SEMANTICS; michael@0: #endif // OS_WIN michael@0: michael@0: const base::PlatformFile file = michael@0: base::CreatePlatformFile(path, flags, NULL, NULL); michael@0: if (file != base::kInvalidPlatformFileValue) { michael@0: bool result = base::TouchPlatformFile(file, last_accessed, last_modified); michael@0: base::ClosePlatformFile(file); michael@0: return result; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool SetLastModifiedTime(const FilePath& path, michael@0: const base::Time& last_modified) { michael@0: return TouchFile(path, last_modified, last_modified); michael@0: } michael@0: michael@0: bool CloseFile(FILE* file) { michael@0: if (file == NULL) michael@0: return true; michael@0: return fclose(file) == 0; michael@0: } michael@0: michael@0: bool TruncateFile(FILE* file) { michael@0: if (file == NULL) michael@0: return false; michael@0: long current_offset = ftell(file); michael@0: if (current_offset == -1) michael@0: return false; michael@0: #if defined(OS_WIN) michael@0: int fd = _fileno(file); michael@0: if (_chsize(fd, current_offset) != 0) michael@0: return false; michael@0: #else michael@0: int fd = fileno(file); michael@0: if (ftruncate(fd, current_offset) != 0) michael@0: return false; michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: int GetUniquePathNumber( michael@0: const FilePath& path, michael@0: const FilePath::StringType& suffix) { michael@0: bool have_suffix = !suffix.empty(); michael@0: if (!PathExists(path) && michael@0: (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) { michael@0: return 0; michael@0: } michael@0: michael@0: FilePath new_path; michael@0: for (int count = 1; count <= kMaxUniqueFiles; ++count) { michael@0: new_path = michael@0: path.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", count)); michael@0: if (!PathExists(new_path) && michael@0: (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) { michael@0: return count; michael@0: } michael@0: } michael@0: michael@0: return -1; michael@0: } michael@0: michael@0: } // namespace file_util