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/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/files/file_enumerator.h" 15 #include "base/files/file_path.h" 16 #include "base/logging.h" 17 #include "base/strings/string_piece.h" 18 #include "base/strings/string_util.h" 19 #include "base/strings/stringprintf.h" 20 #include "base/strings/utf_string_conversions.h" 21 22 namespace base { 23 24 namespace { 25 26 const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.'); 27 28 // The maximum number of 'uniquified' files we will try to create. 29 // This is used when the filename we're trying to download is already in use, 30 // so we create a new unique filename by appending " (nnn)" before the 31 // extension, where 1 <= nnn <= kMaxUniqueFiles. 32 // Also used by code that cleans up said files. 33 static const int kMaxUniqueFiles = 100; 34 35 } // namespace 36 37 bool g_bug108724_debug = false; 38 39 int64 ComputeDirectorySize(const FilePath& root_path) { 40 int64 running_size = 0; 41 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES); 42 while (!file_iter.Next().empty()) 43 running_size += file_iter.GetInfo().GetSize(); 44 return running_size; 45 } 46 47 bool Move(const FilePath& from_path, const FilePath& to_path) { 48 if (from_path.ReferencesParent() || to_path.ReferencesParent()) 49 return false; 50 return internal::MoveUnsafe(from_path, to_path); 51 } 52 53 bool CopyFile(const FilePath& from_path, const FilePath& to_path) { 54 if (from_path.ReferencesParent() || to_path.ReferencesParent()) 55 return false; 56 return internal::CopyFileUnsafe(from_path, to_path); 57 } 58 59 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { 60 // We open the file in binary format even if they are text files because 61 // we are just comparing that bytes are exactly same in both files and not 62 // doing anything smart with text formatting. 63 std::ifstream file1(filename1.value().c_str(), 64 std::ios::in | std::ios::binary); 65 std::ifstream file2(filename2.value().c_str(), 66 std::ios::in | std::ios::binary); 67 68 // Even if both files aren't openable (and thus, in some sense, "equal"), 69 // any unusable file yields a result of "false". 70 if (!file1.is_open() || !file2.is_open()) 71 return false; 72 73 const int BUFFER_SIZE = 2056; 74 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE]; 75 do { 76 file1.read(buffer1, BUFFER_SIZE); 77 file2.read(buffer2, BUFFER_SIZE); 78 79 if ((file1.eof() != file2.eof()) || 80 (file1.gcount() != file2.gcount()) || 81 (memcmp(buffer1, buffer2, file1.gcount()))) { 82 file1.close(); 83 file2.close(); 84 return false; 85 } 86 } while (!file1.eof() || !file2.eof()); 87 88 file1.close(); 89 file2.close(); 90 return true; 91 } 92 93 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { 94 std::ifstream file1(filename1.value().c_str(), std::ios::in); 95 std::ifstream file2(filename2.value().c_str(), std::ios::in); 96 97 // Even if both files aren't openable (and thus, in some sense, "equal"), 98 // any unusable file yields a result of "false". 99 if (!file1.is_open() || !file2.is_open()) 100 return false; 101 102 do { 103 std::string line1, line2; 104 getline(file1, line1); 105 getline(file2, line2); 106 107 // Check for mismatched EOF states, or any error state. 108 if ((file1.eof() != file2.eof()) || 109 file1.bad() || file2.bad()) { 110 return false; 111 } 112 113 // Trim all '\r' and '\n' characters from the end of the line. 114 std::string::size_type end1 = line1.find_last_not_of("\r\n"); 115 if (end1 == std::string::npos) 116 line1.clear(); 117 else if (end1 + 1 < line1.length()) 118 line1.erase(end1 + 1); 119 120 std::string::size_type end2 = line2.find_last_not_of("\r\n"); 121 if (end2 == std::string::npos) 122 line2.clear(); 123 else if (end2 + 1 < line2.length()) 124 line2.erase(end2 + 1); 125 126 if (line1 != line2) 127 return false; 128 } while (!file1.eof() || !file2.eof()); 129 130 return true; 131 } 132 133 } // namespace base 134 135 // ----------------------------------------------------------------------------- 136 137 namespace file_util { 138 139 using base::FileEnumerator; 140 using base::FilePath; 141 using base::kExtensionSeparator; 142 using base::kMaxUniqueFiles; 143 144 bool ReadFileToString(const FilePath& path, std::string* contents) { 145 if (path.ReferencesParent()) 146 return false; 147 FILE* file = OpenFile(path, "rb"); 148 if (!file) { 149 return false; 150 } 151 152 char buf[1 << 16]; 153 size_t len; 154 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) { 155 if (contents) 156 contents->append(buf, len); 157 } 158 CloseFile(file); 159 160 return true; 161 } 162 163 bool IsDirectoryEmpty(const FilePath& dir_path) { 164 FileEnumerator files(dir_path, false, 165 FileEnumerator::FILES | FileEnumerator::DIRECTORIES); 166 if (files.Next().empty()) 167 return true; 168 return false; 169 } 170 171 FILE* CreateAndOpenTemporaryFile(FilePath* path) { 172 FilePath directory; 173 if (!GetTempDir(&directory)) 174 return NULL; 175 176 return CreateAndOpenTemporaryFileInDir(directory, path); 177 } 178 179 bool CreateDirectory(const base::FilePath& full_path) { 180 return CreateDirectoryAndGetError(full_path, NULL); 181 } 182 183 bool GetFileSize(const FilePath& file_path, int64* file_size) { 184 base::PlatformFileInfo info; 185 if (!GetFileInfo(file_path, &info)) 186 return false; 187 *file_size = info.size; 188 return true; 189 } 190 191 bool TouchFile(const FilePath& path, 192 const base::Time& last_accessed, 193 const base::Time& last_modified) { 194 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE_ATTRIBUTES; 195 196 #if defined(OS_WIN) 197 // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory. 198 if (DirectoryExists(path)) 199 flags |= base::PLATFORM_FILE_BACKUP_SEMANTICS; 200 #endif // OS_WIN 201 202 const base::PlatformFile file = 203 base::CreatePlatformFile(path, flags, NULL, NULL); 204 if (file != base::kInvalidPlatformFileValue) { 205 bool result = base::TouchPlatformFile(file, last_accessed, last_modified); 206 base::ClosePlatformFile(file); 207 return result; 208 } 209 210 return false; 211 } 212 213 bool SetLastModifiedTime(const FilePath& path, 214 const base::Time& last_modified) { 215 return TouchFile(path, last_modified, last_modified); 216 } 217 218 bool CloseFile(FILE* file) { 219 if (file == NULL) 220 return true; 221 return fclose(file) == 0; 222 } 223 224 bool TruncateFile(FILE* file) { 225 if (file == NULL) 226 return false; 227 long current_offset = ftell(file); 228 if (current_offset == -1) 229 return false; 230 #if defined(OS_WIN) 231 int fd = _fileno(file); 232 if (_chsize(fd, current_offset) != 0) 233 return false; 234 #else 235 int fd = fileno(file); 236 if (ftruncate(fd, current_offset) != 0) 237 return false; 238 #endif 239 return true; 240 } 241 242 int GetUniquePathNumber( 243 const FilePath& path, 244 const FilePath::StringType& suffix) { 245 bool have_suffix = !suffix.empty(); 246 if (!PathExists(path) && 247 (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) { 248 return 0; 249 } 250 251 FilePath new_path; 252 for (int count = 1; count <= kMaxUniqueFiles; ++count) { 253 new_path = 254 path.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", count)); 255 if (!PathExists(new_path) && 256 (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) { 257 return count; 258 } 259 } 260 261 return -1; 262 } 263 264 } // namespace file_util 265