mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-07 03:17:44 +00:00
parent
f996697fd5
commit
1feaf75605
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2013 Genode Labs GmbH
|
||||
* Copyright (C) 2012-2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
@ -22,420 +22,425 @@
|
||||
|
||||
namespace File_system {
|
||||
|
||||
using namespace Genode;
|
||||
using Genode::Noncopyable;
|
||||
|
||||
class Chunk_base;
|
||||
|
||||
template <unsigned> class Chunk;
|
||||
template <unsigned, typename> class Chunk_index;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Common base class of both 'Chunk' and 'Chunk_index'
|
||||
*/
|
||||
class Chunk_base : Noncopyable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Common base class of both 'Chunk' and 'Chunk_index'
|
||||
*/
|
||||
class File_system::Chunk_base : Noncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
class Index_out_of_range { };
|
||||
class Index_out_of_range { };
|
||||
|
||||
protected:
|
||||
protected:
|
||||
|
||||
seek_off_t const _base_offset;
|
||||
size_t _num_entries; /* corresponds to last used entry */
|
||||
seek_off_t const _base_offset;
|
||||
size_t _num_entries; /* corresponds to last used entry */
|
||||
|
||||
/**
|
||||
* Test if specified range lies within the chunk
|
||||
/**
|
||||
* Test if specified range lies within the chunk
|
||||
*/
|
||||
void assert_valid_range(seek_off_t start, size_t len,
|
||||
file_size_t chunk_size) const
|
||||
{
|
||||
if (is_zero()) return;
|
||||
|
||||
if (start < _base_offset)
|
||||
throw Index_out_of_range();
|
||||
|
||||
if (start + len > _base_offset + chunk_size)
|
||||
throw Index_out_of_range();
|
||||
}
|
||||
|
||||
Chunk_base(seek_off_t base_offset)
|
||||
: _base_offset(base_offset), _num_entries(0) { }
|
||||
|
||||
/**
|
||||
* Construct zero chunk
|
||||
*
|
||||
* A zero chunk is a chunk that cannot be written to. When reading
|
||||
* from it, it returns zeros. Because there is a single zero chunk
|
||||
* for each chunk type, the base offset is meaningless. We use a
|
||||
* base offset of ~0 as marker to identify zero chunks.
|
||||
*/
|
||||
Chunk_base() : _base_offset(~0L), _num_entries(0) { }
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Return absolute base offset of chunk in bytes
|
||||
*/
|
||||
seek_off_t base_offset() const { return _base_offset; }
|
||||
|
||||
/**
|
||||
* Return true if chunk is a read-only zero chunk
|
||||
*/
|
||||
bool is_zero() const { return _base_offset == (seek_off_t)(~0L); }
|
||||
|
||||
/**
|
||||
* Return true if chunk has no allocated sub chunks
|
||||
*/
|
||||
bool empty() const { return _num_entries == 0; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Chunk of bytes used as leaf in hierarchy of chunk indices
|
||||
*/
|
||||
template <unsigned CHUNK_SIZE>
|
||||
class File_system::Chunk : public Chunk_base
|
||||
{
|
||||
private:
|
||||
|
||||
char _data[CHUNK_SIZE];
|
||||
|
||||
public:
|
||||
|
||||
enum { SIZE = CHUNK_SIZE };
|
||||
|
||||
/**
|
||||
* Construct byte chunk
|
||||
*
|
||||
* \param base_offset absolute offset of chunk in bytes
|
||||
*
|
||||
* The first argument is unused. Its mere purpose is to make the
|
||||
* signature of the constructor compatible to the constructor
|
||||
* of 'Chunk_index'.
|
||||
*/
|
||||
Chunk(Allocator &, seek_off_t base_offset)
|
||||
:
|
||||
Chunk_base(base_offset)
|
||||
{
|
||||
memset(_data, 0, CHUNK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct zero chunk
|
||||
*/
|
||||
Chunk() { }
|
||||
|
||||
/**
|
||||
* Return number of used entries
|
||||
*
|
||||
* The returned value corresponds to the index of the last used
|
||||
* entry + 1. It does not correlate to the number of actually
|
||||
* allocated entries (there may be ranges of zero blocks).
|
||||
*/
|
||||
file_size_t used_size() const { return _num_entries; }
|
||||
|
||||
void write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
assert_valid_range(seek_offset, len, SIZE);
|
||||
|
||||
/* offset relative to this chunk */
|
||||
seek_off_t const local_offset = seek_offset - base_offset();
|
||||
|
||||
memcpy(&_data[local_offset], src, len);
|
||||
|
||||
_num_entries = max(_num_entries, local_offset + len);
|
||||
}
|
||||
|
||||
void read(char *dst, size_t len, seek_off_t seek_offset) const
|
||||
{
|
||||
assert_valid_range(seek_offset, len, SIZE);
|
||||
|
||||
memcpy(dst, &_data[seek_offset - base_offset()], len);
|
||||
}
|
||||
|
||||
void truncate(file_size_t size)
|
||||
{
|
||||
assert_valid_range(size, 0, SIZE);
|
||||
|
||||
/*
|
||||
* Offset of the first free position (relative to the beginning
|
||||
* this chunk).
|
||||
*/
|
||||
void assert_valid_range(seek_off_t start, size_t len,
|
||||
file_size_t chunk_size) const
|
||||
{
|
||||
if (is_zero()) return;
|
||||
seek_off_t const local_offset = size - base_offset();
|
||||
|
||||
if (start < _base_offset)
|
||||
throw Index_out_of_range();
|
||||
if (local_offset >= _num_entries)
|
||||
return;
|
||||
|
||||
if (start + len > _base_offset + chunk_size)
|
||||
throw Index_out_of_range();
|
||||
}
|
||||
memset(&_data[local_offset], 0, _num_entries - local_offset);
|
||||
|
||||
Chunk_base(seek_off_t base_offset)
|
||||
: _base_offset(base_offset), _num_entries(0) { }
|
||||
_num_entries = local_offset;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct zero chunk
|
||||
*
|
||||
* A zero chunk is a chunk that cannot be written to. When reading
|
||||
* from it, it returns zeros. Because there is a single zero chunk
|
||||
* for each chunk type, the base offset is meaningless. We use a
|
||||
* base offset of ~0 as marker to identify zero chunks.
|
||||
|
||||
template <unsigned NUM_ENTRIES, typename ENTRY_TYPE>
|
||||
class File_system::Chunk_index : public Chunk_base
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ENTRY_TYPE Entry;
|
||||
|
||||
enum { ENTRY_SIZE = ENTRY_TYPE::SIZE,
|
||||
SIZE = ENTRY_SIZE*NUM_ENTRIES };
|
||||
|
||||
private:
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
Entry * _entries[NUM_ENTRIES];
|
||||
|
||||
/**
|
||||
* Return instance of a zero sub chunk
|
||||
*/
|
||||
static Entry const &_zero_chunk()
|
||||
{
|
||||
static Entry zero_chunk;
|
||||
return zero_chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return sub chunk at given index
|
||||
*
|
||||
* If there is no sub chunk at the specified index, this function
|
||||
* transparently allocates one. Hence, the returned sub chunk
|
||||
* is ready to be written to.
|
||||
*/
|
||||
Entry &_entry_for_writing(unsigned index)
|
||||
{
|
||||
if (index >= NUM_ENTRIES)
|
||||
throw Index_out_of_range();
|
||||
|
||||
if (_entries[index])
|
||||
return *_entries[index];
|
||||
|
||||
seek_off_t entry_offset = base_offset() + index*ENTRY_SIZE;
|
||||
|
||||
_entries[index] = new (&_alloc) Entry(_alloc, entry_offset);
|
||||
|
||||
_num_entries = max(_num_entries, index + 1);
|
||||
|
||||
return *_entries[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return sub chunk at given index (for reading only)
|
||||
*
|
||||
* This function transparently provides a zero sub chunk for any
|
||||
* index that is not populated by a real chunk.
|
||||
*/
|
||||
Entry const &_entry_for_reading(unsigned index) const
|
||||
{
|
||||
if (index >= NUM_ENTRIES)
|
||||
throw Index_out_of_range();
|
||||
|
||||
if (_entries[index])
|
||||
return *_entries[index];
|
||||
|
||||
return _zero_chunk();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return index of entry located at specified byte offset
|
||||
*
|
||||
* The caller of this function must make sure that the offset
|
||||
* parameter is within the bounds of the chunk.
|
||||
*/
|
||||
unsigned _index_by_offset(seek_off_t offset) const
|
||||
{
|
||||
return (offset - base_offset()) / ENTRY_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply operation 'func' to a range of entries
|
||||
*/
|
||||
template <typename THIS, typename DATA, typename FUNC>
|
||||
static void _range_op(THIS &obj, DATA *data, size_t len,
|
||||
seek_off_t seek_offset, FUNC const &func)
|
||||
{
|
||||
/*
|
||||
* Depending on whether this function is called for reading
|
||||
* (const function) or writing (non-const function), the
|
||||
* operand type is const or non-const Entry. The correct type
|
||||
* is embedded as a trait in the 'FUNC' functor type.
|
||||
*/
|
||||
Chunk_base() : _base_offset(~0L), _num_entries(0) { }
|
||||
typedef typename FUNC::Entry Const_qualified_entry;
|
||||
|
||||
public:
|
||||
obj.assert_valid_range(seek_offset, len, SIZE);
|
||||
|
||||
/**
|
||||
* Return absolute base offset of chunk in bytes
|
||||
*/
|
||||
seek_off_t base_offset() const { return _base_offset; }
|
||||
while (len > 0) {
|
||||
|
||||
/**
|
||||
* Return true if chunk is a read-only zero chunk
|
||||
*/
|
||||
bool is_zero() const { return _base_offset == (seek_off_t)(~0L); }
|
||||
unsigned const index = obj._index_by_offset(seek_offset);
|
||||
|
||||
/**
|
||||
* Return true if chunk has no allocated sub chunks
|
||||
*/
|
||||
bool empty() const { return _num_entries == 0; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Chunk of bytes used as leaf in hierarchy of chunk indices
|
||||
*/
|
||||
template <unsigned CHUNK_SIZE>
|
||||
class Chunk : public Chunk_base
|
||||
{
|
||||
private:
|
||||
|
||||
char _data[CHUNK_SIZE];
|
||||
|
||||
public:
|
||||
|
||||
enum { SIZE = CHUNK_SIZE };
|
||||
|
||||
/**
|
||||
* Construct byte chunk
|
||||
*
|
||||
* \param base_offset absolute offset of chunk in bytes
|
||||
*
|
||||
* The first argument is unused. Its mere purpose is to make the
|
||||
* signature of the constructor compatible to the constructor
|
||||
* of 'Chunk_index'.
|
||||
*/
|
||||
Chunk(Allocator &, seek_off_t base_offset)
|
||||
:
|
||||
Chunk_base(base_offset)
|
||||
{
|
||||
memset(_data, 0, CHUNK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct zero chunk
|
||||
*/
|
||||
Chunk() { }
|
||||
|
||||
/**
|
||||
* Return number of used entries
|
||||
*
|
||||
* The returned value corresponds to the index of the last used
|
||||
* entry + 1. It does not correlate to the number of actually
|
||||
* allocated entries (there may be ranges of zero blocks).
|
||||
*/
|
||||
file_size_t used_size() const { return _num_entries; }
|
||||
|
||||
void write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
assert_valid_range(seek_offset, len, SIZE);
|
||||
|
||||
/* offset relative to this chunk */
|
||||
seek_off_t const local_offset = seek_offset - base_offset();
|
||||
|
||||
memcpy(&_data[local_offset], src, len);
|
||||
|
||||
_num_entries = max(_num_entries, local_offset + len);
|
||||
}
|
||||
|
||||
void read(char *dst, size_t len, seek_off_t seek_offset) const
|
||||
{
|
||||
assert_valid_range(seek_offset, len, SIZE);
|
||||
|
||||
memcpy(dst, &_data[seek_offset - base_offset()], len);
|
||||
}
|
||||
|
||||
void truncate(file_size_t size)
|
||||
{
|
||||
assert_valid_range(size, 0, SIZE);
|
||||
Const_qualified_entry &entry = FUNC::lookup(obj, index);
|
||||
|
||||
/*
|
||||
* Offset of the first free position (relative to the beginning
|
||||
* this chunk).
|
||||
* Calculate byte offset relative to the chunk
|
||||
*
|
||||
* We cannot use 'entry.base_offset()' for this calculation
|
||||
* because in the const case, the lookup might return a
|
||||
* zero chunk, which has no defined base offset. Therefore,
|
||||
* we calculate the base offset via index*ENTRY_SIZE.
|
||||
*/
|
||||
seek_off_t const local_offset = size - base_offset();
|
||||
seek_off_t const local_seek_offset =
|
||||
seek_offset - obj.base_offset() - index*ENTRY_SIZE;
|
||||
|
||||
if (local_offset >= _num_entries)
|
||||
return;
|
||||
/* available capacity at 'entry' starting at seek offset */
|
||||
seek_off_t const capacity = ENTRY_SIZE - local_seek_offset;
|
||||
seek_off_t const curr_len = min(len, capacity);
|
||||
|
||||
memset(&_data[local_offset], 0, _num_entries - local_offset);
|
||||
/* apply functor (read or write) to entry */
|
||||
func(entry, data, curr_len, seek_offset);
|
||||
|
||||
_num_entries = local_offset;
|
||||
/* advance to next entry */
|
||||
len -= curr_len;
|
||||
data += curr_len;
|
||||
seek_offset += curr_len;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <unsigned NUM_ENTRIES, typename ENTRY_TYPE>
|
||||
class Chunk_index : public Chunk_base
|
||||
{
|
||||
public:
|
||||
}
|
||||
|
||||
struct Write_func
|
||||
{
|
||||
typedef ENTRY_TYPE Entry;
|
||||
|
||||
enum { ENTRY_SIZE = ENTRY_TYPE::SIZE,
|
||||
SIZE = ENTRY_SIZE*NUM_ENTRIES };
|
||||
static Entry &lookup(Chunk_index &chunk, unsigned i) {
|
||||
return chunk._entry_for_writing(i); }
|
||||
|
||||
private:
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
Entry * _entries[NUM_ENTRIES];
|
||||
|
||||
/**
|
||||
* Return instance of a zero sub chunk
|
||||
*/
|
||||
static Entry const &_zero_chunk()
|
||||
void operator () (Entry &entry, char const *src, size_t len,
|
||||
seek_off_t seek_offset) const
|
||||
{
|
||||
static Entry zero_chunk;
|
||||
return zero_chunk;
|
||||
entry.write(src, len, seek_offset);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return sub chunk at given index
|
||||
*
|
||||
* If there is no sub chunk at the specified index, this function
|
||||
* transparently allocates one. Hence, the returned sub chunk
|
||||
* is ready to be written to.
|
||||
*/
|
||||
Entry &_entry_for_writing(unsigned index)
|
||||
struct Read_func
|
||||
{
|
||||
typedef ENTRY_TYPE const Entry;
|
||||
|
||||
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
|
||||
return chunk._entry_for_reading(i); }
|
||||
|
||||
void operator () (Entry &entry, char *dst, size_t len,
|
||||
seek_off_t seek_offset) const
|
||||
{
|
||||
if (index >= NUM_ENTRIES)
|
||||
throw Index_out_of_range();
|
||||
|
||||
if (_entries[index])
|
||||
return *_entries[index];
|
||||
|
||||
seek_off_t entry_offset = base_offset() + index*ENTRY_SIZE;
|
||||
|
||||
_entries[index] = new (&_alloc) Entry(_alloc, entry_offset);
|
||||
|
||||
_num_entries = max(_num_entries, index + 1);
|
||||
|
||||
return *_entries[index];
|
||||
if (entry.is_zero())
|
||||
memset(dst, 0, len);
|
||||
else
|
||||
entry.read(dst, len, seek_offset);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return sub chunk at given index (for reading only)
|
||||
*
|
||||
* This function transparently provides a zero sub chunk for any
|
||||
* index that is not populated by a real chunk.
|
||||
*/
|
||||
Entry const &_entry_for_reading(unsigned index) const
|
||||
{
|
||||
if (index >= NUM_ENTRIES)
|
||||
throw Index_out_of_range();
|
||||
void _init_entries()
|
||||
{
|
||||
for (unsigned i = 0; i < NUM_ENTRIES; i++)
|
||||
_entries[i] = 0;
|
||||
}
|
||||
|
||||
if (_entries[index])
|
||||
return *_entries[index];
|
||||
|
||||
return _zero_chunk();
|
||||
void _destroy_entry(unsigned i)
|
||||
{
|
||||
if (_entries[i] && (i < _num_entries)) {
|
||||
destroy(&_alloc, _entries[i]);
|
||||
_entries[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return index of entry located at specified byte offset
|
||||
*
|
||||
* The caller of this function must make sure that the offset
|
||||
* parameter is within the bounds of the chunk.
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc allocator to use for allocating sub-chunk
|
||||
* indices and chunks
|
||||
* \param base_offset absolute offset of the chunk in bytes
|
||||
*/
|
||||
Chunk_index(Allocator &alloc, seek_off_t base_offset)
|
||||
: Chunk_base(base_offset), _alloc(alloc) { _init_entries(); }
|
||||
|
||||
/**
|
||||
* Construct zero chunk
|
||||
*/
|
||||
Chunk_index() : _alloc(*(Allocator *)0) { }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Chunk_index()
|
||||
{
|
||||
for (unsigned i = 0; i < NUM_ENTRIES; i++)
|
||||
_destroy_entry(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return size of chunk in bytes
|
||||
*
|
||||
* The returned value corresponds to the position after the highest
|
||||
* offset that was written to.
|
||||
*/
|
||||
file_size_t used_size() const
|
||||
{
|
||||
if (_num_entries == 0)
|
||||
return 0;
|
||||
|
||||
/* size of entries that lie completely within the used range */
|
||||
file_size_t const size_whole_entries = ENTRY_SIZE*(_num_entries - 1);
|
||||
|
||||
Entry *last_entry = _entries[_num_entries - 1];
|
||||
if (!last_entry)
|
||||
return size_whole_entries;
|
||||
|
||||
return size_whole_entries + last_entry->used_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to chunk
|
||||
*/
|
||||
void write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
_range_op(*this, src, len, seek_offset, Write_func());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from chunk
|
||||
*/
|
||||
void read(char *dst, size_t len, seek_off_t seek_offset) const
|
||||
{
|
||||
_range_op(*this, dst, len, seek_offset, Read_func());
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate chunk to specified size in bytes
|
||||
*
|
||||
* This function can be used to shrink a chunk only. Specifying a
|
||||
* 'size' larger than 'used_size' has no effect. The value returned
|
||||
* by 'used_size' refers always to the position of the last byte
|
||||
* written to the chunk.
|
||||
*/
|
||||
void truncate(file_size_t size)
|
||||
{
|
||||
unsigned const trunc_index = _index_by_offset(size);
|
||||
|
||||
if (trunc_index >= _num_entries)
|
||||
return;
|
||||
|
||||
for (unsigned i = trunc_index + 1; i < _num_entries; i++)
|
||||
_destroy_entry(i);
|
||||
|
||||
/* traverse into sub chunks */
|
||||
if (_entries[trunc_index])
|
||||
_entries[trunc_index]->truncate(size);
|
||||
|
||||
_num_entries = trunc_index + 1;
|
||||
|
||||
/*
|
||||
* If the truncated at a chunk boundary, we can release the
|
||||
* empty trailing chunk at 'trunc_index'.
|
||||
*/
|
||||
unsigned _index_by_offset(seek_off_t offset) const
|
||||
{
|
||||
return (offset - base_offset()) / ENTRY_SIZE;
|
||||
if (_entries[trunc_index] && _entries[trunc_index]->empty()) {
|
||||
_destroy_entry(trunc_index);
|
||||
_num_entries--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply operation 'func' to a range of entries
|
||||
*/
|
||||
template <typename THIS, typename DATA, typename FUNC>
|
||||
static void _range_op(THIS &obj, DATA *data, size_t len,
|
||||
seek_off_t seek_offset, FUNC const &func)
|
||||
{
|
||||
/*
|
||||
* Depending on whether this function is called for reading
|
||||
* (const function) or writing (non-const function), the
|
||||
* operand type is const or non-const Entry. The correct type
|
||||
* is embedded as a trait in the 'FUNC' functor type.
|
||||
*/
|
||||
typedef typename FUNC::Entry Const_qualified_entry;
|
||||
|
||||
obj.assert_valid_range(seek_offset, len, SIZE);
|
||||
|
||||
while (len > 0) {
|
||||
|
||||
unsigned const index = obj._index_by_offset(seek_offset);
|
||||
|
||||
Const_qualified_entry &entry = FUNC::lookup(obj, index);
|
||||
|
||||
/*
|
||||
* Calculate byte offset relative to the chunk
|
||||
*
|
||||
* We cannot use 'entry.base_offset()' for this calculation
|
||||
* because in the const case, the lookup might return a
|
||||
* zero chunk, which has no defined base offset. Therefore,
|
||||
* we calculate the base offset via index*ENTRY_SIZE.
|
||||
*/
|
||||
seek_off_t const local_seek_offset =
|
||||
seek_offset - obj.base_offset() - index*ENTRY_SIZE;
|
||||
|
||||
/* available capacity at 'entry' starting at seek offset */
|
||||
seek_off_t const capacity = ENTRY_SIZE - local_seek_offset;
|
||||
seek_off_t const curr_len = min(len, capacity);
|
||||
|
||||
/* apply functor (read or write) to entry */
|
||||
func(entry, data, curr_len, seek_offset);
|
||||
|
||||
/* advance to next entry */
|
||||
len -= curr_len;
|
||||
data += curr_len;
|
||||
seek_offset += curr_len;
|
||||
}
|
||||
}
|
||||
|
||||
struct Write_func
|
||||
{
|
||||
typedef ENTRY_TYPE Entry;
|
||||
|
||||
static Entry &lookup(Chunk_index &chunk, unsigned i) {
|
||||
return chunk._entry_for_writing(i); }
|
||||
|
||||
void operator () (Entry &entry, char const *src, size_t len,
|
||||
seek_off_t seek_offset) const
|
||||
{
|
||||
entry.write(src, len, seek_offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct Read_func
|
||||
{
|
||||
typedef ENTRY_TYPE const Entry;
|
||||
|
||||
static Entry &lookup(Chunk_index const &chunk, unsigned i) {
|
||||
return chunk._entry_for_reading(i); }
|
||||
|
||||
void operator () (Entry &entry, char *dst, size_t len,
|
||||
seek_off_t seek_offset) const
|
||||
{
|
||||
if (entry.is_zero())
|
||||
memset(dst, 0, len);
|
||||
else
|
||||
entry.read(dst, len, seek_offset);
|
||||
}
|
||||
};
|
||||
|
||||
void _init_entries()
|
||||
{
|
||||
for (unsigned i = 0; i < NUM_ENTRIES; i++)
|
||||
_entries[i] = 0;
|
||||
}
|
||||
|
||||
void _destroy_entry(unsigned i)
|
||||
{
|
||||
if (_entries[i] && (i < _num_entries)) {
|
||||
destroy(&_alloc, _entries[i]);
|
||||
_entries[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc allocator to use for allocating sub-chunk
|
||||
* indices and chunks
|
||||
* \param base_offset absolute offset of the chunk in bytes
|
||||
*/
|
||||
Chunk_index(Allocator &alloc, seek_off_t base_offset)
|
||||
: Chunk_base(base_offset), _alloc(alloc) { _init_entries(); }
|
||||
|
||||
/**
|
||||
* Construct zero chunk
|
||||
*/
|
||||
Chunk_index() : _alloc(*(Allocator *)0) { }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Chunk_index()
|
||||
{
|
||||
for (unsigned i = 0; i < NUM_ENTRIES; i++)
|
||||
_destroy_entry(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return size of chunk in bytes
|
||||
*
|
||||
* The returned value corresponds to the position after the highest
|
||||
* offset that was written to.
|
||||
*/
|
||||
file_size_t used_size() const
|
||||
{
|
||||
if (_num_entries == 0)
|
||||
return 0;
|
||||
|
||||
/* size of entries that lie completely within the used range */
|
||||
file_size_t const size_whole_entries = ENTRY_SIZE*(_num_entries - 1);
|
||||
|
||||
Entry *last_entry = _entries[_num_entries - 1];
|
||||
if (!last_entry)
|
||||
return size_whole_entries;
|
||||
|
||||
return size_whole_entries + last_entry->used_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to chunk
|
||||
*/
|
||||
void write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
_range_op(*this, src, len, seek_offset, Write_func());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from chunk
|
||||
*/
|
||||
void read(char *dst, size_t len, seek_off_t seek_offset) const
|
||||
{
|
||||
_range_op(*this, dst, len, seek_offset, Read_func());
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate chunk to specified size in bytes
|
||||
*
|
||||
* This function can be used to shrink a chunk only. Specifying a
|
||||
* 'size' larger than 'used_size' has no effect. The value returned
|
||||
* by 'used_size' refers always to the position of the last byte
|
||||
* written to the chunk.
|
||||
*/
|
||||
void truncate(file_size_t size)
|
||||
{
|
||||
unsigned const trunc_index = _index_by_offset(size);
|
||||
|
||||
if (trunc_index >= _num_entries)
|
||||
return;
|
||||
|
||||
for (unsigned i = trunc_index + 1; i < _num_entries; i++)
|
||||
_destroy_entry(i);
|
||||
|
||||
/* traverse into sub chunks */
|
||||
if (_entries[trunc_index])
|
||||
_entries[trunc_index]->truncate(size);
|
||||
|
||||
_num_entries = trunc_index + 1;
|
||||
|
||||
/*
|
||||
* If the truncated at a chunk boundary, we can release the
|
||||
* empty trailing chunk at 'trunc_index'.
|
||||
*/
|
||||
if (_entries[trunc_index] && _entries[trunc_index]->empty()) {
|
||||
_destroy_entry(trunc_index);
|
||||
_num_entries--;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__RAM_FS__CHUNK_H_ */
|
||||
|
@ -4,6 +4,13 @@
|
||||
* \date 2012-04-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__RAM_FS__DIRECTORY_H_
|
||||
#define _INCLUDE__RAM_FS__DIRECTORY_H_
|
||||
|
||||
@ -15,207 +22,207 @@
|
||||
#include <ram_fs/file.h>
|
||||
#include <ram_fs/symlink.h>
|
||||
|
||||
namespace File_system {
|
||||
namespace File_system { class Directory; }
|
||||
|
||||
class Directory : public Node
|
||||
{
|
||||
private:
|
||||
|
||||
List<Node> _entries;
|
||||
size_t _num_entries;
|
||||
class File_system::Directory : public Node
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
List<Node> _entries;
|
||||
size_t _num_entries;
|
||||
|
||||
Directory(char const *name) : _num_entries(0) { Node::name(name); }
|
||||
public:
|
||||
|
||||
Node *entry_unsynchronized(size_t index)
|
||||
{
|
||||
Node *node = _entries.first();
|
||||
for (unsigned i = 0; i < index && node; node = node->next(), i++);
|
||||
return node;
|
||||
}
|
||||
Directory(char const *name) : _num_entries(0) { Node::name(name); }
|
||||
|
||||
bool has_sub_node_unsynchronized(char const *name) const
|
||||
{
|
||||
Node const *sub_node = _entries.first();
|
||||
for (; sub_node; sub_node = sub_node->next())
|
||||
if (strcmp(sub_node->name(), name) == 0)
|
||||
return true;
|
||||
Node *entry_unsynchronized(size_t index)
|
||||
{
|
||||
Node *node = _entries.first();
|
||||
for (unsigned i = 0; i < index && node; node = node->next(), i++);
|
||||
return node;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool has_sub_node_unsynchronized(char const *name) const
|
||||
{
|
||||
Node const *sub_node = _entries.first();
|
||||
for (; sub_node; sub_node = sub_node->next())
|
||||
if (strcmp(sub_node->name(), name) == 0)
|
||||
return true;
|
||||
|
||||
void adopt_unsynchronized(Node *node)
|
||||
{
|
||||
/*
|
||||
* XXX inc ref counter
|
||||
*/
|
||||
_entries.insert(node);
|
||||
_num_entries++;
|
||||
return false;
|
||||
}
|
||||
|
||||
mark_as_updated();
|
||||
}
|
||||
|
||||
void discard_unsynchronized(Node *node)
|
||||
{
|
||||
_entries.remove(node);
|
||||
_num_entries--;
|
||||
|
||||
mark_as_updated();
|
||||
}
|
||||
|
||||
Node *lookup_and_lock(char const *path, bool return_parent = false)
|
||||
{
|
||||
if (strcmp(path, "") == 0) {
|
||||
lock();
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!path || path[0] == '/')
|
||||
throw Lookup_failed();
|
||||
|
||||
/* find first path delimiter */
|
||||
unsigned i = 0;
|
||||
for (; path[i] && path[i] != '/'; i++);
|
||||
|
||||
/*
|
||||
* If no path delimiter was found, we are the parent of the
|
||||
* specified path.
|
||||
*/
|
||||
if (path[i] == 0 && return_parent) {
|
||||
lock();
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* The offset 'i' corresponds to the end of the first path
|
||||
* element, which can be either the end of the string or the
|
||||
* first '/' character.
|
||||
*/
|
||||
|
||||
/* try to find entry that matches the first path element */
|
||||
Node *sub_node = _entries.first();
|
||||
for (; sub_node; sub_node = sub_node->next())
|
||||
if ((strlen(sub_node->name()) == i) &&
|
||||
(strcmp(sub_node->name(), path, i) == 0))
|
||||
break;
|
||||
|
||||
if (!sub_node)
|
||||
throw Lookup_failed();
|
||||
|
||||
if (is_basename(path)) {
|
||||
|
||||
/*
|
||||
* Because 'path' is a basename that corresponds to an
|
||||
* existing sub_node, we have found what we were looking
|
||||
* for.
|
||||
*/
|
||||
sub_node->lock();
|
||||
return sub_node;
|
||||
}
|
||||
|
||||
/*
|
||||
* As 'path' contains one or more path delimiters, traverse
|
||||
* into the sub directory names after the first path element.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We cannot traverse into anything other than a directory.
|
||||
*
|
||||
* XXX we might follow symlinks here
|
||||
*/
|
||||
Directory *sub_dir = dynamic_cast<Directory *>(sub_node);
|
||||
if (!sub_dir)
|
||||
throw Lookup_failed();
|
||||
|
||||
return sub_dir->lookup_and_lock(path + i + 1, return_parent);
|
||||
}
|
||||
|
||||
Directory *lookup_and_lock_dir(char const *path)
|
||||
{
|
||||
Node *node = lookup_and_lock(path);
|
||||
|
||||
Directory *dir = dynamic_cast<Directory *>(node);
|
||||
if (dir)
|
||||
return dir;
|
||||
|
||||
node->unlock();
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
File *lookup_and_lock_file(char const *path)
|
||||
{
|
||||
Node *node = lookup_and_lock(path);
|
||||
|
||||
File *file = dynamic_cast<File *>(node);
|
||||
if (file)
|
||||
return file;
|
||||
|
||||
node->unlock();
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
Symlink *lookup_and_lock_symlink(char const *path)
|
||||
{
|
||||
Node *node = lookup_and_lock(path);
|
||||
|
||||
Symlink *symlink = dynamic_cast<Symlink *>(node);
|
||||
if (symlink)
|
||||
return symlink;
|
||||
|
||||
node->unlock();
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup parent directory of the specified path
|
||||
*
|
||||
* \throw Lookup_failed
|
||||
void adopt_unsynchronized(Node *node)
|
||||
{
|
||||
/*
|
||||
* XXX inc ref counter
|
||||
*/
|
||||
Directory *lookup_and_lock_parent(char const *path)
|
||||
{
|
||||
return static_cast<Directory *>(lookup_and_lock(path, true));
|
||||
_entries.insert(node);
|
||||
_num_entries++;
|
||||
|
||||
mark_as_updated();
|
||||
}
|
||||
|
||||
void discard_unsynchronized(Node *node)
|
||||
{
|
||||
_entries.remove(node);
|
||||
_num_entries--;
|
||||
|
||||
mark_as_updated();
|
||||
}
|
||||
|
||||
Node *lookup_and_lock(char const *path, bool return_parent = false)
|
||||
{
|
||||
if (strcmp(path, "") == 0) {
|
||||
lock();
|
||||
return this;
|
||||
}
|
||||
|
||||
size_t read(char *dst, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
if (len < sizeof(Directory_entry)) {
|
||||
PERR("read buffer too small for directory entry");
|
||||
return 0;
|
||||
}
|
||||
if (!path || path[0] == '/')
|
||||
throw Lookup_failed();
|
||||
|
||||
seek_off_t index = seek_offset / sizeof(Directory_entry);
|
||||
/* find first path delimiter */
|
||||
unsigned i = 0;
|
||||
for (; path[i] && path[i] != '/'; i++);
|
||||
|
||||
if (seek_offset % sizeof(Directory_entry)) {
|
||||
PERR("seek offset not alighed to sizeof(Directory_entry)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Node *node = entry_unsynchronized(index);
|
||||
|
||||
/* index out of range */
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
Directory_entry *e = (Directory_entry *)(dst);
|
||||
|
||||
if (dynamic_cast<File *>(node)) e->type = Directory_entry::TYPE_FILE;
|
||||
if (dynamic_cast<Directory *>(node)) e->type = Directory_entry::TYPE_DIRECTORY;
|
||||
if (dynamic_cast<Symlink *>(node)) e->type = Directory_entry::TYPE_SYMLINK;
|
||||
|
||||
strncpy(e->name, node->name(), sizeof(e->name));
|
||||
|
||||
return sizeof(Directory_entry);
|
||||
/*
|
||||
* If no path delimiter was found, we are the parent of the
|
||||
* specified path.
|
||||
*/
|
||||
if (path[i] == 0 && return_parent) {
|
||||
lock();
|
||||
return this;
|
||||
}
|
||||
|
||||
size_t write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
/* writing to directory nodes is not supported */
|
||||
/*
|
||||
* The offset 'i' corresponds to the end of the first path
|
||||
* element, which can be either the end of the string or the
|
||||
* first '/' character.
|
||||
*/
|
||||
|
||||
/* try to find entry that matches the first path element */
|
||||
Node *sub_node = _entries.first();
|
||||
for (; sub_node; sub_node = sub_node->next())
|
||||
if ((strlen(sub_node->name()) == i) &&
|
||||
(strcmp(sub_node->name(), path, i) == 0))
|
||||
break;
|
||||
|
||||
if (!sub_node)
|
||||
throw Lookup_failed();
|
||||
|
||||
if (is_basename(path)) {
|
||||
|
||||
/*
|
||||
* Because 'path' is a basename that corresponds to an
|
||||
* existing sub_node, we have found what we were looking
|
||||
* for.
|
||||
*/
|
||||
sub_node->lock();
|
||||
return sub_node;
|
||||
}
|
||||
|
||||
/*
|
||||
* As 'path' contains one or more path delimiters, traverse
|
||||
* into the sub directory names after the first path element.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We cannot traverse into anything other than a directory.
|
||||
*
|
||||
* XXX we might follow symlinks here
|
||||
*/
|
||||
Directory *sub_dir = dynamic_cast<Directory *>(sub_node);
|
||||
if (!sub_dir)
|
||||
throw Lookup_failed();
|
||||
|
||||
return sub_dir->lookup_and_lock(path + i + 1, return_parent);
|
||||
}
|
||||
|
||||
Directory *lookup_and_lock_dir(char const *path)
|
||||
{
|
||||
Node *node = lookup_and_lock(path);
|
||||
|
||||
Directory *dir = dynamic_cast<Directory *>(node);
|
||||
if (dir)
|
||||
return dir;
|
||||
|
||||
node->unlock();
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
File *lookup_and_lock_file(char const *path)
|
||||
{
|
||||
Node *node = lookup_and_lock(path);
|
||||
|
||||
File *file = dynamic_cast<File *>(node);
|
||||
if (file)
|
||||
return file;
|
||||
|
||||
node->unlock();
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
Symlink *lookup_and_lock_symlink(char const *path)
|
||||
{
|
||||
Node *node = lookup_and_lock(path);
|
||||
|
||||
Symlink *symlink = dynamic_cast<Symlink *>(node);
|
||||
if (symlink)
|
||||
return symlink;
|
||||
|
||||
node->unlock();
|
||||
throw Lookup_failed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup parent directory of the specified path
|
||||
*
|
||||
* \throw Lookup_failed
|
||||
*/
|
||||
Directory *lookup_and_lock_parent(char const *path)
|
||||
{
|
||||
return static_cast<Directory *>(lookup_and_lock(path, true));
|
||||
}
|
||||
|
||||
size_t read(char *dst, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
if (len < sizeof(Directory_entry)) {
|
||||
PERR("read buffer too small for directory entry");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t num_entries() const { return _num_entries; }
|
||||
};
|
||||
}
|
||||
seek_off_t index = seek_offset / sizeof(Directory_entry);
|
||||
|
||||
if (seek_offset % sizeof(Directory_entry)) {
|
||||
PERR("seek offset not alighed to sizeof(Directory_entry)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Node *node = entry_unsynchronized(index);
|
||||
|
||||
/* index out of range */
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
Directory_entry *e = (Directory_entry *)(dst);
|
||||
|
||||
if (dynamic_cast<File *>(node)) e->type = Directory_entry::TYPE_FILE;
|
||||
if (dynamic_cast<Directory *>(node)) e->type = Directory_entry::TYPE_DIRECTORY;
|
||||
if (dynamic_cast<Symlink *>(node)) e->type = Directory_entry::TYPE_SYMLINK;
|
||||
|
||||
strncpy(e->name, node->name(), sizeof(e->name));
|
||||
|
||||
return sizeof(Directory_entry);
|
||||
}
|
||||
|
||||
size_t write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
/* writing to directory nodes is not supported */
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t num_entries() const { return _num_entries; }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__RAM_FS__DIRECTORY_H_ */
|
||||
|
@ -4,6 +4,13 @@
|
||||
* \date 2012-04-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__RAM_FS__FILE_H_
|
||||
#define _INCLUDE__RAM_FS__FILE_H_
|
||||
|
||||
@ -14,93 +21,93 @@
|
||||
#include <ram_fs/node.h>
|
||||
#include <ram_fs/chunk.h>
|
||||
|
||||
namespace File_system {
|
||||
namespace File_system { class File; }
|
||||
|
||||
class File : public Node
|
||||
{
|
||||
private:
|
||||
|
||||
typedef Chunk<4096> Chunk_level_3;
|
||||
typedef Chunk_index<128, Chunk_level_3> Chunk_level_2;
|
||||
typedef Chunk_index<64, Chunk_level_2> Chunk_level_1;
|
||||
typedef Chunk_index<64, Chunk_level_1> Chunk_level_0;
|
||||
class File_system::File : public Node
|
||||
{
|
||||
private:
|
||||
|
||||
Chunk_level_0 _chunk;
|
||||
typedef Chunk<4096> Chunk_level_3;
|
||||
typedef Chunk_index<128, Chunk_level_3> Chunk_level_2;
|
||||
typedef Chunk_index<64, Chunk_level_2> Chunk_level_1;
|
||||
typedef Chunk_index<64, Chunk_level_1> Chunk_level_0;
|
||||
|
||||
file_size_t _length;
|
||||
Chunk_level_0 _chunk;
|
||||
|
||||
public:
|
||||
file_size_t _length;
|
||||
|
||||
File(Allocator &alloc, char const *name)
|
||||
: _chunk(alloc, 0), _length(0) { Node::name(name); }
|
||||
public:
|
||||
|
||||
size_t read(char *dst, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
file_size_t const chunk_used_size = _chunk.used_size();
|
||||
File(Allocator &alloc, char const *name)
|
||||
: _chunk(alloc, 0), _length(0) { Node::name(name); }
|
||||
|
||||
if (seek_offset >= _length)
|
||||
return 0;
|
||||
size_t read(char *dst, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
file_size_t const chunk_used_size = _chunk.used_size();
|
||||
|
||||
/*
|
||||
* Constrain read transaction to available chunk data
|
||||
*
|
||||
* Note that 'chunk_used_size' may be lower than '_length'
|
||||
* because 'Chunk' may have truncated tailing zeros.
|
||||
*/
|
||||
if (seek_offset + len >= _length)
|
||||
len = _length - seek_offset;
|
||||
if (seek_offset >= _length)
|
||||
return 0;
|
||||
|
||||
file_size_t read_len = len;
|
||||
/*
|
||||
* Constrain read transaction to available chunk data
|
||||
*
|
||||
* Note that 'chunk_used_size' may be lower than '_length'
|
||||
* because 'Chunk' may have truncated tailing zeros.
|
||||
*/
|
||||
if (seek_offset + len >= _length)
|
||||
len = _length - seek_offset;
|
||||
|
||||
if (seek_offset + read_len > chunk_used_size) {
|
||||
if (chunk_used_size >= seek_offset)
|
||||
read_len = chunk_used_size - seek_offset;
|
||||
else
|
||||
read_len = 0;
|
||||
}
|
||||
file_size_t read_len = len;
|
||||
|
||||
_chunk.read(dst, read_len, seek_offset);
|
||||
|
||||
/* add zero padding if needed */
|
||||
if (read_len < len)
|
||||
memset(dst + read_len, 0, len - read_len);
|
||||
|
||||
return len;
|
||||
if (seek_offset + read_len > chunk_used_size) {
|
||||
if (chunk_used_size >= seek_offset)
|
||||
read_len = chunk_used_size - seek_offset;
|
||||
else
|
||||
read_len = 0;
|
||||
}
|
||||
|
||||
size_t write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
if (seek_offset == (seek_off_t)(~0))
|
||||
seek_offset = _chunk.used_size();
|
||||
_chunk.read(dst, read_len, seek_offset);
|
||||
|
||||
if (seek_offset + len >= Chunk_level_0::SIZE)
|
||||
throw Size_limit_reached();
|
||||
/* add zero padding if needed */
|
||||
if (read_len < len)
|
||||
memset(dst + read_len, 0, len - read_len);
|
||||
|
||||
_chunk.write(src, len, (size_t)seek_offset);
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep track of file length. We cannot use 'chunk.used_size()'
|
||||
* as file length because trailing zeros may by represented
|
||||
* by zero chunks, which do not contribute to 'used_size()'.
|
||||
*/
|
||||
_length = max(_length, seek_offset + len);
|
||||
size_t write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
if (seek_offset == (seek_off_t)(~0))
|
||||
seek_offset = _chunk.used_size();
|
||||
|
||||
mark_as_updated();
|
||||
return len;
|
||||
}
|
||||
if (seek_offset + len >= Chunk_level_0::SIZE)
|
||||
throw Size_limit_reached();
|
||||
|
||||
file_size_t length() const { return _length; }
|
||||
_chunk.write(src, len, (size_t)seek_offset);
|
||||
|
||||
void truncate(file_size_t size)
|
||||
{
|
||||
if (size < _chunk.used_size())
|
||||
_chunk.truncate(size);
|
||||
/*
|
||||
* Keep track of file length. We cannot use 'chunk.used_size()'
|
||||
* as file length because trailing zeros may by represented
|
||||
* by zero chunks, which do not contribute to 'used_size()'.
|
||||
*/
|
||||
_length = max(_length, seek_offset + len);
|
||||
|
||||
_length = size;
|
||||
mark_as_updated();
|
||||
return len;
|
||||
}
|
||||
|
||||
mark_as_updated();
|
||||
}
|
||||
};
|
||||
}
|
||||
file_size_t length() const { return _length; }
|
||||
|
||||
void truncate(file_size_t size)
|
||||
{
|
||||
if (size < _chunk.used_size())
|
||||
_chunk.truncate(size);
|
||||
|
||||
_length = size;
|
||||
|
||||
mark_as_updated();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__RAM_FS__FILE_H_ */
|
||||
|
@ -4,6 +4,13 @@
|
||||
* \date 2012-04-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__RAM_FS__NODE_H_
|
||||
#define _INCLUDE__RAM_FS__NODE_H_
|
||||
|
||||
@ -12,47 +19,46 @@
|
||||
#include <file_system/node.h>
|
||||
#include <util/list.h>
|
||||
|
||||
namespace File_system {
|
||||
namespace File_system { class Node; }
|
||||
|
||||
class Node : public Node_base, public List<Node>::Element
|
||||
{
|
||||
public:
|
||||
|
||||
typedef char Name[128];
|
||||
class File_system::Node : public Node_base, public List<Node>::Element
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
typedef char Name[128];
|
||||
|
||||
int _ref_count;
|
||||
Name _name;
|
||||
unsigned long const _inode;
|
||||
private:
|
||||
|
||||
/**
|
||||
* Generate unique inode number
|
||||
*/
|
||||
static unsigned long _unique_inode()
|
||||
{
|
||||
static unsigned long inode_count;
|
||||
return ++inode_count;
|
||||
}
|
||||
int _ref_count;
|
||||
Name _name;
|
||||
unsigned long const _inode;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Generate unique inode number
|
||||
*/
|
||||
static unsigned long _unique_inode()
|
||||
{
|
||||
static unsigned long inode_count;
|
||||
return ++inode_count;
|
||||
}
|
||||
|
||||
Node()
|
||||
: _ref_count(0), _inode(_unique_inode())
|
||||
{ _name[0] = 0; }
|
||||
public:
|
||||
|
||||
unsigned long inode() const { return _inode; }
|
||||
char const *name() const { return _name; }
|
||||
Node()
|
||||
: _ref_count(0), _inode(_unique_inode())
|
||||
{ _name[0] = 0; }
|
||||
|
||||
/**
|
||||
* Assign name
|
||||
*/
|
||||
void name(char const *name) { strncpy(_name, name, sizeof(_name)); }
|
||||
unsigned long inode() const { return _inode; }
|
||||
char const *name() const { return _name; }
|
||||
|
||||
virtual size_t read(char *dst, size_t len, seek_off_t) = 0;
|
||||
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
|
||||
};
|
||||
/**
|
||||
* Assign name
|
||||
*/
|
||||
void name(char const *name) { strncpy(_name, name, sizeof(_name)); }
|
||||
|
||||
}
|
||||
virtual size_t read(char *dst, size_t len, seek_off_t) = 0;
|
||||
virtual size_t write(char const *src, size_t len, seek_off_t) = 0;
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__RAM_FS__NODE_H_ */
|
||||
|
@ -4,44 +4,51 @@
|
||||
* \date 2012-04-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__RAM_FS__SYMLINK_H_
|
||||
#define _INCLUDE__RAM_FS__SYMLINK_H_
|
||||
|
||||
/* local includes */
|
||||
#include <ram_fs/node.h>
|
||||
|
||||
namespace File_system {
|
||||
namespace File_system { class Symlink; }
|
||||
|
||||
class Symlink : public Node
|
||||
{
|
||||
private:
|
||||
|
||||
char _link_to[MAX_PATH_LEN];
|
||||
size_t _len;
|
||||
class File_system::Symlink : public Node
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
char _link_to[MAX_PATH_LEN];
|
||||
size_t _len;
|
||||
|
||||
Symlink(char const *name): _len(0) { Node::name(name); }
|
||||
public:
|
||||
|
||||
size_t read(char *dst, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
size_t count = min(len, _len-seek_offset);
|
||||
Genode::memcpy(dst, _link_to+seek_offset, count);
|
||||
return count;
|
||||
}
|
||||
Symlink(char const *name): _len(0) { Node::name(name); }
|
||||
|
||||
size_t write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
/* Ideal symlink operations are atomic. */
|
||||
if (seek_offset) return 0;
|
||||
size_t read(char *dst, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
size_t count = min(len, _len-seek_offset);
|
||||
Genode::memcpy(dst, _link_to+seek_offset, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
_len = min(len, sizeof(_link_to));
|
||||
Genode::memcpy(_link_to, src, _len);
|
||||
return _len;
|
||||
}
|
||||
size_t write(char const *src, size_t len, seek_off_t seek_offset)
|
||||
{
|
||||
/* Ideal symlink operations are atomic. */
|
||||
if (seek_offset) return 0;
|
||||
|
||||
file_size_t length() const { return _len; }
|
||||
};
|
||||
}
|
||||
_len = min(len, sizeof(_link_to));
|
||||
Genode::memcpy(_link_to, src, _len);
|
||||
return _len;
|
||||
}
|
||||
|
||||
file_size_t length() const { return _len; }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__RAM_FS__SYMLINK_H_ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user