genode/repos/os/src/server/lx_fs/directory.h

261 lines
5.5 KiB
C++

/*
* \brief File-system directory node
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-11
*/
/*
* Copyright (C) 2013-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 _DIRECTORY_H_
#define _DIRECTORY_H_
/* Genode include */
#include <file_system/util.h>
#include <os/path.h>
/* local includes */
#include <node.h>
#include <file.h>
#include <lx_util.h>
#include <stdio.h>
namespace Lx_fs {
using namespace Genode;
using namespace File_system;
class Directory;
}
class Lx_fs::Directory : public Node
{
private:
/*
* Noncopyable
*/
Directory(Directory const &);
Directory &operator = (Directory const &);
typedef Genode::Path<MAX_PATH_LEN> Path;
DIR *_fd;
Path _path;
Allocator &_alloc;
unsigned long _inode(char const *path, bool create)
{
int ret;
if (create) {
mode_t ugo = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
ret = mkdir(path, ugo);
if (ret == -1) {
switch (errno) {
case ENAMETOOLONG: throw Name_too_long();
case EACCES: throw Permission_denied();
case ENOENT: throw Lookup_failed();
case EEXIST: throw Node_already_exists();
case ENOSPC:
default: throw No_space();
}
}
}
struct stat s { };
ret = lstat(path, &s);
if (ret == -1)
throw Lookup_failed();
return s.st_ino;
}
DIR *_open(char const *path)
{
DIR *fd = opendir(path);
if (!fd)
throw Lookup_failed();
return fd;
}
size_t _num_entries() const
{
unsigned num = 0;
rewinddir(_fd);
while (readdir(_fd)) ++num;
return num;
}
public:
Directory(Allocator &alloc, char const *path, bool create)
:
Node(_inode(path, create)),
_fd(_open(path)),
_path(path, "./"),
_alloc(alloc)
{
Node::name(basename(path));
}
virtual ~Directory()
{
closedir(_fd);
}
void update_modification_time(Timestamp const time) override
{
struct timespec ts[2] = {
{ .tv_sec = (time_t)0, .tv_nsec = 0 },
{ .tv_sec = (time_t)time.value, .tv_nsec = 0 }
};
/* silently ignore errors */
futimens(dirfd(_fd), (const timespec*)&ts);
}
void rename(Directory &dir_to, char const *name_from, char const *name_to)
{
int ret = renameat(dirfd(_fd), name_from,
dirfd(dir_to._fd), name_to);
if (ret != 0)
throw Unavailable();
}
/* FIXME returned file node must be locked */
File * file(char const *name, Mode mode, bool create) override
{
File *file = new (&_alloc) File(dirfd(_fd), name, mode, create);
return file;
}
/* FIXME returned directory node must be locked */
Directory * subdir(char const *path, bool create)
{
Path dir_path(path, _path.base());
Directory *dir = new (&_alloc) Directory(_alloc, dir_path.base(), create);
return dir;
}
Node * node(char const *path)
{
Path node_path(path, _path.base());
/*
* XXX Currently, symlinks are transparently dereferenced by the
* use of stat(). For symlink detection we would need lstat()
* and implement special handling of the root, which may be a
* link!
*/
struct stat s;
int ret = stat(node_path.base(), &s);
if (ret == -1)
throw Lookup_failed();
Node *node = 0;
if (S_ISDIR(s.st_mode))
node = new (&_alloc) Directory(_alloc, node_path.base(), false);
else if (S_ISREG(s.st_mode))
node = new (&_alloc) File(node_path.base(), STAT_ONLY);
else
throw Lookup_failed();
return node;
}
size_t read(char *dst, size_t len, seek_off_t seek_offset) override
{
if (len < sizeof(Directory_entry)) {
Genode::error("read buffer too small for directory entry");
return 0;
}
if (seek_offset % sizeof(Directory_entry)) {
Genode::error("seek offset not aligned to sizeof(Directory_entry)");
return 0;
}
seek_off_t index = seek_offset / sizeof(Directory_entry);
/* seek to index and read entry */
struct dirent *dent;
rewinddir(_fd);
for (unsigned i = 0; i <= index; ++i) {
dent = readdir(_fd);
}
if (!dent)
return 0;
Path dent_path(dent->d_name, _path.base());
struct stat st { };
lstat(dent_path.base(), &st);
auto type = [] (unsigned char type)
{
switch (type) {
case DT_REG: return Node_type::CONTINUOUS_FILE;
case DT_DIR: return Node_type::DIRECTORY;
case DT_LNK: return Node_type::SYMLINK;
default: return Node_type::CONTINUOUS_FILE;
}
};
Directory_entry &e = *(Directory_entry *)(dst);
e = {
.inode = (unsigned long)dent->d_ino,
.type = type(dent->d_type),
.rwx = { .readable = (st.st_mode & S_IRUSR),
.writeable = (st.st_mode & S_IWUSR),
.executable = (st.st_mode & S_IXUSR) },
.name = { dent->d_name }
};
return sizeof(Directory_entry);
}
size_t write(char const *, size_t, seek_off_t) override
{
/* writing to directory nodes is not supported */
return 0;
}
Status status() override
{
struct stat st { };
int fd = dirfd(_fd);
if (fd == -1 || fstat(fd, &st) < 0)
st.st_mtime = 0;
return {
.size = _num_entries() * sizeof(File_system::Directory_entry),
.type = Node_type::DIRECTORY,
.rwx = { .readable = (st.st_mode & S_IRUSR),
.writeable = (st.st_mode & S_IWUSR),
.executable = (st.st_mode & S_IXUSR) },
.inode = inode(),
.modification_time = { st.st_mtime }
};
}
};
#endif /* _DIRECTORY_H_ */