src/File.cpp
author jbm@humility
Mon Sep 28 23:39:08 2009 -0700 (2009-09-28)
changeset 8 21a6c17f4e3e
parent 2 851de24ecb61
child 9 629f61203db1
permissions -rw-r--r--
jbm: it compiles... but bombs in unit tests
     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, bool writeable, bool create)  throw(Error)
    25         :_fd(_check( ::open(filename, writeable ?(create ?O_RDWR|O_CREAT :O_RDWR) :O_RDONLY, 0644) )),
    26          _memoryMap(NULL),
    27          _locked(0)
    28     { }
    29 
    30     File::File (const char *filename, int oflag, bool locked)  throw(Error)
    31 #if BSD
    32         :_fd(_check( ::open(filename, oflag | (locked ? O_EXLOCK : 0), 0644) )),
    33 #else
    34         :_fd(_check( ::open(filename, oflag, 0644) )),
    35 #endif
    36          _memoryMap(NULL),
    37          _locked( locked ? 1 : 0)
    38     {
    39 #if !BSDISH
    40       _check( ::flock(_fd, LOCK_EX) );
    41 #endif
    42     }
    43 
    44     File::~File() {
    45         delete _memoryMap;
    46         _unlock();
    47         ::close(_fd);
    48     }
    49     
    50     
    51     off_t File::length() const  throw(Error) {
    52         struct stat s;
    53         _check( ::fstat(_fd,&s) );
    54         return s.st_size;
    55     }
    56      
    57     void File::setLength (off_t length)  throw(Error) {
    58         _check( ftruncate(_fd,length) );
    59     }
    60 
    61     off_t File::position() const  throw(Error) {
    62         return _check( lseek(_fd,0,SEEK_CUR) );
    63     }
    64     
    65     void File::setPosition (off_t pos)  throw(Error) {
    66         _check( lseek(_fd,pos,SEEK_SET) );
    67     }
    68     
    69     off_t File::setPositionToEnd (off_t bytesBefore)  throw(Error) {
    70         return _check( lseek(_fd,-bytesBefore,SEEK_END) );
    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 OSX
   115         _check( ::fcntl(_fd,F_FULLFSYNC) );
   116 #else
   117         _check( fsync(_fd) );
   118 #endif
   119     }
   120     
   121     bool File::hasPath (const char *path) const  throw(Error) {
   122         struct stat myStat, pathStat;
   123         _check( ::fstat(_fd, &myStat) );
   124         if ( ::stat(path, &pathStat) != 0 ) {
   125             if (errno == ENOENT)
   126                 return false;
   127             _check(errno);
   128         }
   129         // Compare my inode number with that of the file at path:
   130         return myStat.st_ino == pathStat.st_ino;
   131     }
   132     
   133     
   134     void File::unlink (const char *filename)  throw(Error) {
   135         _check( ::unlink(filename) );
   136     }
   137     
   138     void File::rename (const char *srcFilename, const char *dstFilename)  throw(Error) {
   139         _check( ::rename(srcFilename, dstFilename) );
   140     }
   141 
   142     
   143 #pragma mark -
   144 #pragma mark UTILITIES:
   145     
   146     int File::_check (int result)  throw(Error) {
   147         if (result >= 0)
   148             return result;
   149         //printf("*** File::_check: Error %i: %s\n", errno, strerror(errno));
   150         throw Error(errno, strerror(errno));
   151     }
   152     
   153     void File::_checkRead (ssize_t result, size_t expectedSize)  throw(Error) {
   154         if ((size_t)result < expectedSize) {
   155             if (result < 0)
   156                 throw Error(errno, strerror(errno));
   157             else
   158                 throw Error(kEOF, "unexpected EOF");
   159         }
   160     }
   161     
   162     File::Error::Error(const char *m)
   163         :code(ERANGE), 
   164          message(m) 
   165     { }
   166     
   167     
   168 #pragma mark -
   169 #pragma mark LOCKS:
   170     
   171     bool File::_lock (bool block) {
   172         if (_locked) {
   173             _locked++;
   174         } else {
   175             int mode = LOCK_EX;
   176             if (block)
   177                 mode |= LOCK_NB;
   178             if (::flock(_fd, mode) == 0)
   179                 _locked = 1;
   180             else if (errno != EWOULDBLOCK)          // may be returned in LOCK_UN mode
   181                 _check(-1);
   182         }
   183         return _locked > 0;
   184     }
   185     
   186     void File::_unlock() {
   187         if (_locked > 0) {
   188             _locked--;
   189             ::flock(_fd, LOCK_UN);
   190         }
   191     }
   192     
   193     File::Lock::Lock (File *file, bool block)  throw(Error)
   194         :_file(file),
   195          _locked( _file->_lock(block) )
   196     { }
   197     
   198     File::Lock::~Lock() {
   199         if (_locked)
   200             _file->_unlock();
   201     }
   202     
   203     bool File::Lock::retry() {
   204         if (!_locked)
   205             _locked = _file->_lock(false);
   206         return _locked;
   207     }
   208     
   209     
   210 #pragma mark -
   211 #pragma mark MEMORY MAP:
   212     
   213     MemoryMap* File::map() {
   214         if (!_memoryMap)
   215             _memoryMap = new MemoryMap(this);
   216         return _memoryMap;
   217     }
   218     
   219     void File::mapRegion (off_t position, size_t length) {
   220         return map()->mapRegion(position,length);
   221     }
   222     
   223     const void* File::mappedPosition (off_t position) const {
   224         return map()->mappedPosition(position);
   225     }
   226 
   227 }