]> rtime.felk.cvut.cz Git - l4.git/blobdiff - l4/pkg/tmpfs/lib/src/fs.cc
update
[l4.git] / l4 / pkg / tmpfs / lib / src / fs.cc
index 0a8fa1ac632b140e5dd4bbada83e047a1a31e4a5..1e5383638a84d5b387bfc05b7495b960fba21927 100644 (file)
 
 #include <sys/stat.h>
 #include <sys/ioctl.h>
-#include <errno.h>
+#include <dirent.h>
 
 #include <cstdio>
 
-
 namespace {
 
 using namespace L4Re::Vfs;
@@ -81,7 +80,10 @@ class Node : public cxx::Avl_tree_node
 public:
   Node(const char *path, mode_t mode)
     : _ref_cnt(0), _path(strdup(path))
-  { _info.st_mode = mode; }
+  {
+    memset(&_info, 0, sizeof(_info));
+    _info.st_mode = mode;
+  }
 
   const char *path() const { return _path; }
   struct stat64 *info() { return &_info; }
@@ -127,15 +129,19 @@ private:
 
 class Pers_dir : public Node
 {
+private:
+  typedef cxx::Avl_tree<Node, Node_get_key, Path_avl_tree_compare> Tree;
+  Tree _tree;
+
 public:
   Pers_dir(const char *name, mode_t mode)
     : Node(name, (mode & 0777) | __S_IFDIR) {}
   Ref_ptr<Node> find_path(cxx::String);
-  int add_node(Ref_ptr<Node> const &);
+  bool add_node(Ref_ptr<Node> const &);
 
-private:
-  typedef cxx::Avl_tree<Node, Node_get_key, Path_avl_tree_compare> Tree;
-  Tree _tree;
+  typedef Tree::Const_iterator Const_iterator;
+  Const_iterator begin() const { return _tree.begin(); }
+  Const_iterator end() const { return _tree.end(); }
 };
 
 Ref_ptr<Node> Pers_dir::find_path(cxx::String path)
@@ -143,11 +149,10 @@ Ref_ptr<Node> Pers_dir::find_path(cxx::String path)
   return cxx::ref_ptr(_tree.find_node(path));
 }
 
-int Pers_dir::add_node(Ref_ptr<Node> const &n)
+bool Pers_dir::add_node(Ref_ptr<Node> const &n)
 {
-  int e;
-  e = _tree.insert(n.ptr()).second;
-  if (!e)
+  bool e = _tree.insert(n.ptr()).second;
+  if (e)
     n->add_ref();
   return e;
 }
@@ -156,9 +161,12 @@ class Tmpfs_dir : public Be_file
 {
 public:
   explicit Tmpfs_dir(Ref_ptr<Pers_dir> const &d) throw()
-    : _dir(d) {}
+    : _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();
@@ -168,41 +176,43 @@ private:
                 Ref_ptr<Node> *ret, cxx::String *remaining = 0);
 
   Ref_ptr<Pers_dir> _dir;
+  bool _getdents_state;
+  Pers_dir::Const_iterator _getdents_iter;
 };
 
-class Tmpfs_file : public Be_file
+class Tmpfs_file : public Be_file_pos
 {
 public:
   explicit Tmpfs_file(Ref_ptr<Pers_file> const &f) throw()
-    : Be_file(), _file(f), _pos(0) {}
+    : Be_file_pos(), _file(f) {}
 
-  ssize_t readv(const struct iovec*, int iovcnt) throw();
-  ssize_t writev(const struct iovec*, int iovcnt) throw();
-  off64_t lseek64(off64_t, int) throw();
+  off64_t size() const throw();
   int fstat64(struct stat64 *buf) const 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;
-  off64_t _pos;
 };
 
-ssize_t Tmpfs_file::readv(const struct iovec *v, int iovcnt) throw()
+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(_pos, v[i].iov_len, v[i].iov_base);
-      _pos += v[i].iov_len;
+      sum  += _file->data().get(p, v[i].iov_len, v[i].iov_base);
+      p += v[i].iov_len;
     }
   return sum;
 }
 
