* Implemented a read-only btrfs file system, tested with a 400MB image.

* Inline extent data isn't read with the file_cache yet as the data is not block aligned.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40282 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Jérôme Duval 2011-01-24 22:10:43 +00:00
parent 85f15b4bf0
commit 6f4c36e297
19 changed files with 3035 additions and 0 deletions

View File

@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel file_systems ;
SubInclude HAIKU_TOP src add-ons kernel file_systems bfs ;
SubInclude HAIKU_TOP src add-ons kernel file_systems bindfs ;
SubInclude HAIKU_TOP src add-ons kernel file_systems btrfs ;
SubInclude HAIKU_TOP src add-ons kernel file_systems cdda ;
SubInclude HAIKU_TOP src add-ons kernel file_systems ext2 ;
SubInclude HAIKU_TOP src add-ons kernel file_systems fat ;

View File

@ -0,0 +1,282 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
//! B+Tree implementation
#include "BPlusTree.h"
#include "CachedBlock.h"
#include <stdlib.h>
#include <string.h>
#include <util/AutoLock.h>
//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
# define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#else
# define TRACE(x...) ;
#endif
# define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
BPlusTree::BPlusTree(Volume* volume, struct btrfs_stream *stream)
:
fStream(stream),
fRootBlock(0),
fVolume(volume)
{
mutex_init(&fIteratorLock, "btrfs b+tree iterator");
}
BPlusTree::BPlusTree(Volume* volume, fsblock_t rootBlock)
:
fStream(NULL),
fRootBlock(rootBlock),
fVolume(volume)
{
mutex_init(&fIteratorLock, "btrfs b+tree iterator");
}
BPlusTree::~BPlusTree()
{
// if there are any TreeIterators left, we need to stop them
// (can happen when the tree's inode gets deleted while
// traversing the tree - a TreeIterator doesn't lock the inode)
mutex_lock(&fIteratorLock);
SinglyLinkedList<TreeIterator>::Iterator iterator
= fIterators.GetIterator();
while (iterator.HasNext())
iterator.Next()->Stop();
mutex_destroy(&fIteratorLock);
}
int32
BPlusTree::_CompareKeys(struct btrfs_key &key1, struct btrfs_key &key2)
{
if (key1.ObjectID() > key2.ObjectID())
return 1;
if (key1.ObjectID() < key2.ObjectID())
return -1;
if (key1.Type() > key2.Type())
return 1;
if (key1.Type() < key2.Type())
return -1;
if (key1.Offset() > key2.Offset())
return 1;
if (key1.Offset() < key2.Offset())
return -1;
return 0;
}
/*! Searches the key in the tree, and stores the allocated found item in
_value, if successful.
Returns B_OK when the key could be found, B_ENTRY_NOT_FOUND if not.
It can also return other errors to indicate that something went wrong.
*/
status_t
BPlusTree::_Find(struct btrfs_key &key, void** _value, size_t* _size,
bplustree_traversing type)
{
TRACE("Find() objectid %lld type %d offset %lld \n", key.ObjectID(),
key.Type(), key.Offset());
btrfs_stream *stream = fStream;
CachedBlock cached(fVolume);
fsblock_t physical;
if (stream == NULL) {
if (fVolume->FindBlock(fRootBlock, physical) != B_OK) {
ERROR("Find() unmapped block %lld\n", fRootBlock);
return B_ERROR;
}
stream = (btrfs_stream *)cached.SetTo(physical);
}
while (stream->header.Level() != 0) {
TRACE("Find() level %d\n", stream->header.Level());
uint32 i = 1;
for (; i < stream->header.ItemCount(); i++) {
int32 comp = _CompareKeys(stream->index[i].key, key);
TRACE("Find() found index %ld at %lld comp %ld\n", i,
stream->index[i].BlockNum(), comp);
if (comp < 0)
continue;
if (comp > 0 || type == BPLUSTREE_BACKWARD)
break;
}
TRACE("Find() getting index %ld at %lld\n", i - 1,
stream->index[i - 1].BlockNum());
if (fVolume->FindBlock(stream->index[i - 1].BlockNum(), physical)
!= B_OK) {
ERROR("Find() unmapped block %lld\n",
stream->index[i - 1].BlockNum());
return B_ERROR;
}
stream = (btrfs_stream *)cached.SetTo(physical);
}
uint32 i;
#ifdef TRACE_BTRFS
TRACE("Find() dump count %ld\n", stream->header.ItemCount());
for (i = 0; i < stream->header.ItemCount(); i++) {
int32 comp = _CompareKeys(key, stream->entries[i].key);
TRACE("Find() dump %ld %ld offset %lld comp %ld\n",
stream->entries[i].Offset(),
stream->entries[i].Size(), stream->entries[i].key.Offset(), comp);
}
#endif
for (i = 0; i < stream->header.ItemCount(); i++) {
int32 comp = _CompareKeys(key, stream->entries[i].key);
TRACE("Find() found %ld %ld oid %lld type %d offset %lld comp %ld\n",
stream->entries[i].Offset(), stream->entries[i].Size(),
stream->entries[i].key.ObjectID(), stream->entries[i].key.Type(),
stream->entries[i].key.Offset(), comp);
if (comp == 0)
break;
if (comp < 0 && i > 0) {
if (type == BPLUSTREE_EXACT)
return B_ENTRY_NOT_FOUND;
if (type == BPLUSTREE_BACKWARD)
i--;
break;
}
}
if (i == stream->header.ItemCount()) {
if (type == BPLUSTREE_BACKWARD)
i--;
else
return B_ENTRY_NOT_FOUND;
}
if (i < stream->header.ItemCount()
&& stream->entries[i].key.Type() == key.Type()) {
TRACE("Find() found %ld %ld\n", stream->entries[i].Offset(),
stream->entries[i].Size());
if (_value != NULL) {
*_value = malloc(stream->entries[i].Size());
memcpy(*_value, ((uint8 *)&stream->entries[0]
+ stream->entries[i].Offset()),
stream->entries[i].Size());
key.SetOffset(stream->entries[i].key.Offset());
if (_size != NULL)
*_size = stream->entries[i].Size();
}
return B_OK;
}
TRACE("Find() not found %lld %lld\n", key.Offset(), key.ObjectID());
return B_ENTRY_NOT_FOUND;
}
status_t
BPlusTree::FindNext(struct btrfs_key &key, void** _value, size_t* _size)
{
return _Find(key, _value, _size, BPLUSTREE_FORWARD);
}
status_t
BPlusTree::FindPrevious(struct btrfs_key &key, void** _value, size_t* _size)
{
return _Find(key, _value, _size, BPLUSTREE_BACKWARD);
}
status_t
BPlusTree::FindExact(struct btrfs_key &key, void** _value, size_t* _size)
{
return _Find(key, _value, _size, BPLUSTREE_EXACT);
}
void
BPlusTree::_AddIterator(TreeIterator* iterator)
{
MutexLocker _(fIteratorLock);
fIterators.Add(iterator);
}
void
BPlusTree::_RemoveIterator(TreeIterator* iterator)
{
MutexLocker _(fIteratorLock);
fIterators.Remove(iterator);
}
// #pragma mark -
TreeIterator::TreeIterator(BPlusTree* tree, struct btrfs_key &key)
:
fTree(tree),
fCurrentKey(key)
{
Rewind();
tree->_AddIterator(this);
}
TreeIterator::~TreeIterator()
{
if (fTree)
fTree->_RemoveIterator(this);
}
/*! Iterates through the tree in the specified direction.
*/
status_t
TreeIterator::Traverse(bplustree_traversing direction, struct btrfs_key &key,
void** value, size_t* size)
{
if (fTree == NULL)
return B_INTERRUPTED;
fCurrentKey.SetOffset(fCurrentKey.Offset() + direction);
status_t status = fTree->_Find(fCurrentKey, value, size,
direction);
if (status != B_OK) {
TRACE("TreeIterator::Traverse() Find failed\n");
return B_ENTRY_NOT_FOUND;
}
return B_OK;
}
/*! just sets the current key in the iterator.
*/
status_t
TreeIterator::Find(struct btrfs_key &key)
{
if (fTree == NULL)
return B_INTERRUPTED;
fCurrentKey = key;
return B_OK;
}
void
TreeIterator::Stop()
{
fTree = NULL;
}

View File

