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 <sys/types.h> 37 #include <dirent.h> 38 #include <sys/stat.h> 39 #include <unistd.h> 40 #endif 41 42 #include "talk/base/basictypes.h" 43 #include "talk/base/common.h" 44 #include "talk/base/scoped_ptr.h" 45 46 namespace talk_base { 47 48 class FileStream; 49 class Pathname; 50 51 ////////////////////////// 52 // Directory Iterator // 53 ////////////////////////// 54 55 // A DirectoryIterator is created with a given directory. It originally points 56 // to the first file in the directory, and can be advanecd with Next(). This 57 // allows you to get information about each file. 58 59 class DirectoryIterator { 60 friend class Filesystem; 61 public: 62 // Constructor 63 DirectoryIterator(); 64 // Destructor 65 virtual ~DirectoryIterator(); 66 67 // Starts traversing a directory 68 // dir is the directory to traverse 69 // returns true if the directory exists and is valid 70 // The iterator will point to the first entry in the directory 71 virtual bool Iterate(const Pathname &path); 72 73 // Advances to the next file 74 // returns true if there were more files in the directory. 75 virtual bool Next(); 76 77 // returns true if the file currently pointed to is a directory 78 virtual bool IsDirectory() const; 79 80 // returns the name of the file currently pointed to 81 virtual std::string Name() const; 82 83 // returns the size of the file currently pointed to 84 virtual size_t FileSize() const; 85 86 // returns the last modified time of the file currently pointed to 87 virtual time_t FileModifyTime() const; 88 89 // checks whether current file is a special directory file "." or ".." IsDots()90 bool IsDots() const { 91 std::string filename(Name()); 92 return (filename.compare(".") == 0) || (filename.compare("..") == 0); 93 } 94 95 private: 96 std::string directory_; 97 #ifdef WIN32 98 WIN32_FIND_DATA data_; 99 HANDLE handle_; 100 #else 101 DIR *dir_; 102 struct dirent *dirent_; 103 struct stat stat_; 104 #endif 105 }; 106 107 enum FileTimeType { FTT_CREATED, FTT_MODIFIED, FTT_ACCESSED }; 108 109 class FilesystemInterface { 110 public: ~FilesystemInterface()111 virtual ~FilesystemInterface() {} 112 113 // Returns a DirectoryIterator for a given pathname. 114 // TODO: Do fancy abstracted stuff IterateDirectory()115 virtual DirectoryIterator *IterateDirectory() { 116 return new DirectoryIterator(); 117 } 118 119 // Opens a file. Returns an open StreamInterface if function succeeds. 120 // Otherwise, returns NULL. 121 virtual FileStream *OpenFile(const Pathname &filename, 122 const std::string &mode) = 0; 123 124 // Atomically creates an empty file accessible only to the current user if one 125 // does not already exist at the given path, otherwise fails. This is the only 126 // secure way to create a file in a shared temp directory (e.g., C:\Temp on 127 // Windows or /tmp on Linux). 128 // Note that if it is essential that a file be successfully created then the 129 // app must generate random names and retry on failure, or else it will be 130 // vulnerable to a trivial DoS. 131 virtual bool CreatePrivateFile(const Pathname &filename) = 0; 132 133 // This will attempt to delete the path located at filename. 134 // It ASSERTS and returns false if the path points to a folder or a 135 // non-existent file. 136 virtual bool DeleteFile(const Pathname &filename) = 0; 137 138 // This will attempt to delete the empty folder located at 'folder' 139 // It ASSERTS and returns false if the path points to a file or a non-existent 140 // folder. It fails normally if the folder is not empty or can otherwise 141 // not be deleted. 142 virtual bool DeleteEmptyFolder(const Pathname &folder) = 0; 143 144 // This will call IterateDirectory, to get a directory iterator, and then 145 // call DeleteFolderAndContents and DeleteFile on every path contained in this 146 // folder. If the folder is empty, this returns true. 147 virtual bool DeleteFolderContents(const Pathname &folder); 148 149 // This deletes the contents of a folder, recursively, and then deletes 150 // the folder itself. DeleteFolderAndContents(const Pathname & folder)151 virtual bool DeleteFolderAndContents(const Pathname &folder) { 152 return DeleteFolderContents(folder) && DeleteEmptyFolder(folder); 153 } 154 155 // This will delete whatever is located at path, be it a file or a folder. 156 // If it is a folder, it will delete it recursively by calling 157 // DeleteFolderAndContents DeleteFileOrFolder(const Pathname & path)158 bool DeleteFileOrFolder(const Pathname &path) { 159 if (IsFolder(path)) 160 return DeleteFolderAndContents(path); 161 else 162 return DeleteFile(path); 163 } 164 165 // Creates a directory. This will call itself recursively to create /foo/bar 166 // even if /foo does not exist. Returns true if the function succeeds. 167 virtual bool CreateFolder(const Pathname &pathname) = 0; 168 169 // This moves a file from old_path to new_path, where "old_path" is a 170 // plain file. This ASSERTs and returns false if old_path points to a 171 // directory, and returns true if the function succeeds. 172 // If the new path is on a different volume than the old path, this function 173 // will attempt to copy and, if that succeeds, delete the old path. 174 virtual bool MoveFolder(const Pathname &old_path, 175 const Pathname &new_path) = 0; 176 177 // This moves a directory from old_path to new_path, where "old_path" is a 178 // directory. This ASSERTs and returns false if old_path points to a plain 179 // file, and returns true if the function succeeds. 180 // If the new path is on a different volume, this function will attempt to 181 // copy and if that succeeds, delete the old path. 182 virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path) = 0; 183 184 // This attempts to move whatever is located at old_path to new_path, 185 // be it a file or folder. MoveFileOrFolder(const Pathname & old_path,const Pathname & new_path)186 bool MoveFileOrFolder(const Pathname &old_path, const Pathname &new_path) { 187 if (IsFile(old_path)) { 188 return MoveFile(old_path, new_path); 189 } else { 190 return MoveFolder(old_path, new_path); 191 } 192 } 193 194 // This copies a file from old_path to new_path. This method ASSERTs and 195 // returns false if old_path is a folder, and returns true if the copy 196 // succeeds. 197 virtual bool CopyFile(const Pathname &old_path, const Pathname &new_path) = 0; 198 199 // This copies a folder from old_path to new_path. 200 bool CopyFolder(const Pathname &old_path, const Pathname &new_path); 201 CopyFileOrFolder(const Pathname & old_path,const Pathname & new_path)202 bool CopyFileOrFolder(const Pathname &old_path, const Pathname &new_path) { 203 if (IsFile(old_path)) 204 return CopyFile(old_path, new_path); 205 else 206 return CopyFolder(old_path, new_path); 207 } 208 209 // Returns true if pathname refers to a directory 210 virtual bool IsFolder(const Pathname& pathname) = 0; 211 212 // Returns true if pathname refers to a file 213 virtual bool IsFile(const Pathname& pathname) = 0; 214 215 // Returns true if pathname refers to no filesystem object, every parent 216 // directory either exists, or is also absent. 217 virtual bool IsAbsent(const Pathname& pathname) = 0; 218 219 // Returns true if pathname represents a temporary location on the system. 220 virtual bool IsTemporaryPath(const Pathname& pathname) = 0; 221 222 // A folder appropriate for storing temporary files (Contents are 223 // automatically deleted when the program exits) 224 virtual bool GetTemporaryFolder(Pathname &path, bool create, 225 const std::string *append) = 0; 226 227 virtual std::string TempFilename(const Pathname &dir, 228 const std::string &prefix) = 0; 229 230 // Determines the size of the file indicated by path. 231 virtual bool GetFileSize(const Pathname& path, size_t* size) = 0; 232 233 // Determines a timestamp associated with the file indicated by path. 234 virtual bool GetFileTime(const Pathname& path, FileTimeType which, 235 time_t* time) = 0; 236 237 // Returns the path to the running application. 238 // Note: This is not guaranteed to work on all platforms. Be aware of the 239 // limitations before using it, and robustly handle failure. 240 virtual bool GetAppPathname(Pathname* path) = 0; 241 242 // Get a folder that is unique to the current application, which is suitable 243 // for sharing data between executions of the app. If the per_user arg is 244 // true, the folder is also specific to the current user. 245 virtual bool GetAppDataFolder(Pathname* path, bool per_user) = 0; 246 247 // Get a temporary folder that is unique to the current user and application. 248 // TODO: Re-evaluate the goals of this function. We probably just need any 249 // directory that won't collide with another existing directory, and which 250 // will be cleaned up when the program exits. 251 virtual bool GetAppTempFolder(Pathname* path) = 0; 252 253 // Delete the contents of the folder returned by GetAppTempFolder 254 bool CleanAppTempFolder(); 255 256 virtual bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) = 0; 257 258 // Returns the absolute path of the current directory. 259 virtual Pathname GetCurrentDirectory() = 0; 260 261 // Note: These might go into some shared config section later, but they're 262 // used by some methods in this interface, so we're leaving them here for now. SetOrganizationName(const std::string & organization)263 void SetOrganizationName(const std::string& organization) { 264 organization_name_ = organization; 265 } GetOrganizationName(std::string * organization)266 void GetOrganizationName(std::string* organization) { 267 ASSERT(NULL != organization); 268 *organization = organization_name_; 269 } SetApplicationName(const std::string & application)270 void SetApplicationName(const std::string& application) { 271 application_name_ = application; 272 } GetApplicationName(std::string * application)273 void GetApplicationName(std::string* application) { 274 ASSERT(NULL != application); 275 *application = application_name_; 276 } 277 278 protected: 279 std::string organization_name_; 280 std::string application_name_; 281 }; 282 283 class Filesystem { 284 public: default_filesystem()285 static FilesystemInterface *default_filesystem() { 286 ASSERT(default_filesystem_.get() != NULL); 287 return default_filesystem_.get(); 288 } 289 set_default_filesystem(FilesystemInterface * filesystem)290 static void set_default_filesystem(FilesystemInterface *filesystem) { 291 default_filesystem_.reset(filesystem); 292 } 293 swap_default_filesystem(FilesystemInterface * filesystem)294 static FilesystemInterface *swap_default_filesystem( 295 FilesystemInterface *filesystem) { 296 FilesystemInterface *cur = default_filesystem_.release(); 297 default_filesystem_.reset(filesystem); 298 return cur; 299 } 300 IterateDirectory()301 static DirectoryIterator *IterateDirectory() { 302 return EnsureDefaultFilesystem()->IterateDirectory(); 303 } 304 CreateFolder(const Pathname & pathname)305 static bool CreateFolder(const Pathname &pathname) { 306 return EnsureDefaultFilesystem()->CreateFolder(pathname); 307 } 308 OpenFile(const Pathname & filename,const std::string & mode)309 static FileStream *OpenFile(const Pathname &filename, 310 const std::string &mode) { 311 return EnsureDefaultFilesystem()->OpenFile(filename, mode); 312 } 313 CreatePrivateFile(const Pathname & filename)314 static bool CreatePrivateFile(const Pathname &filename) { 315 return EnsureDefaultFilesystem()->CreatePrivateFile(filename); 316 } 317 DeleteFile(const Pathname & filename)318 static bool DeleteFile(const Pathname &filename) { 319 return EnsureDefaultFilesystem()->DeleteFile(filename); 320 } 321 DeleteEmptyFolder(const Pathname & folder)322 static bool DeleteEmptyFolder(const Pathname &folder) { 323 return EnsureDefaultFilesystem()->DeleteEmptyFolder(folder); 324 } 325 DeleteFolderContents(const Pathname & folder)326 static bool DeleteFolderContents(const Pathname &folder) { 327 return EnsureDefaultFilesystem()->DeleteFolderContents(folder); 328 } 329 DeleteFolderAndContents(const Pathname & folder)330 static bool DeleteFolderAndContents(const Pathname &folder) { 331 return EnsureDefaultFilesystem()->DeleteFolderAndContents(folder); 332 } 333 MoveFolder(const Pathname & old_path,const Pathname & new_path)334 static bool MoveFolder(const Pathname &old_path, const Pathname &new_path) { 335 return EnsureDefaultFilesystem()->MoveFolder(old_path, new_path); 336 } 337 MoveFile(const Pathname & old_path,const Pathname & new_path)338 static bool MoveFile(const Pathname &old_path, const Pathname &new_path) { 339 return EnsureDefaultFilesystem()->MoveFile(old_path, new_path); 340 } 341 CopyFolder(const Pathname & old_path,const Pathname & new_path)342 static bool CopyFolder(const Pathname &old_path, const Pathname &new_path) { 343 return EnsureDefaultFilesystem()->CopyFolder(old_path, new_path); 344 } 345 CopyFile(const Pathname & old_path,const Pathname & new_path)346 static bool CopyFile(const Pathname &old_path, const Pathname &new_path) { 347 return EnsureDefaultFilesystem()->CopyFile(old_path, new_path); 348 } 349 IsFolder(const Pathname & pathname)350 static bool IsFolder(const Pathname& pathname) { 351 return EnsureDefaultFilesystem()->IsFolder(pathname); 352 } 353 IsFile(const Pathname & pathname)354 static bool IsFile(const Pathname &pathname) { 355 return EnsureDefaultFilesystem()->IsFile(pathname); 356 } 357 IsAbsent(const Pathname & pathname)358 static bool IsAbsent(const Pathname &pathname) { 359 return EnsureDefaultFilesystem()->IsAbsent(pathname); 360 } 361 IsTemporaryPath(const Pathname & pathname)362 static bool IsTemporaryPath(const Pathname& pathname) { 363 return EnsureDefaultFilesystem()->IsTemporaryPath(pathname); 364 } 365 GetTemporaryFolder(Pathname & path,bool create,const std::string * append)366 static bool GetTemporaryFolder(Pathname &path, bool create, 367 const std::string *append) { 368 return EnsureDefaultFilesystem()->GetTemporaryFolder(path, create, append); 369 } 370 TempFilename(const Pathname & dir,const std::string & prefix)371 static std::string TempFilename(const Pathname &dir, 372 const std::string &prefix) { 373 return EnsureDefaultFilesystem()->TempFilename(dir, prefix); 374 } 375 GetFileSize(const Pathname & path,size_t * size)376 static bool GetFileSize(const Pathname& path, size_t* size) { 377 return EnsureDefaultFilesystem()->GetFileSize(path, size); 378 } 379 GetFileTime(const Pathname & path,FileTimeType which,time_t * time)380 static bool GetFileTime(const Pathname& path, FileTimeType which, 381 time_t* time) { 382 return EnsureDefaultFilesystem()->GetFileTime(path, which, time); 383 } 384 GetAppPathname(Pathname * path)385 static bool GetAppPathname(Pathname* path) { 386 return EnsureDefaultFilesystem()->GetAppPathname(path); 387 } 388 GetAppDataFolder(Pathname * path,bool per_user)389 static bool GetAppDataFolder(Pathname* path, bool per_user) { 390 return EnsureDefaultFilesystem()->GetAppDataFolder(path, per_user); 391 } 392 GetAppTempFolder(Pathname * path)393 static bool GetAppTempFolder(Pathname* path) { 394 return EnsureDefaultFilesystem()->GetAppTempFolder(path); 395 } 396 CleanAppTempFolder()397 static bool CleanAppTempFolder() { 398 return EnsureDefaultFilesystem()->CleanAppTempFolder(); 399 } 400 GetDiskFreeSpace(const Pathname & path,int64 * freebytes)401 static bool GetDiskFreeSpace(const Pathname& path, int64 *freebytes) { 402 return EnsureDefaultFilesystem()->GetDiskFreeSpace(path, freebytes); 403 } 404 405 // Definition has to be in the .cc file due to returning forward-declared 406 // Pathname by value. 407 static Pathname GetCurrentDirectory(); 408 SetOrganizationName(const std::string & organization)409 static void SetOrganizationName(const std::string& organization) { 410 EnsureDefaultFilesystem()->SetOrganizationName(organization); 411 } 412 GetOrganizationName(std::string * organization)413 static void GetOrganizationName(std::string* organization) { 414 EnsureDefaultFilesystem()->GetOrganizationName(organization); 415 } 416 SetApplicationName(const std::string & application)417 static void SetApplicationName(const std::string& application) { 418 EnsureDefaultFilesystem()->SetApplicationName(application); 419 } 420 GetApplicationName(std::string * application)421 static void GetApplicationName(std::string* application) { 422 EnsureDefaultFilesystem()->GetApplicationName(application); 423 } 424 425 private: 426 static scoped_ptr<FilesystemInterface> default_filesystem_; 427 428 static FilesystemInterface *EnsureDefaultFilesystem(); 429 DISALLOW_IMPLICIT_CONSTRUCTORS(Filesystem); 430 }; 431 432 class FilesystemScope{ 433 public: FilesystemScope(FilesystemInterface * new_fs)434 explicit FilesystemScope(FilesystemInterface *new_fs) { 435 old_fs_ = Filesystem::swap_default_filesystem(new_fs); 436 } ~FilesystemScope()437 ~FilesystemScope() { 438 Filesystem::set_default_filesystem(old_fs_); 439 } 440 private: 441 FilesystemInterface* old_fs_; 442 DISALLOW_IMPLICIT_CONSTRUCTORS(FilesystemScope); 443 }; 444 445 // Generates a unique filename based on the input path. If no path component 446 // is specified, it uses the temporary directory. If a filename is provided, 447 // up to 100 variations of form basename-N.extension are tried. When 448 // create_empty is true, an empty file of this name is created (which 449 // decreases the chance of a temporary filename collision with another 450 // process). 451 bool CreateUniqueFile(Pathname& path, bool create_empty); 452 453 } // namespace talk_base 454 455 #endif // TALK_BASE_FILEUTILS_H_ 456 457