// @file dur_journalformat.h The format of our journal files. /** * Copyright (C) 2010 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #pragma once #include "../util/md5.hpp" namespace mongo { namespace dur { #pragma pack(1) /** beginning header for a journal/j._ file there is nothing important int this header at this time. except perhaps version #. */ struct JHeader { JHeader() { } JHeader(string fname); char magic[2]; // "j\n". j means journal, then a linefeed, fwiw if you were to run "less" on the file or something... // x4142 is asci--readable if you look at the file with head/less -- thus the starting values were near // that. simply incrementing the version # is safe on a fwd basis. enum { CurrentVersion = 0x4147 }; unsigned short _version; // these are just for diagnostic ease (make header more useful as plain text) char n1; // '\n' char ts[20]; // ascii timestamp of file generation. for user reading, not used by code. char n2; // '\n' char dbpath[128]; // path/filename of this file for human reading and diagnostics. not used by code. char n3, n4; // '\n', '\n' unsigned long long fileId; // unique identifier that will be in each JSectHeader. important as we recycle prealloced files char reserved3[8026]; // 8KB total for the file header char txt2[2]; // "\n\n" at the end bool versionOk() const { return _version == CurrentVersion; } bool valid() const { return magic[0] == 'j' && txt2[1] == '\n' && fileId; } }; /** "Section" header. A section corresponds to a group commit. len is length of the entire section including header and footer. */ struct JSectHeader { unsigned len; // length in bytes of the whole section unsigned long long seqNumber; // sequence number that can be used on recovery to not do too much work unsigned long long fileId; // matches JHeader::fileId }; /** an individual write operation within a group commit section. Either the entire section should be applied, or nothing. (We check the md5 for the whole section before doing anything on recovery.) */ struct JEntry { enum OpCodes { OpCode_Footer = 0xffffffff, OpCode_DbContext = 0xfffffffe, OpCode_FileCreated = 0xfffffffd, OpCode_DropDb = 0xfffffffc, OpCode_Min = 0xfffff000 }; union { unsigned len; // length in bytes of the data of the JEntry. does not include the JEntry header OpCodes opcode; }; unsigned ofs; // offset in file // sentinel and masks for _fileNo enum { DotNsSuffix = 0x7fffffff, // ".ns" file LocalDbBit = 0x80000000 // assuming "local" db instead of using the JDbContext }; int _fileNo; // high bit is set to indicate it should be the /local database // char data[len] follows const char * srcData() const { const int *i = &_fileNo; return (const char *) (i+1); } int getFileNo() const { return _fileNo & (~LocalDbBit); } void setFileNo(int f) { _fileNo = f; } bool isNsSuffix() const { return getFileNo() == DotNsSuffix; } void setLocalDbContextBit() { _fileNo |= LocalDbBit; } bool isLocalDbContext() const { return _fileNo & LocalDbBit; } void clearLocalDbContextBit() { _fileNo = getFileNo(); } static string suffix(int fileno) { if( fileno == DotNsSuffix ) return "ns"; stringstream ss; ss << fileno; return ss.str(); } }; /** group commit section footer. md5 is a key field. */ struct JSectFooter { JSectFooter(const void* begin, int len) { // needs buffer to compute hash sentinel = JEntry::OpCode_Footer; reserved = 0; magic[0] = magic[1] = magic[2] = magic[3] = '\n'; // skip section header since size modified after hashing (const char*&)begin += sizeof(JSectHeader); len -= sizeof(JSectHeader); md5(begin, len, hash); } unsigned sentinel; md5digest hash; // unsigned char[16] unsigned long long reserved; char magic[4]; // "\n\n\n\n" bool checkHash(const void* begin, int len) const { // skip section header since size modified after hashing (const char*&)begin += sizeof(JSectHeader); len -= sizeof(JSectHeader); md5digest current; md5(begin, len, current); DEV log() << "checkHash len:" << len << " hash:" << toHex(hash, 16) << " current:" << toHex(current, 16) << endl; return (memcmp(hash, current, sizeof(hash)) == 0); } }; /** declares "the next entry(s) are for this database / file path prefix" */ struct JDbContext { JDbContext() : sentinel(JEntry::OpCode_DbContext) { } const unsigned sentinel; // compare to JEntry::len -- zero is our sentinel //char dbname[]; }; /** "last sequence number" */ struct LSNFile { unsigned ver; unsigned reserved2; unsigned long long lsn; unsigned long long checkbytes; unsigned long long reserved[8]; void set(unsigned long long lsn); unsigned long long get(); }; #pragma pack() } }