mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-09 10:51:45 +00:00
Restrict VFS Dir_filesystem allocations to 'opendir' only
Allocations made at the client-side of the VFS must be contained within functions that may return errors indicating resource exhaustion. Fix #2569
This commit is contained in:
parent
a0c0f3d7c9
commit
07b36573ea
@ -34,23 +34,22 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
|
|
||||||
struct Dir_vfs_handle : Vfs_handle
|
struct Dir_vfs_handle : Vfs_handle
|
||||||
{
|
{
|
||||||
struct Sync_dir_handle_element;
|
struct Subdir_handle_element;
|
||||||
|
|
||||||
typedef Genode::Registry<Sync_dir_handle_element> Sync_dir_handle_registry;
|
typedef Genode::Registry<Subdir_handle_element> Subdir_handle_registry;
|
||||||
|
|
||||||
struct Sync_dir_handle_element : Sync_dir_handle_registry::Element
|
struct Subdir_handle_element : Subdir_handle_registry::Element
|
||||||
{
|
{
|
||||||
Vfs_handle &vfs_handle;
|
Vfs_handle &vfs_handle;
|
||||||
Sync_dir_handle_element(Sync_dir_handle_registry ®istry,
|
Subdir_handle_element(Subdir_handle_registry ®istry,
|
||||||
Vfs_handle &vfs_handle)
|
Vfs_handle &vfs_handle)
|
||||||
: Sync_dir_handle_registry::Element(registry, *this),
|
: Subdir_handle_registry::Element(registry, *this),
|
||||||
vfs_handle(vfs_handle) { }
|
vfs_handle(vfs_handle) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
Absolute_path path;
|
Absolute_path path;
|
||||||
File_system *fs_for_complete_read { nullptr };
|
Vfs_handle *queued_read_handle { nullptr };
|
||||||
Vfs_handle *fs_dir_handle { nullptr };
|
Subdir_handle_registry subdir_handle_registry;
|
||||||
Sync_dir_handle_registry sync_dir_handle_registry;
|
|
||||||
|
|
||||||
Dir_vfs_handle(Directory_service &ds,
|
Dir_vfs_handle(Directory_service &ds,
|
||||||
File_io_service &fs,
|
File_io_service &fs,
|
||||||
@ -58,6 +57,16 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
char const *path)
|
char const *path)
|
||||||
: Vfs_handle(ds, fs, alloc, 0),
|
: Vfs_handle(ds, fs, alloc, 0),
|
||||||
path(path) { }
|
path(path) { }
|
||||||
|
|
||||||
|
~Dir_vfs_handle()
|
||||||
|
{
|
||||||
|
/* close all sub-handles */
|
||||||
|
auto f = [&] (Subdir_handle_element &e) {
|
||||||
|
e.vfs_handle.ds().close(&e.vfs_handle);
|
||||||
|
destroy(alloc(), &e);
|
||||||
|
};
|
||||||
|
subdir_handle_registry.for_each(f);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* pointer to first child file system */
|
/* pointer to first child file system */
|
||||||
@ -197,6 +206,10 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
|
|
||||||
bool _queue_read_of_file_systems(Dir_vfs_handle *dir_vfs_handle)
|
bool _queue_read_of_file_systems(Dir_vfs_handle *dir_vfs_handle)
|
||||||
{
|
{
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
dir_vfs_handle->queued_read_handle = nullptr;
|
||||||
|
|
||||||
file_offset index = dir_vfs_handle->seek() / sizeof(Dirent);
|
file_offset index = dir_vfs_handle->seek() / sizeof(Dirent);
|
||||||
|
|
||||||
char const *sub_path = _sub_path(dir_vfs_handle->path.base());
|
char const *sub_path = _sub_path(dir_vfs_handle->path.base());
|
||||||
@ -204,60 +217,49 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
if (strlen(sub_path) == 0)
|
if (strlen(sub_path) == 0)
|
||||||
sub_path = "/";
|
sub_path = "/";
|
||||||
|
|
||||||
|
/* base of composite directory index */
|
||||||
int base = 0;
|
int base = 0;
|
||||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
|
||||||
|
auto f = [&] (Dir_vfs_handle::Subdir_handle_element &handle_element) {
|
||||||
|
if (dir_vfs_handle->queued_read_handle) return; /* skip through */
|
||||||
|
|
||||||
|
Vfs_handle &vfs_handle = handle_element.vfs_handle;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine number of matching directory entries within
|
* Determine number of matching directory entries within
|
||||||
* the current file system.
|
* the current file system.
|
||||||
*/
|
*/
|
||||||
int const fs_num_dirent = fs->num_dirent(sub_path);
|
int const fs_num_dirent = vfs_handle.ds().num_dirent(sub_path);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Query directory entry if index lies with the file
|
* Query directory entry if index lies with the file
|
||||||
* system.
|
* system.
|
||||||
*/
|
*/
|
||||||
if (index - base < fs_num_dirent) {
|
if (index - base < fs_num_dirent) {
|
||||||
|
/* set this handle to be used for read completion */
|
||||||
|
dir_vfs_handle->queued_read_handle = &vfs_handle;
|
||||||
|
|
||||||
dir_vfs_handle->fs_for_complete_read = fs;
|
/* seek to file-system local index */
|
||||||
|
|
||||||
Opendir_result opendir_result =
|
|
||||||
fs->opendir(sub_path, false,
|
|
||||||
&dir_vfs_handle->fs_dir_handle,
|
|
||||||
dir_vfs_handle->alloc());
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Errors of this kind can only be communicated by
|
|
||||||
* 'complete_read()'
|
|
||||||
*/
|
|
||||||
if (opendir_result != OPENDIR_OK)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
dir_vfs_handle->fs_dir_handle->context =
|
|
||||||
dir_vfs_handle->context;
|
|
||||||
|
|
||||||
index = index - base;
|
index = index - base;
|
||||||
dir_vfs_handle->fs_dir_handle->seek(index * sizeof(Dirent));
|
vfs_handle.seek(index * sizeof(Dirent));
|
||||||
|
|
||||||
bool result = fs->queue_read(dir_vfs_handle->fs_dir_handle,
|
/* forward the handle context */
|
||||||
sizeof(Dirent));
|
vfs_handle.context = dir_vfs_handle->context;
|
||||||
|
|
||||||
|
result = vfs_handle.fs().queue_read(&vfs_handle, sizeof(Dirent));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dir_vfs_handle->subdir_handle_registry.for_each(f);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* adjust base index for next file system */
|
|
||||||
base += fs_num_dirent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Read_result _complete_read_of_file_systems(Dir_vfs_handle *dir_vfs_handle,
|
Read_result _complete_read_of_file_systems(Dir_vfs_handle *dir_vfs_handle,
|
||||||
char *dst, file_size count,
|
char *dst, file_size count,
|
||||||
file_size &out_count)
|
file_size &out_count)
|
||||||
{
|
{
|
||||||
if (!dir_vfs_handle->fs_for_complete_read ||
|
if (!dir_vfs_handle->queued_read_handle) {
|
||||||
!dir_vfs_handle->fs_dir_handle) {
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* no fs was found for the given index or
|
* no fs was found for the given index or
|
||||||
@ -275,16 +277,14 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
return READ_OK;
|
return READ_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Read_result result = dir_vfs_handle->fs_for_complete_read->
|
Read_result result = dir_vfs_handle->queued_read_handle->fs().
|
||||||
complete_read(dir_vfs_handle->fs_dir_handle,
|
complete_read(dir_vfs_handle->queued_read_handle,
|
||||||
dst, count, out_count);
|
dst, count, out_count);
|
||||||
|
|
||||||
if (result != READ_OK)
|
if (result == READ_QUEUED)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
dir_vfs_handle->fs_for_complete_read->close(dir_vfs_handle->fs_dir_handle);
|
dir_vfs_handle->queued_read_handle = nullptr;
|
||||||
dir_vfs_handle->fs_dir_handle = nullptr;
|
|
||||||
dir_vfs_handle->fs_for_complete_read = nullptr;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -537,16 +537,60 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
return OPEN_ERR_UNACCESSIBLE;
|
return OPEN_ERR_UNACCESSIBLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call 'opendir()' on each file system and store handles in
|
||||||
|
* a registry.
|
||||||
|
*/
|
||||||
|
Opendir_result open_composite_dirs(char const *sub_path,
|
||||||
|
Dir_vfs_handle &dir_vfs_handle)
|
||||||
|
{
|
||||||
|
Opendir_result result = OPENDIR_OK;
|
||||||
|
try {
|
||||||
|
for (File_system *fs = _first_file_system; (fs && result == OPENDIR_OK); fs = fs->next) {
|
||||||
|
Vfs_handle *sub_dir_handle = nullptr;
|
||||||
|
|
||||||
|
Opendir_result r = fs->opendir(
|
||||||
|
sub_path, false, &sub_dir_handle, dir_vfs_handle.alloc());
|
||||||
|
|
||||||
|
switch (r) {
|
||||||
|
case OPENDIR_OK:
|
||||||
|
break;
|
||||||
|
case OPENDIR_ERR_LOOKUP_FAILED:
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
result = r; /* loop will break */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
new (dir_vfs_handle.alloc())
|
||||||
|
Dir_vfs_handle::Subdir_handle_element(
|
||||||
|
dir_vfs_handle.subdir_handle_registry, *sub_dir_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Genode::Out_of_ram) { return OPENDIR_ERR_OUT_OF_RAM; }
|
||||||
|
catch (Genode::Out_of_caps) { return OPENDIR_ERR_OUT_OF_CAPS; }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Opendir_result opendir(char const *path, bool create,
|
Opendir_result opendir(char const *path, bool create,
|
||||||
Vfs_handle **out_handle, Allocator &alloc) override
|
Vfs_handle **out_handle, Allocator &alloc) override
|
||||||
{
|
{
|
||||||
|
Opendir_result result = OPENDIR_OK;
|
||||||
|
|
||||||
/* path equals "/" (for reading the name of this directory) */
|
/* path equals "/" (for reading the name of this directory) */
|
||||||
if (strcmp(path, "/") == 0) {
|
if (strcmp(path, "/") == 0) {
|
||||||
if (create)
|
if (create)
|
||||||
return OPENDIR_ERR_PERMISSION_DENIED;
|
return OPENDIR_ERR_PERMISSION_DENIED;
|
||||||
*out_handle = new (alloc) Dir_vfs_handle(*this, *this, alloc,
|
Dir_vfs_handle *root_handle = new (alloc)
|
||||||
path);
|
Dir_vfs_handle(*this, *this, alloc, path);
|
||||||
return OPENDIR_OK;
|
result = open_composite_dirs("/", *root_handle);
|
||||||
|
if (result == OPENDIR_OK) {
|
||||||
|
*out_handle = root_handle;
|
||||||
|
} else {
|
||||||
|
/* close the root handle and the rest will follow */
|
||||||
|
close(root_handle);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const *sub_path = _sub_path(path);
|
char const *sub_path = _sub_path(path);
|
||||||
@ -562,7 +606,7 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
fs.opendir(path, true, &tmp_handle, alloc);
|
fs.opendir(path, true, &tmp_handle, alloc);
|
||||||
if (opendir_result == OPENDIR_OK)
|
if (opendir_result == OPENDIR_OK)
|
||||||
fs.close(tmp_handle);
|
fs.close(tmp_handle);
|
||||||
return opendir_result;
|
return opendir_result; /* return from lambda */
|
||||||
};
|
};
|
||||||
|
|
||||||
Opendir_result opendir_result =
|
Opendir_result opendir_result =
|
||||||
@ -575,10 +619,17 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
return opendir_result;
|
return opendir_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_handle = new (alloc) Dir_vfs_handle(*this, *this, alloc,
|
Dir_vfs_handle *dir_vfs_handle = new (alloc)
|
||||||
path);
|
Dir_vfs_handle(*this, *this, alloc, path);
|
||||||
|
|
||||||
return OPENDIR_OK;
|
result = open_composite_dirs(sub_path, *dir_vfs_handle);
|
||||||
|
if (result == OPENDIR_OK) {
|
||||||
|
*out_handle = dir_vfs_handle;
|
||||||
|
} else {
|
||||||
|
/* close the master handle and the rest will follow */
|
||||||
|
close(dir_vfs_handle);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Openlink_result openlink(char const *path, bool create,
|
Openlink_result openlink(char const *path, bool create,
|
||||||
@ -757,54 +808,23 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
|
|
||||||
bool queue_sync(Vfs_handle *vfs_handle) override
|
bool queue_sync(Vfs_handle *vfs_handle) override
|
||||||
{
|
{
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
Dir_vfs_handle *dir_vfs_handle =
|
Dir_vfs_handle *dir_vfs_handle =
|
||||||
static_cast<Dir_vfs_handle*>(vfs_handle);
|
static_cast<Dir_vfs_handle*>(vfs_handle);
|
||||||
|
|
||||||
char const *sub_path = _sub_path(dir_vfs_handle->path.base());
|
auto f = [&result, dir_vfs_handle] (Dir_vfs_handle::Subdir_handle_element &e) {
|
||||||
|
/* forward the handle context */
|
||||||
|
e.vfs_handle.context = dir_vfs_handle->context;
|
||||||
|
|
||||||
if (strlen(sub_path) == 0)
|
if (!e.vfs_handle.fs().queue_sync(&e.vfs_handle)) {
|
||||||
sub_path = "/";
|
result = false;
|
||||||
|
|
||||||
/*
|
|
||||||
* Call 'opendir()' on each file system and, if successful,
|
|
||||||
* 'queue_sync()'. If one file system's 'queue_sync()' returns
|
|
||||||
* false, this function returns false. Any VFS handles returned by
|
|
||||||
* 'opendir()' are kept in a registry.
|
|
||||||
*/
|
|
||||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
|
||||||
|
|
||||||
bool fs_dir_already_open = false;
|
|
||||||
|
|
||||||
dir_vfs_handle->sync_dir_handle_registry.for_each(
|
|
||||||
[&] (Dir_vfs_handle::Sync_dir_handle_element &sync_dir_handle_element) {
|
|
||||||
if (&sync_dir_handle_element.vfs_handle.fs() == fs) {
|
|
||||||
fs_dir_already_open = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
if (fs_dir_already_open)
|
dir_vfs_handle->subdir_handle_registry.for_each(f);
|
||||||
continue;
|
|
||||||
|
|
||||||
Vfs_handle *sync_dir_handle;
|
return result;
|
||||||
|
|
||||||
if (fs->opendir(sub_path, false, &sync_dir_handle,
|
|
||||||
dir_vfs_handle->alloc()) != OPENDIR_OK)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sync_dir_handle->context = dir_vfs_handle->context;
|
|
||||||
|
|
||||||
new (dir_vfs_handle->alloc())
|
|
||||||
Dir_vfs_handle::Sync_dir_handle_element(
|
|
||||||
dir_vfs_handle->sync_dir_handle_registry,
|
|
||||||
*sync_dir_handle);
|
|
||||||
|
|
||||||
if (!sync_dir_handle->fs().queue_sync(sync_dir_handle))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sync_result complete_sync(Vfs_handle *vfs_handle) override
|
Sync_result complete_sync(Vfs_handle *vfs_handle) override
|
||||||
@ -814,21 +834,13 @@ class Vfs::Dir_file_system : public File_system
|
|||||||
Dir_vfs_handle *dir_vfs_handle =
|
Dir_vfs_handle *dir_vfs_handle =
|
||||||
static_cast<Dir_vfs_handle*>(vfs_handle);
|
static_cast<Dir_vfs_handle*>(vfs_handle);
|
||||||
|
|
||||||
dir_vfs_handle->sync_dir_handle_registry.for_each(
|
auto f = [&result, dir_vfs_handle] (Dir_vfs_handle::Subdir_handle_element &e) {
|
||||||
[&] (Dir_vfs_handle::Sync_dir_handle_element &sync_dir_handle_element) {
|
Sync_result r = e.vfs_handle.fs().complete_sync(&e.vfs_handle);
|
||||||
|
if (r != SYNC_OK)
|
||||||
|
result = r;
|
||||||
|
};
|
||||||
|
|
||||||
Vfs_handle *vfs_handle = &sync_dir_handle_element.vfs_handle;
|
dir_vfs_handle->subdir_handle_registry.for_each(f);
|
||||||
|
|
||||||
result =
|
|
||||||
vfs_handle->fs().complete_sync(vfs_handle);
|
|
||||||
|
|
||||||
if (result != SYNC_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
vfs_handle->ds().close(vfs_handle);
|
|
||||||
destroy(vfs_handle->alloc(), &sync_dir_handle_element);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user