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 
00175 #ifndef BLOCXX_WIN32
00176 bool
00177 isExecutable(const String& path)
00178 {
00179    return _ACCESS(path.c_str(), X_OK) == 0;
00180 }
00181 #endif
00182 
00184 bool
00185 canRead(const String& path)
00186 {
00187    return _ACCESS(path.c_str(), R_OK) == 0;
00188 }
00190 bool
00191 canWrite(const String& path)
00192 {
00193    return _ACCESS(path.c_str(), W_OK) == 0;
00194 }
00196 #ifndef BLOCXX_WIN32
00197 bool
00198 isLink(const String& path)
00199 {
00200    struct stat st;
00201    if ( ::lstat(path.c_str(), &st) != 0)
00202    {
00203       return false;
00204    }
00205    return S_ISLNK(st.st_mode);
00206 }
00207 #endif
00208 
00209 bool
00210 isDirectory(const String& path)
00211 {
00212 #ifdef BLOCXX_WIN32
00213    struct _stat st;
00214    if (_stat(path.c_str(), &st) != 0)
00215    {
00216       return false;
00217    }
00218    return ((st.st_mode & _S_IFDIR) != 0);
00219 #else
00220    struct stat st;
00221    if ( ::stat(path.c_str(), &st) != 0)
00222    {
00223       return false;
00224    }
00225    return S_ISDIR(st.st_mode);
00226 #endif
00227 }
00229 bool
00230 changeDirectory(const String& path)
00231 {
00232    return _CHDIR(path.c_str()) == 0;
00233 }
00235 bool
00236 makeDirectory(const String& path, int mode)
00237 {
00238    return _MKDIR(path.c_str(), mode) == 0;
00239 }
00241 bool
00242 getFileSize(const String& path, off_t& size)
00243 {
00244 #ifdef BLOCXX_WIN32
00245    struct _stat st;
00246    if (_stat(path.c_str(), &st) != 0)
00247    {
00248       return false;
00249    }
00250 #else
00251    struct stat st;
00252    if (::stat(path.c_str(), &st) != 0)
00253    {
00254       return false;
00255    }
00256 #endif
00257    size = st.st_size;
00258    return true;
00259 }
00261 bool
00262 removeDirectory(const String& path)
00263 {
00264    return _RMDIR(path.c_str()) == 0;
00265 }
00267 bool
00268 removeFile(const String& path)
00269 {
00270    return _UNLINK(path.c_str()) == 0;
00271 }
00273 bool
00274 getDirectoryContents(const String& path,
00275    StringArray& dirEntries)
00276 {
00277    static Mutex readdirGuard;
00278    MutexLock lock(readdirGuard);
00279 
00280 #ifdef BLOCXX_WIN32
00281    struct _finddata_t dentry;
00282    long hFile;
00283    String _path = path;
00284 
00285    // Find first directory entry
00286    if (!_path.endsWith(BLOCXX_FILENAME_SEPARATOR))
00287    {
00288       _path += BLOCXX_FILENAME_SEPARATOR;
00289    }
00290    _path += "*";
00291     if ((hFile = _findfirst( _path.c_str(), &dentry)) == -1L)
00292    {
00293       return false;
00294    }
00295    dirEntries.clear();
00296    while (_findnext(hFile, &dentry) == 0)
00297    {
00298       dirEntries.append(String(dentry.name));
00299    }
00300    _findclose(hFile);
00301 #else
00302    DIR* dp(0);
00303    struct dirent* dentry(0);
00304    if ((dp = opendir(path.c_str())) == NULL)
00305    {
00306       return false;
00307    }
00308    dirEntries.clear();
00309    while ((dentry = readdir(dp)) != NULL)
00310    {
00311       dirEntries.append(String(dentry->d_name));
00312    }
00313    closedir(dp);
00314 #endif
00315    return true;
00316 }
00318 bool
00319 renameFile(const String& oldFileName,
00320    const String& newFileName)
00321 {
00322    return ::rename(oldFileName.c_str(), newFileName.c_str()) == 0;
00323 }
00325 size_t
00326 read(const FileHandle& hdl, void* bfr, size_t numberOfBytes,
00327    off_t offset)
00328 {
00329 #ifdef BLOCXX_WIN32
00330    OVERLAPPED ov = { 0, 0, 0, 0, NULL };
00331    OVERLAPPED *pov = NULL;
00332    if(offset != -1L)
00333    {
00334       ov.Offset = (DWORD) offset;
00335       pov = &ov;
00336    }
00337 
00338    DWORD bytesRead;
00339    size_t cc = (size_t)-1;
00340    if(::ReadFile(hdl, bfr, (DWORD)numberOfBytes, &bytesRead, pov))
00341    {
00342       cc = (size_t)bytesRead;
00343    }
00344       
00345    return cc;
00346 #else
00347    if (offset != -1L)
00348    {
00349       ::lseek(hdl, offset, SEEK_SET);
00350    }
00351    return ::read(hdl, bfr, numberOfBytes);
00352 #endif
00353 }
00355 size_t
00356 write(FileHandle& hdl, const void* bfr, size_t numberOfBytes,
00357    off_t offset)
00358 {
00359 #ifdef BLOCXX_WIN32
00360    OVERLAPPED ov = { 0, 0, 0, 0, NULL };
00361    OVERLAPPED *pov = NULL;
00362    if(offset != -1L)
00363    {
00364       ov.Offset = (DWORD) offset;
00365       pov = &ov;
00366    }
00367 
00368    DWORD bytesWritten;
00369    size_t cc = (size_t)-1;
00370    if(::WriteFile(hdl, bfr, (DWORD)numberOfBytes, &bytesWritten, pov))
00371    {
00372       cc = (size_t)bytesWritten;
00373    }
00374    return cc;
00375 #else
00376 
00377    if (offset != -1L)
00378    {
00379       ::lseek(hdl, offset, SEEK_SET);
00380    }
00381    return ::write(hdl, bfr, numberOfBytes);
00382 #endif
00383 }
00385 off_t
00386 seek(const FileHandle& hdl, off_t offset, int whence)
00387 {
00388 #ifdef BLOCXX_WIN32
00389    DWORD moveMethod;
00390    switch(whence)
00391    {
00392       case SEEK_END: moveMethod = FILE_END; break;
00393       case SEEK_CUR: moveMethod = FILE_CURRENT; break;
00394       default: moveMethod = FILE_BEGIN; break;
00395    }
00396    return (off_t) ::SetFilePointer(hdl, (LONG)offset, NULL, moveMethod);
00397 #else
00398    return ::lseek(hdl, offset, whence);
00399 #endif
00400 }
00402 off_t
00403 tell(const FileHandle& hdl)
00404 {
00405 #ifdef BLOCXX_WIN32
00406    return (off_t) ::SetFilePointer(hdl, 0L, NULL, FILE_CURRENT);
00407 #else
00408    return ::lseek(hdl, 0, SEEK_CUR);
00409 #endif
00410 }
00412 void
00413 rewind(const FileHandle& hdl)
00414 {
00415 #ifdef BLOCXX_WIN32
00416    ::SetFilePointer(hdl, 0L, NULL, FILE_BEGIN);
00417 #else
00418    ::lseek(hdl, 0, SEEK_SET);
00419 #endif
00420 }
00422 int
00423 close(const FileHandle& hdl)
00424 {
00425 #ifdef BLOCXX_WIN32
00426    return (::CloseHandle(hdl)) ? 0 : -1;
00427 #else
00428    return ::close(hdl);
00429 #endif
00430 }
00432 int
00433 flush(FileHandle& hdl)
00434 {
00435 #ifdef BLOCXX_WIN32
00436    return (::FlushFileBuffers(hdl)) ? 0 : -1;
00437 #else
00438    #ifdef BLOCXX_DARWIN
00439       return ::fsync(hdl);
00440    #else
00441       return 0;
00442    #endif
00443 #endif
00444 }
00446 void
00447 initRandomFile(const String& filename)
00448 {
00449 #ifdef BLOCXX_WIN32
00450    char bfr[1024];
00451    RandomNumber rnum(0, 0xFF);
00452    for (size_t i = 0; i < 1024; ++i)
00453    {
00454       bfr[i] = (char)rnum.getNextNumber();
00455    }
00456    HANDLE fh = ::CreateFile(filename.c_str(), GENERIC_WRITE,
00457       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
00458       FILE_ATTRIBUTE_NORMAL, NULL);
00459    if(fh == INVALID_HANDLE_VALUE)
00460    {
00461       BLOCXX_THROW(FileSystemException,
00462          Format("Can't open random file %1 for writing",
00463          filename).c_str());
00464    }
00465    DWORD bytesWritten;
00466    size_t cc = (size_t)-1;
00467    bool success = (::WriteFile(fh, bfr, (DWORD)1024, &bytesWritten, NULL) != 0);
00468    ::CloseHandle(fh);
00469    if(!success || bytesWritten < 1024)
00470    {
00471       BLOCXX_THROW(FileSystemException,
00472          Format("Failed writing data to random file %1", filename).c_str());
00473    }
00474 #else
00475    int hdl = ::open(filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0600);
00476    if (hdl == -1)
00477    {
00478       BLOCXX_THROW(FileSystemException, Format("Can't open random file %1 for writing", filename).c_str());
00479    }
00480    RandomNumber rnum(0, 0xFF);
00481    for (size_t i = 0; i < 1024; ++i)
00482    {
00483       char c = rnum.getNextNumber();
00484 		::write(hdl, &c, 1);
00485    }
00486 	::close(hdl);
00487 #endif
00488 }
00489 
00491 String getFileContents(const String& filename)
00492 {
00493    std::ifstream in(filename.c_str());
00494    if (!in)
00495    {
00496       BLOCXX_THROW(FileSystemException, Format("Failed to open file %1", filename).c_str());
00497    }
00498    OStringStream ss;
00499    ss << in.rdbuf();
00500    return ss.toString();
00501 }
00502 
00504 StringArray getFileLines(const String& filename)
00505 {
00506    return getFileContents(filename).tokenize("\r\n");
00507 }
00508 
00510 String readSymbolicLink(const String& path)
00511 {
00512 #ifdef BLOCXX_WIN32
00513    return Path::realPath(path);
00514 #else
00515    std::vector<char> buf(MAXPATHLEN);
00516    int rc;
00517    do
00518    {
00519       rc = ::readlink(path.c_str(), &buf[0], buf.size());
00520       if (rc >= 0)
00521       {
00522          buf.resize(rc);
00523          buf.push_back('\0');
00524          return String(&buf[0]);
00525       }
00526       buf.resize(buf.size() * 2);
00527    } while (rc < 0 && errno == ENAMETOOLONG);
00528    BLOCXX_THROW_ERRNO(FileSystemException);
00529 #endif
00530 }
00531 
00533 namespace Path
00534 {
00535 
00537 String realPath(const String& path)
00538 {
00539 #ifdef BLOCXX_WIN32
00540    char c, *bfr, *pname;
00541    const char *pathcstr;
00542    DWORD cc;
00543 
00544    pathcstr = path.c_str();
00545    while (*pathcstr == '/' || *pathcstr == '\\')
00546    {
00547       ++pathcstr;
00548    }
00549 
00550    // if we ate some '\' or '/' chars, the back up to
00551    // allow for 1
00552    if(pathcstr != path.c_str())
00553    {
00554       --pathcstr;
00555    }
00556       
00557    cc = GetFullPathName(path.c_str(), 1, &c, &pname);
00558    if(!cc)
00559    {
00560       BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
00561    }
00562    bfr = new char[cc];
00563    cc = GetFullPathName(path.c_str(), cc, bfr, &pname);
00564    if(!cc)
00565    {
00566       delete [] bfr;
00567       BLOCXX_THROW(FileSystemException, Format("Can't get full path name for path %s", path).c_str());
00568    }
00569    String rstr(bfr);
00570    delete [] bfr;
00571    return rstr;
00572 #else
00573    String workingPath(path);
00574    String resolvedPath;
00575    int numLinks = 0;
00576 
00577    // handle relative paths.
00578    if (workingPath.length() > 0 && workingPath[0] != '/')
00579    {
00580       // result of getCurrentWorkingDirectory is already resolved.
00581       resolvedPath = getCurrentWorkingDirectory();
00582    }
00583 
00584    const char* pathCompBegin(workingPath.c_str());
00585    const char* pathCompEnd(pathCompBegin);
00586    while (*pathCompBegin != '\0')
00587    {
00588       // skip bunches of ////
00589       while (*pathCompBegin == '/')
00590       {
00591          ++pathCompBegin;
00592       }
00593 
00594       // find end of the path component
00595       pathCompEnd = pathCompBegin;
00596       while (*pathCompEnd != '\0' && *pathCompEnd != '/')
00597       {
00598          ++pathCompEnd;
00599       }
00600 
00601       if (pathCompEnd - pathCompBegin == 0)
00602       {
00603          break;
00604       }
00605       else if (pathCompEnd - pathCompBegin == 1 && pathCompBegin[0] == '.')
00606       {
00607          ;// don't add . to the result
00608       }
00609       else if (pathCompEnd - pathCompBegin == 2 && pathCompBegin[0] == '.' && pathCompBegin[1] == '.')
00610       {
00611          // hit .. so remove the last directory from the result
00612          size_t lastSlash = resolvedPath.lastIndexOf('/');
00613          if (lastSlash != String::npos)
00614          {
00615             resolvedPath.erase(lastSlash);
00616          }
00617       }
00618       else
00619       {
00620          resolvedPath += '/';
00621          resolvedPath += String(pathCompBegin, pathCompEnd - pathCompBegin);
00622 
00623          // now check the path actually exists
00624          struct stat pathStats;
00625 #ifdef BLOCXX_NETWARE
00626          if (::stat(resolvedPath.c_str(), &pathStats) < 0)
00627          {
00628             BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00629          }
00630 #else
00631          if (::lstat(resolvedPath.c_str(), &pathStats) < 0)
00632          {
00633             BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00634          }
00635          if (S_ISLNK(pathStats.st_mode))
00636          {
00637             ++numLinks;
00638             if (numLinks > MAXSYMLINKS)
00639             {
00640                errno = ELOOP;
00641                BLOCXX_THROW_ERRNO_MSG(FileSystemException, resolvedPath);
00642             }
00643             String linkTarget(readSymbolicLink(resolvedPath));
00644 
00645             if (linkTarget.length() > 0 && linkTarget[0] != '/')
00646             {
00647                // relative link. Remove the link from the resolvedPath and add the linkTarget
00648                BLOCXX_ASSERT(resolvedPath.lastIndexOf('/') != String::npos); // should always happen, we just added a / to the string
00649                resolvedPath.erase(resolvedPath.lastIndexOf('/'));
00650 
00651                resolvedPath += '/';
00652                resolvedPath += linkTarget;
00653             }
00654             else
00655             {
00656                // absolute link
00657                resolvedPath = linkTarget;
00658             }
00659 
00660             // now reset and start over on the new path
00661             resolvedPath += pathCompEnd;
00662             workingPath = resolvedPath;
00663             pathCompBegin = pathCompEnd = workingPath.c_str();
00664             resolvedPath.erase();
00665          }
00666 #endif
00667       }
00668 
00669       // keep the loop flowing
00670       pathCompBegin = pathCompEnd;
00671    }
00672 
00673    if (resolvedPath.empty())
00674    {
00675       resolvedPath = "/";
00676    }
00677 
00678    return resolvedPath;
00679 #endif
00680 }
00681 
00683 String dirname(const String& filename)
00684 {
00685    // skip over trailing slashes
00686    size_t lastSlash = filename.length() - 1;
00687    while (lastSlash > 0 
00688       && filename[lastSlash] == BLOCXX_FILENAME_SEPARATOR_C)
00689    {
00690       --lastSlash;
00691    }
00692    
00693    lastSlash = filename.lastIndexOf(BLOCXX_FILENAME_SEPARATOR_C, lastSlash);
00694 
00695    if (lastSlash == String::npos)
00696    {
00697       return ".";
00698    }
00699 
00700    while (lastSlash > 0 && filename[lastSlash - 1] == BLOCXX_FILENAME_SEPARATOR_C)
00701    {
00702       --lastSlash;
00703    }
00704 
00705    if (lastSlash == 0)
00706    {
00707       return BLOCXX_FILENAME_SEPARATOR;
00708    }
00709 
00710    return filename.substring(0, lastSlash);
00711 }
00712 
00714 String getCurrentWorkingDirectory()
00715 {
00716    std::vector<char> buf(MAXPATHLEN);
00717    char* p;
00718    do
00719    {
00720       p = ::getcwd(&buf[0], buf.size());
00721       if (p != 0)
00722       {
00723          return p;
00724       }
00725       buf.resize(buf.size() * 2);
00726    } while (p == 0 && errno == ERANGE);
00727 
00728    BLOCXX_THROW_ERRNO(FileSystemException);
00729 }
00730 
00731 } // end namespace Path
00732 } // end namespace FileSystem
00733 } // end namespace BLOCXX_NAMESPACE
00734 

Generated on Fri Jun 16 15:39:08 2006 for blocxx by  doxygen 1.4.6