jens@0: /*
jens@0:  *  File.cpp
jens@0:  *  Ottoman
jens@0:  *
jens@0:  *  Created by Jens Alfke on 8/20/09.
jens@0:  *  Copyright 2009 Jens Alfke. All rights reserved.
jens@0:  *  BSD-Licensed: See the file "LICENSE.txt" for details.
jens@0:  */
jens@0: 
jens@0: #include "File.h"
jens@0: #include "MemoryMap.h"
jens@0: 
jens@0: #include <assert.h>
jens@0: #include <errno.h>
jens@0: #include <fcntl.h>
jens@0: #include <stdio.h>
jens@0: #include <sys/stat.h>
jens@0: #include <sys/uio.h>
jens@0: #include <unistd.h>
jens@0: 
jens@0: namespace Mooseyard {
jens@0:     
jens@0:     File::File (const char *filename, bool writeable, bool create)  throw(Error)
jens@0:         :_fd(_check( ::open(filename, writeable ?(create ?O_RDWR|O_CREAT :O_RDWR) :O_RDONLY, 0644) )),
jens@0:          _memoryMap(NULL),
jens@0:          _locked(0)
jens@0:     { }
jens@0: 
jens@0:     File::File (const char *filename, int oflag)  throw(Error)
jens@0:         :_fd(_check( ::open(filename, oflag, 0644) )),
jens@0:          _memoryMap(NULL),
jens@0:          _locked( (oflag | O_EXLOCK) ?1 :0)
jens@0:     { }
jens@0: 
jens@0:     File::~File() {
jens@0:         delete _memoryMap;
jens@0:         _unlock();
jens@0:         ::close(_fd);
jens@0:     }
jens@0:     
jens@0:     
jens@0:     off_t File::length() const  throw(Error) {
jens@0:         struct stat s;
jens@0:         _check( ::fstat(_fd,&s) );
jens@0:         return s.st_size;
jens@0:     }
jens@0:      
jens@0:     void File::setLength (off_t length)  throw(Error) {
jens@0:         _check( ftruncate(_fd,length) );
jens@0:     }
jens@0: 
jens@0:     off_t File::position() const  throw(Error) {
jens@0:         return _check( lseek(_fd,0,SEEK_CUR) );
jens@0:     }
jens@0:     
jens@0:     void File::setPosition (off_t pos)  throw(Error) {
jens@0:         _check( lseek(_fd,pos,SEEK_SET) );
jens@0:     }
jens@0:     
jens@0:     off_t File::setPositionToEnd (off_t bytesBefore)  throw(Error) {
jens@0:         return _check( lseek(_fd,-bytesBefore,SEEK_END) );
jens@0:     }
jens@0:     
jens@0:     void File::read (void *dst, size_t size)  throw(Error) {
jens@0:         _checkRead( ::read(_fd, dst, size), size );
jens@0:     }
jens@0:     
jens@0:     size_t File::write (const void *src, size_t size)  throw(Error) {
jens@0:         return _check( ::write(_fd, src, size) );
jens@0:     }
jens@0:     
jens@0:     
jens@0:     void File::readFrom (off_t where, void *dst, size_t size)  throw(Error) {
jens@0:         _checkRead( ::pread(_fd, dst, size, where), size );
jens@0:     }
jens@0:     
jens@0:     void File::writeTo (off_t where, const void *src, size_t size)  throw(Error) {
jens@0:         _check( ::pwrite(_fd, src, size, where) );
jens@0:     }
jens@0:     
jens@0:     size_t File::writeMultiple (Blob blobs[], int count) throw(Error) {
jens@0:         struct iovec vec[count];
jens@0:         for (int i=0; i<count; i++) {
jens@0:             vec[i].iov_base = (void*) blobs[i].bytes;
jens@0:             vec[i].iov_len = blobs[i].length;
jens@0:         }
jens@0:         return _check( ::writev(_fd, vec, count) );
jens@0:     }
jens@0:     
jens@0:     size_t File::writePadding (int alignment) {
jens@2:         int pad = alignment - (int)(position() & (alignment-1));
jens@0:         if (pad == alignment)
jens@0:             return 0;
jens@0:         uint8_t zero[pad];
jens@0:         memset(zero, 0, pad);
jens@0:         return write(zero, pad);
jens@0:     }
jens@0:     
jens@0:     
jens@0:     void File::flush()  throw(Error) {
jens@0:         _check( ::fsync(_fd) );
jens@0:     }
jens@0:     
jens@0:     void File::flushDisk()  throw(Error) {
jens@0:         _check( ::fcntl(_fd,F_FULLFSYNC) );
jens@0:     }
jens@0:     
jens@0:     bool File::hasPath (const char *path) const  throw(Error) {
jens@0:         struct stat myStat, pathStat;
jens@0:         _check( ::fstat(_fd, &myStat) );
jens@0:         if ( ::stat(path, &pathStat) != 0 ) {
jens@0:             if (errno == ENOENT)
jens@0:                 return false;
jens@0:             _check(errno);
jens@0:         }
jens@0:         // Compare my inode number with that of the file at path:
jens@0:         return myStat.st_ino == pathStat.st_ino;
jens@0:     }
jens@0:     
jens@0:     
jens@0:     void File::unlink (const char *filename)  throw(Error) {
jens@0:         _check( ::unlink(filename) );
jens@0:     }
jens@0:     
jens@0:     void File::rename (const char *srcFilename, const char *dstFilename)  throw(Error) {
jens@0:         _check( ::rename(srcFilename, dstFilename) );
jens@0:     }
jens@0: 
jens@0:     
jens@0: #pragma mark -
jens@0: #pragma mark UTILITIES:
jens@0:     
jens@0:     int File::_check (int result)  throw(Error) {
jens@0:         if (result >= 0)
jens@0:             return result;
jens@0:         //printf("*** File::_check: Error %i: %s\n", errno, strerror(errno));
jens@0:         throw Error(errno, strerror(errno));
jens@0:     }
jens@0:     
jens@0:     void File::_checkRead (ssize_t result, size_t expectedSize)  throw(Error) {
jens@0:         if ((size_t)result < expectedSize) {
jens@0:             if (result < 0)
jens@0:                 throw Error(errno, strerror(errno));
jens@0:             else
jens@0:                 throw Error(kEOF, "unexpected EOF");
jens@0:         }
jens@0:     }
jens@0:     
jens@0:     File::Error::Error(const char *m)
jens@0:         :code(ERANGE), 
jens@0:          message(m) 
jens@0:     { }
jens@0:     
jens@0:     
jens@0: #pragma mark -
jens@0: #pragma mark LOCKS:
jens@0:     
jens@0:     bool File::_lock (bool block) {
jens@0:         if (_locked) {
jens@0:             _locked++;
jens@0:         } else {
jens@0:             int mode = LOCK_EX;
jens@0:             if (block)
jens@0:                 mode |= LOCK_NB;
jens@0:             if (::flock(_fd, mode) == 0)
jens@0:                 _locked = 1;
jens@0:             else if (errno != EWOULDBLOCK)          // may be returned in LOCK_UN mode
jens@0:                 _check(-1);
jens@0:         }
jens@0:         return _locked > 0;
jens@0:     }
jens@0:     
jens@0:     void File::_unlock() {
jens@0:         if (_locked > 0) {
jens@0:             _locked--;
jens@0:             ::flock(_fd, LOCK_UN);
jens@0:         }
jens@0:     }
jens@0:     
jens@0:     File::Lock::Lock (File *file, bool block)  throw(Error)
jens@0:         :_file(file),
jens@0:          _locked( _file->_lock(block) )
jens@0:     { }
jens@0:     
jens@0:     File::Lock::~Lock() {
jens@0:         if (_locked)
jens@0:             _file->_unlock();
jens@0:     }
jens@0:     
jens@0:     bool File::Lock::retry() {
jens@0:         if (!_locked)
jens@0:             _locked = _file->_lock(false);
jens@0:         return _locked;
jens@0:     }
jens@0:     
jens@0:     
jens@0: #pragma mark -
jens@0: #pragma mark MEMORY MAP:
jens@0:     
jens@0:     MemoryMap* File::map() {
jens@0:         if (!_memoryMap)
jens@0:             _memoryMap = new MemoryMap(this);
jens@0:         return _memoryMap;
jens@0:     }
jens@0:     
jens@0:     void File::mapRegion (off_t position, size_t length) {
jens@0:         return map()->mapRegion(position,length);
jens@0:     }
jens@0:     
jens@0:     const void* File::mappedPosition (off_t position) const {
jens@0:         return map()->mappedPosition(position);
jens@0:     }
jens@0: 
jens@0: }