@ -0,0 +1,135 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#ifndef B_PLUS_TREE_H
#define B_PLUS_TREE_H
#include "btrfs.h"
#include <util/SinglyLinkedList.h>
#include "Volume.h"
#define BPLUSTREE_NULL -1LL
#define BPLUSTREE_FREE -2LL
enum bplustree_traversing {
BPLUSTREE_FORWARD = 1,
BPLUSTREE_EXACT = 0,
BPLUSTREE_BACKWARD = -1,
BPLUSTREE_BEGIN = 0,
BPLUSTREE_END = -1
};
// #pragma mark - in-memory structures
template<class T> class Stack;
class TreeIterator;
// needed for searching (utilizing a stack)
struct node_and_key {
off_t nodeOffset;
uint16 keyIndex;
};
class BPlusTree {
public:
BPlusTree(Volume* volume,
struct btrfs_stream *stream);
BPlusTree(Volume* volume,
fsblock_t rootBlock);
~BPlusTree();
status_t FindExact(struct btrfs_key &key, void** value,
size_t* size = NULL);
status_t FindNext(struct btrfs_key &key, void** value,
size_t* size = NULL);
status_t FindPrevious(struct btrfs_key &key, void** value,
size_t* size = NULL);
private:
BPlusTree(const BPlusTree& other);
BPlusTree& operator=(const BPlusTree& other);
// no implementation
int32 _CompareKeys(struct btrfs_key &key1,
struct btrfs_key &key2);
status_t _Find(struct btrfs_key &key, void** value,
size_t* size, bplustree_traversing type);
void _AddIterator(TreeIterator* iterator);
void _RemoveIterator(TreeIterator* iterator);
private:
friend class TreeIterator;
struct btrfs_stream* fStream;
fsblock_t fRootBlock;
Volume* fVolume;
mutex fIteratorLock;
SinglyLinkedList<TreeIterator> fIterators;
};
class TreeIterator : public SinglyLinkedListLinkImpl<TreeIterator> {
public:
TreeIterator(BPlusTree* tree, struct btrfs_key &key);
~TreeIterator();
status_t Traverse(bplustree_traversing direction,
struct btrfs_key &key, void** value,
size_t *size = NULL);
status_t Find(struct btrfs_key &key);
status_t Rewind();
status_t GetNextEntry(struct btrfs_key &key, void** value,
size_t *size = NULL);
status_t GetPreviousEntry(struct btrfs_key &key, void** value,
size_t *size = NULL);
BPlusTree* Tree() const { return fTree; }
private:
friend class BPlusTree;
// called by BPlusTree
void Stop();
private:
BPlusTree* fTree;
struct btrfs_key fCurrentKey;
};
// #pragma mark - TreeIterator inline functions
inline status_t
TreeIterator::Rewind()
{
fCurrentKey.SetOffset(BPLUSTREE_BEGIN);
return B_OK;
}
inline status_t
TreeIterator::GetNextEntry(struct btrfs_key &key, void** value, size_t *size)
{
return Traverse(BPLUSTREE_FORWARD, key, value, size);
}
inline status_t
TreeIterator::GetPreviousEntry(struct btrfs_key &key, void** value,
size_t *size)
{
return Traverse(BPLUSTREE_BACKWARD, key, value, size);
}
#endif // B_PLUS_TREE_H

View File

@ -0,0 +1,69 @@
/*
* Copyright 2011, Haiku Inc. All rights reserved.
* This file may be used under the terms of the MIT License.
*
* Authors:
* Jérôme Duval
*/
#include <SupportDefs.h>
//! CRC 03667067501 table, as generated by crc_table.cpp
static uint32 kCrcTable[256] = {
0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351
};
/*! \brief Calculates the UDF crc checksum for the given byte stream.
Based on crc code from UDF-2.50 6.5, as permitted.
This is reversed.
\param data Pointer to the byte stream.
\param length Length of the byte stream in bytes.
\return The crc checksum, or 0 if an error occurred.
*/
uint32
calculate_crc(uint32 crc, uint8 *data, uint16 length)
{
if (data) {
for ( ; length > 0; length--, data++)
crc = kCrcTable[(crc ^ *data) & 0xff] ^ (crc >> 8);
}
return crc;
}

View File

@ -0,0 +1,10 @@
/*
* Copyright 2011, Haiku Inc. All rights reserved.
* This file may be used under the terms of the MIT License.
*
* Authors:
* Jérôme Duval
*/
uint32 calculate_crc(uint32 crc, uint8 *data, uint16 length);

View File

