1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/Ottoman.cpp Sun Sep 20 15:14:12 2009 -0700
1.3 @@ -0,0 +1,261 @@
1.4 +/*
1.5 + * Ottoman.cpp
1.6 + * Ottoman
1.7 + *
1.8 + * Created by Jens Alfke on 8/31/09.
1.9 + * Copyright 2009 Jens Alfke. All rights reserved.
1.10 + * BSD-Licensed: See the file "LICENSE.txt" for details.
1.11 + */
1.12 +
1.13 +#include "Ottoman.h"
1.14 +#include "VersionDictionary.h"
1.15 +#include "Chunk.h"
1.16 +#include "Index.h"
1.17 +#include "File.h"
1.18 +#include <fcntl.h>
1.19 +#include <stdio.h>
1.20 +#include <unistd.h>
1.21 +
1.22 +namespace Mooseyard {
1.23 +
1.24 + class DictionaryFileHeader :public Chunk {
1.25 + public:
1.26 + DictionaryFileHeader()
1.27 + :Chunk(sizeof(DictionaryFileHeader),kChunkType)
1.28 + {
1.29 + memcpy(&magicNumber, &kMagicNumber, sizeof(magicNumber));
1.30 + }
1.31 +
1.32 + bool valid() const {
1.33 + return type()==kChunkType && memcmp(&magicNumber,&kMagicNumber,sizeof(magicNumber))==0;
1.34 + }
1.35 +
1.36 + uint8_t magicNumber[8];
1.37 +
1.38 + static const uint16_t kChunkType = 4;
1.39 + static const uint8_t kMagicNumber[8];
1.40 + };
1.41 +
1.42 + const uint8_t DictionaryFileHeader::kMagicNumber[8] = {0x4A, 0x54, 0xF1, 0x1E, 'h', 'A', 's', 'H'};
1.43 +
1.44 +
1.45 +
1.46 + Ottoman::Ottoman()
1.47 + :_writeable(true),
1.48 + _filename(NULL),
1.49 + _lastVersion(NULL),
1.50 + _current( new OverlayDictionary(&Dictionary::kEmpty) )
1.51 + {
1.52 + }
1.53 +
1.54 + Ottoman::Ottoman (const char *filename, bool writeable)
1.55 + :_writeable(writeable),
1.56 + _filename(strdup(filename)),
1.57 + _lastVersion(),
1.58 + _current()
1.59 + {
1.60 + _file = new File(_filename, writeable ?O_RDWR :O_RDONLY);
1.61 + _lastVersion = new VersionDictionary(_file);
1.62 + if (writeable)
1.63 + _current = new OverlayDictionary(_lastVersion);
1.64 + }
1.65 +
1.66 + Ottoman::~Ottoman() {
1.67 + delete _current;
1.68 + delete _lastVersion;
1.69 + delete _file;
1.70 + free(_filename);
1.71 + }
1.72 +
1.73 +
1.74 + bool Ottoman::needsSync() const {
1.75 + return _file && (!_lastVersion->isLatestVersion() || !_file->hasPath(_filename));
1.76 + }
1.77 +
1.78 + bool Ottoman::sync() {
1.79 + if (!needsSync())
1.80 + return false;
1.81 + File *curFile = _file;
1.82 + if (!curFile->hasPath(_filename))
1.83 + curFile = new File(_filename, _writeable ?O_RDWR :O_RDONLY);
1.84 +
1.85 + VersionDictionary *newest = new VersionDictionary(curFile);
1.86 + bool changed = (newest->generation() != _lastVersion->generation());
1.87 + if (changed) {
1.88 + if (newest->generation() < _lastVersion->generation())
1.89 + throw File::Error("Versions got lost in file update");
1.90 +
1.91 + if (_current)
1.92 + _current->rebase(newest);
1.93 +
1.94 + delete _lastVersion;
1.95 + _lastVersion = newest;
1.96 + }
1.97 +
1.98 + if (_file != curFile) {
1.99 + delete _file;
1.100 + _file = curFile;
1.101 + }
1.102 + return changed;
1.103 + }
1.104 +
1.105 +
1.106 + ChunkIterator* Ottoman::chunkIterator() const {
1.107 + return _file ?new ChunkIterator(_file, sizeof(DictionaryFileHeader)) :NULL;
1.108 + }
1.109 +
1.110 + bool Ottoman::scavenge (bool repair) {
1.111 + if (!_file)
1.112 + return true;
1.113 +
1.114 + const off_t actualEOF = _file->length();
1.115 + fprintf(stderr, "Ottoman::scavenge: Scanning %s (%llu / 0x%llX bytes) ...\n",
1.116 + _filename, actualEOF, actualEOF);
1.117 + _file->setPosition(0);
1.118 + ChunkIterator it(_file, 0);
1.119 + const DictionaryFileHeader *header = (const DictionaryFileHeader *) it.chunk();
1.120 + if (!header || !header->valid()) {
1.121 + fprintf(stderr, "Ottoman::scavenge: No valid file header at all!\n");
1.122 + return false;
1.123 + }
1.124 +
1.125 + // Scan through all the chunks:
1.126 + FilePosition lastValidTrailer = 0;
1.127 + FilePosition validEOF = 0;
1.128 + int lastType = -1;
1.129 + int count = 0;
1.130 + try{
1.131 + for (; it; ++it) {
1.132 + const Chunk *chunk = it.chunk();
1.133 +
1.134 + uint16_t type = it.chunk()->type();
1.135 + if (type != lastType) {
1.136 + if (count > 0)
1.137 + printf("%6d\n", count);
1.138 + fprintf(stderr, "Ottoman::scavenge: at 0x%08X: type %u ... ",
1.139 + it.position(), type);
1.140 + lastType = type;
1.141 + count = 0;
1.142 + }
1.143 + count++;
1.144 +
1.145 + switch (type) {
1.146 + case KeyValueChunk::kChunkType:
1.147 + ((const KeyValueChunk*)chunk)->validate();
1.148 + break;
1.149 + case Index::kChunkType:
1.150 + ((const Index*)chunk)->validateEntries(_file);
1.151 + break;
1.152 + case VersionDictionary::kChunkType: {
1.153 + fprintf(stderr, "Found dictionary trailer at %u!\n", it.position());
1.154 + count = 0;
1.155 + // Validate by instantiating the VersionDictionary:
1.156 + VersionDictionary tempDict(_file, it.position());
1.157 + // If constructor succeeded, it's valid, so remember it:
1.158 + lastValidTrailer = it.position();
1.159 + validEOF = lastValidTrailer + chunk->size();
1.160 + fprintf(stderr, "Ottoman::scavenge: Valid dictionary trailer at %X--%X\n",
1.161 + lastValidTrailer, validEOF);
1.162 + break;
1.163 + }
1.164 + }
1.165 + }
1.166 + if (count > 0)
1.167 + fprintf(stderr, "%6d\n", count);
1.168 + } catch (File::Error &err) {
1.169 + fprintf(stderr, "Ottoman::scavenge caught File::Error(%i,\"%s\") at pos %llX\n",
1.170 + err.code, err.message, _file->position());
1.171 + // Keep going; we can recover the dictionary(ies) before the bad one.
1.172 + }
1.173 +
1.174 + if (lastValidTrailer == 0) {
1.175 + fprintf(stderr, "Ottoman::scavenge: No valid dictionaries found!\n");
1.176 + return false;
1.177 + } else if (validEOF < actualEOF) {
1.178 + fprintf(stderr, "Ottoman::scavenge: Need to truncate to 0x%X (0x%llX bytes)\n",
1.179 + lastValidTrailer, actualEOF-lastValidTrailer);
1.180 + if (repair) {
1.181 + _file->setLength(validEOF);
1.182 + _file->flushDisk();
1.183 + }
1.184 + return false;
1.185 + }
1.186 +
1.187 + fprintf(stderr, "Ottoman::scavenge: File is OK!\n");
1.188 + return true;
1.189 + }
1.190 +
1.191 +
1.192 +#pragma mark -
1.193 +#pragma mark SAVING:
1.194 +
1.195 +
1.196 + // low-level write. Does not lock or check for conflicts!
1.197 + void Ottoman::_append (File *dstFile) {
1.198 + VersionDictionary *lastVersion = _lastVersion;
1.199 + if (!lastVersion)
1.200 + lastVersion = new VersionDictionary(dstFile);
1.201 + VersionDictionary *saved = lastVersion->_appendAndOpen(_current->overlay(),
1.202 + dstFile,
1.203 + _current->baseReplaced());
1.204 + // (don't delete _lastVersion: saved->_previousVersion now points to it.)
1.205 + _lastVersion = saved;
1.206 + _current->revertTo(_lastVersion);
1.207 + }
1.208 +
1.209 + bool Ottoman::save() {
1.210 + if (!_file)
1.211 + return false;
1.212 + if (_current && _current->isChanged()) {
1.213 + File::Lock lock(_file);
1.214 + if (needsSync())
1.215 + return false; // conflict!
1.216 + _append(_file);
1.217 + }
1.218 + return true;
1.219 + }
1.220 +
1.221 + File* Ottoman::_writeTo (const char *dstFileName, bool overwriteAllowed) {
1.222 + int mode = O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK;
1.223 + if (!overwriteAllowed)
1.224 + mode |= O_EXCL;
1.225 + File *dstFile = new File(dstFileName, mode);
1.226 + try {
1.227 + dstFile->write(DictionaryFileHeader());
1.228 + _append(dstFile);
1.229 + } catch (...) {
1.230 + delete dstFile;
1.231 + File::unlink(dstFileName);
1.232 + throw;
1.233 + }
1.234 + return dstFile;
1.235 + }
1.236 +
1.237 + void Ottoman::saveAs (const char *dstFileName, bool overwriteAllowed) {
1.238 + File *dstFile = _writeTo(dstFileName, overwriteAllowed);
1.239 + free(_filename);
1.240 + _filename = strdup(dstFileName);
1.241 + _file = dstFile;
1.242 + }
1.243 +
1.244 + bool Ottoman::saveAndCompact() {
1.245 + if (!_file)
1.246 + return false;
1.247 + char tempFileName[1024];
1.248 + strlcpy(tempFileName, _filename, sizeof(tempFileName)-1);
1.249 + strlcat(tempFileName, "~", sizeof(tempFileName));
1.250 + File *tempFile;
1.251 + {
1.252 + // Check for conflict in existing file:
1.253 + File::Lock lock(_file);
1.254 + if (needsSync())
1.255 + return false;
1.256 + tempFile = _writeTo(tempFileName, false);
1.257 + File::rename(tempFileName, _filename);
1.258 + }
1.259 + delete _file;
1.260 + _file = tempFile;
1.261 + return true;
1.262 + }
1.263 +
1.264 +}