blocxx

PosixFileSystem.cpp

Go to the documentation of this file.
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