@ -0,0 +1,97 @@
/*
* Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#ifndef CACHED_BLOCK_H
#define CACHED_BLOCK_H
//! interface for the block cache
#include <fs_cache.h>
#include "Volume.h"
class CachedBlock {
public:
CachedBlock(Volume* volume);
CachedBlock(Volume* volume, off_t block);
~CachedBlock();
void Keep();
void Unset();
const uint8* SetTo(off_t block);
const uint8* Block() const { return fBlock; }
off_t BlockNumber() const { return fBlockNumber; }
private:
CachedBlock(const CachedBlock &);
CachedBlock &operator=(const CachedBlock &);
// no implementation
protected:
Volume* fVolume;
off_t fBlockNumber;
uint8* fBlock;
};
// inlines
inline
CachedBlock::CachedBlock(Volume* volume)
:
fVolume(volume),
fBlockNumber(0),
fBlock(NULL)
{
}
inline
CachedBlock::CachedBlock(Volume* volume, off_t block)
:
fVolume(volume),
fBlockNumber(0),
fBlock(NULL)
{
SetTo(block);
}
inline
CachedBlock::~CachedBlock()
{
Unset();
}
inline void
CachedBlock::Keep()
{
fBlock = NULL;
}
inline void
CachedBlock::Unset()
{
if (fBlock != NULL) {
block_cache_put(fVolume->BlockCache(), fBlockNumber);
fBlock = NULL;
}
}
inline const uint8 *
CachedBlock::SetTo(off_t block)
{
Unset();
fBlockNumber = block;
return fBlock = (uint8 *)block_cache_get(fVolume->BlockCache(), block);
}
#endif // CACHED_BLOCK_H

View File

@ -0,0 +1,77 @@
/*
* Copyright 2011, Haiku Inc. All rights reserved.
* This file may be used under the terms of the MIT License.
*
* Authors:
* Jérôme Duval
*/
#include "Chunk.h"
#include <stdlib.h>
#include <string.h>
//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
# define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#else
# define TRACE(x...) ;
#endif
# define FATAL(x...) dprintf("\33[34mbtrfs:\33[0m " x)
Chunk::Chunk(struct btrfs_chunk* chunk, fsblock_t offset)
:
fChunk(NULL),
fInitStatus(B_OK)
{
fChunkOffset = offset;
fChunk = (struct btrfs_chunk*)malloc(sizeof(struct btrfs_chunk)
+ chunk->StripeCount() * sizeof(struct btrfs_stripe));
if (fChunk == NULL)
fInitStatus = B_NO_MEMORY;
memcpy(fChunk, chunk, sizeof(struct btrfs_chunk)
+ chunk->StripeCount() * sizeof(struct btrfs_stripe));
TRACE("chunk[0] length %llu owner %llu stripe_length %llu type %llu "
"stripe_count %u sub_stripes %u sector_size %lu\n", chunk->Length(),
chunk->Owner(), chunk->StripeLength(), chunk->Type(),
chunk->StripeCount(), chunk->SubStripes(), chunk->SectorSize());
for(int32 i = 0; i < chunk->StripeCount(); i++) {
TRACE("chunk.stripe[%ld].physical %lld deviceid %lld\n", i,
chunk->stripes[i].Offset(), chunk->stripes[i].DeviceID());
}
}
Chunk::~Chunk()
{
free(fChunk);
}
uint32
Chunk::Size() const
{
return sizeof(struct btrfs_chunk)
+ fChunk->StripeCount() * sizeof(struct btrfs_stripe);
}
status_t
Chunk::FindBlock(off_t logical, off_t &physical)
{
if (fChunk == NULL)
return B_NO_INIT;
if (logical < fChunkOffset
|| logical > (fChunkOffset + fChunk->Length()))
return B_BAD_VALUE;
// only one stripe
physical = logical + fChunk->stripes[0].Offset() - fChunkOffset;
return B_OK;
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2011, Haiku Inc. All rights reserved.
* This file may be used under the terms of the MIT License.
*
* Authors:
* Jérôme Duval
*/
#ifndef CHUNK_H
#define CHUNK_H
#include "btrfs.h"
class Chunk {
public:
Chunk(struct btrfs_chunk* chunk,
fsblock_t offset);
~Chunk();
uint32 Size() const;
status_t FindBlock(off_t logical, off_t &physical);
fsblock_t Offset() const { return fChunkOffset; }
fsblock_t End() const
{ return fChunkOffset + fChunk->Length(); }
private:
struct btrfs_chunk* fChunk;
fsblock_t fChunkOffset;
status_t fInitStatus;
};
#endif // CHUNK_H

View File

@ -0,0 +1,155 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* This file may be used under the terms of the MIT License.
*/
#include "DirectoryIterator.h"
#include <new>
#include <stdlib.h>
#include "CRCTable.h"
//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
# define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#else
# define TRACE(x...) ;
#endif
# define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
DirectoryIterator::DirectoryIterator(Inode* inode)
:
fOffset(0),
fInode(inode),
fIterator(NULL)
{
struct btrfs_key key;
key.SetType(BTRFS_KEY_TYPE_DIR_INDEX);
key.SetObjectID(inode->ID());
fIterator = new(std::nothrow) TreeIterator(inode->GetVolume()->FSTree(),
key);
}
DirectoryIterator::~DirectoryIterator()
{
delete fIterator;
}
status_t
DirectoryIterator::InitCheck()
{
return fIterator != NULL ? B_OK : B_NO_MEMORY;
}
status_t
DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id)
{
if (fOffset == 0) {
*_nameLength = 3;
strlcpy(name, "..", *_nameLength);
*_id = fInode->ID();
fOffset = 1;
return B_OK;
} else if (fOffset == 1) {
*_nameLength = 2;
strlcpy(name, ".", *_nameLength);
fOffset = 2;
if (fInode->ID() == BTRFS_OBJECT_ID_CHUNK_TREE) {
*_id = fInode->ID();
return B_OK;
}
return fInode->FindParent(_id);
}
btrfs_key key;
btrfs_dir_entry *entries;
size_t entries_length;
status_t status = fIterator->GetNextEntry(key, (void**)&entries,
&entries_length);
if (status != B_OK)
return status;
btrfs_dir_entry *entry = entries;
uint16 current = 0;
while (current < entries_length) {
current += entry->Length();
break;
// TODO there could be several entries with the same name hash
entry = (btrfs_dir_entry *)((uint8*)entry + entry->Length());
}
TRACE("DirectoryIterator::GetNext() entries_length %ld name_length %d\n",
entries_length, entry->NameLength());
memcpy(name, entry + 1, entry->NameLength());
name[entry->NameLength()] = '\0';
*_nameLength = entry->NameLength();
*_id = entry->InodeID();
free(entries);
return B_OK;
}
status_t
DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id)
{
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
if (strcmp(name, ".") == 0
|| fInode->ID() == BTRFS_OBJECT_ID_CHUNK_TREE) {
*_id = fInode->ID();
return B_OK;
}
return fInode->FindParent(_id);
}
uint32 hash = calculate_crc((uint32)~1, (uint8*)name, nameLength);
struct btrfs_key key;
key.SetType(BTRFS_KEY_TYPE_DIR_ITEM);
key.SetObjectID(fInode->ID());
key.SetOffset(hash);
btrfs_dir_entry *entries;
size_t length;
status_t status = fInode->GetVolume()->FSTree()->FindExact(key,
(void**)&entries, &length);
if (status != B_OK) {
TRACE("DirectoryIterator::Lookup(): Couldn't find entry with hash %lu "
"\"%s\"\n", hash, name);
return status;
}
btrfs_dir_entry *entry = entries;
uint16 current = 0;
while (current < length) {
current += entry->Length();
break;
// TODO there could be several entries with the same name hash
entry = (btrfs_dir_entry *)((uint8*)entry + entry->Length());
}
TRACE("DirectoryIterator::Lookup() entries_length %ld name_length %d\n",
length, entry->NameLength());
*_id = entry->InodeID();
free(entries);
return B_OK;
}
status_t
DirectoryIterator::Rewind()
{
fIterator->Rewind();
fOffset = BPLUSTREE_BEGIN;
return B_OK;
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* This file may be used under the terms of the MIT License.
*/
#ifndef DIRECTORYITERATOR_H
#define DIRECTORYITERATOR_H
#include "BPlusTree.h"
#include "Inode.h"
class DirectoryIterator {
public:
DirectoryIterator(Inode* inode);
~DirectoryIterator();
status_t InitCheck();
status_t GetNext(char* name, size_t* _nameLength, ino_t* _id);
status_t Lookup(const char* name, size_t nameLength, ino_t* _id);
status_t Rewind();
private:
uint64 fOffset;
Inode* fInode;
TreeIterator* fIterator;
};
#endif // DIRECTORYITERATOR_H

View File

@ -0,0 +1,253 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#include "Inode.h"
#include <string.h>
#include <stdlib.h>
#include "BPlusTree.h"
#include "CachedBlock.h"
#include "Utility.h"
#undef ASSERT
//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
# define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
# define ASSERT(x) { if (!(x)) kernel_debugger("btrfs: assert failed: " #x "\n"); }
#else
# define TRACE(x...) ;
# define ASSERT(x) ;
#endif
#define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
Inode::Inode(Volume* volume, ino_t id)
:
fVolume(volume),
fID(id),
fCache(NULL),
fMap(NULL)
{
rw_lock_init(&fLock, "btrfs inode");
fInitStatus = UpdateNodeFromDisk();
if (fInitStatus == B_OK) {
if (!IsDirectory() && !IsSymLink()) {
fCache = file_cache_create(fVolume->ID(), ID(), Size());
fMap = file_map_create(fVolume->ID(), ID(), Size());
}
}
}
Inode::Inode(Volume* volume)
:
fVolume(volume),
fID(0),
fCache(NULL),
fMap(NULL),
fInitStatus(B_NO_INIT)
{
rw_lock_init(&fLock, "btrfs inode");
}
Inode::~Inode()
{
TRACE("Inode destructor\n");
file_cache_delete(FileCache());
file_map_delete(Map());
TRACE("Inode destructor: Done\n");
}
status_t
Inode::InitCheck()
{
return fInitStatus;
}
status_t
Inode::UpdateNodeFromDisk()
{
struct btrfs_key search_key;
search_key.SetType(BTRFS_KEY_TYPE_INODE_ITEM);
search_key.SetObjectID(fID);
search_key.SetOffset(0);
struct btrfs_inode *node;
if (fVolume->FSTree()->FindExact(search_key, (void**)&node) != B_OK) {
ERROR("Inode::UpdateNodeFromDisk(): Couldn't find inode %"
B_PRIdINO "\n", fID);
return B_ENTRY_NOT_FOUND;
}
memcpy(&fNode, node, sizeof(struct btrfs_inode));
free(node);
return B_OK;
}
status_t
Inode::CheckPermissions(int accessMode) const
{
// you never have write access to a read-only volume
if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly())
return B_READ_ONLY_DEVICE;
// get node permissions
mode_t mode = Mode();
int userPermissions = (mode & S_IRWXU) >> 6;
int groupPermissions = (mode & S_IRWXG) >> 3;
int otherPermissions = mode & S_IRWXO;
// get the node permissions for this uid/gid
int permissions = 0;
uid_t uid = geteuid();
gid_t gid = getegid();
if (uid == 0) {
// user is root
// root has always read/write permission, but at least one of the
// X bits must be set for execute permission
permissions = userPermissions | groupPermissions | otherPermissions
| R_OK | W_OK;
} else if (uid == (uid_t)fNode.UserID()) {
// user is node owner
permissions = userPermissions;
} else if (gid == (gid_t)fNode.GroupID()) {
// user is in owning group
permissions = groupPermissions;
} else {
// user is one of the others
permissions = otherPermissions;
}
return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
return B_OK;
}
status_t
Inode::FindBlock(off_t pos, off_t& physical, off_t *_length)
{
struct btrfs_key search_key;
search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA);
search_key.SetObjectID(fID);
search_key.SetOffset(pos + 1);
btrfs_extent_data *extent_data;
status_t status = fVolume->FSTree()->FindPrevious(search_key,
(void**)&extent_data);
if (status != B_OK) {
ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%lx\n", status);
return status;
}
TRACE("Inode::FindBlock(%" B_PRIdINO ") key.Offset() %lld\n", ID(),
search_key.Offset());
off_t diff = pos - search_key.Offset();
off_t logical = 0;
if (extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR)
logical = diff + extent_data->disk_offset;
else
panic("unknown extent type; %d\n", extent_data->Type());
status = fVolume->FindBlock(logical, physical);
if (_length != NULL)
*_length = extent_data->Size() - diff;
TRACE("Inode::FindBlock(%" B_PRIdINO ") %lld physical %lld\n", ID(),
pos, physical);
free(extent_data);
return status;
}
status_t
Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
{
size_t length = *_length;
// set/check boundaries for pos/length
if (pos < 0) {
ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %lld, length %lu)\n",
ID(), pos, length);
return B_BAD_VALUE;
}
if (pos >= Size() || length == 0) {
TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %lld, length %lu)\n",
ID(), pos, length);
*_length = 0;
return B_NO_ERROR;
}
// the file cache doesn't seem to like non block aligned file offset
// so we avoid the file cache for inline extents
struct btrfs_key search_key;
search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA);
search_key.SetObjectID(fID);
search_key.SetOffset(pos + 1);
btrfs_extent_data *extent_data;
status_t status = fVolume->FSTree()->FindPrevious(search_key,
(void**)&extent_data);
if (status != B_OK) {
ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%lx\n", status);
return status;
}
if (FileCache() != NULL
&& extent_data->Type() == BTRFS_EXTENT_DATA_REGULAR) {
TRACE("inode %" B_PRIdINO ": ReadAt cache (pos %lld, length %lu)\n",
ID(), pos, length);
free(extent_data);
return file_cache_read(FileCache(), NULL, pos, buffer, _length);
}
TRACE("Inode::ReadAt(%" B_PRIdINO ") key.Offset() %lld\n", ID(),
search_key.Offset());
off_t diff = pos - search_key.Offset();
if (extent_data->Type() != BTRFS_EXTENT_DATA_INLINE)
panic("unknown extent type; %d\n", extent_data->Type());
*_length = min_c(extent_data->MemoryBytes() - diff, *_length);
memcpy(buffer, extent_data->inline_data, *_length);
free(extent_data);
return B_OK;
}
status_t
Inode::FindParent(ino_t *id)
{
struct btrfs_key search_key;
search_key.SetType(BTRFS_KEY_TYPE_INODE_REF);
search_key.SetObjectID(fID);
search_key.SetOffset(-1);
void *node_ref;
if (fVolume->FSTree()->FindPrevious(search_key, &node_ref) != B_OK) {
ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n",
fID);
return B_ERROR;
}
free(node_ref);
*id = search_key.Offset();
TRACE("Inode::FindParent() for %" B_PRIdINO ": %" B_PRIdINO "\n", fID,
*id);
return B_OK;
}

View File

