1 /* 2 * libjingle 3 * Copyright 2004--2006, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifndef TALK_BASE_FILEUTILS_H_ 29 #define TALK_BASE_FILEUTILS_H_ 30 31 #include <string> 32 33 #ifdef WIN32 34 #include "talk/base/win32.h" 35 #else 36 #include <dirent.h> 37 #include <stdio.h> 38 #include <sys/stat.h> 39 #include <sys/types.h> 40 #include <unistd.h> 41 #endif 42 43 #include "talk/base/basictypes.h" 44 #include "talk/base/common.h" 45 #include "talk/base/scoped_ptr.h" 46 47 namespace talk_base { 48 49 class FileStream; 50 class Pathname; 51 52 ////////////////////////// 53 // Directory Iterator // 54 ////////////////////////// 55 56 // A DirectoryIterator is created with a given directory. It originally points 57 // to the first file in the directory, and can be advanecd with Next(). This 58 // allows you to get information about each file. 59 60 class DirectoryIterator { 61 friend class Filesystem; 62 public: 63 // Constructor 64 DirectoryIterator(); 65 // Destructor 66 virtual ~DirectoryIterator(); 67 68 // Starts traversing a directory 69 // dir is the directory to traverse 70 // returns true if the directory exists and is valid 71 // The iterator will point to the first entry in the directory 72 virtual bool Iterate(const Pathname &path); 73 74 // Advances to the next file 75 // returns true if there were more files in the directory. 76 virtual bool Next(); 77 78 // returns true if the file currently pointed to is a directory 79 virtual bool IsDirectory() const; 80 81 // returns the name of the file currently pointed to 82 virtual std::string Name() const; 83 84 // returns the size of the file currently pointed to 85 virtual size_t FileSize() const; 86 87 // returns the last modified time of the file currently pointed to 88 virtual time_t FileModifyTime() const; 89 90 // checks whether current file is a special directory file "." or ".." 91 bool IsDots() const { 92 std::string filename(Name()); 93 return (filename.compare(".") == 0) || (filename.compare("..") == 0); 94 } 95 96 private: 97 std::string directory_; 98 #ifdef WIN32 99 WIN32_FIND_DATA data_; 100 HANDLE handle_; 101 #else 102 DIR *dir_; 103 struct dirent *dirent_; 104 struct stat stat_; 105 #endif 106 }; 107 108 enum FileTimeType { FTT_CREATED, FTT_MODIFIED, FTT_ACCESSED }; 109 110 class FilesystemInterface { 111 public: 112 virtual ~FilesystemInterface() {} 113 114 // Returns a DirectoryIterator for a given pathname. 115 // TODO: Do fancy abstracted stuff 116 virtual DirectoryIterator *IterateDirectory() { 117 return new DirectoryIterator(); 118 } 119 120 // Opens a file. Returns an open StreamInterface if function succeeds. 121 // Otherwise, returns NULL. 122 // TODO: Add an error param to indicate failure reason, similar to 123 // FileStream::Open 124 virtual FileStream *OpenFile(const Pathname &filename, 125 const std::string &mode) = 0; 126 127 // Atomically creates an empty file accessible only to the current user if one 128 // does not already exist at the given path, otherwise fails. This is the only 129 // secure way to create a file in a shared temp directory (e.g., C:\Temp on 130 // Windows or /tmp on Linux). 131 // Note that if it is essential that a file be successfully created then the 132 // app must generate random names and retry on failure, or else it will be 133 // vulnerable to a trivial DoS. 134 virtual bool CreatePrivateFile(const Pathname &filename) = 0; 135 136 // This will attempt to delete the path located at filename. 137 // It ASSERTS and returns false if the path points to a folder or a 138 // non-existent file. 139 virtual bool DeleteFile(const Pathname &filename) = 0; 140 141 // This will attempt to delete the empty folder located at 'folder' 142 // It ASSERTS and returns false if the path points to a file or a non-existent 143 // folder. It fails normally if the folder is not empty or can otherwise 144 // not be deleted. 145 virtual bool DeleteEmptyFolder(const Pathname &folder) = 0; 146 147 // This will call IterateDirectory, to get a directory iterator, and then 148 // call DeleteFolderAndContents and DeleteFile on every path contained in this 149 // folder. If the folder is empty, this returns true. 150 virtual bool DeleteFolderContents(const Pathname &folder); 151 152 // This deletes the contents of a folder, recursively, and then deletes 153 // the folder itself. 154 virtual bool DeleteFolderAndContents(const Pathname &folder) { 155 return DeleteFolderContents(folder) && DeleteEmptyFolder(folder); 156 } 157 158 // This will delete whatever is located at path, be it a file or a folder. 159 // If it is a folder, it will delete it recursively by calling 160 // DeleteFolderAndContents 161 bool DeleteFileOrFolder(const Pathname &path) { 162 if (IsFolder(path)) 163 return DeleteFolderAndContents(path); 164 else 165 return DeleteFile(path); 166 } 167 168 // Creates a directory. This will call itself recursively to create /foo/bar 169 // even if /foo does not exist. Returns true if the function succeeds. 170 virtual bool CreateFolder(const Pathname &pathname) = 0; 171 172 // This moves a file from old_path to new_path, where "old_path" is a 173 // plain file. This ASSERTs and returns false if old_path points to a 174 // directory, and returns true if the function succeeds. 175 // If the new path is on a different volume than the old path, this function 176 // will attempt to copy and, if that succeeds, delete the old path. 177 virtual bool MoveFolder(const Pathname &old_path, 178 const Pathname &new_path) = 0; 179 180 // This moves a directory from old_path to new_path, where "old_path" is a 181 // directory. This ASSERTs and returns false if old_path points to a plain 182 // file, and returns true if the function succeeds. 183 // If the new path is on a different volume, this function will attempt to 184 // copy and if that succeeds, delete the old path. 185 virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path) = 0; 186 187 // This attempts to move whatever is located at old_path to new_path, 188 // be it a file or folder. 189 bool MoveFileOrFolder(const Pathname &old_path, const Pathname &new_path) { 190 if (IsFile(old_path)) { 191 return MoveFile(old_path, new_path); 192 } else { 193 return MoveFolder(old_path, new_path); 194 } 195 } 196 197 // This copies a file from old_path to new_path. This method ASSERTs and 198 // returns false if old_path is a folder, and returns true if the copy 199 // succeeds. 200 virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path) = 0; 201 202 // This copies a folder from old_path to new_path. 203 bool CopyFolder(const Pathname &old_path, const Pathname &new_path); 204 205 bool CopyFileOrFolder(const Pathname &old_path, const Pathname &new_path) { 206 if (IsFile(old_path)) 207 return CopyFile(old_path, new_path); 208 else 209 return CopyFolder(old_path, new_path); 210 } 211 212 // Returns true if pathname refers to a directory 213 virtual bool IsFolder(const Pathname& pathname) = 0; 214 215 // Returns true if pathname refers to a file 216 virtual bool IsFile(const Pathname& pathname) = 0; 217 218 // Returns true if pathname refers to no filesystem object, every parent 219 // directory either exists, or is also absent. 220 virtual bool IsAbsent(const Pathname& pathname) = 0; 221 222 // Returns true if pathname represents a temporary location on the system. 223 virtual bool IsTemporaryPath(const Pathname& pathname) = 0; 224 225 // A folder appropriate for storing temporary files (Contents are 226 // automatically deleted when the program exits) 227 virtual bool GetTemporaryFolder(Pathname &path, bool create, 228 const std::string *append) = 0; 229 230 virtual std::string TempFilename(const Pathname &dir, 231 const std::string &prefix) = 0; 232 233 // Determines the size of the file indicated by path. 234 virtual bool GetFileSize(const Pathname& path, size_t* size) = 0; 235 236 // Determines a timestamp associated with the file indicated by path. 237 virtual bool GetFileTime(const Pathname& path, FileTimeType which, 238 time_t* time) = 0; 239 240 // Returns the path to the running application. 241 // Note: This is not guaranteed to work on all platforms. Be aware of the 242 // limitations before using it, and robustly handle failure. 243 virtual bool GetAppPathname(Pathname* path) = 0; 244 245 // Get a folder that is unique to the current application, which is suitable 246 // for sharing data between executions of the app. If the per_user arg is 247 // true, the folder is also specific to the current user. 248 virtual bool GetAppDataFolder(Pathname* path, bool per_user) = 0; 249 250 // Get a temporary folder that is unique to the current user and application. 251 // TODO: Re-evaluate the goals of this function. We probably just need any 252 // directory that won't collide with another existing directory, and which 253 // will be cleaned up when the program exits. 254 virtual bool GetAppTempFolder(Pathname* path) = 0; 255 256 // Delete the contents of the folder returned by GetAppTempFolder 257 bool CleanAppTempFolder(); 258 259 virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) = 0; 260 261 // Returns the absolute path of the current directory. 262 virtual Pathname GetCurrentDirectory() = 0; 263 264 // Note: These might go into some shared config section later, but they're 265 // used by some methods in this interface, so we're leaving them here for now. 266 void SetOrganizationName(const std::string& organization) { 267 organization_name_ = organization; 268 } 269 void GetOrganizationName(std::string* organization) { 270 ASSERT(NULL != organization); 271 *organization = organization_name_; 272 } 273 void SetApplicationName(const std::string& application) { 274 application_name_ = application; 275 } 276 void GetApplicationName(std::string* application) { 277 ASSERT(NULL != application); 278 *application = application_name_; 279 } 280 281 protected: 282 std::string organization_name_; 283 std::string application_name_; 284 }; 285 286 class Filesystem { 287 public: 288 static FilesystemInterface *default_filesystem() { 289 ASSERT(default_filesystem_ != NULL); 290 return default_filesystem_; 291 } 292 293 static void set_default_filesystem(FilesystemInterface *filesystem) { 294 default_filesystem_ = filesystem; 295 } 296 297 static FilesystemInterface *swap_default_filesystem( 298 FilesystemInterface *filesystem) { 299 FilesystemInterface *cur = default_filesystem_; 300 default_filesystem_ = filesystem; 301 return cur; 302 } 303 304 static DirectoryIterator *IterateDirectory() { 305 return EnsureDefaultFilesystem()->IterateDirectory(); 306 } 307 308 static bool CreateFolder(const Pathname &pathname) { 309 return EnsureDefaultFilesystem()->CreateFolder(pathname); 310 } 311 312 static FileStream *OpenFile(const Pathname &filename, 313 const std::string &mode) { 314 return EnsureDefaultFilesystem()->OpenFile(filename, mode); 315 } 316 317 static bool CreatePrivateFile(const Pathname &filename) { 318 return EnsureDefaultFilesystem()->CreatePrivateFile(filename); 319 } 320 321 static bool DeleteFile(const Pathname &filename) { 322 return EnsureDefaultFilesystem()->DeleteFile(filename); 323 } 324 325 static bool DeleteEmptyFolder(const Pathname &folder) { 326 return EnsureDefaultFilesystem()->DeleteEmptyFolder(folder); 327 } 328 329 static bool DeleteFolderContents(const Pathname &folder) { 330 return EnsureDefaultFilesystem()->DeleteFolderContents(folder); 331 } 332 333 static bool DeleteFolderAndContents(const Pathname &folder) { 334 return EnsureDefaultFilesystem()->DeleteFolderAndContents(folder); 335 } 336 337 static bool MoveFolder(const Pathname &old_path, const Pathname &new_path) { 338 return EnsureDefaultFilesystem()->MoveFolder(old_path, new_path); 339 } 340 341 static bool MoveFile(const Pathname &old_path, const Pathname &new_path) { 342 return EnsureDefaultFilesystem()->MoveFile(old_path, new_path); 343 } 344 345 static bool CopyFolder(const Pathname &old_path, const Pathname &new_path) { 346 return EnsureDefaultFilesystem()->CopyFolder(old_path, new_path); 347 } 348 349 static bool CopyFile(const Pathname &old_path, const Pathname &new_path) { 350 return EnsureDefaultFilesystem()->CopyFile(old_path, new_path); 351 } 352 353 static bool IsFolder(const Pathname& pathname) { 354 return EnsureDefaultFilesystem()->IsFolder(pathname); 355 } 356 357 static bool IsFile(const Pathname &pathname) { 358 return EnsureDefaultFilesystem()->IsFile(pathname); 359 } 360 361 static bool IsAbsent(const Pathname &pathname) { 362 return EnsureDefaultFilesystem()->IsAbsent(pathname); 363 } 364 365 static bool IsTemporaryPath(const Pathname& pathname) { 366 return EnsureDefaultFilesystem()->IsTemporaryPath(pathname); 367 } 368 369 static bool GetTemporaryFolder(Pathname &path, bool create, 370 const std::string *append) { 371 return EnsureDefaultFilesystem()->GetTemporaryFolder(path, create, append); 372 } 373 374 static std::string TempFilename(const Pathname &dir, 375 const std::string &prefix) { 376 return EnsureDefaultFilesystem()->TempFilename(dir, prefix); 377 } 378 379 static bool GetFileSize(const Pathname& path, size_t* size) { 380 return EnsureDefaultFilesystem()->GetFileSize(path, size); 381 } 382 383 static bool GetFileTime(const Pathname& path, FileTimeType which, 384 time_t* time) { 385 return EnsureDefaultFilesystem()->GetFileTime(path, which, time); 386 } 387 388 static bool GetAppPathname(Pathname* path) { 389 return EnsureDefaultFilesystem()->GetAppPathname(path); 390 } 391 392 static bool GetAppDataFolder(Pathname* path, bool per_user) { 393 return EnsureDefaultFilesystem()->GetAppDataFolder(path, per_user); 394 } 395 396 static bool GetAppTempFolder(Pathname* path) { 397 return EnsureDefaultFilesystem()->GetAppTempFolder(path); 398 } 399 400 static bool CleanAppTempFolder() { 401 return EnsureDefaultFilesystem()->CleanAppTempFolder(); 402 } 403 404 static bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { 405 return EnsureDefaultFilesystem()->GetDiskFreeSpace(path, freebytes); 406 } 407 408 // Definition has to be in the .cc file due to returning forward-declared 409 // Pathname by value. 410 static Pathname GetCurrentDirectory(); 411 412 static void SetOrganizationName(const std::string& organization) { 413 EnsureDefaultFilesystem()->SetOrganizationName(organization); 414 } 415 416 static void GetOrganizationName(std::string* organization) { 417 EnsureDefaultFilesystem()->GetOrganizationName(organization); 418 } 419 420 static void SetApplicationName(const std::string& application) { 421 EnsureDefaultFilesystem()->SetApplicationName(application); 422 } 423 424 static void GetApplicationName(std::string* application) { 425 EnsureDefaultFilesystem()->GetApplicationName(application); 426 } 427 428 private: 429 static FilesystemInterface* default_filesystem_; 430 431 static FilesystemInterface *EnsureDefaultFilesystem(); 432 DISALLOW_IMPLICIT_CONSTRUCTORS(Filesystem); 433 }; 434 435 class FilesystemScope{ 436 public: 437 explicit FilesystemScope(FilesystemInterface *new_fs) { 438 old_fs_ = Filesystem::swap_default_filesystem(new_fs); 439 } 440 ~FilesystemScope() { 441 Filesystem::set_default_filesystem(old_fs_); 442 } 443 private: 444 FilesystemInterface* old_fs_; 445 DISALLOW_IMPLICIT_CONSTRUCTORS(FilesystemScope); 446 }; 447 448 // Generates a unique filename based on the input path. If no path component 449 // is specified, it uses the temporary directory. If a filename is provided, 450 // up to 100 variations of form basename-N.extension are tried. When 451 // create_empty is true, an empty file of this name is created (which 452 // decreases the chance of a temporary filename collision with another 453 // process). 454 bool CreateUniqueFile(Pathname& path, bool create_empty); 455 456 // Taken from Chromium's base/platform_file.h. 457 // Don't use ClosePlatformFile to close a file opened with FdopenPlatformFile. 458 // Use fclose instead. 459 // TODO(grunell): Remove when Chromium has started to use AEC in each source. 460 // http://crbug.com/264611. 461 #if defined(WIN32) 462 typedef HANDLE PlatformFile; 463 const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE; 464 #elif defined(POSIX) 465 typedef int PlatformFile; 466 const PlatformFile kInvalidPlatformFileValue = -1; 467 #else 468 #error Unsupported platform 469 #endif 470 471 FILE* FdopenPlatformFileForWriting(PlatformFile file); 472 bool ClosePlatformFile(PlatformFile file); 473 474 } // namespace talk_base 475 476 #endif // TALK_BASE_FILEUTILS_H_ 477