fs_query: report dir content in alphabetic order

By sorting the reported output, all consumers of the reports become able
to rely on a deterministic order. For example, the file browser of
Sculpt OS, the launcher menu items, and the depot-selection items will
appear in a predictable order.

Fixes #4054
This commit is contained in:
Norman Feske 2021-03-19 13:56:51 +01:00
parent 8f1db47c26
commit e9ac14ed49
5 changed files with 177 additions and 12 deletions

View File

@ -202,10 +202,10 @@ regsub {.*report 'fs_query -> listing'} $output {} output
compare_output_to {
[init -> report_rom] <listing>
[init -> report_rom] <dir path="/fs/items">
[init -> report_rom] <file name="4" writeable="yes">fourth</file>
[init -> report_rom] <file name="1" writeable="yes">first</file>
[init -> report_rom] <file name="3" writeable="yes">updated
[init -> report_rom] </file>
[init -> report_rom] <file name="4" writeable="yes">fourth</file>
[init -> report_rom] </dir>
[init -> report_rom] </listing>
}

View File

@ -0,0 +1,73 @@
/*
* \brief Utility for iterating over subdirectory names
* \author Norman Feske
* \date 2021-03-19
*/
/*
* Copyright (C) 2021 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 _FOR_EACH_SUBDIR_NAME_H_
#define _FOR_EACH_SUBDIR_NAME_H_
/* Genode includes */
#include <os/vfs.h>
/* local includes */
#include <sorted_for_each.h>
namespace Genode {
template <typename FN>
static void for_each_subdir_name(Allocator &, Directory const &, FN const &);
}
template <typename FN>
static void Genode::for_each_subdir_name(Allocator &alloc, Directory const &dir,
FN const &fn)
{
using Dirname = Directory::Entry::Name;
struct Name : Interface, private Dirname
{
using Dirname::string;
Name(Dirname const &name) : Dirname(name) { }
bool higher(Name const &other) const
{
return (strcmp(other.string(), string()) > 0);
}
};
/* obtain list of sub directory names */
Registry<Registered<Name>> names { };
dir.for_each_entry([&] (Directory::Entry const &entry) {
if (entry.dir())
new (alloc) Registered<Name>(names, entry.name()); });
auto destroy_names = [&] ()
{
names.for_each([&] (Registered<Name> &name) {
destroy(alloc, &name); });
};
/* iterate over sorted list */
try {
sorted_for_each(alloc, names, [&] (Name const &name) {
fn(name.string()); });
}
catch (...) {
destroy_names();
throw;
}
destroy_names();
}
#endif /* _FOR_EACH_SUBDIR_NAME_H_ */

View File

@ -16,9 +16,13 @@
#include <base/component.h>
#include <base/heap.h>
#include <base/attached_rom_dataspace.h>
#include <util/avl_tree.h>
#include <os/reporter.h>
#include <os/vfs.h>
/* local includes */
#include <for_each_subdir_name.h>
namespace Fs_query {
using namespace Genode;
struct Watched_file;
@ -32,6 +36,14 @@ struct Fs_query::Watched_file
{
File_content::Path const _name;
/**
* Support for 'sorted_for_each'
*/
bool higher(Watched_file const &other) const
{
return (strcmp(other._name.string(), _name.string()) > 0);
}
Node_rwx const _rwx;
Watcher _watcher;
@ -136,12 +148,11 @@ struct Fs_query::Watched_directory
xml.node("dir", [&] () {
xml.attribute("path", _rel_path);
_dir.for_each_entry([&] (Directory::Entry const &entry) {
if (entry.dir())
xml.node("dir", [&] () {
xml.attribute("name", entry.name()); }); });
for_each_subdir_name(_alloc, _dir, [&] (Directory::Entry::Name const &name) {
xml.node("dir", [&] () {
xml.attribute("name", name); }); });
_files.for_each([&] (Watched_file const &file) {
sorted_for_each(_alloc, _files, [&] (Watched_file const &file) {
file.gen_query_response(xml, query, _alloc, _dir); });
});
}
@ -172,9 +183,9 @@ struct Fs_query::Main : Vfs::Watch_response_handler
Vfs_env(Main &main) : _main(main) { }
Genode::Env &env() override { return _main._env; }
Allocator &alloc() override { return _main._heap; }
Vfs::File_system &root_dir() override { return _main._root_dir_fs; }
Genode::Env &env() override { return _main._env; }
Allocator &alloc() override { return _main._heap; }
Vfs::File_system &root_dir() override { return _main._root_dir_fs; }
} _vfs_env { *this };

View File

@ -0,0 +1,80 @@
/*
* \brief Utility for accessing registry elements in a sorted order
* \author Norman Feske
* \date 2021-03-19
*/
/*
* Copyright (C) 2021 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 _SORTED_FOR_EACH_H_
#define _SORTED_FOR_EACH_H_
#include <base/registry.h>
#include <base/allocator.h>
namespace Genode {
template <typename T, typename FN>
static inline void sorted_for_each(Allocator &, Registry<T> const &, FN const &);
}
/**
* Execute 'fn' for each registry element
*
* The type T must be equipped with a method that defines the sort criterion:
*
* bool higher(T const &other) const
*
* It must implement a strict order over all registry elements. E.g., if the
* registry contains a set of names, no name must occur twice. The allocator
* passed as 'alloc' is used to for temporary allocations.
*/
template <typename T, typename FN>
static inline void Genode::sorted_for_each(Allocator &alloc,
Registry<T> const &registry,
FN const &fn)
{
struct Sorted_item : Avl_node<Sorted_item>
{
T const &element;
Sorted_item(T const &element) : element(element) { }
bool higher(Sorted_item const *item) const
{
return item ? element.higher(item->element) : false;
}
};
/* build temporary AVL tree of sorted elements */
Avl_tree<Sorted_item> sorted { };
registry.for_each([&] (T const &element) {
sorted.insert(new (alloc) Sorted_item(element)); });
auto destroy_sorted = [&] ()
{
while (Sorted_item *item = sorted.first()) {
sorted.remove(item);
destroy(alloc, item);
}
};
/* iterate over sorted elements, 'fn' may throw */
try {
sorted.for_each([&] (Sorted_item const &item) {
fn(item.element); });
}
catch (...) {
destroy_sorted();
throw;
}
destroy_sorted();
}
#endif /* _SORTED_FOR_EACH_H_ */

View File

@ -1,3 +1,4 @@
TARGET = fs_query
SRC_CC = main.cc
LIBS += base vfs
TARGET := fs_query
SRC_CC := main.cc
LIBS += base vfs
INC_DIR += $(PRG_DIR)