@ -0,0 +1,152 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#ifndef INODE_H
#define INODE_H
#include <fs_cache.h>
#include <lock.h>
#include <string.h>
#include "btrfs.h"
#include "Volume.h"
//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
# define TRACEI(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#else
# define TRACEI(x...) ;
#endif
class Inode {
public:
Inode(Volume* volume, ino_t id);
~Inode();
status_t InitCheck();
ino_t ID() const { return fID; }
rw_lock* Lock() { return &fLock; }
status_t UpdateNodeFromDisk();
bool IsDirectory() const
{ return S_ISDIR(Mode()); }
bool IsFile() const
{ return S_ISREG(Mode()); }
bool IsSymLink() const
{ return S_ISLNK(Mode()); }
status_t CheckPermissions(int accessMode) const;
mode_t Mode() const { return fNode.Mode(); }
off_t Size() const { return fNode.Size(); }
uid_t UserID() const { return fNode.UserID(); }
gid_t GroupID() const { return fNode.GroupID(); }
void GetChangeTime(struct timespec &timespec) const
{ fNode.GetChangeTime(timespec); }
void GetModificationTime(struct timespec &timespec) const
{ fNode.GetModificationTime(timespec); }
void GetCreationTime(struct timespec &timespec) const
{ fNode.GetCreationTime(timespec); }
void GetAccessTime(struct timespec &timespec) const
{ fNode.GetCreationTime(timespec); }
Volume* GetVolume() const { return fVolume; }
status_t FindBlock(off_t logical, off_t& physical,
off_t *_length = NULL);
status_t ReadAt(off_t pos, uint8 *buffer, size_t *length);
status_t FillGapWithZeros(off_t start, off_t end);
void* FileCache() const { return fCache; }
void* Map() const { return fMap; }
status_t FindParent(ino_t *id);
private:
Inode(Volume* volume);
Inode(const Inode&);
Inode &operator=(const Inode&);
// no implementation
uint64 _NumBlocks();
rw_lock fLock;
::Volume* fVolume;
ino_t fID;
void* fCache;
void* fMap;
status_t fInitStatus;
struct btrfs_inode fNode;
};
// The Vnode class provides a convenience layer upon get_vnode(), so that
// you don't have to call put_vnode() anymore, which may make code more
// readable in some cases
class Vnode {
public:
Vnode(Volume* volume, ino_t id)
:
fInode(NULL)
{
SetTo(volume, id);
}
Vnode()
:
fStatus(B_NO_INIT),
fInode(NULL)
{
}
~Vnode()
{
Unset();
}
status_t InitCheck()
{
return fStatus;
}
void Unset()
{
if (fInode != NULL) {
put_vnode(fInode->GetVolume()->FSVolume(), fInode->ID());
fInode = NULL;
fStatus = B_NO_INIT;
}
}
status_t SetTo(Volume* volume, ino_t id)
{
Unset();
return fStatus = get_vnode(volume->FSVolume(), id, (void**)&fInode);
}
status_t Get(Inode** _inode)
{
*_inode = fInode;
return fStatus;
}
void Keep()
{
TRACEI("Vnode::Keep()\n");
fInode = NULL;
}
private:
status_t fStatus;
Inode* fInode;
};
#endif // INODE_H

View File

@ -0,0 +1,15 @@
SubDir HAIKU_TOP src add-ons kernel file_systems btrfs ;
UsePrivateHeaders [ FDirName kernel util ] ;
UsePrivateHeaders shared storage ;
UsePrivateKernelHeaders ;
KernelAddon btrfs :
kernel_interface.cpp
BPlusTree.cpp
Chunk.cpp
CRCTable.cpp
DirectoryIterator.cpp
Inode.cpp
Volume.cpp
;

View File

@ -0,0 +1,41 @@
/*
* Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#ifndef UTILITY_H
#define UTILITY_H
#include "btrfs.h"
enum inode_type {
S_DIRECTORY = S_IFDIR,
S_FILE = S_IFREG,
S_SYMLINK = S_IFLNK,
S_INDEX_TYPES = (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX
| S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX
| S_FLOAT_INDEX | S_DOUBLE_INDEX),
S_EXTENDED_TYPES = (S_ATTR_DIR | S_ATTR | S_INDEX_DIR)
};
/*! Converts the open mode, the open flags given to bfs_open(), into
access modes, e.g. since O_RDONLY requires read access to the
file, it will be converted to R_OK.
*/
inline int
open_mode_to_access(int openMode)
{
openMode &= O_RWMASK;
if (openMode == O_RDONLY)
return R_OK;
if (openMode == O_WRONLY)
return W_OK;
return R_OK | W_OK;
}
#endif // UTILITY_H

View File

