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