src/File.cpp
author Jens Alfke <jens@mooseyard.com>
Tue Sep 29 15:46:42 2009 -0700 (2009-09-29)
changeset 9 629f61203db1
parent 8 21a6c17f4e3e
permissions -rw-r--r--
* Merged in jbm's changes for Linux compatibility, fixing Mac compatibility in the process :)
* Fixed two regressions having to do with the _previousTrailerPosition in VersionDictionary.cpp.
* Made sure RTTI is enabled in the OttomanTest target because gtest requires it.
     1 /*
     2  *  File.cpp
     3  *  Ottoman
     4  *
     5  *  Created by Jens Alfke on 8/20/09.
     6  *  Copyright 2009 Jens Alfke. All rights reserved.
     7  *  BSD-Licensed: See the file "LICENSE.txt" for details.
     8  */
     9 
    10 #include "File.h"
    11 #include "MemoryMap.h"
    12 
    13 #include <sys/file.h>
    14 #include <assert.h>
    15 #include <errno.h>
    16 #include <fcntl.h>
    17 #include <stdio.h>
    18 #include <sys/stat.h>
    19 #include <sys/uio.h>
    20 #include <unistd.h>
    21 
    22 namespace Mooseyard {
    23     
    24     File::File (const char *filename, int oflag, bool locked)  throw(Error)
    25 #ifdef _DARWIN_C_SOURCE
    26         :_fd(_check( ::open(filename, oflag | (locked ? O_EXLOCK : 0), 0644) )),
    27 #else
    28         :_fd(_check( ::open(filename, oflag, 0644) )),
    29 #endif
    30          _memoryMap(NULL),
    31          _locked( locked ? 1 : 0)
    32     {
    33 #ifndef _DARWIN_C_SOURCE
    34       _check( ::flock(_fd, LOCK_EX) );
    35 #endif
    36     }
    37 
    38     File::~File() {
    39         delete _memoryMap;
    40         _unlock();
    41         ::close(_fd);
    42     }
    43     
    44     
    45     off_t File::length() const  throw(Error) {
    46         struct stat s;
    47         _check( ::fstat(_fd,&s) );
    48         return s.st_size;
    49     }
    50      
    51     void File::setLength (off_t length)  throw(Error) {
    52         _check( ftruncate(_fd,length) );
    53     }
    54 
    55     off_t File::position() const  throw(Error) {
    56         off_t pos = lseek(_fd,0,SEEK_CUR);
    57         if (pos < 0)
    58             _check(-1);
    59         return pos;
    60     }
    61     
    62     void File::setPosition (off_t pos)  throw(Error) {
    63         _check( (int) lseek(_fd,pos,SEEK_SET) );
    64     }
    65     
    66     off_t File::setPositionToEnd (off_t bytesBefore)  throw(Error) {
    67         off_t pos = lseek(_fd,-bytesBefore,SEEK_END);
    68         if (pos < 0)
    69             _check(-1);
    70         return pos;
    71     }
    72     
    73     void File::read (void *dst, size_t size)  throw(Error) {
    74         _checkRead( ::read(_fd, dst, size), size );
    75     }
    76     
    77     size_t File::write (const void *src, size_t size)  throw(Error) {
    78         return _check( ::write(_fd, src, size) );
    79     }
    80     
    81     
    82     void File::readFrom (off_t where, void *dst, size_t size)  throw(Error) {
    83         _checkRead( ::pread(_fd, dst, size, where), size );
    84     }
    85     
    86     void File::writeTo (off_t where, const void *src, size_t size)  throw(Error) {
    87         _check( ::pwrite(_fd, src, size, where) );
    88     }
    89     
    90     size_t File::writeMultiple (Blob blobs[], int count) throw(Error) {
    91         struct iovec vec[count];
    92         for (int i=0; i<count; i++) {
    93             vec[i].iov_base = (void*) blobs[i].bytes;
    94             vec[i].iov_len = blobs[i].length;
    95         }
    96         return _check( ::writev(_fd, vec, count) );
    97     }
    98     
    99     size_t File::writePadding (int alignment) {
   100         int pad = alignment - (int)(position() & (alignment-1));
   101         if (pad == alignment)
   102             return 0;
   103         uint8_t zero[pad];
   104         memset(zero, 0, pad);
   105         return write(zero, pad);
   106     }
   107     
   108     
   109     void File::flush()  throw(Error) {
   110         _check( ::fsync(_fd) );
   111     }
   112     
   113     void File::flushDisk()  throw(Error) {
   114 #if _DARWIN_C_SOURCE
   115         /* F_FULLFSYNC is Mac/Darwin specific. From the man page:
   116          Does the same thing as fsync(2) then asks the drive to flush all buffered data to
   117          the permanent storage device (arg is ignored).  This is currently implemented on
   118          HFS, MS-DOS (FAT), and Universal Disk Format (UDF) file systems.  The operation may
   119          take quite a while to complete.  Certain FireWire drives have also been known to
   120          ignore the request to flush their buffered data. */
   121         _check( ::fcntl(_fd,F_FULLFSYNC) );
   122 #else
   123         _check( fsync(_fd) );
   124 #endif
   125     }
   126     
   127     bool File::hasPath (const char *path) const  throw(Error) {
   128         struct stat myStat, pathStat;
   129         _check( ::fstat(_fd, &myStat) );
   130         if ( ::stat(path, &pathStat) != 0 ) {
   131             if (errno == ENOENT)
   132                 return false;
   133             _check(errno);
   134         }
   135         // Compare my inode number with that of the file at path:
   136         return myStat.st_ino == pathStat.st_ino;
   137     }
   138     
   139     
   140     void File::unlink (const char *filename)  throw(Error) {
   141         _check( ::unlink(filename) );
   142     }
   143     
   144     void File::rename (const char *srcFilename, const char *dstFilename)  throw(Error) {
   145         _check( ::rename(srcFilename, dstFilename) );
   146     }
   147 
   148     
   149 #pragma mark -
   150 #pragma mark UTILITIES:
   151     
   152     int File::_check (int result)  throw(Error) {
   153         if (result >= 0)
   154             return result;
   155         //printf("*** File::_check: Error %i: %s\n", errno, strerror(errno));
   156         throw Error(errno, strerror(errno));
   157     }
   158     
   159     void File::_checkRead (ssize_t result, size_t expectedSize)  throw(Error) {
   160         if ((size_t)result < expectedSize) {
   161             if (result < 0)
   162                 throw Error(errno, strerror(errno));
   163             else
   164                 throw Error(kEOF, "unexpected EOF");
   165         }
   166     }
   167     
   168     File::Error::Error(const char *m)
   169         :code(ERANGE), 
   170          message(m) 
   171     { }
   172     
   173     
   174 #pragma mark -
   175 #pragma mark LOCKS:
   176     
   177     bool File::_lock (bool block) {
   178         if (_locked) {
   179             _locked++;
   180         } else {
   181             int mode = LOCK_EX;
   182             if (block)
   183                 mode |= LOCK_NB;
   184             if (::flock(_fd, mode) == 0)
   185                 _locked = 1;
   186             else if (errno != EWOULDBLOCK)          // may be returned in LOCK_UN mode
   187                 _check(-1);
   188         }
   189         return _locked > 0;
   190     }
   191     
   192     void File::_unlock() {
   193         if (_locked > 0) {
   194             _locked--;
   195             ::flock(_fd, LOCK_UN);
   196         }
   197     }
   198     
   199     File::Lock::Lock (File *file, bool block)  throw(Error)
   200         :_file(file),
   201          _locked( _file->_lock(block) )
   202     { }
   203     
   204     File::Lock::~Lock() {
   205         if (_locked)
   206             _file->_unlock();
   207     }
   208     
   209     bool File::Lock::retry() {
   210         if (!_locked)
   211             _locked = _file->_lock(false);
   212         return _locked;
   213     }
   214     
   215     
   216 #pragma mark -
   217 #pragma mark MEMORY MAP:
   218     
   219     MemoryMap* File::map() {
   220         if (!_memoryMap)
   221             _memoryMap = new MemoryMap(this);
   222         return _memoryMap;
   223     }
   224     
   225     void File::mapRegion (off_t position, size_t length) {
   226         return map()->mapRegion(position,length);
   227     }
   228     
   229     const void* File::mappedPosition (off_t position) const {
   230         return map()->mappedPosition(position);
   231     }
   232 
   233 }