@ -0,0 +1,535 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
//! Super block, mounting, etc.
#include "Volume.h"
#include <errno.h>
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fs_cache.h>
#include <fs_volume.h>
#include <util/AutoLock.h>
#include "BPlusTree.h"
#include "CachedBlock.h"
#include "Chunk.h"
#include "Inode.h"
//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
# define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#else
# define TRACE(x...) ;
#endif
# define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
class DeviceOpener {
public:
DeviceOpener(int fd, int mode);
DeviceOpener(const char* device, int mode);
~DeviceOpener();
int Open(const char* device, int mode);
int Open(int fd, int mode);
void* InitCache(off_t numBlocks, uint32 blockSize);
void RemoveCache(bool allowWrites);
void Keep();
int Device() const { return fDevice; }
int Mode() const { return fMode; }
bool IsReadOnly() const
{ return _IsReadOnly(fMode); }
status_t GetSize(off_t* _size, uint32* _blockSize = NULL);
private:
static bool _IsReadOnly(int mode)
{ return (mode & O_RWMASK) == O_RDONLY;}
static bool _IsReadWrite(int mode)
{ return (mode & O_RWMASK) == O_RDWR;}
int fDevice;
int fMode;
void* fBlockCache;
};
DeviceOpener::DeviceOpener(const char* device, int mode)
:
fBlockCache(NULL)
{
Open(device, mode);
}
DeviceOpener::DeviceOpener(int fd, int mode)
:
fBlockCache(NULL)
{
Open(fd, mode);
}
DeviceOpener::~DeviceOpener()
{
if (fDevice >= 0) {
RemoveCache(false);
close(fDevice);
}
}
int
DeviceOpener::Open(const char* device, int mode)
{
fDevice = open(device, mode | O_NOCACHE);
if (fDevice < 0)
fDevice = errno;
if (fDevice < 0 && _IsReadWrite(mode)) {
// try again to open read-only (don't rely on a specific error code)
return Open(device, O_RDONLY | O_NOCACHE);
}
if (fDevice >= 0) {
// opening succeeded
fMode = mode;
if (_IsReadWrite(mode)) {
// check out if the device really allows for read/write access
device_geometry geometry;
if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) {
if (geometry.read_only) {
// reopen device read-only
close(fDevice);
return Open(device, O_RDONLY | O_NOCACHE);
}
}
}
}
return fDevice;
}
int
DeviceOpener::Open(int fd, int mode)
{
fDevice = dup(fd);
if (fDevice < 0)
return errno;
fMode = mode;
return fDevice;
}
void*
DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
{
return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
IsReadOnly());
}
void
DeviceOpener::RemoveCache(bool allowWrites)
{
if (fBlockCache == NULL)
return;
block_cache_delete(fBlockCache, allowWrites);
fBlockCache = NULL;
}
void
DeviceOpener::Keep()
{
fDevice = -1;
}
/*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
to compute the size, or fstat() if that failed.
*/
status_t
DeviceOpener::GetSize(off_t* _size, uint32* _blockSize)
{
device_geometry geometry;
if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) {
// maybe it's just a file
struct stat stat;
if (fstat(fDevice, &stat) < 0)
return B_ERROR;
if (_size)
*_size = stat.st_size;
if (_blockSize) // that shouldn't cause us any problems
*_blockSize = 512;
return B_OK;
}
if (_size) {
*_size = 1ULL * geometry.head_count * geometry.cylinder_count
* geometry.sectors_per_track * geometry.bytes_per_sector;
}
if (_blockSize)
*_blockSize = geometry.bytes_per_sector;
return B_OK;
}
// #pragma mark -
bool
btrfs_super_block::IsValid()
{
// TODO: check some more values!
if (strncmp(magic, BTRFS_SUPER_BLOCK_MAGIC, sizeof(magic)) != 0)
return false;
return true;
}
// #pragma mark -
Volume::Volume(fs_volume* volume)
:
fFSVolume(volume),
fFlags(0),
fChunk(NULL),
fChunkTree(NULL)
{
mutex_init(&fLock, "btrfs volume");
}
Volume::~Volume()
{
TRACE("Volume destructor.\n");
}
bool
Volume::IsValidSuperBlock()
{
return fSuperBlock.IsValid();
}
const char*
Volume::Name() const
{
if (fSuperBlock.label[0])
return fSuperBlock.label;
return fName;
}
status_t
Volume::Mount(const char* deviceName, uint32 flags)
{
flags |= B_MOUNT_READ_ONLY;
// we only support read-only for now
if ((flags & B_MOUNT_READ_ONLY) != 0) {
TRACE("Volume::Mount(): Read only\n");
} else {
TRACE("Volume::Mount(): Read write\n");
}
DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
? O_RDONLY : O_RDWR);
fDevice = opener.Device();
if (fDevice < B_OK) {
ERROR("Volume::Mount(): couldn't open device\n");
return fDevice;
}
if (opener.IsReadOnly())
fFlags |= VOLUME_READ_ONLY;
// read the super block
status_t status = Identify(fDevice, &fSuperBlock);
if (status != B_OK) {
ERROR("Volume::Mount(): Identify() failed\n");
return status;
}
fBlockSize = fSuperBlock.BlockSize();
TRACE("block size %ld\n", fBlockSize);
uint8* start = (uint8*)&fSuperBlock.system_chunk_array[0];
uint8* end = (uint8*)&fSuperBlock.system_chunk_array[2048];
while (start < end) {
struct btrfs_key* key = (struct btrfs_key*)start;
TRACE("system_chunk_array object_id 0x%llx offset 0x%llx type 0x%x\n",
key->ObjectID(), key->Offset(), key->Type());
if (key->Type() != BTRFS_KEY_TYPE_CHUNK_ITEM) {
break;
}
struct btrfs_chunk* chunk = (struct btrfs_chunk*)(key + 1);
fChunk = new(std::nothrow) Chunk(chunk, key->Offset());
if (fChunk == NULL)
return B_ERROR;
start += sizeof(struct btrfs_key) + fChunk->Size();
}
TRACE("Volume::Mount() generation: %lld\n", fSuperBlock.Generation());
fsblock_t physical = 0;
FindBlock(fSuperBlock.Root(), physical);
TRACE("Volume::Mount() root: %lld (physical %lld)\n",
fSuperBlock.Root(), physical);
FindBlock(fSuperBlock.ChunkRoot(), physical);
TRACE("Volume::Mount() chunk_root: %lld (physical %lld)\n",
fSuperBlock.ChunkRoot(), physical);
FindBlock(fSuperBlock.LogRoot(), physical);
TRACE("Volume::Mount() log_root: %lld (physical %lld)\n",
fSuperBlock.LogRoot(), physical);
// check if the device size is large enough to hold the file system
off_t diskSize;
status = opener.GetSize(&diskSize);
if (status != B_OK)
return status;
if (diskSize < (off_t)fSuperBlock.TotalSize())
return B_BAD_VALUE;
fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize,
fBlockSize);
if (fBlockCache == NULL)
return B_ERROR;
TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
fChunkTree = new(std::nothrow) BPlusTree(this, fSuperBlock.ChunkRoot());
if (fChunkTree == NULL)
return B_NO_MEMORY;
FindBlock(fSuperBlock.Root(), physical);
TRACE("Volume::Mount() root: %lld (physical %lld)\n",
fSuperBlock.Root(), physical);
FindBlock(fSuperBlock.ChunkRoot(), physical);
TRACE("Volume::Mount() chunk_root: %lld (physical %lld)\n",
fSuperBlock.ChunkRoot(), physical);
FindBlock(fSuperBlock.LogRoot(), physical);
TRACE("Volume::Mount() log_root: %lld (physical %lld)\n",
fSuperBlock.LogRoot(), physical);
fRootTree = new(std::nothrow) BPlusTree(this, fSuperBlock.Root());
if (fRootTree == NULL)
return B_NO_MEMORY;
TRACE("Volume::Mount(): Searching extent root\n");
struct btrfs_key search_key;
search_key.SetOffset(0);
search_key.SetType(BTRFS_KEY_TYPE_ROOT_ITEM);
search_key.SetObjectID(BTRFS_OBJECT_ID_EXTENT_TREE);
struct btrfs_root *root;
if (fRootTree->FindNext(search_key, (void**)&root) != B_OK) {
ERROR("Volume::Mount(): Couldn't find extent root\n");
return B_ERROR;
}
TRACE("Volume::Mount(): Found extent root: %lld\n", root->BlockNum());
fExtentTree = new(std::nothrow) BPlusTree(this, root->BlockNum());
free(root);
if (fExtentTree == NULL)
return B_NO_MEMORY;
search_key.SetOffset(0);
search_key.SetObjectID(BTRFS_OBJECT_ID_FS_TREE);
if (fRootTree->FindNext(search_key, (void**)&root) != B_OK) {
ERROR("Volume::Mount(): Couldn't find fs root\n");
return B_ERROR;
}
TRACE("Volume::Mount(): Found fs root: %lld\n", root->BlockNum());
fFSTree = new(std::nothrow) BPlusTree(this, root->BlockNum());
free(root);
if (fFSTree == NULL)
return B_NO_MEMORY;
search_key.SetOffset(0);
search_key.SetObjectID(BTRFS_OBJECT_ID_DEV_TREE);
if (fRootTree->FindNext(search_key, (void**)&root) != B_OK) {
ERROR("Volume::Mount(): Couldn't find dev root\n");
return B_ERROR;
}
TRACE("Volume::Mount(): Found dev root: %lld\n", root->BlockNum());
fDevTree = new(std::nothrow) BPlusTree(this, root->BlockNum());
free(root);
if (fDevTree == NULL)
return B_NO_MEMORY;
search_key.SetOffset(0);
search_key.SetObjectID(BTRFS_OBJECT_ID_CHECKSUM_TREE);
if (fRootTree->FindNext(search_key, (void**)&root) != B_OK) {
ERROR("Volume::Mount(): Couldn't find checksum root\n");
return B_ERROR;
}
TRACE("Volume::Mount(): Found checksum root: %lld\n", root->BlockNum());
fChecksumTree = new(std::nothrow) BPlusTree(this, root->BlockNum());
free(root);
if (fChecksumTree == NULL)
return B_NO_MEMORY;
// ready
status = get_vnode(fFSVolume, BTRFS_OBJECT_ID_CHUNK_TREE,
(void**)&fRootNode);
if (status != B_OK) {
ERROR("could not create root node: get_vnode() failed!\n");
return status;
}
TRACE("Volume::Mount(): Found root node: %lld (%s)\n", fRootNode->ID(),
strerror(fRootNode->InitCheck()));
// all went fine
opener.Keep();
if (!fSuperBlock.label[0]) {
// generate a more or less descriptive volume name
off_t divisor = 1ULL << 40;
char unit = 'T';
if (diskSize < divisor) {
divisor = 1UL << 30;
unit = 'G';
if (diskSize < divisor) {
divisor = 1UL << 20;
unit = 'M';
}
}
double size = double((10 * diskSize + divisor - 1) / divisor);
// %g in the kernel does not support precision...
snprintf(fName, sizeof(fName), "%g %cB Btrfs Volume",
size / 10, unit);
}
return B_OK;
}
status_t
Volume::Unmount()
{
TRACE("Volume::Unmount()\n");
delete fExtentTree;
delete fChecksumTree;
delete fFSTree;
delete fDevTree;
fExtentTree = NULL;
fChecksumTree = NULL;
fFSTree = NULL;
fDevTree = NULL;
TRACE("Volume::Unmount(): Putting root node\n");
put_vnode(fFSVolume, RootNode()->ID());
TRACE("Volume::Unmount(): Deleting the block cache\n");
block_cache_delete(fBlockCache, !IsReadOnly());
TRACE("Volume::Unmount(): Closing device\n");
close(fDevice);
TRACE("Volume::Unmount(): Done\n");
return B_OK;
}
status_t
Volume::LoadSuperBlock()
{
CachedBlock cached(this);
const uint8* block = cached.SetTo(BTRFS_SUPER_BLOCK_OFFSET / fBlockSize);
if (block == NULL)
return B_IO_ERROR;
memcpy(&fSuperBlock, block + BTRFS_SUPER_BLOCK_OFFSET % fBlockSize,
sizeof(fSuperBlock));
return B_OK;
}
status_t
Volume::FindBlock(off_t logical, fsblock_t &physicalBlock)
{
off_t physical;
status_t status = FindBlock(logical, physical);
if (status != B_OK)
return status;
physicalBlock = physical / fBlockSize;
return B_OK;
}
status_t
Volume::FindBlock(off_t logical, off_t &physical)
{
if (fChunkTree == NULL
|| (logical >= fChunk->Offset() && logical < fChunk->End())) {
// try with fChunk
return fChunk->FindBlock(logical, physical);
}
struct btrfs_key search_key;
search_key.SetOffset(logical);
search_key.SetType(BTRFS_KEY_TYPE_CHUNK_ITEM);
search_key.SetObjectID(BTRFS_OBJECT_ID_CHUNK_TREE);
struct btrfs_chunk *chunk;
size_t chunk_length;
status_t status = fChunkTree->FindPrevious(search_key, (void**)&chunk,
&chunk_length);
if (status != B_OK)
return status;
Chunk _chunk(chunk, search_key.Offset());
free(chunk);
status = _chunk.FindBlock(logical, physical);
if (status != B_OK)
return status;
TRACE("Volume::FindBlock(): logical: %lld, physical: %lld\n", logical,
physical);
return B_OK;
}
// #pragma mark - Disk scanning and initialization
/*static*/ status_t
Volume::Identify(int fd, btrfs_super_block* superBlock)
{
if (read_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, superBlock,
sizeof(btrfs_super_block)) != sizeof(btrfs_super_block))
return B_IO_ERROR;
if (!superBlock->IsValid()) {
ERROR("invalid super block!\n");
return B_BAD_VALUE;
}
return B_OK;
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#ifndef VOLUME_H
#define VOLUME_H
#include <lock.h>
#include "btrfs.h"
enum volume_flags {
VOLUME_READ_ONLY = 0x0001
};
class BPlusTree;
class Chunk;
class Inode;
class Volume {
public:
Volume(fs_volume* volume);
~Volume();
status_t Mount(const char* device, uint32 flags);
status_t Unmount();
bool IsValidSuperBlock();
bool IsReadOnly() const
{ return (fFlags & VOLUME_READ_ONLY) != 0; }
Inode* RootNode() const { return fRootNode; }
int Device() const { return fDevice; }
dev_t ID() const
{ return fFSVolume ? fFSVolume->id : -1; }
fs_volume* FSVolume() const { return fFSVolume; }
const char* Name() const;
BPlusTree* FSTree() const { return fFSTree; }
BPlusTree* RootTree() const { return fRootTree; }
uint32 BlockSize() const { return fBlockSize; }
btrfs_super_block& SuperBlock() { return fSuperBlock; }
status_t LoadSuperBlock();
// cache access
void* BlockCache() { return fBlockCache; }
static status_t Identify(int fd, btrfs_super_block* superBlock);
status_t FindBlock(off_t logical, fsblock_t &physical);
status_t FindBlock(off_t logical, off_t &physical);
private:
mutex fLock;
fs_volume* fFSVolume;
int fDevice;
btrfs_super_block fSuperBlock;
char fName[32];
uint32 fFlags;
uint32 fBlockSize;
void* fBlockCache;
Inode* fRootNode;
Chunk* fChunk;
BPlusTree* fChunkTree;
BPlusTree* fRootTree;
BPlusTree* fDevTree;
BPlusTree* fExtentTree;
BPlusTree* fFSTree;
BPlusTree* fChecksumTree;
};
#endif // VOLUME_H

