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 #include <assert.h> 29 30 #ifdef WIN32 31 // TODO(grunell): Remove io.h includes when Chromium has started 32 // to use AEC in each source. http://crbug.com/264611. 33 #include <io.h> 34 #include "talk/base/win32.h" 35 #endif 36 37 #include "talk/base/pathutils.h" 38 #include "talk/base/fileutils.h" 39 #include "talk/base/stringutils.h" 40 #include "talk/base/stream.h" 41 42 #ifdef WIN32 43 #include "talk/base/win32filesystem.h" 44 #else 45 #include "talk/base/unixfilesystem.h" 46 #endif 47 48 #ifndef WIN32 49 #define MAX_PATH 260 50 #endif 51 52 namespace talk_base { 53 54 ////////////////////////// 55 // Directory Iterator // 56 ////////////////////////// 57 58 // A DirectoryIterator is created with a given directory. It originally points 59 // to the first file in the directory, and can be advanecd with Next(). This 60 // allows you to get information about each file. 61 62 // Constructor 63 DirectoryIterator::DirectoryIterator() 64 #ifdef _WIN32 65 : handle_(INVALID_HANDLE_VALUE) { 66 #else 67 : dir_(NULL), dirent_(NULL) { 68 #endif 69 } 70 71 // Destructor 72 DirectoryIterator::~DirectoryIterator() { 73 #ifdef WIN32 74 if (handle_ != INVALID_HANDLE_VALUE) 75 ::FindClose(handle_); 76 #else 77 if (dir_) 78 closedir(dir_); 79 #endif 80 } 81 82 // Starts traversing a directory. 83 // dir is the directory to traverse 84 // returns true if the directory exists and is valid 85 bool DirectoryIterator::Iterate(const Pathname &dir) { 86 directory_ = dir.pathname(); 87 #ifdef WIN32 88 if (handle_ != INVALID_HANDLE_VALUE) 89 ::FindClose(handle_); 90 std::string d = dir.pathname() + '*'; 91 handle_ = ::FindFirstFile(ToUtf16(d).c_str(), &data_); 92 if (handle_ == INVALID_HANDLE_VALUE) 93 return false; 94 #else 95 if (dir_ != NULL) 96 closedir(dir_); 97 dir_ = ::opendir(directory_.c_str()); 98 if (dir_ == NULL) 99 return false; 100 dirent_ = readdir(dir_); 101 if (dirent_ == NULL) 102 return false; 103 104 if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0) 105 return false; 106 #endif 107 return true; 108 } 109 110 // Advances to the next file 111 // returns true if there were more files in the directory. 112 bool DirectoryIterator::Next() { 113 #ifdef WIN32 114 return ::FindNextFile(handle_, &data_) == TRUE; 115 #else 116 dirent_ = ::readdir(dir_); 117 if (dirent_ == NULL) 118 return false; 119 120 return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0; 121 #endif 122 } 123 124 // returns true if the file currently pointed to is a directory 125 bool DirectoryIterator::IsDirectory() const { 126 #ifdef WIN32 127 return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE; 128 #else 129 return S_ISDIR(stat_.st_mode); 130 #endif 131 } 132 133 // returns the name of the file currently pointed to 134 std::string DirectoryIterator::Name() const { 135 #ifdef WIN32 136 return ToUtf8(data_.cFileName); 137 #else 138 assert(dirent_ != NULL); 139 return dirent_->d_name; 140 #endif 141 } 142 143 // returns the size of the file currently pointed to 144 size_t DirectoryIterator::FileSize() const { 145 #ifndef WIN32 146 return stat_.st_size; 147 #else 148 return data_.nFileSizeLow; 149 #endif 150 } 151 152 // returns the last modified time of this file 153 time_t DirectoryIterator::FileModifyTime() const { 154 #ifdef WIN32 155 time_t val; 156 FileTimeToUnixTime(data_.ftLastWriteTime, &val); 157 return val; 158 #else 159 return stat_.st_mtime; 160 #endif 161 } 162 163 FilesystemInterface* Filesystem::default_filesystem_ = NULL; 164 165 FilesystemInterface *Filesystem::EnsureDefaultFilesystem() { 166 if (!default_filesystem_) { 167 #ifdef WIN32 168 default_filesystem_ = new Win32Filesystem(); 169 #else 170 default_filesystem_ = new UnixFilesystem(); 171 #endif 172 } 173 return default_filesystem_; 174 } 175 176 bool FilesystemInterface::CopyFolder(const Pathname &old_path, 177 const Pathname &new_path) { 178 bool success = true; 179 VERIFY(IsFolder(old_path)); 180 Pathname new_dir; 181 new_dir.SetFolder(new_path.pathname()); 182 Pathname old_dir; 183 old_dir.SetFolder(old_path.pathname()); 184 if (!CreateFolder(new_dir)) 185 return false; 186 DirectoryIterator *di = IterateDirectory(); 187 if (!di) 188 return false; 189 if (di->Iterate(old_dir.pathname())) { 190 do { 191 if (di->Name() == "." || di->Name() == "..") 192 continue; 193 Pathname source; 194 Pathname dest; 195 source.SetFolder(old_dir.pathname()); 196 dest.SetFolder(new_path.pathname()); 197 source.SetFilename(di->Name()); 198 dest.SetFilename(di->Name()); 199 if (!CopyFileOrFolder(source, dest)) 200 success = false; 201 } while (di->Next()); 202 } 203 delete di; 204 return success; 205 } 206 207 bool FilesystemInterface::DeleteFolderContents(const Pathname &folder) { 208 bool success = true; 209 VERIFY(IsFolder(folder)); 210 DirectoryIterator *di = IterateDirectory(); 211 if (!di) 212 return false; 213 if (di->Iterate(folder)) { 214 do { 215 if (di->Name() == "." || di->Name() == "..") 216 continue; 217 Pathname subdir; 218 subdir.SetFolder(folder.pathname()); 219 if (di->IsDirectory()) { 220 subdir.AppendFolder(di->Name()); 221 if (!DeleteFolderAndContents(subdir)) { 222 success = false; 223 } 224 } else { 225 subdir.SetFilename(di->Name()); 226 if (!DeleteFile(subdir)) { 227 success = false; 228 } 229 } 230 } while (di->Next()); 231 } 232 delete di; 233 return success; 234 } 235 236 bool FilesystemInterface::CleanAppTempFolder() { 237 Pathname path; 238 if (!GetAppTempFolder(&path)) 239 return false; 240 if (IsAbsent(path)) 241 return true; 242 if (!IsTemporaryPath(path)) { 243 ASSERT(false); 244 return false; 245 } 246 return DeleteFolderContents(path); 247 } 248 249 Pathname Filesystem::GetCurrentDirectory() { 250 return EnsureDefaultFilesystem()->GetCurrentDirectory(); 251 } 252 253 bool CreateUniqueFile(Pathname& path, bool create_empty) { 254 LOG(LS_INFO) << "Path " << path.pathname() << std::endl; 255 // If no folder is supplied, use the temporary folder 256 if (path.folder().empty()) { 257 Pathname temporary_path; 258 if (!Filesystem::GetTemporaryFolder(temporary_path, true, NULL)) { 259 printf("Get temp failed\n"); 260 return false; 261 } 262 path.SetFolder(temporary_path.pathname()); 263 } 264 265 // If no filename is supplied, use a temporary name 266 if (path.filename().empty()) { 267 std::string folder(path.folder()); 268 std::string filename = Filesystem::TempFilename(folder, "gt"); 269 path.SetPathname(filename); 270 if (!create_empty) { 271 Filesystem::DeleteFile(path.pathname()); 272 } 273 return true; 274 } 275 276 // Otherwise, create a unique name based on the given filename 277 // foo.txt -> foo-N.txt 278 const std::string basename = path.basename(); 279 const size_t MAX_VERSION = 100; 280 size_t version = 0; 281 while (version < MAX_VERSION) { 282 std::string pathname = path.pathname(); 283 284 if (!Filesystem::IsFile(pathname)) { 285 if (create_empty) { 286 FileStream* fs = Filesystem::OpenFile(pathname, "w"); 287 delete fs; 288 } 289 return true; 290 } 291 version += 1; 292 char version_base[MAX_PATH]; 293 sprintfn(version_base, ARRAY_SIZE(version_base), "%s-%u", 294 basename.c_str(), version); 295 path.SetBasename(version_base); 296 } 297 return true; 298 } 299 300 // Taken from Chromium's base/platform_file_*.cc. 301 // TODO(grunell): Remove when Chromium has started to use AEC in each source. 302 // http://crbug.com/264611. 303 FILE* FdopenPlatformFileForWriting(PlatformFile file) { 304 #if defined(WIN32) 305 if (file == kInvalidPlatformFileValue) 306 return NULL; 307 int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0); 308 if (fd < 0) 309 return NULL; 310 return _fdopen(fd, "w"); 311 #else 312 return fdopen(file, "w"); 313 #endif 314 } 315 316 bool ClosePlatformFile(PlatformFile file) { 317 #if defined(WIN32) 318 return CloseHandle(file) != 0; 319 #else 320 return close(file); 321 #endif 322 } 323 324 } // namespace talk_base 325