/*
* Copyright 2022-2023, ttcoder
* All rights reserved. Distributed under the terms of the MIT license.
*/
// base
#include <os/path.h>
#include <os/vfs.h> // Directory::Path
#include <file_system_session/file_system_session.h> // Directory_entry
#include <libc/component.h>
#include <vfs/file_system_factory.h>
#include <vfs/file_system.h>
#include <vfs/vfs_handle.h>
// vfs_fuse_ntfs.so ...
#include "xattr_mapping.h"
// project
#include "IndexStore.h"
/**********/
static const char bquery_support_prefix[] = "/_BQuery_support";
static const char bquery_index[] = "_Hai_Genode_INDEX_v0.txt";
//
// In the ctor we create a top-level folder named "_Hai_Genode_Root_Folder_", and enforce
// a sort of _chroot_ to within that folder, ourselves.
// That buys us a minor benefit, and a big one
// - the minor benefit is, when re-purposing an existing Windows partition, Genode and Windows do not "pollute" each other (though, as a downside, we cannot "see" the windows files, even if we wanted to look for e.g. audio files there...)
// - the big benefit is, we hide/make-inaccessible the Index file ; otherwise the user might mess with the index file or delete it, not knowing what it is.
//
static const char _ChrootFolder[] = "_Hai_Genode_Root_Folder_";
///ToDo: weird that this whole new-opendir change seems to ONLY be needed to fix CreateSymLink()... I should
// beef up test suite "jam t7" with deep folders, to find other "trigger" cases beside the symlink case...
//
//#define OLD_STYLE_OPENDIR
#if 0
#define TRACE(x, ...) Genode::log( "---> idx.", x, ##__VA_ARGS__ )
#else
#define TRACE(x, ...)
#endif
namespace {
using namespace Vfs;
using Genode::Directory;
};
// Inspiration/guides:
// look at gems/src/lib/vfs/import for an example of how to call a 'remote' FS...
//
//maybe later:
// grep -r WriteAttrs shows these:
// misc-apps/pulse/Prefs.cpp (so use Pulse instead of Clock <=====)
// haiku-on-genode/kits/mail/MailMessage.cpp (BeMail: probably too ambitious)
struct QueryDir_vfs_handle : Vfs::Vfs_handle
{
private:
//BObjectList< String<MAX_PATH_LEN> > entryList { 20, true }; // set "owner = true" to delete on exit
//void * resultsBuf;
//const file_size bufSize;
BufIO resultsBuf;
public:
QueryDir_vfs_handle(
Vfs::Directory_service & ds,
Vfs::File_io_service & fs,
Genode::Allocator & alloc,
const void * buf,
file_size bufsize
)
: Vfs_handle(ds, fs, alloc, 0)
//,entryList()///
//,resultsBuf()
//,bufSize( bufsize )
,resultsBuf( alloc )
{
//resultsBuf = alloc.alloc( bufsize );//new (alloc) char[bufsize];
//Genode::memcpy( resultsBuf, buf, bufsize );
resultsBuf.Append( buf, bufsize );
}
/*
~QueryDir_vfs_handle()
{
if( resultsBuf )
alloc().free( resultsBuf, bufSize );//destroy( alloc(), (char*)resultsBuf );
}
*/
File_io_service::Read_result read( char *dest, file_size len, file_size &out_count )
{
TRACE(" query.read");
out_count = 0;
#if 0
BDirectory-style access:
if (len < sizeof(::File_system::Directory_entry)) {
Genode::error("read buffer too small for directory entry");
return File_io_service::READ_ERR_INVALID;
}
if (seek() % sizeof(::File_system::Directory_entry)) {
Genode::error("seek offset not aligned to sizeof(Directory_entry)");
return File_io_service::READ_ERR_INVALID;
}
const file_size
index = seek() / sizeof(::File_system::Directory_entry);
if( index >= entryList.CountItems() )//size() )
return File_io_service::READ_ERR_INVALID;
Directory_service::Stat stat;
{
/*
auto ret = _rawfs.stat(
entryList.at( index ),
&stat
);
Genode::log( " underlying stat() returns: ", ret );
*/
}
/*
struct dirent *dent = de + index;
if (!dent)
return File_io_service::READ_ERR_INVALID;
*/
::File_system::Directory_entry * e = (::File_system::Directory_entry *)(dest);
Genode::Path<512> last;
last.import( entryList.ItemAt(index)->string() );
last.keep_only_last_element();
//.size...
e->type = nodeType( stat.type );
//.rwx...
//.inode..
//.device..
//.modification_time..
copy_cstring( e->name.buf, last.last_element(), Genode::strlen(last.last_element()) +1 );//entryList.at(index).string(), entryList.at(index).length() );
e->sanitize(); // null-terminate the string
out_count = sizeof(::File_system::Directory_entry);
return File_io_service::READ_OK;
#else
// BFile-style access:
//Genode::log( " seek: ", seek(), " bufsize: ", bufSize );//Genode::strlen(resultsBuf));
if( seek() >= resultsBuf.BufferLength() )//bufSize )//Genode::strlen(resultsBuf) )
// careful, the right way to handle EoF is to return READ_OK with "out count == 0",
// if we return READ_ERR_INVALID the system goes haywire, read() returns double the amount of read bytes (instead of 0) !
return File_io_service::READ_OK;
const char * from = static_cast<const char*>( resultsBuf.Buffer() ) + seek();
//Genode::log( " PROCEEDING with read() of result-buf: ", from );
const file_size tx = Genode::min(
len,
resultsBuf.BufferLength() - seek()
);
Genode::memcpy(
dest,
from,
tx
);
out_count = tx;
return File_io_service::READ_OK;
#endif
}
};
class Attr_vfs_handle : public Vfs::Vfs_handle
{
public: ////
String<256>/**/ pathOurs; ///ToDo
String<256> _attrname;
Vfs_handle * _handle_raw;
public:
Attr_vfs_handle(
Vfs::Directory_service & ds,
Vfs::File_io_service & fs,
Allocator & alloc,
Vfs_handle * underlying_rawfs_handle,
const char * path,
const char * attrname
)
: Vfs_handle(ds, fs, alloc, 0)
,pathOurs( path )
,_attrname( attrname )
,_handle_raw( underlying_rawfs_handle )
{
}
};
class Dir_vfs_handle : public Vfs::Vfs_handle
{
public: ///
Vfs_handle * _handle_raw;
public:
Dir_vfs_handle(
Vfs::Directory_service & ds,
Vfs::File_io_service & fs,
Allocator & alloc,
Vfs_handle * underlying_rawfs_handle
)
: Vfs_handle(ds, fs, alloc, 0)
,_handle_raw( underlying_rawfs_handle )
{
}
};
//#pragma mark -
class Indexed_file_system : public Vfs::File_system
{
typedef Vfs::File_system inherited;
private:
// Business logic:
IndexStore indexStore;
// Genode:
//
Vfs::Env &_vfs_env;
Directory::Path _redirection;
Vfs::File_system & _rawfs;
public:
Indexed_file_system( Vfs::Env & env, Genode::Xml_node config )
: indexStore( env.env() )
,_vfs_env( env )
,_redirection() // below
,_rawfs( env.root_dir() )
{
TRACE( "Indexer Ctor" );
// e.g. "/raw_fs"
Genode::Directory::Path path(
config.attribute_value(
"fs_path",
Genode::Directory::Path()
));
_redirection = String<128>( path, "/", _ChrootFolder );
Genode::log( "-- Starting Index-Augmented FS above '", path, "' -- (chroot'ed to ", _redirection, ")" );
// sanity check: supporting-FS path present ?
if( path.length() <= 0 )
Genode::error( "indexer misconfiguration: absent/empty path to supporting-fs" );
// sanity check <path>: is there an actual supporting FS at that path ?
{
Vfs_handle * handle = nullptr;
auto res = _rawfs.opendir( path.string(), false, &handle, env.alloc() );
if( res != OPENDIR_OK || nullptr == handle )
Genode::error( "indexer misconfiguration: No underlying file system at specified path: ", path );
else
_rawfs.close( handle );
}
// create/open <_redirection> chroot: we chroot within a *subfolder* of the underlying FS:
{
Vfs_handle * handle = nullptr;
auto res = _rawfs
.opendir( _redirection.string(), true /* ! */, &handle, env.alloc() );
if( OPENDIR_ERR_NODE_ALREADY_EXISTS == res && NULL == handle )
// we're good -> do nothing
;
else if( OPENDIR_OK == res && handle )
// we're good, but need to close the opened handle
_rawfs.close( handle );
else
{
Genode::error( "(RES: ", int(res), ") indexer cannot work: this chroot subfolder does not exist, and cannot be created (-> FS is absent or read-only): ", _redirection );
//+ Read-Only filesystem: attempt to give read-only access to the very-top-folder? or no point in doing that ?
if( handle )
_rawfs.close( handle );
}
}
// load Index
loadIndex( String<MAX_PATH_LEN>(path, "/", bquery_index) );
}
~Indexed_file_system()
{
//+ sync...
}
Directory::Path mapRaw( const Directory::Path & ours )
{
// if <path_ours> is prefixed with the redirection path (!), that's a big OOPS:
// (might happen in opendir(), num_dirent()...)
if( 0 == Genode::strcmp(ours.string(), _redirection.string(), _redirection.length()-sizeof('\0') /*sigh*/) )
{
Genode::error( "indexer misconfiguration: infinite-loop redirection detected, probably a missing supporting-fs combined with us having top-level/root realm responsibility ?" );
// Bail out! Otherwise we get into some sort of infinite loop, if the "/" root ALSO points to us:
// idx.opendir path=/raw_boot_sans_indexing/raw_boot_sans_indexing/raw_boot_sans_indexing/raw_boot_sans_indexing/raw_boot_sans_indexing/raw_boot_sans_indexing/raw_boot_sans_indexing/raw_boot_sans_indexing/raw_boot_sans_indexing/r
//
throw Genode::Exception();
}
// e.g. "/Music" => "/raw_ntfs/_Hai_Genode_Root_Folder_/Music"
Directory::Path path(
_redirection,
ours
);
// fix-up "/raw_ntfs/_Hai_Genode_Root_Folder_/" (un-normalized trailing slash) : normalize <path> !
// -- didn't observe that behavior from Directory::Path before implementing a chroot, but ever since chroot was added we get that problem, exclusively in the case of "top of chroot" corner case
Genode::Path<1024> norm;
norm.import( path.string() ); // calls canonicalize(), which calls strip_superfluous_slashes() //XXX only with the proper patch! see ticket:
///ToDo: fix ticket https://github.com/genodelabs/genode/issues/4708
//Genode::log( norm, " from: ", path );
return norm;//path;
}
void loadIndex( const String<MAX_PATH_LEN> & path )
{
// <path> param: purpose: take the path as param, in order to decrease the T-/T+ risk, since we're called from the ctor, where <_redirection> is calculated.
//if( debug )
Genode::log( "open Attr-Index at <", path, ">" );
file_size size = 0;
#if 1
Stat stat;
auto stat_res = _rawfs.stat( path.string(), stat );
if( STAT_OK != stat_res || stat.size <= 0 )
{
Genode::warning( "indexer: Attr-Index empty or not stat()able" );
//
return;
//
}
// else: open():
size = stat.size;
#endif
Vfs_handle * handle = nullptr;
///later: why does NTFS return OPEN_OK on first run (provided the above "return" statement is absent), when the file does not even exist yet ?
auto res = _rawfs.open(
path.string(),
OPEN_MODE_RDONLY,
&handle, _vfs_env.alloc()
);
if( OPEN_OK != res )//&& OPEN_ERR_EXISTS != res )
{
Genode::warning( "(RES: ", int(res), ") indexer: Attr-Index does not exist, probably just the first time we run on this volume." );
// Genode::error( "(RES: ", int(res), ") indexer: Attr-Index does not exist and cannot create Index.txt file (-> read-only filesystem ?!): " );
//+ Read-Only filesystem: attempt to give read-only access to the very-top-folder? or no point in doing that ?
//
return;
//
}
// else: read() contents:
char * buf = static_cast<char*>( _vfs_env.alloc().alloc(size) );
bool res1 = handle->fs().queue_read( handle, sizeof(buf) );
//Genode::log( " INDEX queue_read: ", res1 );
file_size gotten = 0;
auto res2 = handle->fs().complete_read( handle, buf, size, gotten );
TRACE( "indexer: load index, complete_read=", int(res2), " vs READ_OK=", int(READ_OK), " Got ", gotten, " bytes" );
if( false == res1 || READ_OK != res2 || gotten != size )
{
Genode::warning( "indexer: could not read Index contents (res=", int(res2), " retrieved=", gotten, ")" );
}
else
{
indexStore.SetAs_And_Check( buf, size );
}
// Epilogue
_vfs_env.alloc().free( buf, size ); buf = nullptr;
_rawfs.close( handle );
}
void saveIndex()
{
Genode::Path<MAX_PATH_LEN> path = _redirection;
{
path.strip_last_element(); // remove "_Hai_Genode_Root_Folder_"
path.append_element( bquery_index ); // append "_Hai_Genode_INDEX.txt"
}
///later: should we truncate() instead of unlink() ?
// unlink()
auto unlink_res = _rawfs.unlink( path.string() );
if( UNLINK_OK != unlink_res )
Genode::warning( "indexer: cannot zero-out Attr-index file, res=", int(unlink_res) );
Vfs_handle * handle = nullptr;
auto res = _rawfs.open(
path.string(),
OPEN_MODE_WRONLY,
&handle, _vfs_env.alloc()
);
if( OPEN_OK != res || nullptr == handle )
{
Genode::error( "(RES: ", int(res), ") indexer: cannot write index to Attr-Index" );
//
return;
//
}
TRACE( "indexer: saving index, ", indexStore.IndexBufferSize(), " bytes" );
file_size written = 0;
auto w_res = handle->fs().write(
handle,
indexStore.IndexBuffer(),
indexStore.IndexBufferSize(),
written
);
if( w_res != WRITE_OK || written != indexStore.IndexBufferSize() )
Genode::error( "indexer: failed to write: res=", int(w_res), " vs WRITE_OK: ", int(WRITE_OK), " written=", written );
// Epilogue
//
_rawfs.close( handle );
// -- stress test/regression testing:
// loadIndex(path);
// -- doing a "loadIndex()" after each save means we re-parse/check the whole index -- a good way to detect corruptions.
}
/***************************
** File_system interface **
***************************/
///?
static char const *name() { return "indexing_fs"; }
char const *type() override { return "indexing_fs"; }
/*********************************
** Directory service interface **
*********************************/
Open_result open(char const *path_ours, unsigned vfs_mode,
Vfs_handle **handle,
Allocator &alloc) override
{
//auto path_raw = mapRaw( path_ours );
TRACE( "open path=", path_ours, " alias: ", mapRaw(path_ours), " mode=", vfs_mode );
#if 0
// in opendir() instead !
#else
// BQuery case
Genode::Path<MAX_PATH_LEN> path;
path.import( path_ours );
if( //OPEN_MODE_RDONLY & vfs_mode &&
path.strip_prefix(bquery_support_prefix) )
{
indexStore.SetQuery( Genode::strlen(path.string())
? path.last_element()
: ""
);
TRACE( " opening (virtual) query 'folder' for query: ", path.string(), " with resulting entrylist Size: ", indexStore.ResultsSize() );
try {
*handle = new (alloc) QueryDir_vfs_handle(
*this, *this,
alloc,
indexStore.ResultsBuf(),
indexStore.ResultsSize()
);
}
catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; }
catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; }
return OPEN_OK;
}
#endif
const bool writing = (vfs_mode & OPEN_MODE_WRONLY) || (vfs_mode & OPEN_MODE_RDWR);
if( writing )
{
// compute e.g. "Music/" from ".Music/"
// (which itself is VFS's truncation of the original "/boot/.Music/") :
VirtualizedXattr isattr( path_ours );
// snoop
if( isattr.initcheck() == Directory_service::STAT_OK )
{
//Genode::log("OPEN OWN fd here, with: ", real_path_raw);
#if 0
// Direct delegation does NOT work for some reason, so do "matrioshka" VFS Handle !
// (same thing with opendir(), a direct handle only works for root but not subdirs...)
Genode::log("delegate to ntfs: ", path_raw);
auto res = _rawfs.open(
path_raw.string(),
vfs_mode,
handle,
alloc
);
return res;
#else
// forward (map() <path_ours> *without* interpreting a possible "dot-file", so that the underlying driver knows to access attributes if applicable -- it will call VirtualizedXattr on its own to detect "dot paths")
Vfs_handle *
underlying_rawfs_handle = nullptr;
auto res = _rawfs.open(
mapRaw( path_ours ).string(),
vfs_mode,
&underlying_rawfs_handle,
alloc
);
#endif
if( OPEN_OK == res && underlying_rawfs_handle )
{
// compute e.g. "/Ntfs/Music/" from "Music/"
// auto real_path_raw = mapRaw( isattr.real_path() );
// snoop: the snooping FD will use the *REAL* and *mapped* path
try {
*handle = new (alloc)
Attr_vfs_handle(
*this,//_rawfs,//*this,
*this,//_rawfs,//*this,
alloc,
underlying_rawfs_handle,
isattr.real_path(),//_d_ real_path_raw.string(),//mapRaw( isattr.real_path() ).string(),//
isattr.xattr_name());
return OPEN_OK;
}
catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; }
catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; }
}
return OPEN_ERR_UNACCESSIBLE;
}
}
// just forward
// (map() the UNCHANGED <path_ours>, so that the underlying driver knows to access attributes if applicable -- it will call VirtualizedXattr on its own)
auto res = _rawfs.open(
mapRaw( path_ours ).string(),
vfs_mode,
handle,
alloc
);
return res;
}
Opendir_result opendir(char const *path_ours, bool create,
Vfs_handle **handle,
Allocator &alloc) override
{
auto path_raw = mapRaw( path_ours );
TRACE( "opendir path=", path_ours, " alias: ", path_raw, " create=", create );
#if 0
// BQuery case
Genode::Path<256> /**/ path;
path.import( path_ours );
if( path.strip_prefix(bquery_support_prefix) )
{
Genode::log( "opening (virtual) query 'folder'..." );
try {
*handle = new (alloc) QueryDir_vfs_handle( *this, *this, alloc );
}
catch (Genode::Out_of_ram) { return OPENDIR_ERR_OUT_OF_RAM; }
catch (Genode::Out_of_caps) { return OPENDIR_ERR_OUT_OF_CAPS; }
return OPENDIR_OK;
}
#else
// in open() instead !
#endif
#ifdef OLD_STYLE_OPENDIR
// just forward
auto res = _rawfs.opendir(
path_raw.string(), create , handle, alloc);
#else
// Direct delegation does NOT work for some reason, so do "matrioshka" VFS Handle:
//
// If we just forward to _rawfs.opendir(), it won't work in most cases (except when opening the top-level directory).
// The created VFS handle will not work read() : genode seems to call
// handle->complete_read() on the wrong fs (i.e. the call does not land in vfs_fuse_ntfs filesystem's complete_read() but in someone else's)
// So, let's use a "wrapper" handle, around the underlying handle, similar to what we do with Attr_vfs_handle:
// forward to mapped path
Vfs_handle * underlying_rawfs_handle = nullptr;
auto res = _rawfs.opendir(
path_raw.string(),
create,
&underlying_rawfs_handle,
alloc
);
if( OPENDIR_OK == res && underlying_rawfs_handle )
{
try {
*handle = new (alloc)
Dir_vfs_handle(
*this,
*this,
alloc,
underlying_rawfs_handle
);
//Genode::log(" ___ idx opendir: IDX dirhandle is ", handle, " with embedded NTFS-dir-handle: ", underlying_rawfs_handle );
return OPENDIR_OK;
}
catch (Genode::Out_of_ram) { return OPENDIR_ERR_OUT_OF_RAM; }
catch (Genode::Out_of_caps) { return OPENDIR_ERR_OUT_OF_CAPS; }
}
TRACE( " ** ==> opendir failed for raw path=", path_raw, " status -> ", int(res) );
return res; // e.g. OPENDIR_ERR_UNACCESSIBLE;
#endif
return res;
}
Openlink_result openlink(char const *path_ours, bool create,
Vfs_handle **handle, Allocator &alloc) override
{
auto path_raw = mapRaw( path_ours );
TRACE( "openlink <", path_ours, "> aka <", path_raw, ">" );
auto res = _rawfs.openlink( path_raw.string(), create, handle, alloc );
//Genode::log( " openlink res: ", (int)res, " versus OPENLINK_ERR_PERMISSION_DENIED: ", (int)OPENLINK_ERR_PERMISSION_DENIED );
return res;
}
void close(Vfs_handle *vfs_handle) override
{
///later: call sync() or sync_state() first ?
TRACE( "close" );
#if 1
Attr_vfs_handle * attr = dynamic_cast< Attr_vfs_handle* >( vfs_handle );
if( attr )
_rawfs.close( attr->_handle_raw );
#endif
#ifdef OLD_STYLE_OPENDIR
// nothing in this case
#else
Dir_vfs_handle * dir = dynamic_cast< Dir_vfs_handle* >( vfs_handle );
if( dir )
_rawfs.close( dir->_handle_raw );
#endif
if (vfs_handle && (&vfs_handle->ds() == this)) {
destroy(vfs_handle->alloc(), vfs_handle);
}
}
/// Watch_result watch(char const *path, ..
Genode::Dataspace_capability dataspace(char const *) override
{
Genode::warning(__func__, " not implemented in indexer plugin");
return Genode::Dataspace_capability();
}
void release(char const *, Genode::Dataspace_capability) override
{
Genode::warning(__func__, " (of dataspace) not implemented in indexer plugin");
}
file_size num_dirent( char const * path_ours ) override
{
auto path_raw = mapRaw( path_ours );
TRACE( "num_dirent path=", path_ours, " alias: ", path_raw );
#if 0
Genode::Path<256> /**/ path;
path.import( path_ours );
if( path.strip_prefix(bquery_support_prefix) )
{
Genode::log( " ... bquery -> pretending to COUNT entries" );
return 99;//1;///////
}
#else
// in stat() instead
#endif
// just forward
return _rawfs.num_dirent( path_raw.string() );
}
bool directory( char const * path_ours ) override
{
auto path_raw = mapRaw( path_ours );
TRACE( "directory path=", path_ours, " alias: ", path_raw );
// Special-Case: "BQuery"
// called BOTH in "vfs server" and "local fs" cases ! ******
Genode::Path<256> /**/ path;
path.import( path_ours );
if( path.strip_prefix(bquery_support_prefix) )
{
#if 0
Genode::log( " ... bquery -> pretending this is a (virtual) directory" );
return true;
#else
// e.g.: stripped == </(Audio:Artist=="Pink*Floyd")>
// We cannot just unconditionally "return true" here to facilitate the remote-vfs case,
// as that would break the local-fs case... We have to be more selective than that:
// if being called for just the top "/_BQuery_support" then we return true,
// as that is the vfs server calling us and it needs to be told it's a directory:
bool is_dir = path.equals( "" ); // test against empty string, not against "/" (there's not trailing slash on a normmalized path)
TRACE( " returns (BQuery case): <", is_dir?"true":"false", ">" );
return is_dir;
#endif
}
// just forward
auto ret = _rawfs.directory( path_raw.string() );
TRACE( " returns: <", ret?"true":"false", ">" );
return ret;
}
char const * leaf_path( char const * path_ours ) override
{
auto path_raw = mapRaw( path_ours );
TRACE( "leaf_path path: [", path_ours, "] alias: [", path_raw, "]" );
const char * ret = nullptr;
// Special-Case: "BQuery"
// (the below code is needed when using a *vfs server* instead of a local file system)
Genode::Path<256> /**/ path; ///ToDo
path.import( path_ours );
if( path.strip_prefix(bquery_support_prefix) )
{
ret = path_ours;
}
else // Base case:
{
// just forward
ret = _rawfs.leaf_path( path_raw.string() );
}
TRACE( " returns: <", ret, ">" );
return ret;
}
Stat_result stat( char const * path_ours, Stat & out ) override
{
auto path_raw = mapRaw( path_ours );
TRACE( "stat path=", path_ours, " alias=", path_raw );
// Special-Case: "BQuery"
// (the below code is needed when using a local-fs)
Genode::Path<256> /**/ path; ///ToDo
path.import( path_ours );
if( path.strip_prefix(bquery_support_prefix) )
{
// BQuery case! :
//Genode::log( " stat: this is a BQuery support request for query: ", path.string() );
out.device = (Genode::addr_t)this;
out.inode = 1;// dodge "0" inode as that would break LibC
out.rwx = Node_rwx::ro();
#if 0
out.type = Node_type::DIRECTORY;
out.size = 0;//99;
#else
out.type = Node_type::CONTINUOUS_FILE;
// optim -- we Query() here in stat() and then again in open(), but <indexStore> does cache query results in between...
indexStore.SetQuery( Genode::strlen(path.string())
? path.last_element()
: ""
);
out.size = indexStore.ResultsSize();
#endif
TRACE( " -> returns (BQuery case): ", int(STAT_OK), " out.device: ", out.device, " out.size: ", out.size, " out.inode: ", out.inode );
return STAT_OK;
}
// just forward
Stat_result ret = _rawfs.stat( path_raw.string(), out );
TRACE( " -> returns: ", int(ret), " out.device: ", out.device, " out.size: ", out.size, " out.inode: ", out.inode );
return ret;
}
Unlink_result unlink( char const * path_ours ) override
{
auto path_raw = mapRaw( path_ours );
TRACE( "unlink path=", path_ours, " alias: ", path_raw );
VirtualizedXattr isattr( path_ours );
///ToDo: if bquery path -> return: illegal
// snoop
if( isattr.initcheck() == Directory_service::STAT_OK )
indexStore.AttrDeleted( isattr.xattr_name(), isattr.real_path() ); ///+make that conditional on <ret> return code
else
indexStore.NodeDeleted( path_ours ); ///+make that conditional on <ret> return code
// forward
return _rawfs.unlink( path_raw.string() );
// sync index
saveIndex();
}
Rename_result rename( char const * from, char const * to ) override
{
auto from_dist = mapRaw( from );
auto to_dist = mapRaw( to );
//TRACE( "rename" );// path=", path_ours, " alias: ", path_raw );
TRACE( "rename from=", from, " to=", to );
///ToDo: if bquery path -> return: illegal
// snoop
indexStore.NodeMoved( from, to ); ///+make that conditional on <ret> return code
// forward
auto ret = _rawfs.rename( from_dist.string(), to_dist.string() );
// sync index
saveIndex();
return ret;
}
/************************
** File I/O interface **
************************/
Write_result write(Vfs_handle *vfs_handle,
char const *buf, file_size buf_size,
file_size &out_count) override
{
TRACE( "write bufsize=", buf_size );
Vfs::File_io_service::Write_result ret;// = 0;
Attr_vfs_handle * attr = dynamic_cast< Attr_vfs_handle* >( vfs_handle );
#ifndef OLD_STYLE_OPENDIR
Dir_vfs_handle * dir = dynamic_cast< Dir_vfs_handle* >( vfs_handle );
if( dir )
{
ret = dir->_handle_raw->fs().write(
attr->_handle_raw, // !
buf,
buf_size,
out_count
);
}
else
#endif
if( attr ) // snoop on attribute ops
{
ret = attr->_handle_raw->fs().write(//_rawfs.write(
attr->_handle_raw, // !
buf,
buf_size,
out_count
);
//Genode::log("ret: ", (int)ret, " buf_size: ", buf_size, " out_count: ", out_count);
if( WRITE_OK == ret && buf_size == out_count )
{
indexStore.AttrMutated(
attr->pathOurs.string(),
attr->_attrname.string(),
buf,
buf_size
);
// sync index
saveIndex();
}
else
Genode::warning( " xa-index left unchanged, since the attribute write is failed or uncomplete" );
}
else
{
// forward
ret = _rawfs.write(
vfs_handle,
buf,
buf_size,
out_count
);
}
return ret;
}
// subtlety warning! we need this even though it's not obvious from the basics:
// (otherwise complete_read() will NOT be called in the Dir case)
bool queue_read(Vfs_handle *vfs_handle, file_size size) override
{
//Genode::warning( "IDX** Queue-Read with size ", size, ", handle ", vfs_handle );
#ifndef OLD_STYLE_OPENDIR
// Dir-wrapper case, i.e. opendir() stuff
Dir_vfs_handle * dir = dynamic_cast< Dir_vfs_handle* > ( vfs_handle );
if( dir )
{
// T-
// This is the place where we need to synchronize seek'ing offsets (only needed at T-, not T+ it seems).
// class BSymlink won't work otherwise, when resolving deep paths via opendir().
// seek() is not *virtual*, hence not overridable for our purpose (see: vfs_file_handle.h),
// so we have no choice but to handle it here:
dir->_handle_raw->seek(
dir->seek()
);
//Genode::warning( " (q) handle.seek: ", dir->seek(), " underlying-handle.seek: ", dir->_handle_raw->seek() );
// T0
auto res = dir->_handle_raw->fs().queue_read(
dir->_handle_raw, // !
size
);
return res;
}
#endif
return inherited::queue_read( vfs_handle, size );
}
///xx: do we need to override queue_sync() etc to foward sync's to ntfs too ?
///ToDo: override this?: virtual bool update_modification_timestamp(Vfs_handle *, Vfs::Timestamp)
Read_result complete_read(Vfs_handle *vfs_handle, char *buf,
file_size buf_size,
file_size &out_count) override
{
TRACE( "complete_read bufsize=", buf_size );
#ifndef OLD_STYLE_OPENDIR
// Dir-wrapper case
Dir_vfs_handle * dir = dynamic_cast< Dir_vfs_handle* > ( vfs_handle );
if( dir )
{
//Genode::log(" _ dynamiccast of ", vfs_handle, " yields: ", dir, " (embedded NTFS handle=", dir->_handle_raw, ")" );
// seek(): unlike in queue_read(), it seems seek offset synchronization is not needed here, neither at T- nor T+...
//Genode::warning( " handle.seek: ", dir->seek(), " underlying-handle.seek: ", dir->_handle_raw->seek() );
auto res = dir->_handle_raw->fs().complete_read(
dir->_handle_raw, // !
buf,
buf_size,
out_count
);
return res;
}
#endif
// BQuery case
auto * query_handle = dynamic_cast< QueryDir_vfs_handle* >( vfs_handle );
if( query_handle )
return query_handle->read( buf, buf_size, out_count );
// there ought to be no other cases (underlying FS reads go directly to underlying's hook, not this hook)
return READ_ERR_INVALID;
}
Ftruncate_result ftruncate(Vfs_handle *vfs_handle, file_size len) override
{
TRACE( "ftruncate len=", len );
Genode::error( "indexer: ftruncate(): not implemented ; when is that called anyway ?" );
return FTRUNCATE_ERR_NO_PERM;//
}
bool read_ready(Vfs_handle *) override { return true; }
};
extern "C" Vfs::File_system_factory *vfs_file_system_factory(void)
{
struct Factory : Vfs::File_system_factory
{
Vfs::File_system *create(Vfs::Env &vfs_env, Genode::Xml_node config) override
{
TRACE( "new idx (Indexer).." );
return new (vfs_env.alloc()) Indexed_file_system(vfs_env, config);
}
};
static Factory f;
return &f;
}