genode/repos/os/src/server/ram_fs/file.h

138 lines
3.3 KiB
C++

/*
* \brief File node
* \author Norman Feske
* \date 2012-04-11
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__RAM_FS__FILE_H_
#define _INCLUDE__RAM_FS__FILE_H_
/* Genode includes */
#include <file_system_session/file_system_session.h>
#include <base/allocator.h>
/* local includes */
#include <ram_fs/chunk.h>
#include <ram_fs/param.h>
#include "node.h"
namespace Ram_fs
{
using File_system::Chunk;
using File_system::Chunk_index;
using File_system::file_size_t;
using File_system::SEEK_TAIL;
class File;
}
class Ram_fs::File : public Node
{
private:
typedef Chunk <num_level_3_entries()> Chunk_level_3;
typedef Chunk_index<num_level_2_entries(), Chunk_level_3> Chunk_level_2;
typedef Chunk_index<num_level_1_entries(), Chunk_level_2> Chunk_level_1;
typedef Chunk_index<num_level_0_entries(), Chunk_level_1> Chunk_level_0;
Chunk_level_0 _chunk;
file_size_t _length;
public:
File(Allocator &alloc, char const *name)
: _chunk(alloc, 0), _length(0) { Node::name(name); }
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
file_size_t const chunk_used_size = _chunk.used_size();
if (seek_offset == SEEK_TAIL)
seek_offset = (len < _length) ? (_length - len) : 0;
else if (seek_offset >= _length)
return 0;
/*
* 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;
file_size_t read_len = 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;
}
_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;
}
size_t write(char const *src, size_t len, seek_off_t seek_offset) override
{
if (seek_offset == SEEK_TAIL)
seek_offset = _length;
if (seek_offset + len >= Chunk_level_0::SIZE) {
len = (Chunk_level_0::SIZE-1) - seek_offset;
Genode::error(name(), ": size limit ", (long)Chunk_level_0::SIZE, " reached");
}
_chunk.write(src, len, (size_t)seek_offset);
/*
* 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);
mark_as_updated();
return len;
}
Status status() override
{
return {
.size = _length,
.type = File_system::Node_type::CONTINUOUS_FILE,
.rwx = { .readable = true,
.writeable = true,
.executable = true },
.inode = inode(),
.modification_time = modification_time()
};
}
void truncate(file_size_t size) override
{
if (size < _chunk.used_size())
_chunk.truncate(size);
_length = size;
mark_as_updated();
}
};
#endif /* _INCLUDE__RAM_FS__FILE_H_ */