#include <l4/l4re_vfs/backend>
#include <l4/cxx/string>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <dirent.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
namespace {
class File_data
{
public:
File_data() : _buf(0), _size(0) {}
unsigned long put(unsigned long offset,
unsigned long bufsize, void *srcbuf);
unsigned long get(unsigned long offset,
unsigned long bufsize, void *dstbuf);
unsigned long size(unsigned long offset);
unsigned long size() const { return _size; }
~File_data() throw() { free(_buf); }
private:
void *_buf;
unsigned long _size;
};
unsigned long
File_data::put(unsigned long offset, unsigned long bufsize, void *srcbuf)
{
if (offset + bufsize > _size)
size(offset + bufsize);
if (!_buf)
return 0;
memcpy((char *)_buf + offset, srcbuf, bufsize);
return bufsize;
}
unsigned long
File_data::get(unsigned long offset, unsigned long bufsize, void *dstbuf)
{
unsigned long s = bufsize;
if (offset > _size)
return 0;
if (offset + bufsize > _size)
s = _size - offset;
memcpy(dstbuf, (char *)_buf + offset, s);
return s;
}
unsigned long
File_data::size(unsigned long offset)
{
if (offset != _size)
{
_size = offset;
_buf = realloc(_buf, _size);
}
if (_buf)
return 0;
return -ENOSPC;
}
{
public:
Node(const char *path, mode_t mode)
: _ref_cnt(0), _path(strdup(path))
{
memset(&_info, 0, sizeof(_info));
_info.st_mode = mode;
}
const char *path() const { return _path; }
struct stat64 *info() { return &_info; }
void add_ref() throw() { ++_ref_cnt; }
int remove_ref() throw() { return --_ref_cnt; }
bool is_dir() const { return S_ISDIR(_info.st_mode); }
virtual ~Node() { free(_path); }
private:
int _ref_cnt;
char *_path;
struct stat64 _info;
};
struct Node_get_key
{
static Key_type key_of(Node const *n)
{ return n->path(); }
};
struct Path_avl_tree_compare
{
bool operator () (const char *l, const char *r) const
{ return strcmp(l, r) < 0; }
{
int v = strncmp(l.start(), r.
start(), cxx::min(l.len(), r.
len()));
return v < 0 || (v == 0 && l.len() < r.
len());
}
};
class Pers_file : public Node
{
public:
Pers_file(const char *name, mode_t mode)
: Node(name, (mode & 0777) | __S_IFREG) {}
File_data const &data() const { return _data; }
File_data &data() { return _data; }
private:
File_data _data;
};
class Pers_dir : public Node
{
private:
Tree _tree;
public:
Pers_dir(const char *name, mode_t mode)
: Node(name, (mode & 0777) | __S_IFDIR) {}
bool add_node(Ref_ptr<Node> const &);
typedef Tree::Const_iterator Const_iterator;
Const_iterator begin() const { return _tree.begin(); }
Const_iterator end() const { return _tree.end(); }
};
{
return cxx::ref_ptr(_tree.find_node(path));
}
bool Pers_dir::add_node(Ref_ptr<Node> const &n)
{
bool e = _tree.insert(n.ptr()).second;
if (e)
n->add_ref();
return e;
}
{
public:
explicit Tmpfs_dir(Ref_ptr<Pers_dir> const &d) throw()
: _dir(d), _getdents_state(false) {}
int get_entry(const char *, int, mode_t, Ref_ptr<File> *) throw();
ssize_t getdents(char *, size_t) throw();
int fstat64(struct stat64 *buf) const throw();
int utime(const struct utimbuf *) throw();
int fchmod(mode_t) throw();
int mkdir(const char *, mode_t) throw();
int unlink(const char *) throw();
int rename(const char *, const char *) throw();
int faccessat(const char *, int, int) throw();
private:
Ref_ptr<Pers_dir> _dir;
bool _getdents_state;
Pers_dir::Const_iterator _getdents_iter;
};
class Tmpfs_file : public Be_file_pos
{
public:
explicit Tmpfs_file(Ref_ptr<Pers_file> const &f) throw()
: Be_file_pos(), _file(f) {}
off64_t size() const throw();
int fstat64(struct stat64 *buf) const throw();
int ftruncate64(off64_t p) throw();
int ioctl(unsigned long, va_list) throw();
int utime(const struct utimbuf *) throw();
int fchmod(mode_t) throw();
private:
ssize_t preadv(const struct iovec *v, int iovcnt, off64_t p) throw();
ssize_t pwritev(const struct iovec *v, int iovcnt, off64_t p) throw();
Ref_ptr<Pers_file> _file;
};
ssize_t Tmpfs_file::preadv(const struct iovec *v, int iovcnt, off64_t p) throw()
{
if (iovcnt < 0)
return -EINVAL;
ssize_t sum = 0;
for (int i = 0; i < iovcnt; ++i)
{
sum += _file->data().get(p, v[i].iov_len, v[i].iov_base);
p += v[i].iov_len;
}
return sum;
}
ssize_t Tmpfs_file::pwritev(const struct iovec *v, int iovcnt, off64_t p) throw()
{
if (iovcnt < 0)
return -EINVAL;
ssize_t sum = 0;
for (int i = 0; i < iovcnt; ++i)
{
sum += _file->data().put(p, v[i].iov_len, v[i].iov_base);
p += v[i].iov_len;
}
return sum;
}
int Tmpfs_file::fstat64(struct stat64 *buf) const throw()
{
_file->info()->st_size = _file->data().size();
memcpy(buf, _file->info(), sizeof(*buf));
return 0;
}
int Tmpfs_file::ftruncate64(off64_t p) throw()
{
if (p < 0)
return -EINVAL;
if (_file->data().size(p) == 0)
return 0;
return -EIO;
}
off64_t Tmpfs_file::size() const throw()
{ return _file->data().size(); }
int
Tmpfs_file::ioctl(unsigned long v, va_list args) throw()
{
switch (v)
{
case FIONREAD:
int *available = va_arg(args, int *);
*available = _file->data().size() - pos();
return 0;
};
return -EINVAL;
}
int
Tmpfs_file::utime(const struct utimbuf *times) throw()
{
_file->info()->st_atime = times->actime;
_file->info()->st_mtime = times->modtime;
return 0;
}
int
Tmpfs_file::fchmod(mode_t m) throw()
{
_file->info()->st_mode = m;
return 0;
}
int
Tmpfs_dir::faccessat(const char *path, int mode, int) throw()
{
Ref_ptr<Node> node;
int err = walk_path(name, &node, &name);
if (err < 0)
return err;
if (mode == F_OK)
return 0;
struct stat64 *stats = node->info();
if ((mode & R_OK) && !(stats->st_mode & S_IRUSR))
return -EACCES;
if ((mode & W_OK) && !(stats->st_mode & S_IWUSR))
return -EACCES;
if ((mode & X_OK) && !(stats->st_mode & S_IXUSR))
return -EACCES;
return 0;
}
int
Tmpfs_dir::get_entry(const char *name, int flags, mode_t mode,
Ref_ptr<File> *file) throw()
{
Ref_ptr<Node> path;
if (!*name)
{
*file = cxx::ref_ptr(this);
return 0;
}
int e = walk_path(n, &path, &n);
if (e == -ENOTDIR)
return e;
if (!(flags & O_CREAT) && e < 0)
return e;
if ((flags & O_CREAT) && e == -ENOENT)
{
Ref_ptr<Node> node(
new Pers_file(n.
start(), mode));
bool e = cxx::ref_ptr_static_cast<Pers_dir>(path)->add_node(node);
if (!e)
return -ENOMEM;
path = node;
}
if (path->is_dir())
*file = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr_static_cast<Pers_dir>(path)));
else
*file = cxx::ref_ptr(new Tmpfs_file(cxx::ref_ptr_static_cast<Pers_file>(path)));
if (!*file)
return -ENOMEM;
return 0;
}
ssize_t
Tmpfs_dir::getdents(char *buf, size_t sz) throw()
{
struct dirent64 *d = (struct dirent64 *)buf;
ssize_t ret = 0;
if (!_getdents_state)
{
_getdents_iter = _dir->begin();
_getdents_state = true;
}
else if (_getdents_iter == _dir->end())
{
_getdents_state = false;
return 0;
}
for (; _getdents_iter != _dir->end(); ++_getdents_iter)
{
unsigned l = strlen(_getdents_iter->path()) + 1;
if (l > sizeof(d->d_name))
l = sizeof(d->d_name);
unsigned n = offsetof (struct dirent64, d_name) + l;
n = (n + sizeof(long) - 1) & ~(sizeof(long) - 1);
if (n > sz)
break;
d->d_ino = 1;
d->d_off = 0;
memcpy(d->d_name, _getdents_iter->path(), l);
d->d_reclen = n;
d->d_type = DT_REG;
ret += n;
sz -= n;
d = (struct dirent64 *)((unsigned long)d + n);
}
return ret;
}
int
Tmpfs_dir::fstat64(struct stat64 *buf) const throw()
{
memcpy(buf, _dir->info(), sizeof(*buf));
return 0;
}
int
Tmpfs_dir::utime(const struct utimbuf *times) throw()
{
_dir->info()->st_atime = times->actime;
_dir->info()->st_mtime = times->modtime;
return 0;
}
int
Tmpfs_dir::fchmod(mode_t m) throw()
{
_dir->info()->st_mode = m;
return 0;
}
int
{
Ref_ptr<Pers_dir> p = _dir;
Ref_ptr<Node> n;
while (1)
{
{
*ret = p;
return 0;
}
{
continue;
}
if (!n)
{
*ret = p;
if (remaining)
return -ENOENT;
}
{
*ret = n;
return 0;
}
if (!n->is_dir())
return -ENOTDIR;
p = cxx::ref_ptr_static_cast<Pers_dir>(n);
}
*ret = n;
return 0;
}
int
Tmpfs_dir::mkdir(const char *name, mode_t mode) throw()
{
Ref_ptr<Node> node = _dir;
while (p.
len() && s == p.
end() - 1)
{
}
{
int e = walk_path(path, &node);
if (e < 0)
return e;
}
if (!node->is_dir())
return -ENOTDIR;
return 0;
Ref_ptr<Pers_dir> dnode = cxx::ref_ptr_static_cast<Pers_dir>(node);
Ref_ptr<Pers_dir> dir(
new Pers_dir(last.
start(), mode));
return dnode->add_node(dir) ? 0 : -EEXIST;
}
int
Tmpfs_dir::unlink(const char *name) throw()
{
int e = walk_path(name, &n);
if (e < 0)
return -ENOENT;
printf("Unimplemented (if file exists): %s(%s)\n", __func__, name);
return -ENOMEM;
}
int
Tmpfs_dir::rename(const char *old, const char *newn) throw()
{
printf("Unimplemented: %s(%s, %s)\n", __func__, old, newn);
return -ENOMEM;
}
{
public:
int mount(char const *source, unsigned long mountflags,
{
(void)mountflags;
(void)source;
(void)data;
*dir = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr(new Pers_dir("root", 0777))));
if (!*dir)
return -ENOMEM;
return 0;
}
};
static Tmpfs_fs _tmpfs L4RE_VFS_FILE_SYSTEM_ATTRIBUTE;
}
Boilerplate class for implementing a L4Re::Vfs::File_system.
Boiler plate class for implementing an open file for L4Re::Vfs.
A reference-counting pointer with automatic cleanup.
Allocation free string class with explicit length field.
Index start() const
Pointer to first character.
Index end() const
Pointer to first byte behind the string.
char const * find(char const *c) const
Find character. Return end() if not found.
String substr(unsigned long idx, unsigned long len=~0UL) const
Substring of length len starting at idx.
String head(Index end) const
Return prefix up to index.
char const * Index
Character index type.
char const * rfind(char const *c) const
Find right-most character. Return end() if not found.
Virtual file system for interfaces in POSIX libc.