View File

@ -0,0 +1,328 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Distributed under the terms of the MIT License.
*/
#ifndef BTRFS_H
#define BTRFS_H
#include <sys/stat.h>
#include <ByteOrder.h>
#include <fs_interface.h>
#include <KernelExport.h>
typedef uint64 fileblock_t; // file block number
typedef uint64 fsblock_t; // filesystem block number
#define BTRFS_SUPER_BLOCK_OFFSET 0x10000
struct btrfs_key {
uint64 object_id;
uint8 type;
uint64 offset;
uint64 ObjectID() const { return B_LENDIAN_TO_HOST_INT64(object_id); }
uint8 Type() const { return type; }
uint64 Offset() const { return B_LENDIAN_TO_HOST_INT64(offset); }
void SetObjectID(uint64 id) { object_id = B_HOST_TO_LENDIAN_INT64(id); }
void SetType(uint8 key_type) { type = key_type; }
void SetOffset(uint64 off) { offset = B_HOST_TO_LENDIAN_INT64(off); }
} _PACKED;
struct btrfs_timespec {
uint64 seconds;
uint32 nanoseconds;
} _PACKED;
struct btrfs_header {
uint8 checksum[32];
uint8 fsid[16];
uint64 blocknum;
uint64 flags;
uint8 chunk_tree_uuid[16];
uint64 generation;
uint64 owner;
uint32 item_count;
uint8 level;
uint64 BlockNum() const { return B_LENDIAN_TO_HOST_INT64(blocknum); }
uint64 Flags() const { return B_LENDIAN_TO_HOST_INT64(flags); }
uint64 Generation() const {
return B_LENDIAN_TO_HOST_INT64(generation); }
uint64 Owner() const {
return B_LENDIAN_TO_HOST_INT64(owner); }
uint32 ItemCount() const {
return B_LENDIAN_TO_HOST_INT32(item_count); }
uint8 Level() const { return level; }
} _PACKED;
struct btrfs_index {
btrfs_key key;
uint64 blocknum;
uint64 generation;
uint64 BlockNum() const { return B_LENDIAN_TO_HOST_INT64(blocknum); }
uint64 Generation() const {
return B_LENDIAN_TO_HOST_INT64(generation); }
} _PACKED;
struct btrfs_entry {
btrfs_key key;
uint32 offset;
uint32 size;
uint32 Offset() const {
return B_LENDIAN_TO_HOST_INT32(offset); }
uint32 Size() const {
return B_LENDIAN_TO_HOST_INT32(size); }
} _PACKED;
struct btrfs_stream {
btrfs_header header;
union {
btrfs_entry entries[0];
btrfs_index index[0];
};
} _PACKED;
struct btrfs_stripe {
uint64 device_id;
uint64 offset;
uint8 device_uuid[16];
uint64 DeviceID() { return B_LENDIAN_TO_HOST_INT64(device_id); }
uint64 Offset() { return B_LENDIAN_TO_HOST_INT64(offset); }
} _PACKED;
struct btrfs_chunk {
uint64 length;
uint64 owner;
uint64 stripe_length;
uint64 type;
uint32 io_align;
uint32 io_width;
uint32 sector_size;
uint16 stripe_count;
uint16 sub_stripes;
struct btrfs_stripe stripes[0];
uint64 Length() const { return B_LENDIAN_TO_HOST_INT64(length); }
uint64 Owner() const { return B_LENDIAN_TO_HOST_INT64(owner); }
uint64 StripeLength() const
{ return B_LENDIAN_TO_HOST_INT64(stripe_length); }
uint64 Type() const { return B_LENDIAN_TO_HOST_INT64(type); }
uint32 IOAlign() const { return B_LENDIAN_TO_HOST_INT32(io_align); }
uint32 IOWidth() const { return B_LENDIAN_TO_HOST_INT32(io_width); }
uint32 SectorSize() const
{ return B_LENDIAN_TO_HOST_INT32(sector_size); }
uint16 StripeCount() const
{ return B_LENDIAN_TO_HOST_INT16(stripe_count); }
uint16 SubStripes() const
{ return B_LENDIAN_TO_HOST_INT16(sub_stripes); }
} _PACKED;
struct btrfs_device {
uint64 id;
uint64 total_size;
uint64 used_size;
uint32 io_align;
uint32 io_width;
uint32 sector_size;
uint64 type;
uint64 generation;
uint64 start_offset;
uint32 group;
uint8 seek_speed;
uint8 bandwidth;
uint8 uuid[16];
uint8 fsid[16];
} _PACKED;
struct btrfs_super_block {
uint8 checksum[32];
uint8 fsid[16];
uint64 blocknum;
uint64 flags;
char magic[8];
uint64 generation;
uint64 root;
uint64 chunk_root;
uint64 log_root;
uint64 log_root_transaction_id;
uint64 total_size;
uint64 used_size;
uint64 root_dir_object_id;
uint64 num_devices;
uint32 sector_size;
uint32 node_size;
uint32 leaf_size;
uint32 stripe_size;
uint32 system_chunk_array_size;
uint64 chunk_root_generation;
uint64 compat_flags;
uint64 readonly_flags;
uint64 incompat_flags;
uint16 checksum_type;
uint8 root_level;
uint8 chunck_root_level;
uint8 log_root_level;
struct btrfs_device device;
char label[256];
uint64 reserved[32];
uint8 system_chunk_array[2048];
bool IsValid();
// implemented in Volume.cpp
uint64 TotalSize() const { return B_LENDIAN_TO_HOST_INT64(total_size); }
uint32 BlockSize() const { return B_LENDIAN_TO_HOST_INT32(sector_size); }
uint64 RootDirObjectID() const {
return B_LENDIAN_TO_HOST_INT64(root_dir_object_id); }
uint64 Generation() const {
return B_LENDIAN_TO_HOST_INT64(generation); }
uint64 Root() const {
return B_LENDIAN_TO_HOST_INT64(root); }
uint64 ChunkRoot() const {
return B_LENDIAN_TO_HOST_INT64(chunk_root); }
uint64 LogRoot() const {
return B_LENDIAN_TO_HOST_INT64(log_root); }
uint8 ChunkRootLevel() { return chunck_root_level; }
} _PACKED;
struct btrfs_inode {
uint64 generation;
uint64 transaction_id;
uint64 size;
uint64 nbytes;
uint64 blockgroup;
uint32 num_links;
uint32 uid;
uint32 gid;
uint32 mode;
uint64 rdev;
uint64 flags;
uint64 sequence;
uint64 reserved[4];
struct btrfs_timespec access_time;
struct btrfs_timespec change_time;
struct btrfs_timespec modification_time;
struct btrfs_timespec creation_time;
uint64 Generation() const { return B_LENDIAN_TO_HOST_INT64(generation); }
uint64 Size() const { return B_LENDIAN_TO_HOST_INT64(size); }
uint32 UserID() const { return B_LENDIAN_TO_HOST_INT32(uid); }
uint32 GroupID() const { return B_LENDIAN_TO_HOST_INT32(gid); }
uint32 Mode() const { return B_LENDIAN_TO_HOST_INT32(mode); }
uint64 Flags() const { return B_LENDIAN_TO_HOST_INT64(flags); }
uint64 Sequence() const { return B_LENDIAN_TO_HOST_INT64(sequence); }
static void _DecodeTime(struct timespec &timespec,
const struct btrfs_timespec &time)
{
timespec.tv_sec = B_LENDIAN_TO_HOST_INT64(time.seconds);
timespec.tv_nsec = B_LENDIAN_TO_HOST_INT32(time.nanoseconds);
}
void GetAccessTime(struct timespec &timespec) const
{ _DecodeTime(timespec, access_time); }
void GetChangeTime(struct timespec &timespec) const
{ _DecodeTime(timespec, change_time); }
void GetModificationTime(struct timespec &timespec) const
{ _DecodeTime(timespec, modification_time); }
void GetCreationTime(struct timespec &timespec) const
{ _DecodeTime(timespec, creation_time); }
} _PACKED;
struct btrfs_root {
btrfs_inode inode;
uint64 generation;
uint64 root_dirid;
uint64 blocknum;
uint64 limit_bytes;
uint64 used_bytes;
uint64 last_snapshot;
uint64 flags;
uint32 refs;
btrfs_key drop_progress;
uint8 drop_level;
uint8 level;
uint64 Generation() const {
return B_LENDIAN_TO_HOST_INT64(generation); }
uint64 BlockNum() const { return B_LENDIAN_TO_HOST_INT64(blocknum); }
} _PACKED;
struct btrfs_dir_entry {
btrfs_key location;
uint64 transaction_id;
uint16 data_length;
uint16 name_length;
uint8 type;
uint16 DataLength() const { return B_LENDIAN_TO_HOST_INT16(data_length); }
uint16 NameLength() const { return B_LENDIAN_TO_HOST_INT16(name_length); }
ino_t InodeID() const { return location.ObjectID(); }
uint16 Length() const
{ return sizeof(this) + NameLength() + DataLength(); }
} _PACKED;
struct btrfs_extent_data {
uint64 generation;
uint64 memory_size;
uint8 compression;
uint8 encryption;
uint16 reserved;
uint8 type;
union {
struct {
uint64 disk_offset;
uint64 disk_size;
uint64 extent_offset;
uint64 size;
};
uint8 inline_data[0];
};
uint64 Generation() const {
return B_LENDIAN_TO_HOST_INT64(generation); }
uint64 MemoryBytes() const {
return B_LENDIAN_TO_HOST_INT64(memory_size); }
uint8 Type() const { return type; }
uint64 DiskOffset() const {
return B_LENDIAN_TO_HOST_INT64(disk_offset); }
uint64 DiskSize() const {
return B_LENDIAN_TO_HOST_INT64(disk_size); }
uint64 ExtentOffset() const {
return B_LENDIAN_TO_HOST_INT64(extent_offset); }
uint64 Size() const {
return B_LENDIAN_TO_HOST_INT64(size); }
} _PACKED;
#define BTRFS_SUPER_BLOCK_MAGIC "_BHRfS_M"
#define BTRFS_OBJECT_ID_ROOT_TREE 1
#define BTRFS_OBJECT_ID_EXTENT_TREE 2
#define BTRFS_OBJECT_ID_DEV_TREE 4
#define BTRFS_OBJECT_ID_FS_TREE 5
#define BTRFS_OBJECT_ID_ROOT_TREE_DIR 6
#define BTRFS_OBJECT_ID_CHECKSUM_TREE 7
#define BTRFS_OBJECT_ID_CHUNK_TREE 256
#define BTRFS_KEY_TYPE_CHUNK_ITEM 228
#define BTRFS_KEY_TYPE_DIR_ITEM 84
#define BTRFS_KEY_TYPE_DIR_INDEX 96
#define BTRFS_KEY_TYPE_EXTENT_DATA 108
#define BTRFS_KEY_TYPE_INODE_ITEM 1
#define BTRFS_KEY_TYPE_INODE_REF 12
#define BTRFS_KEY_TYPE_ROOT_ITEM 132
#define BTRFS_EXTENT_DATA_INLINE 0
#define BTRFS_EXTENT_DATA_REGULAR 1
#define BTRFS_EXTENT_DATA_PRE 2
struct file_cookie {
bigtime_t last_notification;
off_t last_size;
int open_mode;
};
#define BTRFS_OPEN_MODE_USER_MASK 0x7fffffff
extern fs_volume_ops gBtrfsVolumeOps;
extern fs_vnode_ops gBtrfsVnodeOps;
#endif // BTRFS_H

