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: }