// @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 . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #pragma once namespace mongo { namespace dur { const unsigned Alignment = 8192; #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. #if defined(_NOCOMPRESS) enum { CurrentVersion = 0x4148 }; #else enum { CurrentVersion = 0x4149 }; #endif 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. header and footer are not compressed, just the stuff in between. */ struct JSectHeader { private: unsigned _sectionLen; // unpadded length in bytes of the whole section public: 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 unsigned sectionLen() const { return _sectionLen; } // we store the unpadded length so we can use that when we uncompress. to // get the true total size this must be rounded up to the Alignment. void setSectionLen(unsigned lenUnpadded) { _sectionLen = lenUnpadded; } unsigned sectionLenWithPadding() const { unsigned x = (sectionLen() + (Alignment-1)) & (~(Alignment-1)); dassert( x % Alignment == 0 ); return x; } }; /** 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(); JSectFooter(const void* begin, int len); // needs buffer to compute hash unsigned sentinel; unsigned char hash[16]; unsigned long long reserved; char magic[4]; // "\n\n\n\n" /** used by recovery to see if buffer is valid @param begin the buffer @param len buffer len @return true if buffer looks valid */ bool checkHash(const void* begin, int len) const; bool magicOk() const { return *((unsigned*)magic) == 0x0a0a0a0a; } }; /** 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() } }