1/* 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#include <assert.h> 12 13#include "webrtc/base/arraysize.h" 14#include "webrtc/base/pathutils.h" 15#include "webrtc/base/fileutils.h" 16#include "webrtc/base/stringutils.h" 17#include "webrtc/base/stream.h" 18 19#if defined(WEBRTC_WIN) 20#include "webrtc/base/win32filesystem.h" 21#else 22#include "webrtc/base/unixfilesystem.h" 23#endif 24 25#if !defined(WEBRTC_WIN) 26#define MAX_PATH 260 27#endif 28 29namespace rtc { 30 31////////////////////////// 32// Directory Iterator // 33////////////////////////// 34 35// A DirectoryIterator is created with a given directory. It originally points 36// to the first file in the directory, and can be advanecd with Next(). This 37// allows you to get information about each file. 38 39 // Constructor 40DirectoryIterator::DirectoryIterator() 41#ifdef WEBRTC_WIN 42 : handle_(INVALID_HANDLE_VALUE) { 43#else 44 : dir_(NULL), dirent_(NULL) { 45#endif 46} 47 48 // Destructor 49DirectoryIterator::~DirectoryIterator() { 50#if defined(WEBRTC_WIN) 51 if (handle_ != INVALID_HANDLE_VALUE) 52 ::FindClose(handle_); 53#else 54 if (dir_) 55 closedir(dir_); 56#endif 57} 58 59 // Starts traversing a directory. 60 // dir is the directory to traverse 61 // returns true if the directory exists and is valid 62bool DirectoryIterator::Iterate(const Pathname &dir) { 63 directory_ = dir.pathname(); 64#if defined(WEBRTC_WIN) 65 if (handle_ != INVALID_HANDLE_VALUE) 66 ::FindClose(handle_); 67 std::string d = dir.pathname() + '*'; 68 handle_ = ::FindFirstFile(ToUtf16(d).c_str(), &data_); 69 if (handle_ == INVALID_HANDLE_VALUE) 70 return false; 71#else 72 if (dir_ != NULL) 73 closedir(dir_); 74 dir_ = ::opendir(directory_.c_str()); 75 if (dir_ == NULL) 76 return false; 77 dirent_ = readdir(dir_); 78 if (dirent_ == NULL) 79 return false; 80 81 if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0) 82 return false; 83#endif 84 return true; 85} 86 87 // Advances to the next file 88 // returns true if there were more files in the directory. 89bool DirectoryIterator::Next() { 90#if defined(WEBRTC_WIN) 91 return ::FindNextFile(handle_, &data_) == TRUE; 92#else 93 dirent_ = ::readdir(dir_); 94 if (dirent_ == NULL) 95 return false; 96 97 return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0; 98#endif 99} 100 101 // returns true if the file currently pointed to is a directory 102bool DirectoryIterator::IsDirectory() const { 103#if defined(WEBRTC_WIN) 104 return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE; 105#else 106 return S_ISDIR(stat_.st_mode); 107#endif 108} 109 110 // returns the name of the file currently pointed to 111std::string DirectoryIterator::Name() const { 112#if defined(WEBRTC_WIN) 113 return ToUtf8(data_.cFileName); 114#else 115 assert(dirent_ != NULL); 116 return dirent_->d_name; 117#endif 118} 119 120 // returns the size of the file currently pointed to 121size_t DirectoryIterator::FileSize() const { 122#if !defined(WEBRTC_WIN) 123 return stat_.st_size; 124#else 125 return data_.nFileSizeLow; 126#endif 127} 128 129bool DirectoryIterator::OlderThan(int seconds) const { 130 time_t file_modify_time; 131#if defined(WEBRTC_WIN) 132 FileTimeToUnixTime(data_.ftLastWriteTime, &file_modify_time); 133#else 134 file_modify_time = stat_.st_mtime; 135#endif 136 return TimeDiff(time(NULL), file_modify_time) >= seconds; 137} 138 139FilesystemInterface* Filesystem::default_filesystem_ = NULL; 140 141FilesystemInterface *Filesystem::EnsureDefaultFilesystem() { 142 if (!default_filesystem_) { 143#if defined(WEBRTC_WIN) 144 default_filesystem_ = new Win32Filesystem(); 145#else 146 default_filesystem_ = new UnixFilesystem(); 147#endif 148 } 149 return default_filesystem_; 150} 151 152DirectoryIterator* FilesystemInterface::IterateDirectory() { 153 return new DirectoryIterator(); 154} 155 156bool FilesystemInterface::CopyFolder(const Pathname &old_path, 157 const Pathname &new_path) { 158 bool success = true; 159 VERIFY(IsFolder(old_path)); 160 Pathname new_dir; 161 new_dir.SetFolder(new_path.pathname()); 162 Pathname old_dir; 163 old_dir.SetFolder(old_path.pathname()); 164 if (!CreateFolder(new_dir)) 165 return false; 166 DirectoryIterator *di = IterateDirectory(); 167 if (!di) 168 return false; 169 if (di->Iterate(old_dir.pathname())) { 170 do { 171 if (di->Name() == "." || di->Name() == "..") 172 continue; 173 Pathname source; 174 Pathname dest; 175 source.SetFolder(old_dir.pathname()); 176 dest.SetFolder(new_path.pathname()); 177 source.SetFilename(di->Name()); 178 dest.SetFilename(di->Name()); 179 if (!CopyFileOrFolder(source, dest)) 180 success = false; 181 } while (di->Next()); 182 } 183 delete di; 184 return success; 185} 186 187bool FilesystemInterface::DeleteFolderContents(const Pathname &folder) { 188 bool success = true; 189 VERIFY(IsFolder(folder)); 190 DirectoryIterator *di = IterateDirectory(); 191 if (!di) 192 return false; 193 if (di->Iterate(folder)) { 194 do { 195 if (di->Name() == "." || di->Name() == "..") 196 continue; 197 Pathname subdir; 198 subdir.SetFolder(folder.pathname()); 199 if (di->IsDirectory()) { 200 subdir.AppendFolder(di->Name()); 201 if (!DeleteFolderAndContents(subdir)) { 202 success = false; 203 } 204 } else { 205 subdir.SetFilename(di->Name()); 206 if (!DeleteFile(subdir)) { 207 success = false; 208 } 209 } 210 } while (di->Next()); 211 } 212 delete di; 213 return success; 214} 215 216bool FilesystemInterface::DeleteFolderAndContents(const Pathname& folder) { 217 return DeleteFolderContents(folder) && DeleteEmptyFolder(folder); 218} 219 220bool FilesystemInterface::CleanAppTempFolder() { 221 Pathname path; 222 if (!GetAppTempFolder(&path)) 223 return false; 224 if (IsAbsent(path)) 225 return true; 226 if (!IsTemporaryPath(path)) { 227 ASSERT(false); 228 return false; 229 } 230 return DeleteFolderContents(path); 231} 232 233Pathname Filesystem::GetCurrentDirectory() { 234 return EnsureDefaultFilesystem()->GetCurrentDirectory(); 235} 236 237bool CreateUniqueFile(Pathname& path, bool create_empty) { 238 LOG(LS_INFO) << "Path " << path.pathname() << std::endl; 239 // If no folder is supplied, use the temporary folder 240 if (path.folder().empty()) { 241 Pathname temporary_path; 242 if (!Filesystem::GetTemporaryFolder(temporary_path, true, NULL)) { 243 printf("Get temp failed\n"); 244 return false; 245 } 246 path.SetFolder(temporary_path.pathname()); 247 } 248 249 // If no filename is supplied, use a temporary name 250 if (path.filename().empty()) { 251 std::string folder(path.folder()); 252 std::string filename = Filesystem::TempFilename(folder, "gt"); 253 path.SetPathname(filename); 254 if (!create_empty) { 255 Filesystem::DeleteFile(path.pathname()); 256 } 257 return true; 258 } 259 260 // Otherwise, create a unique name based on the given filename 261 // foo.txt -> foo-N.txt 262 const std::string basename = path.basename(); 263 const size_t MAX_VERSION = 100; 264 size_t version = 0; 265 while (version < MAX_VERSION) { 266 std::string pathname = path.pathname(); 267 268 if (!Filesystem::IsFile(pathname)) { 269 if (create_empty) { 270 FileStream* fs = Filesystem::OpenFile(pathname, "w"); 271 delete fs; 272 } 273 return true; 274 } 275 version += 1; 276 char version_base[MAX_PATH]; 277 sprintfn(version_base, arraysize(version_base), "%s-%u", basename.c_str(), 278 version); 279 path.SetBasename(version_base); 280 } 281 return true; 282} 283 284} // namespace rtc 285