libc: configurable initial FDs

The libc already supports the configuration of 'stdin', 'stdout', and
'stderr' using '<libc>' config attributes. This patch equips the libc
with the additional ability to pre-initialize any other file descriptor.
A file descriptor is configured as follows:

<config>
  ...
  <libc ...>
    <fd id="3" path="/dev/log" writeable="yes" readable="no" seek="10"/>
    ...
  </libc>
</config>

Furthermore, this patch moves the FD initialization code from the VFS
plugin to the libc kernel initialization because opening the FDs
depends on 'malloc' ('strdup'), which should not be used at early
'Libc::Kernel' initialization time.

Issue #3478
This commit is contained in:
Norman Feske 2019-08-16 19:04:04 +02:00 committed by Christian Helmuth
parent 6e38b53001
commit 65f75589e9
4 changed files with 94 additions and 54 deletions

View File

@ -109,6 +109,11 @@ namespace Libc {
*/
void free(File_descriptor *fdo);
/**
* Prevent the use of the specified file descriptor
*/
void preserve(int libc_fd);
File_descriptor *find_by_libc_fd(int libc_fd);
};

View File

@ -70,6 +70,13 @@ void File_descriptor_allocator::free(File_descriptor *fdo)
}
void File_descriptor_allocator::preserve(int fd)
{
if (!find_by_libc_fd(fd))
alloc(nullptr, nullptr, fd);
}
File_descriptor *File_descriptor_allocator::find_by_libc_fd(int libc_fd)
{
Lock::Guard guard(_lock);

View File

@ -558,12 +558,16 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
return timeout_ms > 0 ? _main_timeout.duration_left() : 0;
}
void _init_file_descriptors();
public:
Kernel(Genode::Env &env, Genode::Allocator &heap)
: _env(env), _heap(heap)
{
_env.ep().register_io_progress_handler(*this);
_init_file_descriptors();
}
~Kernel() { Genode::error(__PRETTY_FUNCTION__, " should not be executed!"); }
@ -818,6 +822,81 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
};
void Libc::Kernel::_init_file_descriptors()
{
auto init_fd = [&] (Genode::Xml_node const &node, char const *attr,
int libc_fd, unsigned flags)
{
if (!node.has_attribute(attr))
return;
typedef Genode::String<Vfs::MAX_PATH_LEN> Path;
Path const path = node.attribute_value(attr, Path());
struct stat out_stat { };
if (stat(path.string(), &out_stat) != 0)
return;
Libc::File_descriptor *fd = _vfs.open(path.string(), flags, libc_fd);
if (fd->libc_fd != libc_fd) {
Genode::error("could not allocate fd ",libc_fd," for ",path,", "
"got fd ",fd->libc_fd);
_vfs.close(fd);
return;
}
/*
* We need to manually register the path. Normally this is done
* by '_open'. But we call the local 'open' function directly
* because we want to explicitly specify the libc fd ID.
*
* We have to allocate the path from the libc (done via 'strdup')
* such that the path can be freed when an stdio fd is closed.
*/
if (fd->fd_path) { Genode::warning("may leak former FD path memory"); }
fd->fd_path = strdup(path.string());
::off_t const seek = node.attribute_value("seek", 0ULL);
if (seek)
_vfs.lseek(fd, seek, SEEK_SET);
};
if (_vfs.root_dir_has_dirents()) {
Xml_node const node = _libc_env.libc_config();
typedef Genode::String<Vfs::MAX_PATH_LEN> Path;
if (node.has_attribute("cwd"))
chdir(node.attribute_value("cwd", Path()).string());
init_fd(node, "stdin", 0, O_RDONLY);
init_fd(node, "stdout", 1, O_WRONLY);
init_fd(node, "stderr", 2, O_WRONLY);
node.for_each_sub_node("fd", [&] (Xml_node fd) {
unsigned const id = fd.attribute_value("id", 0U);
bool const rd = fd.attribute_value("readable", false);
bool const wr = fd.attribute_value("writeable", false);
unsigned const flags = rd ? (wr ? O_RDWR : O_RDONLY)
: (wr ? O_WRONLY : 0);
if (!fd.has_attribute("path"))
warning("Invalid <fd> node, 'path' attribute is missing");
init_fd(fd, "path", id, flags);
});
/* prevent use of IDs of stdin, stdout, and stderr for other files */
for (unsigned fd = 0; fd <= 2; fd++)
Libc::file_descriptor_allocator()->preserve(fd);
}
}
/**
* Libc kernel singleton
*

View File

@ -43,41 +43,6 @@ class Libc::Vfs_plugin : public Libc::Plugin
Vfs::File_system &_root_dir;
Vfs::Io_response_handler &_response_handler;
void _open_stdio(Genode::Xml_node const &node, char const *attr,
int libc_fd, unsigned flags)
{
if (!node.has_attribute(attr)) {
Libc::file_descriptor_allocator()->alloc(nullptr, nullptr, libc_fd);
return;
}
typedef Genode::String<Vfs::MAX_PATH_LEN> Path;
Path const path = node.attribute_value(attr, Path());
struct stat out_stat { };
if (stat(path.string(), &out_stat) != 0)
return;
Libc::File_descriptor *fd = open(path.string(), flags, libc_fd);
if (fd->libc_fd != libc_fd) {
Genode::error("could not allocate fd ",libc_fd," for ",path,", "
"got fd ",fd->libc_fd);
close(fd);
return;
}
/*
* We need to manually register the path. Normally this is done
* by '_open'. But we call the local 'open' function directly
* because we want to explicitly specify the libc fd ID.
*
* We have to allocate the path from the libc (done via 'strdup')
* such that the path can be freed when an stdio fd is closed.
*/
if (fd->fd_path) { Genode::warning("may leak former FD path memory"); }
fd->fd_path = strdup(path.string());
}
/**
* Sync a handle and propagate errors
*/
@ -90,28 +55,12 @@ class Libc::Vfs_plugin : public Libc::Plugin
Vfs::Io_response_handler &handler)
:
_alloc(alloc), _root_dir(env.vfs()), _response_handler(handler)
{
using Genode::Xml_node;
if (_root_dir.num_dirent("/"))
env.config([&] (Xml_node const &top) {
top.with_sub_node("libc", [&] (Xml_node node) {
typedef Genode::String<Vfs::MAX_PATH_LEN> Path;
if (node.has_attribute("cwd"))
chdir(node.attribute_value("cwd", Path()).string());
_open_stdio(node, "stdin", 0, O_RDONLY);
_open_stdio(node, "stdout", 1, O_WRONLY);
_open_stdio(node, "stderr", 2, O_WRONLY);
});
});
}
{ }
~Vfs_plugin() final { }
bool root_dir_has_dirents() const { return _root_dir.num_dirent("/") > 0; }
bool supports_access(const char *, int) override { return true; }
bool supports_mkdir(const char *, mode_t) override { return true; }
bool supports_open(const char *, int) override { return true; }