* 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:
parent
85f15b4bf0
commit
6f4c36e297
@ -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 ;
|
||||
|
282
src/add-ons/kernel/file_systems/btrfs/BPlusTree.cpp
Normal file
282
src/add-ons/kernel/file_systems/btrfs/BPlusTree.cpp
Normal 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;
|
||||
}
|
||||
|
135
src/add-ons/kernel/file_systems/btrfs/BPlusTree.h
Normal file
135
src/add-ons/kernel/file_systems/btrfs/BPlusTree.h
Normal 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
|
69
src/add-ons/kernel/file_systems/btrfs/CRCTable.cpp
Normal file
69
src/add-ons/kernel/file_systems/btrfs/CRCTable.cpp
Normal 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;
|
||||
}
|
||||
|
10
src/add-ons/kernel/file_systems/btrfs/CRCTable.h
Normal file
10
src/add-ons/kernel/file_systems/btrfs/CRCTable.h
Normal 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);
|
||||
|
97
src/add-ons/kernel/file_systems/btrfs/CachedBlock.h
Normal file
97
src/add-ons/kernel/file_systems/btrfs/CachedBlock.h
Normal 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
|
77
src/add-ons/kernel/file_systems/btrfs/Chunk.cpp
Normal file
77
src/add-ons/kernel/file_systems/btrfs/Chunk.cpp
Normal 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;
|
||||
}
|
||||
|
31
src/add-ons/kernel/file_systems/btrfs/Chunk.h
Normal file
31
src/add-ons/kernel/file_systems/btrfs/Chunk.h
Normal 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
|
155
src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp
Normal file
155
src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.cpp
Normal 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;
|
||||
}
|
||||
|
30
src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.h
Normal file
30
src/add-ons/kernel/file_systems/btrfs/DirectoryIterator.h
Normal 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
|
253
src/add-ons/kernel/file_systems/btrfs/Inode.cpp
Normal file
253
src/add-ons/kernel/file_systems/btrfs/Inode.cpp
Normal 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;
|
||||
}
|
||||
|
152
src/add-ons/kernel/file_systems/btrfs/Inode.h
Normal file
152
src/add-ons/kernel/file_systems/btrfs/Inode.h
Normal 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 ×pec) const
|
||||
{ fNode.GetChangeTime(timespec); }
|
||||
void GetModificationTime(struct timespec ×pec) const
|
||||
{ fNode.GetModificationTime(timespec); }
|
||||
void GetCreationTime(struct timespec ×pec) const
|
||||
{ fNode.GetCreationTime(timespec); }
|
||||
void GetAccessTime(struct timespec ×pec) 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
|
15
src/add-ons/kernel/file_systems/btrfs/Jamfile
Normal file
15
src/add-ons/kernel/file_systems/btrfs/Jamfile
Normal 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
|
||||
;
|
41
src/add-ons/kernel/file_systems/btrfs/Utility.h
Normal file
41
src/add-ons/kernel/file_systems/btrfs/Utility.h
Normal 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
|
535
src/add-ons/kernel/file_systems/btrfs/Volume.cpp
Normal file
535
src/add-ons/kernel/file_systems/btrfs/Volume.cpp
Normal 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;
|
||||
}
|
||||
|
82
src/add-ons/kernel/file_systems/btrfs/Volume.h
Normal file
82
src/add-ons/kernel/file_systems/btrfs/Volume.h
Normal 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
|
||||
|
328
src/add-ons/kernel/file_systems/btrfs/btrfs.h
Normal file
328
src/add-ons/kernel/file_systems/btrfs/btrfs.h
Normal 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 ×pec,
|
||||
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 ×pec) const
|
||||
{ _DecodeTime(timespec, access_time); }
|
||||
void GetChangeTime(struct timespec ×pec) const
|
||||
{ _DecodeTime(timespec, change_time); }
|
||||
void GetModificationTime(struct timespec ×pec) const
|
||||
{ _DecodeTime(timespec, modification_time); }
|
||||
void GetCreationTime(struct timespec ×pec) 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
|
68
src/add-ons/kernel/file_systems/btrfs/crc_table.cpp
Normal file
68
src/add-ons/kernel/file_systems/btrfs/crc_table.cpp
Normal 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;
|
||||
}
|
674
src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp
Normal file
674
src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp
Normal 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,
|
||||
};
|
Loading…
Reference in New Issue
Block a user