mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-26 17:01:07 +00:00
2912096f6e
This patch changes the 'Single_file_system' to return NO_PERM only if the to-be-unlinked file corresponds to the single file. This way, a <rom> co-mounted with a <ram> file-system does not stand in the way of unlinking files from the <ram>. The concrete symptom occurred the following scenario: <vfs> <dir name="home"> <ram/> <rom name="..."/> </dir> </vfs> The following sequence of commands wrongly resulted in "Operation not permitted": $ mkdir -p /home/a/b/c $ rm -f /home/a/b/c/d In this case, rm should not fail (unlink should return ENOENT) Fixes #3690
307 lines
6.9 KiB
C++
307 lines
6.9 KiB
C++
/*
|
|
* \brief File system that hosts a single node
|
|
* \author Norman Feske
|
|
* \date 2014-04-07
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2014-2017 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 _INCLUDE__VFS__SINGLE_FILE_SYSTEM_H_
|
|
#define _INCLUDE__VFS__SINGLE_FILE_SYSTEM_H_
|
|
|
|
#include <vfs/file_system.h>
|
|
#include <vfs/vfs_handle.h>
|
|
|
|
namespace Vfs { class Single_file_system; }
|
|
|
|
|
|
class Vfs::Single_file_system : public File_system
|
|
{
|
|
private:
|
|
|
|
Node_type const _type;
|
|
Node_rwx const _rwx;
|
|
|
|
typedef String<64> Filename;
|
|
|
|
Filename _filename { };
|
|
|
|
protected:
|
|
|
|
struct Single_vfs_handle : Vfs_handle
|
|
{
|
|
using Vfs_handle::Vfs_handle;
|
|
|
|
virtual Read_result read(char *dst, file_size count,
|
|
file_size &out_count) = 0;
|
|
|
|
virtual Write_result write(char const *src, file_size count,
|
|
file_size &out_count) = 0;
|
|
|
|
virtual Sync_result sync()
|
|
{
|
|
return SYNC_OK;
|
|
}
|
|
|
|
virtual bool read_ready() = 0;
|
|
};
|
|
|
|
struct Single_vfs_dir_handle : Single_vfs_handle
|
|
{
|
|
private:
|
|
|
|
Node_type const _type;
|
|
Node_rwx const _rwx;
|
|
|
|
Filename const &_filename;
|
|
|
|
/*
|
|
* Noncopyable
|
|
*/
|
|
Single_vfs_dir_handle(Single_vfs_dir_handle const &);
|
|
Single_vfs_dir_handle &operator = (Single_vfs_dir_handle const &);
|
|
|
|
public:
|
|
|
|
Single_vfs_dir_handle(Directory_service &ds,
|
|
File_io_service &fs,
|
|
Genode::Allocator &alloc,
|
|
Node_type type,
|
|
Node_rwx rwx,
|
|
Filename const &filename)
|
|
:
|
|
Single_vfs_handle(ds, fs, alloc, 0),
|
|
_type(type), _rwx(rwx), _filename(filename)
|
|
{ }
|
|
|
|
Read_result read(char *dst, file_size count,
|
|
file_size &out_count) override
|
|
{
|
|
out_count = 0;
|
|
|
|
if (count < sizeof(Dirent))
|
|
return READ_ERR_INVALID;
|
|
|
|
file_size index = seek() / sizeof(Dirent);
|
|
|
|
Dirent &out = *(Dirent*)dst;
|
|
|
|
auto dirent_type = [&] ()
|
|
{
|
|
switch (_type) {
|
|
case Node_type::DIRECTORY: return Dirent_type::DIRECTORY;
|
|
case Node_type::SYMLINK: return Dirent_type::SYMLINK;
|
|
case Node_type::CONTINUOUS_FILE: return Dirent_type::CONTINUOUS_FILE;
|
|
case Node_type::TRANSACTIONAL_FILE: return Dirent_type::TRANSACTIONAL_FILE;
|
|
}
|
|
return Dirent_type::END;
|
|
};
|
|
|
|
if (index == 0) {
|
|
out = {
|
|
.fileno = (Genode::addr_t)this,
|
|
.type = dirent_type(),
|
|
.rwx = _rwx,
|
|
.name = { _filename.string() }
|
|
};
|
|
} else {
|
|
out = {
|
|
.fileno = (Genode::addr_t)this,
|
|
.type = Dirent_type::END,
|
|
.rwx = { },
|
|
.name = { }
|
|
};
|
|
}
|
|
|
|
out_count = sizeof(Dirent);
|
|
|
|
return READ_OK;
|
|
}
|
|
|
|
Write_result write(char const *, file_size, file_size &) override
|
|
{
|
|
return WRITE_ERR_INVALID;
|
|
}
|
|
|
|
bool read_ready() override { return true; }
|
|
};
|
|
|
|
bool _root(const char *path)
|
|
{
|
|
return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0);
|
|
}
|
|
|
|
bool _single_file(const char *path)
|
|
{
|
|
return (strlen(path) == (strlen(_filename.string()) + 1)) &&
|
|
(strcmp(&path[1], _filename.string()) == 0);
|
|
}
|
|
|
|
public:
|
|
|
|
Single_file_system(Node_type node_type,
|
|
char const *type_name,
|
|
Node_rwx rwx,
|
|
Xml_node config)
|
|
:
|
|
_type(node_type), _rwx(rwx),
|
|
_filename(config.attribute_value("name", Filename(type_name)))
|
|
{ }
|
|
|
|
|
|
/*********************************
|
|
** Directory-service interface **
|
|
*********************************/
|
|
|
|
Dataspace_capability dataspace(char const *) override
|
|
{
|
|
return Dataspace_capability();
|
|
}
|
|
|
|
void release(char const *, Dataspace_capability) override { }
|
|
|
|
Stat_result stat(char const *path, Stat &out) override
|
|
{
|
|
out = Stat { };
|
|
out.device = (Genode::addr_t)this;
|
|
|
|
if (_root(path)) {
|
|
out.type = Node_type::DIRECTORY;
|
|
|
|
} else if (_single_file(path)) {
|
|
out.type = _type;
|
|
out.rwx = _rwx;
|
|
out.inode = 1;
|
|
} else {
|
|
return STAT_ERR_NO_ENTRY;
|
|
}
|
|
return STAT_OK;
|
|
}
|
|
|
|
file_size num_dirent(char const *path) override
|
|
{
|
|
if (_root(path))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
bool directory(char const *path) override
|
|
{
|
|
if (_root(path))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
char const *leaf_path(char const *path) override
|
|
{
|
|
return _single_file(path) ? path : 0;
|
|
}
|
|
|
|
Opendir_result opendir(char const *path, bool create,
|
|
Vfs_handle **out_handle,
|
|
Allocator &alloc) override
|
|
{
|
|
if (!_root(path))
|
|
return OPENDIR_ERR_LOOKUP_FAILED;
|
|
|
|
if (create)
|
|
return OPENDIR_ERR_PERMISSION_DENIED;
|
|
|
|
try {
|
|
*out_handle = new (alloc)
|
|
Single_vfs_dir_handle(*this, *this, alloc,
|
|
_type, _rwx, _filename);
|
|
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; }
|
|
}
|
|
|
|
void close(Vfs_handle *handle) override
|
|
{
|
|
if (handle && (&handle->ds() == this))
|
|
destroy(handle->alloc(), handle);
|
|
}
|
|
|
|
Unlink_result unlink(char const *path) override
|
|
{
|
|
if (_single_file(path))
|
|
return UNLINK_ERR_NO_PERM;
|
|
|
|
return UNLINK_ERR_NO_ENTRY;
|
|
}
|
|
|
|
Rename_result rename(char const *from, char const *to) override
|
|
{
|
|
if (_single_file(from) || _single_file(to))
|
|
return RENAME_ERR_NO_PERM;
|
|
return RENAME_ERR_NO_ENTRY;
|
|
}
|
|
|
|
|
|
/********************************
|
|
** File I/O service interface **
|
|
********************************/
|
|
|
|
Read_result complete_read(Vfs_handle *vfs_handle, char *dst,
|
|
file_size count,
|
|
file_size &out_count) override
|
|
{
|
|
Single_vfs_handle *handle =
|
|
static_cast<Single_vfs_handle*>(vfs_handle);
|
|
|
|
if (handle)
|
|
return handle->read(dst, count, out_count);
|
|
|
|
return READ_ERR_INVALID;
|
|
}
|
|
|
|
Write_result write(Vfs_handle *vfs_handle, char const *src, file_size count,
|
|
file_size &out_count) override
|
|
{
|
|
Single_vfs_handle *handle =
|
|
static_cast<Single_vfs_handle*>(vfs_handle);
|
|
|
|
if (handle)
|
|
return handle->write(src, count, out_count);
|
|
|
|
return WRITE_ERR_INVALID;
|
|
}
|
|
|
|
bool read_ready(Vfs_handle *vfs_handle) override
|
|
{
|
|
Single_vfs_handle *handle =
|
|
static_cast<Single_vfs_handle*>(vfs_handle);
|
|
|
|
if (handle)
|
|
return handle->read_ready();
|
|
|
|
return false;
|
|
}
|
|
|
|
Ftruncate_result ftruncate(Vfs_handle *, file_size) override
|
|
{
|
|
return FTRUNCATE_ERR_NO_PERM;
|
|
}
|
|
|
|
Sync_result complete_sync(Vfs_handle *vfs_handle) override
|
|
{
|
|
Single_vfs_handle *handle =
|
|
static_cast<Single_vfs_handle*>(vfs_handle);
|
|
|
|
if (handle)
|
|
return handle->sync();
|
|
|
|
return SYNC_ERR_INVALID;
|
|
}
|
|
};
|
|
|
|
#endif /* _INCLUDE__VFS__SINGLE_FILE_SYSTEM_H_ */
|