// data_file.h
/**
* Copyright (C) 2013 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
#include "mongo/db/storage/mmap_v1/diskloc.h"
#include "mongo/db/storage/mmap_v1/durable_mapped_file.h"
namespace mongo {
class OperationContext;
#pragma pack(1)
class DataFileVersion {
public:
DataFileVersion(uint32_t major, uint32_t minor) :_major(major), _minor(minor) {}
static DataFileVersion defaultForNewFiles() {
return DataFileVersion(kCurrentMajor, kIndexes24AndNewer
| kMayHave28Freelist
);
}
bool isCompatibleWithCurrentCode() const {
if (_major != kCurrentMajor)
return false;
if (_minor & ~kUsedMinorFlagsMask)
return false;
const uint32_t indexCleanliness = _minor & kIndexPluginMask;
if (indexCleanliness != kIndexes24AndNewer && indexCleanliness != kIndexes22AndOlder)
return false;
// We are compatible with either setting of kMayHave28Freelist.
return true;
}
bool is24IndexClean() const { return (_minor & kIndexPluginMask) == kIndexes24AndNewer; }
void setIs24IndexClean() { _minor = ((_minor & ~kIndexPluginMask) | kIndexes24AndNewer); }
bool mayHave28Freelist() const { return _minor & kMayHave28Freelist; }
void setMayHave28Freelist() { _minor |= kMayHave28Freelist; }
uint32_t majorRaw() const { return _major; }
uint32_t minorRaw() const { return _minor; }
private:
static const uint32_t kCurrentMajor = 4;
// minor layout:
// first 4 bits - index plugin cleanliness.
// see IndexCatalog::_upgradeDatabaseMinorVersionIfNeeded for details
// 5th bit - 1 if started with 3.0-style freelist implementation (SERVER-14081)
// 6th through 31st bit - reserved and must be set to 0.
static const uint32_t kIndexPluginMask = 0xf;
static const uint32_t kIndexes22AndOlder = 5;
static const uint32_t kIndexes24AndNewer = 6;
static const uint32_t kMayHave28Freelist = (1 << 4);
// All set bits we know about are covered by this mask.
static const uint32_t kUsedMinorFlagsMask = 0x1f;
uint32_t _major;
uint32_t _minor;
};
// Note: Intentionally not defining relational operators for DataFileVersion as there is no
// total ordering of all versions now that '_minor' is used as a bit vector.
#pragma pack()
/* a datafile - i.e. the "dbname.<#>" files :
----------------------
DataFileHeader
----------------------
Extent (for a particular namespace)
MmapV1RecordHeader
...
MmapV1RecordHeader (some chained for unused space)
----------------------
more Extents...
----------------------
*/
#pragma pack(1)
class DataFileHeader {
public:
DataFileVersion version;
int fileLength;
DiskLoc unused; /* unused is the portion of the file that doesn't belong to any allocated extents. -1 = no more */
int unusedLength;
DiskLoc freeListStart;
DiskLoc freeListEnd;
char reserved[8192 - 4*4 - 8*3];
char data[4]; // first extent starts here
enum { HeaderSize = 8192 };
bool uninitialized() const { return version.majorRaw() == 0; }
void init(OperationContext* txn, int fileno, int filelength, const char* filename);
void checkUpgrade(OperationContext* txn);
bool isEmpty() const {
return uninitialized() || ( unusedLength == fileLength - HeaderSize - 16 );
}
};
#pragma pack()
class DataFile {
public:
DataFile(int fn) : _fileNo(fn), _mb(NULL) {
}
/** @return true if found and opened. if uninitialized (prealloc only) does not open. */
Status openExisting(const char *filename );
/** creates if DNE */
void open(OperationContext* txn,
const char *filename,
int requestedDataSize = 0,
bool preallocateOnly = false);
DiskLoc allocExtentArea( OperationContext* txn, int size );
DataFileHeader* getHeader() { return header(); }
const DataFileHeader* getHeader() const { return header(); }
HANDLE getFd() { return mmf.getFd(); }
unsigned long long length() const { return mmf.length(); }
/* return max size an extent may be */
static int maxSize();
/** fsync */
void flush( bool sync );
private:
friend class MmapV1ExtentManager;
void badOfs(int) const;
int _defaultSize() const;
void grow(DiskLoc dl, int size);
char* p() const { return (char *) _mb; }
DataFileHeader* header() { return static_cast( _mb ); }
const DataFileHeader* header() const { return static_cast( _mb ); }
const int _fileNo;
DurableMappedFile mmf;
void *_mb; // the memory mapped view
};
}