mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-25 08:21:08 +00:00
807be83b1b
Fixes #1963
231 lines
5.0 KiB
C++
231 lines
5.0 KiB
C++
/*
|
|
* \brief File-system directory node
|
|
* \author Norman Feske
|
|
* \date 2012-04-11
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2011-2015 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#ifndef _INCLUDE__RAM_FS__DIRECTORY_H_
|
|
#define _INCLUDE__RAM_FS__DIRECTORY_H_
|
|
|
|
/* Genode includes */
|
|
#include <file_system/util.h>
|
|
|
|
/* local includes */
|
|
#include <ram_fs/node.h>
|
|
#include <ram_fs/file.h>
|
|
#include <ram_fs/symlink.h>
|
|
|
|
namespace File_system { class Directory; }
|
|
|
|
|
|
class File_system::Directory : public Node
|
|
{
|
|
private:
|
|
|
|
List<Node> _entries;
|
|
size_t _num_entries;
|
|
|
|
public:
|
|
|
|
Directory(char const *name) : _num_entries(0) { Node::name(name); }
|
|
|
|
Node *entry_unsynchronized(size_t index)
|
|
{
|
|
Node *node = _entries.first();
|
|
for (unsigned i = 0; i < index && node; node = node->next(), i++);
|
|
return node;
|
|
}
|
|
|
|
bool has_sub_node_unsynchronized(char const *name) const
|
|
{
|
|
Node const *sub_node = _entries.first();
|
|
for (; sub_node; sub_node = sub_node->next())
|
|
if (strcmp(sub_node->name(), name) == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void adopt_unsynchronized(Node *node)
|
|
{
|
|
/*
|
|
* XXX inc ref counter
|
|
*/
|
|
_entries.insert(node);
|
|
_num_entries++;
|
|
|
|
mark_as_updated();
|
|
}
|
|
|
|
void discard_unsynchronized(Node *node)
|
|
{
|
|
_entries.remove(node);
|
|
_num_entries--;
|
|
|
|
mark_as_updated();
|
|
}
|
|
|
|
Node *lookup_and_lock(char const *path, bool return_parent = false)
|
|
{
|
|
if (strcmp(path, "") == 0) {
|
|
lock();
|
|
return this;
|
|
}
|
|
|
|
if (!path || path[0] == '/')
|
|
throw Lookup_failed();
|
|
|
|
/* find first path delimiter */
|
|
unsigned i = 0;
|
|
for (; path[i] && path[i] != '/'; i++);
|
|
|
|
/*
|
|
* If no path delimiter was found, we are the parent of the
|
|
* specified path.
|
|
*/
|
|
if (path[i] == 0 && return_parent) {
|
|
lock();
|
|
return this;
|
|
}
|
|
|
|
/*
|
|
* The offset 'i' corresponds to the end of the first path
|
|
* element, which can be either the end of the string or the
|
|
* first '/' character.
|
|
*/
|
|
|
|
/* try to find entry that matches the first path element */
|
|
Node *sub_node = _entries.first();
|
|
for (; sub_node; sub_node = sub_node->next())
|
|
if ((strlen(sub_node->name()) == i) &&
|
|
(strcmp(sub_node->name(), path, i) == 0))
|
|
break;
|
|
|
|
if (!sub_node)
|
|
throw Lookup_failed();
|
|
|
|
if (!contains_path_delimiter(path)) {
|
|
|
|
/*
|
|
* Because 'path' is a basename that corresponds to an
|
|
* existing sub_node, we have found what we were looking
|
|
* for.
|
|
*/
|
|
sub_node->lock();
|
|
return sub_node;
|
|
}
|
|
|
|
/*
|
|
* As 'path' contains one or more path delimiters, traverse
|
|
* into the sub directory names after the first path element.
|
|
*/
|
|
|
|
/*
|
|
* We cannot traverse into anything other than a directory.
|
|
*
|
|
* XXX we might follow symlinks here
|
|
*/
|
|
Directory *sub_dir = dynamic_cast<Directory *>(sub_node);
|
|
if (!sub_dir)
|
|
throw Lookup_failed();
|
|
|
|
return sub_dir->lookup_and_lock(path + i + 1, return_parent);
|
|
}
|
|
|
|
Directory *lookup_and_lock_dir(char const *path)
|
|
{
|
|
Node *node = lookup_and_lock(path);
|
|
|
|
Directory *dir = dynamic_cast<Directory *>(node);
|
|
if (dir)
|
|
return dir;
|
|
|
|
node->unlock();
|
|
throw Lookup_failed();
|
|
}
|
|
|
|
File *lookup_and_lock_file(char const *path)
|
|
{
|
|
Node *node = lookup_and_lock(path);
|
|
|
|
File *file = dynamic_cast<File *>(node);
|
|
if (file)
|
|
return file;
|
|
|
|
node->unlock();
|
|
throw Lookup_failed();
|
|
}
|
|
|
|
Symlink *lookup_and_lock_symlink(char const *path)
|
|
{
|
|
Node *node = lookup_and_lock(path);
|
|
|
|
Symlink *symlink = dynamic_cast<Symlink *>(node);
|
|
if (symlink)
|
|
return symlink;
|
|
|
|
node->unlock();
|
|
throw Lookup_failed();
|
|
}
|
|
|
|
/**
|
|
* Lookup parent directory of the specified path
|
|
*
|
|
* \throw Lookup_failed
|
|
*/
|
|
Directory *lookup_and_lock_parent(char const *path)
|
|
{
|
|
return static_cast<Directory *>(lookup_and_lock(path, true));
|
|
}
|
|
|
|
size_t read(char *dst, size_t len, seek_off_t seek_offset)
|
|
{
|
|
if (len < sizeof(Directory_entry)) {
|
|
PERR("read buffer too small for directory entry");
|
|
return 0;
|
|
}
|
|
|
|
seek_off_t index = seek_offset / sizeof(Directory_entry);
|
|
|
|
if (seek_offset % sizeof(Directory_entry)) {
|
|
PERR("seek offset not alighed to sizeof(Directory_entry)");
|
|
return 0;
|
|
}
|
|
|
|
Node *node = entry_unsynchronized(index);
|
|
|
|
/* index out of range */
|
|
if (!node)
|
|
return 0;
|
|
|
|
Directory_entry *e = (Directory_entry *)(dst);
|
|
|
|
e->inode = node->inode();
|
|
|
|
if (dynamic_cast<File *>(node)) e->type = Directory_entry::TYPE_FILE;
|
|
if (dynamic_cast<Directory *>(node)) e->type = Directory_entry::TYPE_DIRECTORY;
|
|
if (dynamic_cast<Symlink *>(node)) e->type = Directory_entry::TYPE_SYMLINK;
|
|
|
|
strncpy(e->name, node->name(), sizeof(e->name));
|
|
|
|
return sizeof(Directory_entry);
|
|
}
|
|
|
|
size_t write(char const *src, size_t len, seek_off_t seek_offset)
|
|
{
|
|
/* writing to directory nodes is not supported */
|
|
return 0;
|
|
}
|
|
|
|
size_t num_entries() const { return _num_entries; }
|
|
};
|
|
|
|
#endif /* _INCLUDE__RAM_FS__DIRECTORY_H_ */
|