mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-20 03:36:33 +00:00
VFS block test
This test reproduces an issue of the VFS block file system when the underlying block device has a coarser granularity than the block requests issued by the VFS client. I.e., if the underlying block device has a block size of 4K, writing a sequence of (non-4K-aligned) 512 blocks that crosss a 4K boundary corrupts the data on the block device. Issue #2262
This commit is contained in:
parent
9adc6f1228
commit
c65f78671f
87
repos/libports/run/libc_vfs_block.run
Normal file
87
repos/libports/run/libc_vfs_block.run
Normal file
@ -0,0 +1,87 @@
|
||||
#
|
||||
# \brief Test for using the block-session support of the libc_vfs plugin
|
||||
# \author Norman Feske
|
||||
# \date 2016-01-28
|
||||
#
|
||||
|
||||
#
|
||||
# Build
|
||||
#
|
||||
|
||||
build { core init server/ram_blk test/libc_vfs_block }
|
||||
|
||||
create_boot_directory
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
|
||||
set config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="RAM"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<start name="ram_blk">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides> <service name="Block"/> </provides>
|
||||
<config size="16K" block_size="4096"/>
|
||||
</start>
|
||||
<start name="test-libc_vfs_block">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<config>
|
||||
<vfs> <dir name="dev"> <block/> </dir> </vfs>
|
||||
<libc/>
|
||||
|
||||
<!-- Populate the block device with a pattern and validate the
|
||||
write operation by reading back the content. -->
|
||||
<sequence>
|
||||
<write content="abcdefghIJKLMNOPqrstuvwxYZABCDEF"/>
|
||||
<expect content="abcdefghIJKLMNOPqrstuvwxYZABCDEF"/>
|
||||
</sequence>
|
||||
|
||||
<!-- Write single 512 block. This requires the successive reading,
|
||||
modification, and writing of the compound 4096 block. -->
|
||||
<sequence>
|
||||
<write at="9" content="%"/>
|
||||
<expect content="abcdefghI%KLMNOPqrstuvwxYZABCDEF"/>
|
||||
</sequence>
|
||||
|
||||
<!-- Reset the pattern and write multiple 512 blocks that cross
|
||||
a 4096 boundary. -->
|
||||
<sequence>
|
||||
<write content="abcdefghIJKLMNOPqrstuvwxYZABCDEF"/>
|
||||
<write at="15" content="123"/>
|
||||
<expect content="abcdefghIJKLMNO123stuvwxYZABCDEF"/>
|
||||
</sequence>
|
||||
</config>
|
||||
</start>
|
||||
</config>
|
||||
}
|
||||
|
||||
install_config $config
|
||||
|
||||
#
|
||||
# Boot modules
|
||||
#
|
||||
|
||||
build_boot_image {
|
||||
core init
|
||||
ld.lib.so libc.lib.so libm.lib.so
|
||||
ram_blk test-libc_vfs_block
|
||||
}
|
||||
|
||||
#
|
||||
# Execute test case
|
||||
#
|
||||
|
||||
append qemu_args " -m 128 -nographic "
|
||||
run_genode_until {.*child "test-libc_vfs_block" exited with exit value 0.*} 60
|
||||
|
278
repos/libports/src/test/libc_vfs_block/main.cc
Normal file
278
repos/libports/src/test/libc_vfs_block/main.cc
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* \brief Test for using the VFS block file system
|
||||
* \author Norman Feske
|
||||
* \date 2017-01-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <libc/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Test {
|
||||
class Buffer;
|
||||
class Block_device;
|
||||
class Main;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dynamically allocated buffer that can be read/written from/to a file
|
||||
*/
|
||||
class Test::Buffer
|
||||
{
|
||||
private:
|
||||
|
||||
size_t const _size;
|
||||
char * const _ptr = (char *)malloc(_size);
|
||||
|
||||
public:
|
||||
|
||||
struct Offset { size_t value; };
|
||||
struct Fd { int value; };
|
||||
|
||||
class Alloc_failed : Genode::Exception { };
|
||||
class Out_of_range : Genode::Exception { };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \throw Alloc_failed
|
||||
*/
|
||||
Buffer(size_t size) : _size(size)
|
||||
{
|
||||
if (!_ptr)
|
||||
throw Alloc_failed();
|
||||
|
||||
memset(_ptr, 0, _size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write char value to specified offset
|
||||
*
|
||||
* \throw Out_of_range
|
||||
*/
|
||||
void content_at(Offset at, char c)
|
||||
{
|
||||
if (at.value >= _size)
|
||||
throw Out_of_range();
|
||||
|
||||
_ptr[at.value] = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return char at specified offset
|
||||
*
|
||||
* \throw Out_of_range
|
||||
*/
|
||||
char content_at(Offset at)
|
||||
{
|
||||
if (at.value >= _size)
|
||||
throw Out_of_range();
|
||||
|
||||
return _ptr[at.value];
|
||||
}
|
||||
|
||||
~Buffer() { free(_ptr); }
|
||||
|
||||
/**
|
||||
* Write buffer to a file at the specified offset
|
||||
*/
|
||||
void write(Fd fd, Offset offset)
|
||||
{
|
||||
::lseek(fd.value, offset.value, SEEK_SET);
|
||||
if ((size_t)::write(fd.value, _ptr, _size) != _size)
|
||||
Genode::error("unexpected 'write' result");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch buffer content from file at specified offset
|
||||
*/
|
||||
void read(Fd fd, Offset offset)
|
||||
{
|
||||
::lseek(fd.value, offset.value, SEEK_SET);
|
||||
if ((size_t)::read(fd.value, _ptr, _size) != _size)
|
||||
Genode::error("unexpected 'read' result");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Test::Block_device
|
||||
{
|
||||
private:
|
||||
|
||||
Buffer::Fd const _fd;
|
||||
|
||||
enum { BLOCK_SIZE = 512 };
|
||||
|
||||
public:
|
||||
|
||||
typedef Genode::String<128> Path;
|
||||
|
||||
class Unavailable : Genode::Exception { };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \throw Unavailable
|
||||
*/
|
||||
Block_device(Path const &path)
|
||||
:
|
||||
_fd(Buffer::Fd{open(path.string(), O_RDWR)})
|
||||
{
|
||||
if (_fd.value < 0)
|
||||
throw Unavailable();
|
||||
}
|
||||
|
||||
~Block_device() { close(_fd.value); }
|
||||
|
||||
struct Block_number { unsigned long value; };
|
||||
|
||||
/**
|
||||
* Issue write operation
|
||||
*
|
||||
* \param block_number first block number
|
||||
* \param content pattern to write
|
||||
*
|
||||
* The pattern is a null-terminated character string. Each character
|
||||
* is written to the begin of one block. Hence, the length of the
|
||||
* string corresponds to the size of the number of blocks written.
|
||||
*/
|
||||
void write(Block_number block_number, char const *content)
|
||||
{
|
||||
/* create local buffer populated with 'content' */
|
||||
size_t const content_len = strlen(content);
|
||||
Buffer buffer(BLOCK_SIZE*content_len);
|
||||
|
||||
for (unsigned i = 0; i < content_len; i++)
|
||||
buffer.content_at(Buffer::Offset{BLOCK_SIZE*i}, content[i]);
|
||||
|
||||
/* write buffer to the block device */
|
||||
buffer.write(_fd, Buffer::Offset{BLOCK_SIZE*block_number.value});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if content of the block device matches expectation
|
||||
*
|
||||
* The argument correspond to the 'write' method.
|
||||
*/
|
||||
bool expect(Block_number block_number, char const *content)
|
||||
{
|
||||
/* read data from block device into local buffer */
|
||||
size_t const content_len = strlen(content);
|
||||
Buffer buffer(BLOCK_SIZE*content_len);
|
||||
buffer.read(_fd, Buffer::Offset{BLOCK_SIZE*block_number.value});
|
||||
|
||||
bool result = true;
|
||||
|
||||
/* validate content */
|
||||
for (unsigned i = 0; i < content_len; i++) {
|
||||
|
||||
char const c = buffer.content_at(Buffer::Offset{BLOCK_SIZE*i});
|
||||
if (c == content[i])
|
||||
continue;
|
||||
|
||||
Genode::error("unexpected content "
|
||||
"at block ", block_number.value + i, ", "
|
||||
"got ", Genode::Char(c), ", "
|
||||
"expected ", Genode::Char(content[i]));
|
||||
|
||||
result = false;
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Test::Main
|
||||
{
|
||||
Genode::Env &_env;
|
||||
|
||||
Genode::Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
typedef Genode::String<128> Content;
|
||||
|
||||
class Step_failed : Genode::Exception { };
|
||||
|
||||
/*
|
||||
* \throw Step_failed
|
||||
*/
|
||||
static void _exec_step(Genode::Xml_node step, Block_device &);
|
||||
|
||||
/*
|
||||
* \throw Step_failed
|
||||
*/
|
||||
void _exec_sequence(Genode::Xml_node sequence);
|
||||
|
||||
/*
|
||||
* \throw Step_failed
|
||||
*/
|
||||
void _exec_sequences(Genode::Xml_node config);
|
||||
|
||||
/*
|
||||
* \throw Step_failed
|
||||
*/
|
||||
Main(Genode::Env &env) : _env(env)
|
||||
{
|
||||
_exec_sequences(_config.xml());
|
||||
_env.parent().exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Test::Main::_exec_step(Genode::Xml_node step, Block_device &block_device)
|
||||
{
|
||||
if (step.has_type("write")) {
|
||||
Block_device::Block_number const at{step.attribute_value("at", 0UL)};
|
||||
Content const content = step.attribute_value("content", Content());
|
||||
Genode::log("write at=", at.value, " content=\"", content, "\"");
|
||||
block_device.write(at, content.string());
|
||||
return;
|
||||
}
|
||||
|
||||
if (step.has_type("expect")) {
|
||||
Block_device::Block_number const at{ step.attribute_value("at", 0UL)};
|
||||
Content const content = step.attribute_value("content", Content());
|
||||
Genode::log("expect at=", at.value, " content=\"", content, "\"");
|
||||
if (block_device.expect(at, content.string()))
|
||||
return;
|
||||
|
||||
Genode::error("step '", step, "' failed");
|
||||
throw Step_failed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Test::Main::_exec_sequence(Genode::Xml_node sequence)
|
||||
{
|
||||
Block_device dev{"/dev/block"};
|
||||
|
||||
sequence.for_each_sub_node([&] (Genode::Xml_node step) {
|
||||
_exec_step(step, dev); });
|
||||
}
|
||||
|
||||
|
||||
void Test::Main::_exec_sequences(Genode::Xml_node config)
|
||||
{
|
||||
config.for_each_sub_node("sequence", [&] (Genode::Xml_node sequence) {
|
||||
_exec_sequence(sequence); });
|
||||
}
|
||||
|
||||
|
||||
void Libc::Component::construct(Libc::Env &env) {
|
||||
static Test::Main main(env);
|
||||
}
|
||||
|
3
repos/libports/src/test/libc_vfs_block/target.mk
Normal file
3
repos/libports/src/test/libc_vfs_block/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-libc_vfs_block
|
||||
SRC_CC = main.cc
|
||||
LIBS += libc
|
@ -14,6 +14,7 @@ libc_vfs
|
||||
libc_vfs_ext2
|
||||
libc_vfs_fs
|
||||
libc_vfs_ram
|
||||
libc_vfs_block
|
||||
timed_semaphore
|
||||
signal
|
||||
sub_rm
|
||||
|
Loading…
Reference in New Issue
Block a user