-ssize_t Tmpfs_file::writev(const struct iovec *v, int iovcnt) throw()
+ssize_t Tmpfs_file::pwritev(const struct iovec *v, int iovcnt, off64_t p) throw()
 {
   if (iovcnt < 0)
     return -EINVAL;
@@ -210,33 +220,21 @@ ssize_t Tmpfs_file::writev(const struct iovec *v, int iovcnt) throw()
   ssize_t sum = 0;
   for (int i = 0; i < iovcnt; ++i)
     {
-      sum  += _file->data().put(_pos, v[i].iov_len, v[i].iov_base);
-      _pos += v[i].iov_len;
+      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;
 }
 
-off64_t Tmpfs_file::lseek64(off64_t offset, int whence) throw()
-{
-  switch (whence)
-    {
-    case SEEK_SET: _pos = offset; break;
-    case SEEK_CUR: _pos += offset; break;
-    case SEEK_END: _pos = _file->data().size() + offset; break;
-    default: return -EINVAL;
-    };
-
-  if (_pos < 0)
-    return -EINVAL;
-
-  return _pos;
-}
+off64_t Tmpfs_file::size() const throw()
+{ return _file->data().size(); }
 
 int
 Tmpfs_file::ioctl(unsigned long v, va_list args) throw()
@@ -245,13 +243,26 @@ Tmpfs_file::ioctl(unsigned long v, va_list args) throw()
     {
     case FIONREAD: // return amount of data still available
       int *available = va_arg(args, int *);
-      *available = _file->data().size() - _pos;
+      *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
@@ -279,9 +290,9 @@ Tmpfs_dir::get_entry(const char *name, int flags, mode_t mode,
     {
       Ref_ptr<Node> node(new Pers_file(n.start(), mode));
       // when ENOENT is return, path is always a directory
-      int e = cxx::ref_ptr_static_cast<Pers_dir>(path)->add_node(node);
-      if (e)
-        return e;
+      bool e = cxx::ref_ptr_static_cast<Pers_dir>(path)->add_node(node);
+      if (!e)
+        return -ENOMEM;
       path = node;
     }
 
@@ -297,6 +308,48 @@ Tmpfs_dir::get_entry(const char *name, int flags, mode_t mode,
   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()
 {
@@ -304,6 +357,21 @@ Tmpfs_dir::fstat64(struct stat64 *buf) const throw()
   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
 Tmpfs_dir::walk_path(cxx::String const &_s,
                      Ref_ptr<Node> *ret, cxx::String *remaining)
@@ -314,8 +382,20 @@ Tmpfs_dir::walk_path(cxx::String const &_s,
 
   while (1)
     {
+      if (s.len() == 0)
+        {
+          *ret = p;
+          return 0;
+        }
+
       cxx::String::Index sep = s.find("/");
 
+      if (sep - s.start() == 1 && *s.start() == '.')
+        {
+          s = s.substr(s.start() + 2);
+          continue;
+        }
+
       n = p->find_path(s.head(sep - s.start()));
 
       if (!n)
@@ -353,6 +433,16 @@ Tmpfs_dir::mkdir(const char *name, mode_t mode) throw()
   cxx::String p = cxx::String(name);
   cxx::String path, last = p;
   cxx::String::Index s = p.rfind("/");
+
+  // trim /'s at the end
+  while (p.len() && s == p.end() - 1)
+    {
+      p.len(p.len() - 1);
+      s = p.rfind("/");
+    }
+
+  //printf("MKDIR '%s' p=%p %p\n", name, p.start(), s);
+
   if (s != p.end())
     {
       path = p.head(s);
@@ -366,16 +456,26 @@ Tmpfs_dir::mkdir(const char *name, mode_t mode) throw()
   if (!node->is_dir())
     return -ENOTDIR;
 
+  // due to path walking we can end up with an empty name
+  if (p.len() == 0 || p == cxx::String("."))
+    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);
+  return dnode->add_node(dir) ? 0 : -EEXIST;
 }
 
 int
 Tmpfs_dir::unlink(const char *name) throw()
 {
-  printf("Unimplemented: %s(%s)\n", __func__, name); 
+  cxx::Ref_ptr<Node> n;
+
+  int e = walk_path(name, &n);
+  if (e < 0)
+    return -ENOENT;
+
+  printf("Unimplemented (if file exists): %s(%s)\n", __func__, name); 
   return -ENOMEM;
 }
 
@@ -405,6 +505,6 @@ public:
   }
 };
 
-static Tmpfs_fs _tmpfs;
+static Tmpfs_fs _tmpfs L4RE_VFS_FILE_SYSTEM_ATTRIBUTE;
 
 }