View File

@ -0,0 +1,68 @@
//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//
// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net
//---------------------------------------------------------------------
/*! \file crc_table.cpp
Standalone program to generate the CRC table used for calculating
UDF tag id CRC values.
This code based off of crc code in UDF-2.50 specs, as permitted.
See UDF-2.50 6.5 for more information.
Reflected version by Jéme Duval
*/
#include <stdio.h>
#include <sys/types.h>
typedef unsigned int uint32 ;
uint32
reflect32 (uint32 b)
{
uint32 rw = 0;
for (int i = 0; i < 32; i++){
if (b & 1)
rw |= 1 << (31 - i);
b >>= 1;
}
return rw;
}
int
main(int argc, char *argv[]) {
uint32 crc, poly;
if (argc != 2) {
fprintf(stderr, "USAGE: crc_table <octal polynomial=3667067501 for btrfs>\n");
return 0;
}
sscanf(argv[1], "%lo", &poly);
printf("//! CRC 0%o table, as generated by crc_table.cpp\n", poly);
printf("static uint32 crc_table[256] = { \n");
for (int n = 0; n < 256; n++) {
if (n%8 == 0)
printf(" ");
crc = reflect32(n);
for (int i = 0; i < 8; i++) {
if (crc & 0x80000000)
crc = (crc << 1) ^ poly;
else
crc <<= 1;
}
crc = reflect32(crc);
printf("0x%08x%s ", crc, (n != 255 ? "," : ""));
if (n%8 == 7)
printf("\n");
}
printf("};\n");
return 0;
}

View File

