mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-05 17:01:47 +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_ext2
|
||||||
libc_vfs_fs
|
libc_vfs_fs
|
||||||
libc_vfs_ram
|
libc_vfs_ram
|
||||||
|
libc_vfs_block
|
||||||
timed_semaphore
|
timed_semaphore
|
||||||
signal
|
signal
|
||||||
sub_rm
|
sub_rm
|
||||||
|
Loading…
x
Reference in New Issue
Block a user