Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

PosixFileSystem.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2004 Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2005 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 *
00011 *  - Redistributions in binary form must reproduce the above copyright notice,
00012 *    this list of conditions and the following disclaimer in the documentation
00013 *    and/or other materials provided with the distribution.
00014 *
00015 *  - Neither the name of Vintela, Inc., Novell, Inc., nor the names of its
00016 *    contributors may be used to endorse or promote products derived from this
00017 *    software without specific prior written permission.
00018 *
00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc., Novell, Inc., OR THE 
00023 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00024 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
00025 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
00026 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
00027 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
00028 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
00029 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 *******************************************************************************/
00031 
00032 
00038 #include "blocxx/BLOCXX_config.h"
00039 #include "blocxx/FileSystem.hpp"
00040 #include "blocxx/RandomNumber.hpp"
00041 #include "blocxx/Mutex.hpp"
00042 #include "blocxx/MutexLock.hpp"
00043 #include "blocxx/File.hpp"
00044 #include "blocxx/String.hpp"
00045 #include "blocxx/Array.hpp"
00046 #include "blocxx/Format.hpp"
00047 #include "blocxx/ExceptionIds.hpp"
00048 #include "blocxx/Assertion.hpp"
00049 
00050 extern "C"
00051 {
00052 #ifdef BLOCXX_WIN32
00053 
00054    #include <direct.h>
00055    #include <io.h>
00056    #include <share.h>
00057    
00058    #define _ACCESS ::_access
00059    #define R_OK 4
00060    #define F_OK 0
00061    #define W_OK 2
00062    #define _CHDIR _chdir
00063    #define _MKDIR(a,b)  _mkdir((a))
00064    #define _RMDIR _rmdir
00065    #define _UNLINK _unlink
00066 
00067 #else
00068 
00069    #ifdef BLOCXX_HAVE_UNISTD_H
00070    #include <unistd.h>
00071    #endif
00072    #ifdef BLOCXX_HAVE_DIRENT_H
00073    #include <dirent.h>
00074    #endif
00075    
00076    #define _ACCESS ::access
00077    #define _CHDIR chdir
00078    #define _MKDIR(a,b) mkdir((a),(b))
00079    #define _RMDIR rmdir
00080    #define _UNLINK unlink
00081 
00082 #ifdef BLOCXX_NETWARE
00083 #define MAXSYMLINKS 20
00084 #endif
00085 
00086 #endif
00087 
00088 #include <sys/stat.h>
00089 #include <sys/types.h>
00090 #include <fcntl.h>
00091 }
00092 
00093 #include <cstdio> // for rename
00094 #include <fstream>
00095 #include <cerrno>
00096 
00097 namespace BLOCXX_NAMESPACE
00098 {
00099 
00100 BLOCXX_DEFINE_EXCEPTION_WITH_ID(FileSystem);
00101 
00102 namespace FileSystem
00103 {
00104 
00106 // STATIC
00107 int
00108 changeFileOwner(const String& filename,
00109    const UserId& userId)
00110 {
00111 #ifdef BLOCXX_WIN32
00112    return 0;   // File ownership on Win32?
00113 #else
00114    return ::chown(filename.c_str(), userId, gid_t(-1));
00115 #endif
00116 }
00118 // STATIC
00119 File
00120 openFile(const String& path)
00121 {
00122 #ifdef BLOCXX_WIN32
00123    HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE,
00124       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
00125       FILE_ATTRIBUTE_NORMAL, NULL);
00126 
00127    return (fh  != INVALID_HANDLE_VALUE) ? File(fh) : File();
00128 #else
00129    return File(::open(path.c_str(), O_RDWR));
00130 #endif
00131 }
00133 // STATIC
00134 File
00135 createFile(const String& path)
00136 {
00137 #ifdef BLOCXX_WIN32
00138    HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE,
00139         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW,
00140       FILE_ATTRIBUTE_NORMAL, NULL);
00141    return (fh  != INVALID_HANDLE_VALUE) ? File(fh) : File();
00142 #else
00143    int fd = ::open(path.c_str(), O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0660);
00144    if (fd != -1)
00145    {
00146       return File(fd);
00147    }
00148    return File();
00149 #endif
00150 
00151 }
00153 // STATIC
00154 File
00155 openOrCreateFile(const String& path)
00156 {
00157 #ifdef BLOCXX_WIN32
00158    HANDLE fh = ::CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE,
00159       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
00160       FILE_ATTRIBUTE_NORMAL, NULL);
00161    return (fh  != INVALID_HANDLE_VALUE) ? File(fh) : File();
00162 #else
00163    return File(::open(path.c_str(), O_RDWR | O_CREAT, 0660));
00164 #endif
00165 }
00166 
00168 bool
00169 exists(const String& path)
00170 {
00171    return _ACCESS(path.c_str(), F_OK) == 0;
00172 }
00173 
00174 bool
00175 isExecutable(const String& path)
00176 {
00177    return _ACCESS(path.c_str(), X_OK) == 0;
00178 }
00179 
00181 bool
00182 canRead(const String& path)
00183 {
00184    return _ACCESS(path.c_str(), R_OK) == 0;
00185 }
00187 bool
00188 canWrite(const String& path)
00189 {
00190    return _ACCESS(path.c_str(), W_OK) == 0;
00191 }
00193 bool
00194 isLink(const String& path)
00195 {
00196 #ifdef BLOCXX_WIN32
00197    return false;
00198 /* This stuff does not compile (_S_IFLNK?)
00199    struct _stat st;
00200    if (_stat(path.c_str(), &st) !=0)
00201    {
00202       return false;
00203    }
00204    return ((st.st_mode & _S_IFLNK) != 0);
00205 */
00206 #else
00207    struct stat st;
00208    if ( ::lstat(path.c_str(), &st) != 0)
00209    {
00210       return false;
00211    }
00212    return S_ISLNK(st.st_mode);
00213 #endif
00214 }
00216 bool
00217 isDirectory(const String& path)
00218 {
00219 #ifdef BLOCXX_WIN32
00220    struct _stat st;
00221    if (_stat(path.c_str(), &st) != 0)
00222    {
00223       return false;
00224    }
00225    return ((st.st_mode & _S_IFDIR) != 0);
00226 #else
00227    struct stat st;
00228    if ( ::stat(path.c_str(), &st) != 0)
00229    {
00230       return false;
00231    }
00232    return S_ISDIR(st.st_mode);
00233 #endif
00234 }
00236 bool
00237 changeDirectory(const String& path)
00238 {
00239    return _CHDIR(path.c_str()) == 0;
00240 }
00242 bool
00243 makeDirectory(const String& path, int mode)
00244 {
00245    return _MKDIR(path.c_str(), mode) == 0;
00246 }
00248 bool
00249 getFileSize(const String& path, off_t& size)
00250 {
00251 #ifdef BLOCXX_WIN32
00252    struct _stat st;
00253    if (_stat(path.c_str(), &st) != 0)
00254    {
00255       return false;
00256    }
00257 #else
00258    struct stat st;
00259    if (::stat(path.c_str(), &st) != 0)
00260    {
00261       return false;
00262    }
00263 #endif
00264    size = st.st_size;
00265    return true;
00266 }
00268 bool
00269 removeDirectory(const String& path)
00270 {
00271    return _RMDIR(path.c_str()) == 0;
00272 }
00274 bool
00275 removeFile(const String& path)
00276 {
00277    return _UNLINK(path.c_str()) == 0;
00278 }
00280 bool
00281 getDirectoryContents(const String& path,
00282    StringArray& dirEntries)
00283 {
00284    static Mutex readdirGuard;
00285    MutexLock lock(readdirGuard);
00286 
00287 #ifdef BLOCXX_WIN32
00288    struct _finddata_t dentry;
00289    long hFile;
00290    String _path = path;
00291 
00292    // Find first directory entry
00293    if (!_path.endsWith(BLOCXX_FILENAME_SEPARATOR))
00294    {
00295       _path += BLOCXX_FILENAME_SEPARATOR;
00296    }
00297    _path += "*";
00298     if ((hFile = _findfirst( _path.c_str(), &dentry)) == -1L)
00299    {
00300       return false;
00301    }
00302    dirEntries.clear();
00303    while (_findnext(hFile, &dentry) == 0)
00304    {
00305       dirEntries.append(String(dentry.name));
00306    }
00307    _findclose(hFile);
00308 #else
00309    DIR* dp(0);
00310    struct dirent* dentry(0);
00311    if ((dp = opendir(path.c_str())) == NULL)
00312    {
00313       return false;
00314    }
00315    dirEntries.clear();
00316    while ((dentry = readdir(dp)) != NULL)
00317    {
00318       dirEntries.append(String(dentry->d_name));
00319    }
00320    closedir(dp);
00321 #endif
00322    return true;
00323 }
00325 bool
00326 renameFile(const String& oldFileName,
00327    const String& newFileName)
00328 {
00329    return ::rename(oldFileName.c_str(), newFileName.c_str()) == 0;
00330 }
00332 size_t
00333 read(const FileHandle& hdl, void* bfr, size_t numberOfBytes,
00334    off_t offset)
00335 {
00336 #ifdef BLOCXX_WIN32
00337    OVERLAPPED ov = { 0, 0, 0, 0, NULL };
00338    OVERLAPPED *pov = NULL;
00339    if(offset != -1L)
00340    {
00341       ov.Offset = (DWORD) offset;
00342       pov = &ov;
00343    }
00344 
00345    DWORD bytesRead;
00346    size_t cc = (size_t)-1;
00347    if(::ReadFile(hdl, bfr, (DWORD)numberOfBytes, &bytesRead, pov))
00348    {
00349       cc = (size_t)bytesRead;
00350    }
00351       
00352    return cc;
00353 #else
00354    if (offset != -1L)
00355    {
00356       ::lseek(hdl, offset, SEEK_SET);
00357    }
00358    return ::read(hdl, bfr, numberOfBytes);
00359 #endif
00360 }
00362 size_t
00363 write(FileHandle& hdl, const void* bfr, size_t numberOfBytes,
00364    off_t offset)
00365 {
00366 #ifdef BLOCXX_WIN32
00367    OVERLAPPED ov = { 0, 0, 0, 0, NULL };
00368    OVERLAPPED *pov = NULL;
00369    if(offset != -1L)
00370    {
00371       ov.Offset = (DWORD) offset;
00372       pov = &ov;
00373    }
00374 
00375    DWORD bytesWritten;
00376    size_t cc = (size_t)-1;
00377    if(::WriteFile(hdl, bfr, (DWORD)numberOfBytes, &bytesWritten, pov))
00378    {
00379       cc = (size_t)bytesWritten;
00380    }
00381    return cc;
00382 #else
00383 
00384    if (offset != -1L)
00385    {
00386       ::lseek(hdl, offset, SEEK_SET);
00387    }
00388    return ::write(hdl, bfr, numberOfBytes);
00389 #endif
00390 }
00392 off_t
00393 seek(const FileHandle& hdl, off_t offset, int whence)
00394 {
00395 #ifdef BLOCXX_WIN32
00396    DWORD moveMethod;
00397    switch(whence)
00398    {
00399       case SEEK_END: moveMethod = FILE_END; break;
00400       case SEEK_CUR: moveMethod = FILE_CURRENT; break;
00401       default: moveMethod = FILE_BEGIN; break;
00402    }
00403    return (off_t) ::SetFilePointer(hdl, (LONG)offset, NULL, moveMethod);
00404 #else
00405    return ::lseek(hdl, offset, whence);
00406 #endif
00407 }
00409 off_t
00410 tell(const FileHandle& hdl)
00411 {
00412 #ifdef BLOCXX_WIN32
00413    return (off_t) ::SetFilePointer(hdl, 0L, NULL, FILE_CURRENT);
00414 #else
00415    return ::lseek(hdl, 0, SEEK_CUR);
00416 #endif
00417 }
00419 void
00420 rewind(const FileHandle& hdl)
00421 {
00422 #ifdef BLOCXX_WIN32
00423    ::SetFilePointer(hdl, 0L, NULL, FILE_BEGIN);
00424 #else
00425    ::lseek(hdl, 0, SEEK_SET);
00426 #endif
00427 }
00429 int
00430 close(const FileHandle& hdl)
00431 {
00432 #ifdef BLOCXX_WIN32
00433    return (::CloseHandle(hdl)) ? 0 : -1;
00434 #else
00435    return ::close(hdl);
00436 #endif
00437 }
00439 int
00440 flush(FileHandle& hdl)
00441 {
00442 #ifdef BLOCXX_WIN32
00443    return (::FlushFileBuffers(hdl)) ? 0 : -1;
00444 #else
00445    #ifdef BLOCXX_DARWIN
00446       return ::fsync(hdl);
00447    #else
00448       return 0;
00449    #endif
00450 #endif
00451 }
00453 void
00454 initRandomFile(const String& filename)
00455 {
00456 #ifdef BLOCXX_WIN32
00457    char bfr[1024];
00458    RandomNumber rnum(0, 0xFF);
00459    for (size_t i = 0; i < 1024; ++i)
00460    {
00461       bfr[i] = (char)rnum.getNextNumber();
00462    }
00463    HANDLE fh = ::CreateFile(filename.c_str(), GENERIC_WRITE,
00464       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
00465       FILE_ATTRIBUTE_NORMAL, NULL);
00466    if(fh == INVALID_HANDLE_VALUE)
00467    {
00468       BLOCXX_THROW(FileSystemException,
00469          Format("Can't open random file %1 for writing",
00470          filename).c_str());
00471    }
00472    DWORD bytesWritten;
00473    size_t cc = (size_t)-1;
00474    bool success = (::WriteFile(fh, bfr, (DWORD)1024, &bytesWritten, NULL) != 0);
00475    ::CloseHandle(fh);
00476    if(!success || bytesWritten < 1024)
00477    {
00478       BLOCXX_THROW(FileSystemException,
00479          Format("Failed writing data to random file %1", filename).c_str());
00480    }
00481 #else
00482    int hdl = ::open(filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0600);
00483    if (hdl == -1)
00484    {
00485       BLOCXX_THROW(FileSystemException, Format("Can't open random file %1 for writing", filename).c_str());
00486    }
00487    RandomNumber rnum(0, 0xFF);
00488    for (size_t i = 0; i < 1024; ++i)
00489    {
00490       char c = rnum.getNextNumber();
00491 		::write(hdl, &c, 1);
00492    }
00493 	::close(hdl);
00494 #endif
00495 }
00496 
00498 String getFileContents(const String& filename)
00499 {
00500    std::ifstream in(filename.c_str());
00501    if (!in)
00502    {
00503       BLOCXX_THROW(FileSystemException, Format("Failed to open file %1", filename).c_str());
00504    }
00505    OStringStream ss;
00506    ss << in.rdbuf();
00507    return ss.toString();
00508 }
00509 
00511 StringArray getFileLines(const String& filename)
00512 {
00513    return getFileContents(filename).tokenize("\r\n");
00514 }
00515 
00517 String readSymbolicLink(const String& path)
00518 {
00519 #ifdef BLOCXX_WIN32
00520    return Path::realPath(path);
00521 #else
00522    std::vector<char> buf(MAXPATHLEN);
00523    int rc;
00524    do
00525    {
00526       rc = ::readlink(path.c_str(), &buf[0], buf.size());
00527       if (rc >= 0)
00528       {
00529          buf.resize(rc);
00530          buf.push_back('\0');
00531          return String(&buf[0]);
00532       }
00533       buf.resize(buf.size() * 2);
00534    } while (rc < 0 && errno == ENAMETOOLONG);
00535    BLOCXX_THROW_ERRNO(FileSystemException);
00536 #endif
00537 }
00538 
00540 namespace Path
00541 {
00542 
00544 String realPath(const String& path)
00545 {
00546 #ifdef BLOCXX_WIN32
00547    char c, *bfr, *pname;
00548    const char *pathcstr;
00549    DWORD cc;
00550 
00551    pathcstr = path.c_str();
00552    while (*pathcstr == '/' || *pathcstr == '\\')
00553    {
00554       ++pathcstr;
00555    }
00556 
00557    // if we ate some '\' or '/' chars, the back up to
00558    // allow for 1
00559    if(pathcstr != path.c_str())
00560    {
00561       --pathcstr;
00562    }
00563       
00564    cc = GetFullPathName(path.c_str(), 1, &c, &pname);
00565    if(!cc)
00566    {
00567       BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
00568    }
00569    bfr = new char[cc];
00570    cc = GetFullPathName(path.c_str(), cc, bfr, &pname);
00571    if(!cc)
00572    {
00573       delete [] bfr;
00574       BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
00575    }
00576    String rstr(bfr);
00577    delete [] bfr;
00578    return rstr;
00579 #else
00580    String workingPath(path);
00581    String resolvedPath;
00582    int numLinks = 0;
00583 
00584    // handle relative paths.
00585    if (workingPath.length() > 0 && workingPath[0] != '/')
00586    {
00587       // result of getCurrentWorkingDirectory is already resolved.
00588       resolvedPath = getCurrentWorkingDirectory();
00589    }
00590 
00591    const char* pathCompBegin(workingPath.c_str());
00592    const char* pathCompEnd(pathCompBegin);
00593    while (*pathCompBegin != '\0')
00594    {
00595       // skip bunches of ////
00596       while (*pathCompBegin == '/')
00597       {
00598          ++pathCompBegin;
00599       }
00600 
00601       // find end of the path component
00602       pathCompEnd = pathCompBegin;
00603       while (*pathCompEnd != '\0' && *pathCompEnd != '/')
00604       {
00605          ++pathCompEnd;
00606       }
00607 
00608       if (pathCompEnd - pathCompBegin == 0)
00609       {
00610          break;
00611       }
00612       else if (pathCompEnd - pathCompBegin == 1 && pathCompBegin[0] == '.')
00613       {
00614          ;// don't add . to the result
00615       }
00616       else if (pathCompEnd - pathCompBegin == 2 && pathCompBegin[0] == '.' && pathCompBegin[1] == '.')
00617       {
00618          // hit .. so remove the last directory from the result
00619          size_t lastSlash = resolvedPath.lastIndexOf('/');
00620          if (lastSlash != String::npos)
00621          {
00622             resolvedPath.erase(lastSlash);
00623          }
00624       }
00625       else
00626       {
00627          resolvedPath += '/';
00628          resolvedPath += String(pathCompBegin, pathCompEnd - pathCompBegin);
00629 
00630          // now check the path actually exists
00631          struct stat pathStats;
00632 #ifdef BLOCXX_NETWARE
00633          if (::stat(resolvedPath.c_str(), &pathStats) < 0)
00634          {
00635             BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00636          }
00637 #else
00638          if (::lstat(resolvedPath.c_str(), &pathStats) < 0)
00639          {
00640             BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00641          }
00642          if (S_ISLNK(pathStats.st_mode))
00643          {
00644             ++numLinks;
00645             if (numLinks > MAXSYMLINKS)
00646             {
00647                errno = ELOOP;
00648                BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00649             }
00650             String linkTarget(readSymbolicLink(resolvedPath));
00651 
00652             if (linkTarget.length() > 0 && linkTarget[0] != '/')
00653             {
00654                // relative link. Remove the link from the resolvedPath and add the linkTarget
00655                BLOCXX_ASSERT(resolvedPath.lastIndexOf('/') != String::npos); // should always happen, we just added a / to the string
00656                resolvedPath.erase(resolvedPath.lastIndexOf('/'));
00657 
00658                resolvedPath += '/';
00659                resolvedPath += linkTarget;
00660             }
00661             else
00662             {
00663                // absolute link
00664                resolvedPath = linkTarget;
00665             }
00666 
00667             // now reset and start over on the new path
00668             resolvedPath += pathCompEnd;
00669             workingPath = resolvedPath;
00670             pathCompBegin = pathCompEnd = workingPath.c_str();
00671             resolvedPath.erase();
00672          }
00673 #endif
00674       }
00675 
00676       // keep the loop flowing
00677       pathCompBegin = pathCompEnd;
00678    }
00679 
00680    if (resolvedPath.empty())
00681    {
00682       resolvedPath = "/";
00683    }
00684 
00685    return resolvedPath;
00686 #endif
00687 }
00688 
00690 String dirname(const String& filename)
00691 {
00692    // skip over trailing slashes
00693    size_t lastSlash = filename.length() - 1;
00694    while (lastSlash > 0 
00695       && filename[lastSlash] == BLOCXX_FILENAME_SEPARATOR_C)
00696    {
00697       --lastSlash;
00698    }
00699    
00700    lastSlash = filename.lastIndexOf(BLOCXX_FILENAME_SEPARATOR_C, lastSlash);
00701 
00702    if (lastSlash == String::npos)
00703    {
00704       return ".";
00705    }
00706 
00707    while (lastSlash > 0 && filename[lastSlash - 1] == BLOCXX_FILENAME_SEPARATOR_C)
00708    {
00709       --lastSlash;
00710    }
00711 
00712    if (lastSlash == 0)
00713    {
00714       return BLOCXX_FILENAME_SEPARATOR;
00715    }
00716 
00717    return filename.substring(0, lastSlash);
00718 }
00719 
00721 String getCurrentWorkingDirectory()
00722 {
00723    std::vector<char> buf(MAXPATHLEN);
00724    char* p;
00725    do
00726    {
00727       p = ::getcwd(&buf[0], buf.size());
00728       if (p != 0)
00729       {
00730          return p;
00731       }
00732       buf.resize(buf.size() * 2);
00733    } while (p == 0 && errno == ERANGE);
00734 
00735    BLOCXX_THROW_ERRNO(FileSystemException);
00736 }
00737 
00738 } // end namespace Path
00739 } // end namespace FileSystem
00740 } // end namespace BLOCXX_NAMESPACE
00741 

Generated on Mon Sep 12 23:56:36 2005 for blocxx by  doxygen 1.4.4