@ -0,0 +1,674 @@
/*
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#include <dirent.h>
#include <util/kernel_cpp.h>
#include <string.h>
#include <AutoDeleter.h>
#include <fs_cache.h>
#include <fs_info.h>
#include <io_requests.h>
#include <NodeMonitor.h>
#include <util/AutoLock.h>
#include "btrfs.h"
#include "DirectoryIterator.h"
#include "Inode.h"
#include "Utility.h"
//#define TRACE_BTRFS
#ifdef TRACE_BTRFS
# define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#else
# define TRACE(x...) ;
#endif
#define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
#define BTRFS_IO_SIZE 65536
struct identify_cookie {
btrfs_super_block super_block;
};
//! btrfs_io() callback hook
static status_t
iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
size_t size, struct file_io_vec* vecs, size_t* _count)
{
Inode* inode = (Inode*)cookie;
return file_map_translate(inode->Map(), offset, size, vecs, _count,
inode->GetVolume()->BlockSize());
}
//! btrfs_io() callback hook
static status_t
iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
bool partialTransfer, size_t bytesTransferred)
{
Inode* inode = (Inode*)cookie;
rw_lock_read_unlock(inode->Lock());
return B_OK;
}
// #pragma mark - Scanning
static float
btrfs_identify_partition(int fd, partition_data *partition, void **_cookie)
{
btrfs_super_block superBlock;
status_t status = Volume::Identify(fd, &superBlock);
if (status != B_OK)
return -1;
identify_cookie *cookie = new identify_cookie;
memcpy(&cookie->super_block, &superBlock, sizeof(btrfs_super_block));
*_cookie = cookie;
return 0.8f;
}
static status_t
btrfs_scan_partition(int fd, partition_data *partition, void *_cookie)
{
identify_cookie *cookie = (identify_cookie *)_cookie;
partition->status = B_PARTITION_VALID;
partition->flags |= B_PARTITION_FILE_SYSTEM;
partition->content_size = cookie->super_block.TotalSize();
partition->block_size = cookie->super_block.BlockSize();
partition->content_name = strdup(cookie->super_block.label);
if (partition->content_name == NULL)
return B_NO_MEMORY;
return B_OK;
}
static void
btrfs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
{
delete (identify_cookie*)_cookie;
}
// #pragma mark -
static status_t
btrfs_mount(fs_volume* _volume, const char* device, uint32 flags,
const char* args, ino_t* _rootID)
{
Volume* volume = new(std::nothrow) Volume(_volume);
if (volume == NULL)
return B_NO_MEMORY;
// TODO: this is a bit hacky: we can't use publish_vnode() to publish
// the root node, or else its file cache cannot be created (we could
// create it later, though). Therefore we're using get_vnode() in Mount(),
// but that requires us to export our volume data before calling it.
_volume->private_volume = volume;
_volume->ops = &gBtrfsVolumeOps;
status_t status = volume->Mount(device, flags);
if (status != B_OK) {
ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
delete volume;
return status;
}
*_rootID = volume->RootNode()->ID();
return B_OK;
}
static status_t
btrfs_unmount(fs_volume *_volume)
{
Volume* volume = (Volume *)_volume->private_volume;
status_t status = volume->Unmount();
delete volume;
return status;
}
static status_t
btrfs_read_fs_info(fs_volume* _volume, struct fs_info* info)
{
Volume* volume = (Volume*)_volume->private_volume;
// File system flags
info->flags = B_FS_IS_PERSISTENT
| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
info->io_size = BTRFS_IO_SIZE;
info->block_size = volume->BlockSize();
info->total_blocks = volume->SuperBlock().TotalSize() / volume->BlockSize();
info->free_blocks = 0; //volume->NumFreeBlocks();
// Volume name
strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
// File system name
strlcpy(info->fsh_name, "btrfs", sizeof(info->fsh_name));
return B_OK;
}
// #pragma mark -
static status_t
btrfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
uint32* _flags, bool reenter)
{
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = new(std::nothrow) Inode(volume, id);
if (inode == NULL)
return B_NO_MEMORY;
status_t status = inode->InitCheck();
if (status != B_OK)
delete inode;
if (status == B_OK) {
_node->private_node = inode;
_node->ops = &gBtrfsVnodeOps;
*_type = inode->Mode();
*_flags = 0;
} else
ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
return status;
}
static status_t
btrfs_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
{
delete (Inode*)_node->private_node;
return B_OK;
}
static bool
btrfs_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
{
return true;
}
static status_t
btrfs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
{
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
if (inode->FileCache() == NULL)
return B_BAD_VALUE;
rw_lock_read_lock(inode->Lock());
uint32 vecIndex = 0;
size_t vecOffset = 0;
size_t bytesLeft = *_numBytes;
status_t status;
while (true) {
file_io_vec fileVecs[8];
uint32 fileVecCount = 8;
status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
&fileVecCount, 0);
if (status != B_OK && status != B_BUFFER_OVERFLOW)
break;
bool bufferOverflow = status == B_BUFFER_OVERFLOW;
size_t bytes = bytesLeft;
status = read_file_io_vec_pages(volume->Device(), fileVecs,
fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
if (status != B_OK || !bufferOverflow)
break;
pos += bytes;
bytesLeft -= bytes;
}
rw_lock_read_unlock(inode->Lock());
return status;
}
static status_t
btrfs_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
{
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
#ifndef BTRFS_SHELL
if (io_request_is_write(request) && volume->IsReadOnly()) {
notify_io_request(request, B_READ_ONLY_DEVICE);
return B_READ_ONLY_DEVICE;
}
#endif
if (inode->FileCache() == NULL) {
#ifndef BTRFS_SHELL
notify_io_request(request, B_BAD_VALUE);
#endif
return B_BAD_VALUE;
}
// We lock the node here and will unlock it in the "finished" hook.
rw_lock_read_lock(inode->Lock());
return do_iterative_fd_io(volume->Device(), request,
iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
}
static status_t
btrfs_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
size_t size, struct file_io_vec* vecs, size_t* _count)
{
TRACE("btrfs_get_file_map()\n");
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
size_t index = 0, max = *_count;
while (true) {
off_t blockOffset;
off_t blockLength;
status_t status = inode->FindBlock(offset, blockOffset, &blockLength);
if (status != B_OK)
return status;
if (index > 0 && (vecs[index - 1].offset
== blockOffset - vecs[index - 1].length)) {
vecs[index - 1].length += blockLength;
} else {
if (index >= max) {
// we're out of file_io_vecs; let's bail out
*_count = index;
return B_BUFFER_OVERFLOW;
}
vecs[index].offset = blockOffset;
vecs[index].length = blockLength;
index++;
}
offset += blockLength;
size -= blockLength;
if (size <= vecs[index - 1].length || offset >= inode->Size()) {
// We're done!
*_count = index;
TRACE("btrfs_get_file_map for inode %lld\n", inode->ID());
return B_OK;
}
}
// can never get here
return B_ERROR;
}
// #pragma mark -
static status_t
btrfs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
ino_t* _vnodeID)
{
TRACE("btrfs_lookup: name address: %p (%s)\n", name, name);
Volume* volume = (Volume*)_volume->private_volume;
Inode* directory = (Inode*)_directory->private_node;
// check access permissions
status_t status = directory->CheckPermissions(X_OK);
if (status < B_OK)
return status;
status = DirectoryIterator(directory).Lookup(name, strlen(name), _vnodeID);
if (status != B_OK)
return status;
return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
}
static status_t
btrfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
void* buffer, size_t bufferLength)
{
TRACE("ioctl: %lu\n", cmd);
/*Volume* volume = (Volume*)_volume->private_volume;*/
return B_OK;
}
static status_t
btrfs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
{
Inode* inode = (Inode*)_node->private_node;
stat->st_dev = inode->GetVolume()->ID();
stat->st_ino = inode->ID();
stat->st_nlink = 1;
stat->st_blksize = BTRFS_IO_SIZE;
stat->st_uid = inode->UserID();
stat->st_gid = inode->GroupID();
stat->st_mode = inode->Mode();
stat->st_type = 0;
inode->GetAccessTime(stat->st_atim);
inode->GetModificationTime(stat->st_mtim);
inode->GetChangeTime(stat->st_ctim);
inode->GetCreationTime(stat->st_crtim);
stat->st_size = inode->Size();
stat->st_blocks = (inode->Size() + 511) / 512;
return B_OK;
}
static status_t
btrfs_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode,
void** _cookie)
{
Inode* inode = (Inode*)_node->private_node;
// opening a directory read-only is allowed, although you can't read
// any data from it.
if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
return B_IS_A_DIRECTORY;
status_t status = inode->CheckPermissions(open_mode_to_access(openMode)
| (openMode & O_TRUNC ? W_OK : 0));
if (status != B_OK)
return status;
// Prepare the cookie
file_cookie* cookie = new(std::nothrow) file_cookie;
if (cookie == NULL)
return B_NO_MEMORY;
ObjectDeleter<file_cookie> cookieDeleter(cookie);
cookie->open_mode = openMode & BTRFS_OPEN_MODE_USER_MASK;
cookie->last_size = inode->Size();
cookie->last_notification = system_time();
if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) {
// Disable the file cache, if requested?
status = file_cache_disable(inode->FileCache());
if (status != B_OK)
return status;
}
cookieDeleter.Detach();
*_cookie = cookie;
return B_OK;
}
static status_t
btrfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
void* buffer, size_t* _length)
{
Inode* inode = (Inode*)_node->private_node;
if (!inode->IsFile()) {
*_length = 0;
return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
}
return inode->ReadAt(pos, (uint8*)buffer, _length);
}
static status_t
btrfs_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
return B_OK;
}
static status_t
btrfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
{
file_cookie* cookie = (file_cookie*)_cookie;
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
if (inode->Size() != cookie->last_size)
notify_stat_changed(volume->ID(), inode->ID(), B_STAT_SIZE);
delete cookie;
return B_OK;
}
static status_t
btrfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
{
Inode* inode = (Inode*)_node->private_node;
return inode->CheckPermissions(accessMode);
}
static status_t
btrfs_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
size_t *_bufferSize)
{
Inode* inode = (Inode*)_node->private_node;
return inode->ReadAt(0, (uint8*)buffer, _bufferSize);
}
// #pragma mark - Directory functions
static status_t
btrfs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
{
Inode* inode = (Inode*)_node->private_node;
status_t status = inode->CheckPermissions(R_OK);
if (status < B_OK)
return status;
if (!inode->IsDirectory())
return B_NOT_A_DIRECTORY;
DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
if (iterator == NULL || iterator->InitCheck() != B_OK) {
delete iterator;
return B_NO_MEMORY;
}
*_cookie = iterator;
return B_OK;
}
static status_t
btrfs_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
struct dirent *dirent, size_t bufferSize, uint32 *_num)
{
DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
size_t length = bufferSize;
ino_t id;
status_t status = iterator->GetNext(dirent->d_name, &length, &id);
if (status == B_ENTRY_NOT_FOUND) {
*_num = 0;
return B_OK;
} else if (status != B_OK)
return status;
Volume* volume = (Volume*)_volume->private_volume;
dirent->d_dev = volume->ID();
dirent->d_ino = id;
dirent->d_reclen = sizeof(struct dirent) + length;
*_num = 1;
return B_OK;
}
static status_t
btrfs_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
{
DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
return iterator->Rewind();
}
static status_t
btrfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
{
return B_OK;
}
static status_t
btrfs_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
{
delete (DirectoryIterator*)_cookie;
return B_OK;
}
fs_volume_ops gBtrfsVolumeOps = {
&btrfs_unmount,
&btrfs_read_fs_info,
NULL, // write_fs_info()
NULL, // fs_sync,
&btrfs_get_vnode,
};
fs_vnode_ops gBtrfsVnodeOps = {
/* vnode operations */
&btrfs_lookup,
NULL,
&btrfs_put_vnode,
NULL, // btrfs_remove_vnode,
/* VM file access */
&btrfs_can_page,
&btrfs_read_pages,
NULL, // btrfs_write_pages,
NULL, // io()
NULL, // cancel_io()
&btrfs_get_file_map,
&btrfs_ioctl,
NULL,
NULL, // fs_select
NULL, // fs_deselect
NULL, // fs_fsync,
&btrfs_read_link,
NULL, // fs_create_symlink,
NULL, // fs_link,
NULL, // fs_unlink,
NULL, // fs_rename,
&btrfs_access,
&btrfs_read_stat,
NULL, // fs_write_stat,
NULL, // fs_preallocate
/* file operations */
NULL, // fs_create,
&btrfs_open,
&btrfs_close,
&btrfs_free_cookie,
&btrfs_read,
NULL, // fs_write,
/* directory operations */
NULL, // fs_create_dir,
NULL, // fs_remove_dir,
&btrfs_open_dir,
&btrfs_close_dir,
&btrfs_free_dir_cookie,
&btrfs_read_dir,
&btrfs_rewind_dir,
/* attribute directory operations */
NULL, // fs_open_attr_dir,
NULL, // fs_close_attr_dir,
NULL, // fs_free_attr_dir_cookie,
NULL, // fs_read_attr_dir,
NULL, // fs_rewind_attr_dir,
/* attribute operations */
NULL, // fs_create_attr,
NULL, // fs_open_attr,
NULL, // fs_close_attr,
NULL, // fs_free_attr_cookie,
NULL, // fs_read_attr,
NULL, // fs_write_attr,
NULL, // fs_read_attr_stat,
NULL, // fs_write_attr_stat,
NULL, // fs_rename_attr,
NULL, // fs_remove_attr,
};
static file_system_module_info sBtrfsFileSystem = {
{
"file_systems/btrfs" B_CURRENT_FS_API_VERSION,
0,
NULL,
},
"btrfs", // short_name
"btrfs File System", // pretty_name
0, // DDM flags
// scanning
btrfs_identify_partition,
btrfs_scan_partition,
btrfs_free_identify_partition_cookie,
NULL, // free_partition_content_cookie()
&btrfs_mount,
NULL,
};
module_info *modules[] = {
(module_info *)&sBtrfsFileSystem,
NULL,
};