blocxx
|
00001 /******************************************************************************* 00002 * Copyright (C) 2005, Quest Software, Inc. All rights reserved. 00003 * Copyright (C) 2006, Novell, Inc. All rights reserved. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * * Redistributions of source code must retain the above copyright notice, 00009 * this list of conditions and the following disclaimer. 00010 * * Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * * Neither the name of 00014 * Quest Software, Inc., 00015 * nor Novell, Inc., 00016 * nor the names of its contributors or employees may be used to 00017 * endorse or promote products derived from this software without 00018 * specific prior written permission. 00019 * 00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00030 * POSSIBILITY OF SUCH DAMAGE. 00031 *******************************************************************************/ 00032 00033 00041 #include "blocxx/BLOCXX_config.h" 00042 #include "blocxx/FileSystem.hpp" 00043 #include "blocxx/Mutex.hpp" 00044 #include "blocxx/MutexLock.hpp" 00045 #include "blocxx/GlobalMutex.hpp" 00046 #include "blocxx/File.hpp" 00047 #include "blocxx/Array.hpp" 00048 #include "blocxx/Format.hpp" 00049 #include "blocxx/ExceptionIds.hpp" 00050 #include "blocxx/Assertion.hpp" 00051 #include "blocxx/GlobalPtr.hpp" 00052 #include "blocxx/FileSystemMockObject.hpp" 00053 #include "blocxx/AutoPtr.hpp" 00054 #include "blocxx/SafeCString.hpp" 00055 #include "blocxx/Logger.hpp" 00056 #include "blocxx/GlobalString.hpp" 00057 00058 extern "C" 00059 { 00060 #ifdef BLOCXX_WIN32 00061 00062 #include <direct.h> 00063 #include <io.h> 00064 #include <share.h> 00065 #include <AccCtrl.h.> 00066 #include <Aclapi.h> 00067 #include "blocxx/PathSecurity.hpp" 00068 using namespace BLOCXX_NAMESPACE; 00069 00071 static unsigned long MapPosixPermissionsMask( PACCESS_ALLOWED_ACE pAce, int PermissionMask ) 00072 { 00073 pAce->Mask = 0; 00074 pAce->Mask |= ((PermissionMask & S_IROTH) == S_IROTH) ? BLOCXX_WIN32_ACCESSMASK_GENERIC_READ : 0; 00075 pAce->Mask |= ((PermissionMask & S_IWOTH) == S_IWOTH) ? BLOCXX_WIN32_ACCESSMASK_GENERIC_WRITE : 0; 00076 pAce->Mask |= ((PermissionMask & S_IXOTH) == S_IXOTH) ? BLOCXX_WIN32_ACCESSMASK_GENERIC_EXEC : 0; 00077 return pAce->Mask; 00078 } 00079 00081 /* 00082 * A wrapper over POSIX chmod functionality for Windows 00083 * It tries to set the appropriate permissions via discretionary access control list 00084 * onto the path security descriptor associated. 00085 */ 00086 static int posix_chmod(const char* path, int mode) 00087 { 00088 int result, nLenghtNeeded; 00089 PSID ppOwnerSid = NULL, ppGroupSid = NULL, pSecurityDescriptor = NULL; 00090 PACL pAcl = NULL; 00091 if ( (result = GetNamedSecurityInfo( (LPTSTR)path, 00092 SE_FILE_OBJECT, 00093 DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, 00094 &ppOwnerSid, 00095 &ppGroupSid, 00096 &pAcl, 00097 NULL, 00098 &pSecurityDescriptor) ) ) 00099 { 00100 return result; 00101 } 00102 /* 00103 * We got the NTFS security information and DACL associated to path specified. 00104 * Now we gonna change it according to UNIX access mode. 00105 */ 00106 for (unsigned short aceIdx = 0; aceIdx < pAcl->AceCount; aceIdx++) 00107 { 00108 ACE_HEADER* pAce; 00109 if (!::GetAce(pAcl, aceIdx, (void**)&pAce)) 00110 { 00111 continue; 00112 } 00113 switch( pAce->AceType ) 00114 { 00115 case ACCESS_ALLOWED_ACE_TYPE: 00116 { 00117 PACCESS_ALLOWED_ACE pAllowedAce = (PACCESS_ALLOWED_ACE) pAce; 00118 unsigned long sNameLen, sDNameLen = sNameLen = MAX_PATH; 00119 char sName[MAX_PATH] = {0}, sDName[MAX_PATH] = {0}; 00120 SID_NAME_USE eUse; 00121 00122 if ( !::LookupAccountSid( NULL, &(pAllowedAce->SidStart), sName, &sNameLen, sDName, &sDNameLen, &eUse) ) 00123 { 00124 continue; 00125 } 00126 00127 if ( EqualSid( ppOwnerSid, &pAllowedAce->SidStart ) || (eUse == SidTypeWellKnownGroup && !strcmp(sName, "CREATOR OWNER")) ) 00128 { 00129 // modifying permissions for the object's owner 00130 int hundreds = mode / 100; 00131 MapPosixPermissionsMask( pAllowedAce, (hundreds - (hundreds/10)*10) ); 00132 break; 00133 } 00134 if ( EqualSid( ppGroupSid, &pAllowedAce->SidStart ) || eUse == WinCreatorGroupSid ) 00135 { 00136 // modifying permissions for the object's group 00137 int decimals = mode / 10; 00138 MapPosixPermissionsMask( pAllowedAce, (decimals - (decimals/10)*10) ); 00139 break; 00140 } 00141 // modifying permissions for others 00142 MapPosixPermissionsMask( pAllowedAce, (mode - (mode/10)*10) ); 00143 } 00144 break; 00145 00146 case ACCESS_DENIED_ACE_TYPE: 00147 { 00148 /* 00149 * There is no need to change it, because the permission modes 00150 * that limitates security descriptor usage are set via allowed ACE 00151 */ 00152 DeleteAce(pAcl, aceIdx); 00153 } 00154 break; 00155 } 00156 } 00157 /* 00158 * All the operations with dACL and its ACEs were made in the shared memory. 00159 * That's why we don't need to make a copy of the new dACL, just apply the 00160 * changed dACL onto path security descriptor. 00161 */ 00162 result = SetNamedSecurityInfo((LPTSTR)path, 00163 SE_FILE_OBJECT, 00164 PROTECTED_DACL_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, 00165 ppOwnerSid, 00166 ppGroupSid, 00167 pAcl, 00168 NULL); 00169 00170 if (pSecurityDescriptor) LocalFree((HLOCAL)pSecurityDescriptor); 00171 00172 return result; 00173 } 00174 00176 static int posix_mkdir(const char* path, int mode) 00177 { 00178 int result; 00179 if ( result = _mkdir(path) ) return result; 00185 return ((mode!=-1) ? result = posix_chmod(path, mode) : result); 00186 } 00187 00189 #define _ACCESS ::_access 00190 #define R_OK 4 00191 #define F_OK 0 00192 #define W_OK 2 00193 #define _CHDIR _chdir 00194 #define _MKDIR(a,b) posix_mkdir((a), (b)) 00195 #define _RMDIR _rmdir 00196 #define _UNLINK _unlink 00197 00198 #else 00199 00200 #ifdef BLOCXX_HAVE_UNISTD_H 00201 #include <unistd.h> 00202 #endif 00203 #ifdef BLOCXX_HAVE_DIRENT_H 00204 #include <dirent.h> 00205 #endif 00206 00207 #define _ACCESS ::access 00208 #define _CHDIR chdir 00209 #define _MKDIR(a,b) mkdir((a),(b)) 00210 #define _RMDIR rmdir 00211 #define _UNLINK unlink 00212 00213 #ifdef BLOCXX_NETWARE 00214 #define MAXSYMLINKS 20 00215 #endif 00216 00217 #endif 00218 00219 #include <sys/stat.h> 00220 #include <sys/types.h> 00221 #include <fcntl.h> 00222 } 00223 00224 #include <cstdio> // for rename 00225 #include <fstream> 00226 #include <cerrno> 00227 00228 namespace BLOCXX_NAMESPACE 00229 { 00230 00231 BLOCXX_DEFINE_EXCEPTION_WITH_ID(FileSystem); 00232 00233 namespace FileSystem 00234 { 00235 00236 typedef GlobalPtr<FileSystemMockObject, NullFactory> FileSystemMockObject_t; 00237 FileSystemMockObject_t g_fileSystemMockObject = BLOCXX_GLOBAL_PTR_INIT; 00238 00239 GlobalString COMPONENT_NAME = BLOCXX_GLOBAL_STRING_INIT("blocxx"); 00240 00242 // STATIC 00243 int 00244 changeFileOwner(const String& filename, 00245 const UserId& userId) 00246 { 00247 #ifdef BLOCXX_WIN32 00248 return 0; // File ownership on Win32? 00249 #else 00250 return ::chown(filename.c_str(), userId, gid_t(-1)); 00251 #endif 00252 } 00254 // STATIC 00255 File 00256 openFile(const String& path) 00257 { 00258 if (g_fileSystemMockObject) 00259 { 00260 return g_fileSystemMockObject->openFile(path); 00261 } 00262 #ifdef BLOCXX_WIN32 00263 HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE, 00264 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 00265 FILE_ATTRIBUTE_NORMAL, NULL); 00266 00267 return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File(); 00268 #else 00269 return File(::open(path.c_str(), O_RDWR)); 00270 #endif 00271 } 00273 // STATIC 00274 File 00275 createFile(const String& path) 00276 { 00277 if (g_fileSystemMockObject) 00278 { 00279 return g_fileSystemMockObject->createFile(path); 00280 } 00281 #ifdef BLOCXX_WIN32 00282 HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE, 00283 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, 00284 FILE_ATTRIBUTE_NORMAL, NULL); 00285 return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File(); 00286 #else 00287 int fd = ::open(path.c_str(), O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0660); 00288 if (fd != -1) 00289 { 00290 return File(fd); 00291 } 00292 return File(); 00293 #endif 00294 00295 } 00297 // STATIC 00298 File 00299 openOrCreateFile(const String& path) 00300 { 00301 if (g_fileSystemMockObject) 00302 { 00303 return g_fileSystemMockObject->openOrCreateFile(path); 00304 } 00305 #ifdef BLOCXX_WIN32 00306 HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE, 00307 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 00308 FILE_ATTRIBUTE_NORMAL, NULL); 00309 return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File(); 00310 #else 00311 return File(::open(path.c_str(), O_RDWR | O_CREAT, 0660)); 00312 #endif 00313 } 00314 00316 // STATIC 00317 File 00318 openForAppendOrCreateFile(const String& path) 00319 { 00320 if (g_fileSystemMockObject) 00321 { 00322 return g_fileSystemMockObject->openForAppendOrCreateFile(path); 00323 } 00324 #ifdef BLOCXX_WIN32 00325 //Exclude FILE_WRITE_DATA flag, because it truncates file if it exists 00326 //Add FILE_SHARE_DELETE flag, because file can be deleted (renamed) by another process 00327 HANDLE fh = ::CreateFile(path.c_str(), FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE, 00328 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, 00329 FILE_ATTRIBUTE_NORMAL, NULL); 00330 return (fh != INVALID_HANDLE_VALUE) ? File(fh) : File(); 00331 #else 00332 return File(::open(path.c_str(), O_WRONLY | O_APPEND | O_CREAT, 0660)); 00333 #endif 00334 } 00335 00337 namespace 00338 { 00339 GlobalMutex tmpfileMutex = BLOCXX_GLOBAL_MUTEX_INIT(); 00340 } 00341 00342 #ifndef BLOCXX_WIN32 00343 00344 File 00345 createTempFile(String& filePath, const String& dir) 00346 { 00347 filePath.erase(); 00348 if (g_fileSystemMockObject) 00349 { 00350 return g_fileSystemMockObject->createTempFile(filePath, dir); 00351 } 00352 00353 String sfname; 00354 if (dir.empty()) 00355 { 00356 const char* envtmp = ::getenv("TMPDIR"); 00357 if (!envtmp) 00358 { 00359 sfname = "/tmp/"; 00360 } 00361 else 00362 { 00363 sfname = envtmp; 00364 if (!sfname.endsWith('/')) 00365 sfname += '/'; 00366 } 00367 } 00368 else 00369 { 00370 sfname = (dir.endsWith('/')) ? dir : dir+"/"; 00371 } 00372 00373 sfname += "blocxxtmpfileXXXXXX"; 00374 size_t len = sfname.length(); 00375 00376 AutoPtrVec<char> filename(new char[len + 1]); 00377 SafeCString::strcpy_check(filename.get(), len + 1, sfname.c_str()); 00378 MutexLock tmpfileML(tmpfileMutex); 00379 int hdl = mkstemp(filename.get()); 00380 if (hdl == -1) 00381 { 00382 return File(); 00383 } 00384 filePath = filename.get(); 00385 return File(hdl); 00386 } 00387 00389 File 00390 createAutoDeleteTempFile(const String& dir) 00391 { 00392 if (g_fileSystemMockObject) 00393 { 00394 return g_fileSystemMockObject->createTempFile(dir); 00395 } 00396 00397 String sfname; 00398 if (dir.empty()) 00399 { 00400 const char* envtmp = ::getenv("TMPDIR"); 00401 if (!envtmp) 00402 { 00403 sfname = "/tmp/"; 00404 } 00405 else 00406 { 00407 sfname = envtmp; 00408 if (!sfname.endsWith('/')) 00409 sfname += '/'; 00410 } 00411 } 00412 else 00413 { 00414 sfname = (dir.endsWith('/')) ? dir : dir+"/"; 00415 } 00416 00417 sfname += "blocxxtmpfileXXXXXX"; 00418 size_t len = sfname.length(); 00419 AutoPtrVec<char> filename(new char[len + 1]); 00420 SafeCString::strcpy_check(filename.get(), len + 1, sfname.c_str()); 00421 MutexLock tmpfileML(tmpfileMutex); 00422 int hdl = mkstemp(filename.get()); 00423 if (hdl == -1) 00424 { 00425 return File(); 00426 } 00427 else 00428 { 00429 if (::unlink(filename.get()) != 0) 00430 { 00431 Logger lgr(COMPONENT_NAME); 00432 BLOCXX_LOG_ERROR(lgr, Format("PosixFileSystem::createTempFile: unlink failed: %1", errno)); 00433 } 00434 } 00435 return File(hdl); 00436 } 00437 #else 00438 00439 File 00440 createTempFile(String& filePath, const String& dir) 00441 { 00442 filePath.erase(); 00443 if (g_fileSystemMockObject) 00444 { 00445 return g_fileSystemMockObject->createTempFile(filePath, dir); 00446 } 00447 00448 int rc = 0 ; 00449 String sfname; 00450 if (dir.empty()) 00451 { 00452 char envtmp[MAX_PATH]; 00453 rc = ::GetTempPath(MAX_PATH, envtmp); 00454 if (rc == 0) 00455 { 00456 sfname = "c:/tmp/"; 00457 } 00458 else 00459 { 00460 sfname = envtmp; 00461 if (!sfname.endsWith('/')) 00462 sfname += '/'; 00463 } 00464 } 00465 else 00466 { 00467 sfname = (dir.endsWith('/')) ? dir : dir+"/"; 00468 } 00469 00470 char szTempName[MAX_PATH]; 00471 // Create a temporary file name. 00472 rc = ::GetTempFileName(sfname.c_str(), // directory for tmp files 00473 "blocxxtmpfile", // temp file name prefix - The function uses the first three characters of this string as the prefix of the file name. 00474 0, // create unique name 00475 szTempName); // buffer for name 00476 if (rc == 0) 00477 { 00478 return File(); 00479 } 00480 00481 sfname = szTempName; 00482 size_t len = sfname.length(); 00483 AutoPtrVec<char> filename(new char[len + 1]); 00484 SafeCString::strcpy_check(filename.get(), len + 1, sfname.c_str()); 00485 MutexLock tmpfileML(tmpfileMutex); 00486 00487 // Create the new file to write the upper-case version to. 00488 FileHandle hdl = ::CreateFile((LPTSTR)sfname.c_str(), // file name 00489 GENERIC_READ | GENERIC_WRITE, // open r-w 00490 0, // do not share 00491 NULL, // default security 00492 CREATE_ALWAYS, // overwrite existing 00493 FILE_ATTRIBUTE_NORMAL,// normal file 00494 NULL); // no template 00495 if (hdl == INVALID_HANDLE_VALUE) 00496 { 00497 return File(); 00498 } 00499 00500 filePath = filename.get(); 00501 return File(hdl); 00502 } 00503 00504 File 00505 createAutoDeleteTempFile(const String& dir) 00506 { 00507 if (g_fileSystemMockObject) 00508 { 00509 return g_fileSystemMockObject->createTempFile(dir); 00510 } 00511 00512 int rc = 0 ; 00513 String sfname; 00514 if (dir.empty()) 00515 { 00516 char envtmp[MAX_PATH]; 00517 rc = ::GetTempPath(MAX_PATH, envtmp); 00518 if (rc == 0) 00519 { 00520 sfname = "c:/tmp/"; 00521 } 00522 else 00523 { 00524 sfname = envtmp; 00525 if (!sfname.endsWith('/')) 00526 sfname += '/'; 00527 } 00528 } 00529 else 00530 { 00531 sfname = (dir.endsWith('/')) ? dir : dir+"/"; 00532 } 00533 00534 char szTempName[MAX_PATH]; 00535 // Create a temporary file name. 00536 rc = ::GetTempFileName(sfname.c_str(), // directory for tmp files 00537 "blocxxtmpfile", // temp file name prefix - The function uses the first three characters of this string as the prefix of the file name. 00538 0, // create unique name 00539 szTempName); // buffer for name 00540 if (rc == 0) 00541 { 00542 return File(); 00543 } 00544 00545 sfname = szTempName; 00546 size_t len = sfname.length(); 00547 AutoPtrVec<char> filename(new char[len + 1]); 00548 SafeCString::strcpy_check(filename.get(), len + 1, sfname.c_str()); 00549 MutexLock tmpfileML(tmpfileMutex); 00550 00551 // Create the new file to write the upper-case version to. 00552 FileHandle hdl = ::CreateFile((LPTSTR)sfname.c_str(), // file name 00553 GENERIC_READ | GENERIC_WRITE, // open r-w 00554 0, // do not share 00555 NULL, // default security 00556 CREATE_ALWAYS, // overwrite existing 00557 FILE_ATTRIBUTE_NORMAL,// normal file 00558 NULL); // no template 00559 if (hdl == INVALID_HANDLE_VALUE) 00560 { 00561 return File(); 00562 } 00563 else 00564 { 00565 if (::unlink(filename.get()) != 0) 00566 { 00567 Logger lgr(COMPONENT_NAME); 00568 BLOCXX_LOG_ERROR(lgr, Format("PosixFileSystem::createTempFile: unlink failed: %1", errno)); 00569 } 00570 } 00571 return File(hdl); 00572 } 00573 #endif 00574 00576 bool 00577 exists(const String& path) 00578 { 00579 if (g_fileSystemMockObject) 00580 { 00581 return g_fileSystemMockObject->exists(path); 00582 } 00583 return _ACCESS(path.c_str(), F_OK) == 0; 00584 } 00585 00587 #ifndef BLOCXX_WIN32 00588 bool 00589 isExecutable(const String& path) 00590 { 00591 if (g_fileSystemMockObject) 00592 { 00593 return g_fileSystemMockObject->isExecutable(path); 00594 } 00595 return _ACCESS(path.c_str(), X_OK) == 0; 00596 } 00597 #endif 00598 00600 bool 00601 canRead(const String& path) 00602 { 00603 if (g_fileSystemMockObject) 00604 { 00605 return g_fileSystemMockObject->canRead(path); 00606 } 00607 return _ACCESS(path.c_str(), R_OK) == 0; 00608 } 00610 bool 00611 canWrite(const String& path) 00612 { 00613 if (g_fileSystemMockObject) 00614 { 00615 return g_fileSystemMockObject->canWrite(path); 00616 } 00617 return _ACCESS(path.c_str(), W_OK) == 0; 00618 } 00620 #ifndef BLOCXX_WIN32 00621 bool 00622 isLink(const String& path) 00623 { 00624 if (g_fileSystemMockObject) 00625 { 00626 return g_fileSystemMockObject->isLink(path); 00627 } 00628 struct stat st; 00629 if (lstat(path.c_str(), &st) != 0) 00630 { 00631 return false; 00632 } 00633 return S_ISLNK(st.st_mode); 00634 } 00635 #endif 00636 00637 bool 00638 isDirectory(const String& path) 00639 { 00640 if (g_fileSystemMockObject) 00641 { 00642 return g_fileSystemMockObject->isDirectory(path); 00643 } 00644 #ifdef BLOCXX_WIN32 00645 struct _stat st; 00646 if (_stat(path.c_str(), &st) != 0) 00647 { 00648 return false; 00649 } 00650 return ((st.st_mode & _S_IFDIR) != 0); 00651 #else 00652 struct stat st; 00653 if (stat(path.c_str(), &st) != 0) 00654 { 00655 return false; 00656 } 00657 return S_ISDIR(st.st_mode); 00658 #endif 00659 } 00661 bool 00662 changeDirectory(const String& path) 00663 { 00664 if (g_fileSystemMockObject) 00665 { 00666 return g_fileSystemMockObject->changeDirectory(path); 00667 } 00668 return _CHDIR(path.c_str()) == 0; 00669 } 00671 bool 00672 makeDirectory(const String& path, int mode) 00673 { 00674 if (g_fileSystemMockObject) 00675 { 00676 return g_fileSystemMockObject->makeDirectory(path, mode); 00677 } 00678 return _MKDIR(path.c_str(), mode) == 0; 00679 } 00681 bool 00682 getFileSize(const String& path, Int64& size) 00683 { 00684 if (g_fileSystemMockObject) 00685 { 00686 return g_fileSystemMockObject->getFileSize(path, size); 00687 } 00688 #ifdef BLOCXX_WIN32 00689 struct _stat st; 00690 if (_stat(path.c_str(), &st) != 0) 00691 { 00692 return false; 00693 } 00694 #else 00695 struct stat st; 00696 if (stat(path.c_str(), &st) != 0) 00697 { 00698 return false; 00699 } 00700 #endif 00701 size = st.st_size; 00702 return true; 00703 } 00705 bool 00706 removeDirectory(const String& path) 00707 { 00708 if (g_fileSystemMockObject) 00709 { 00710 return g_fileSystemMockObject->removeDirectory(path); 00711 } 00712 return _RMDIR(path.c_str()) == 0; 00713 } 00715 bool 00716 removeFile(const String& path) 00717 { 00718 if (g_fileSystemMockObject) 00719 { 00720 return g_fileSystemMockObject->removeFile(path); 00721 } 00722 return _UNLINK(path.c_str()) == 0; 00723 } 00725 bool 00726 getDirectoryContents(const String& path, 00727 StringArray& dirEntries) 00728 { 00729 if (g_fileSystemMockObject) 00730 { 00731 return g_fileSystemMockObject->getDirectoryContents(path, dirEntries); 00732 } 00733 static Mutex readdirGuard; 00734 MutexLock lock(readdirGuard); 00735 00736 #ifdef BLOCXX_WIN32 00737 struct _finddata_t dentry; 00738 long hFile; 00739 String _path = path; 00740 00741 // Find first directory entry 00742 if (!_path.endsWith(BLOCXX_FILENAME_SEPARATOR)) 00743 { 00744 _path += BLOCXX_FILENAME_SEPARATOR; 00745 } 00746 _path += "*"; 00747 if ((hFile = _findfirst( _path.c_str(), &dentry)) == -1L) 00748 { 00749 return false; 00750 } 00751 dirEntries.clear(); 00752 while (_findnext(hFile, &dentry) == 0) 00753 { 00754 dirEntries.append(String(dentry.name)); 00755 } 00756 _findclose(hFile); 00757 #else 00758 DIR* dp(0); 00759 struct dirent* dentry(0); 00760 if ((dp = opendir(path.c_str())) == NULL) 00761 { 00762 return false; 00763 } 00764 dirEntries.clear(); 00765 while ((dentry = readdir(dp)) != NULL) 00766 { 00767 dirEntries.append(String(dentry->d_name)); 00768 } 00769 closedir(dp); 00770 #endif 00771 return true; 00772 } 00774 bool 00775 renameFile(const String& oldFileName, 00776 const String& newFileName) 00777 { 00778 if (g_fileSystemMockObject) 00779 { 00780 return g_fileSystemMockObject->renameFile(oldFileName, newFileName); 00781 } 00782 return ::rename(oldFileName.c_str(), newFileName.c_str()) == 0; 00783 } 00785 size_t 00786 read(const FileHandle& hdl, void* bfr, size_t numberOfBytes, 00787 Int64 offset) 00788 { 00789 if (g_fileSystemMockObject) 00790 { 00791 return g_fileSystemMockObject->read(hdl, bfr, numberOfBytes, offset); 00792 } 00793 #ifdef BLOCXX_WIN32 00794 OVERLAPPED ov = { 0, 0, 0, 0, NULL }; 00795 OVERLAPPED *pov = NULL; 00796 if(offset != -1L) 00797 { 00798 ov.Offset = (DWORD) offset; 00799 // check for truncation 00800 if (ov.Offset != offset) 00801 { 00802 BLOCXX_THROW(FileSystemException, "offset out of range"); 00803 } 00804 pov = &ov; 00805 } 00806 00807 DWORD bytesRead; 00808 size_t cc = (size_t)-1; 00809 if(::ReadFile(hdl, bfr, (DWORD)numberOfBytes, &bytesRead, pov)) 00810 { 00811 cc = (size_t)bytesRead; 00812 } 00813 00814 return cc; 00815 #else 00816 if (offset != -1L) 00817 { 00818 ::off_t offset2 = static_cast< ::off_t>(offset); 00819 // check for truncation 00820 if (offset2 != offset) 00821 { 00822 BLOCXX_THROW(FileSystemException, "offset out of range"); 00823 } 00824 00825 ::lseek(hdl, offset2, SEEK_SET); 00826 } 00827 return ::read(hdl, bfr, numberOfBytes); 00828 #endif 00829 } 00831 size_t 00832 write(FileHandle hdl, const void* bfr, size_t numberOfBytes, 00833 Int64 offset) 00834 { 00835 if (g_fileSystemMockObject) 00836 { 00837 return g_fileSystemMockObject->write(hdl, bfr, numberOfBytes, offset); 00838 } 00839 #ifdef BLOCXX_WIN32 00840 OVERLAPPED ov = { 0, 0, 0, 0, NULL }; 00841 OVERLAPPED *pov = NULL; 00842 if(offset != -1L) 00843 { 00844 ov.Offset = (DWORD) offset; 00845 // check for truncation 00846 if (ov.Offset != offset) 00847 { 00848 BLOCXX_THROW(FileSystemException, "offset out of range"); 00849 } 00850 pov = &ov; 00851 } 00852 00853 DWORD bytesWritten; 00854 size_t cc = (size_t)-1; 00855 if(::WriteFile(hdl, bfr, (DWORD)numberOfBytes, &bytesWritten, pov)) 00856 { 00857 cc = (size_t)bytesWritten; 00858 } 00859 return cc; 00860 #else 00861 00862 if (offset != -1L) 00863 { 00864 ::off_t offset2 = static_cast< ::off_t>(offset); 00865 // check for truncation 00866 if (offset2 != offset) 00867 { 00868 BLOCXX_THROW(FileSystemException, "offset out of range"); 00869 } 00870 ::lseek(hdl, offset2, SEEK_SET); 00871 } 00872 return ::write(hdl, bfr, numberOfBytes); 00873 #endif 00874 } 00875 00877 Int64 00878 seek(const FileHandle& hdl, Int64 offset, int whence) 00879 { 00880 if (g_fileSystemMockObject) 00881 { 00882 return g_fileSystemMockObject->seek(hdl, offset, whence); 00883 } 00884 #ifdef BLOCXX_WIN32 00885 DWORD moveMethod; 00886 switch(whence) 00887 { 00888 case SEEK_END: moveMethod = FILE_END; break; 00889 case SEEK_CUR: moveMethod = FILE_CURRENT; break; 00890 default: moveMethod = FILE_BEGIN; break; 00891 } 00892 00893 LARGE_INTEGER li; 00894 li.QuadPart = offset; 00895 li.LowPart = SetFilePointer(hdl, li.LowPart, &li.HighPart, moveMethod); 00896 00897 if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) 00898 { 00899 li.QuadPart = -1; 00900 } 00901 00902 return li.QuadPart; 00903 #else 00904 ::off_t offset2 = static_cast< ::off_t>(offset); 00905 // check for truncation 00906 if (offset2 != offset) 00907 { 00908 BLOCXX_THROW(FileSystemException, "offset out of range"); 00909 } 00910 return ::lseek(hdl, offset2, whence); 00911 #endif 00912 } 00914 Int64 00915 tell(const FileHandle& hdl) 00916 { 00917 if (g_fileSystemMockObject) 00918 { 00919 return g_fileSystemMockObject->tell(hdl); 00920 } 00921 #ifdef BLOCXX_WIN32 00922 LARGE_INTEGER li; 00923 li.QuadPart = 0; 00924 li.LowPart = SetFilePointer(hdl, li.LowPart, &li.HighPart, FILE_CURRENT); 00925 00926 if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) 00927 { 00928 li.QuadPart = -1; 00929 } 00930 00931 return li.QuadPart; 00932 #else 00933 return ::lseek(hdl, 0, SEEK_CUR); 00934 #endif 00935 } 00937 UInt64 fileSize(FileHandle fh) 00938 { 00939 if (g_fileSystemMockObject) 00940 { 00941 return g_fileSystemMockObject->fileSize(fh); 00942 } 00943 00944 #ifndef BLOCXX_WIN32 00945 00946 struct stat st; 00947 int rc = ::fstat(fh, &st); 00948 if (rc != 0) 00949 { 00950 BLOCXX_THROW_ERRNO_MSG(FileSystemException, "Could not stat file handle: "); 00951 } 00952 return st.st_size; 00953 00954 #else 00955 LARGE_INTEGER FileSize; 00956 BOOL rc = GetFileSizeEx(fh, &FileSize); 00957 if(!rc) 00958 { 00959 BLOCXX_THROW_ERRNO_MSG(FileSystemException, "Could not GetFileSizeEx() for file handle: "); 00960 } 00961 00962 UInt64 tmp = FileSize.QuadPart; 00963 return tmp; 00964 00965 #endif 00966 } 00968 void 00969 rewind(const FileHandle& hdl) 00970 { 00971 if (g_fileSystemMockObject) 00972 { 00973 return g_fileSystemMockObject->rewind(hdl); 00974 } 00975 #ifdef BLOCXX_WIN32 00976 ::SetFilePointer(hdl, 0L, NULL, FILE_BEGIN); 00977 #else 00978 ::lseek(hdl, 0, SEEK_SET); 00979 #endif 00980 } 00982 int 00983 close(const FileHandle& hdl) 00984 { 00985 if (g_fileSystemMockObject) 00986 { 00987 return g_fileSystemMockObject->close(hdl); 00988 } 00989 #ifdef BLOCXX_WIN32 00990 return (::CloseHandle(hdl)) ? 0 : -1; 00991 #else 00992 return ::close(hdl); 00993 #endif 00994 } 00996 int 00997 flush(FileHandle& hdl) 00998 { 00999 if (g_fileSystemMockObject) 01000 { 01001 return g_fileSystemMockObject->flush(hdl); 01002 } 01003 #ifdef BLOCXX_WIN32 01004 return (::FlushFileBuffers(hdl)) ? 0 : -1; 01005 #else 01006 return ::fsync(hdl); 01007 #endif 01008 } 01010 String getFileContents(const String& filename) 01011 { 01012 if (g_fileSystemMockObject) 01013 { 01014 return g_fileSystemMockObject->getFileContents(filename); 01015 } 01016 std::ifstream in(filename.c_str()); 01017 if (!in) 01018 { 01019 BLOCXX_THROW(FileSystemException, Format("Failed to open file %1", filename).c_str()); 01020 } 01021 OStringStream ss; 01022 ss << in.rdbuf(); 01023 return ss.toString(); 01024 } 01025 01027 StringArray getFileLines(const String& filename) 01028 { 01029 if (g_fileSystemMockObject) 01030 { 01031 return g_fileSystemMockObject->getFileLines(filename); 01032 } 01033 return getFileContents(filename).tokenize("\r\n"); 01034 } 01035 01037 String readSymbolicLink(const String& path) 01038 { 01039 if (g_fileSystemMockObject) 01040 { 01041 return g_fileSystemMockObject->readSymbolicLink(path); 01042 } 01043 #ifdef BLOCXX_WIN32 01044 return Path::realPath(path); 01045 #else 01046 std::vector<char> buf(MAXPATHLEN + 1); 01047 int rc; 01048 while (true) 01049 { 01050 rc = ::readlink(path.c_str(), &buf[0], buf.size()); 01051 // If the link value is too big to fit into buf, but 01052 // there is no other error, then rc == buf.size(); in particular, 01053 // we do NOT get rc < 0 with errno == ENAMETOOLONG (this indicates 01054 // a problem with the input path, not the link value returned). 01055 if (rc < 0) 01056 { 01057 BLOCXX_THROW_ERRNO_MSG(FileSystemException, path); 01058 } 01059 else if (static_cast<unsigned>(rc) == buf.size()) 01060 { 01061 buf.resize(buf.size() * 2); 01062 } 01063 else 01064 { 01065 buf.resize(rc); 01066 buf.push_back('\0'); 01067 return String(&buf[0]); 01068 } 01069 } 01070 #endif 01071 // Not reachable. 01072 return String(); 01073 } 01074 01076 namespace Path 01077 { 01078 01080 String realPath(const String& path) 01081 { 01082 if (g_fileSystemMockObject) 01083 { 01084 return g_fileSystemMockObject->realPath(path); 01085 } 01086 #ifdef BLOCXX_WIN32 01087 char c, *bfr, *pname; 01088 const char *pathcstr; 01089 DWORD cc; 01090 01091 pathcstr = path.c_str(); 01092 while (*pathcstr == '/' || *pathcstr == '\\') 01093 { 01094 ++pathcstr; 01095 } 01096 01097 // if we ate some '\' or '/' chars, the back up to 01098 // allow for 1 01099 if(pathcstr != path.c_str()) 01100 { 01101 --pathcstr; 01102 } 01103 01104 cc = GetFullPathName(path.c_str(), 1, &c, &pname); 01105 if(!cc) 01106 { 01107 BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str()); 01108 } 01109 bfr = new char[cc]; 01110 cc = GetFullPathName(path.c_str(), cc, bfr, &pname); 01111 if(!cc) 01112 { 01113 delete [] bfr; 01114 BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str()); 01115 } 01116 String rstr(bfr); 01117 delete [] bfr; 01118 return rstr; 01119 #else 01120 if (path.startsWith("/")) 01121 { 01122 return security(path, 0).second; 01123 } 01124 else 01125 { 01126 return security(getCurrentWorkingDirectory(), path, 0).second; 01127 } 01128 #endif 01129 } 01130 01132 String dirname(const String& filename) 01133 { 01134 if (g_fileSystemMockObject) 01135 { 01136 return g_fileSystemMockObject->dirname(filename); 01137 } 01138 // skip over trailing slashes 01139 if (filename.length() == 0) 01140 { 01141 return "."; 01142 } 01143 size_t lastSlash = filename.length() - 1; 01144 while (lastSlash > 0 01145 && filename[lastSlash] == BLOCXX_FILENAME_SEPARATOR_C) 01146 { 01147 --lastSlash; 01148 } 01149 01150 lastSlash = filename.lastIndexOf(BLOCXX_FILENAME_SEPARATOR_C, lastSlash); 01151 01152 if (lastSlash == String::npos) 01153 { 01154 return "."; 01155 } 01156 01157 while (lastSlash > 0 && filename[lastSlash - 1] == BLOCXX_FILENAME_SEPARATOR_C) 01158 { 01159 --lastSlash; 01160 } 01161 01162 if (lastSlash == 0) 01163 { 01164 return BLOCXX_FILENAME_SEPARATOR; 01165 } 01166 01167 return filename.substring(0, lastSlash); 01168 } 01169 01171 String basename(const String& filename) 01172 { 01173 if (g_fileSystemMockObject) 01174 { 01175 return g_fileSystemMockObject->basename(filename); 01176 } 01177 if (filename.length() == 0) 01178 { 01179 return filename; 01180 } 01181 size_t end = filename.length() -1; 01182 while (end > 0 01183 && filename[end] == BLOCXX_FILENAME_SEPARATOR_C) 01184 { 01185 --end; 01186 } 01187 if (end == 0 && filename[0] == BLOCXX_FILENAME_SEPARATOR_C) 01188 { 01189 return BLOCXX_FILENAME_SEPARATOR; 01190 } 01191 if (end == filename.length() -1) 01192 { 01193 end = String::npos; 01194 } 01195 size_t beg = filename.lastIndexOf(BLOCXX_FILENAME_SEPARATOR_C, end); 01196 if (beg == String::npos) 01197 { 01198 beg = 0; 01199 } 01200 else 01201 { 01202 ++beg; 01203 } 01204 size_t len = end == String::npos? end : ++end - beg; 01205 return filename.substring(beg, len); 01206 } 01207 01209 String getCurrentWorkingDirectory() 01210 { 01211 if (g_fileSystemMockObject) 01212 { 01213 return g_fileSystemMockObject->getCurrentWorkingDirectory(); 01214 } 01215 std::vector<char> buf(MAXPATHLEN); 01216 char* p; 01217 do 01218 { 01219 p = ::getcwd(&buf[0], buf.size()); 01220 if (p != 0) 01221 { 01222 return p; 01223 } 01224 buf.resize(buf.size() * 2); 01225 } while (p == 0 && errno == ERANGE); 01226 01227 BLOCXX_THROW_ERRNO(FileSystemException); 01228 } 01229 01230 } // end namespace Path 01231 } // end namespace FileSystem 01232 } // end namespace BLOCXX_NAMESPACE 01233