4 * (c) 2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
5 * Alexander Warg <warg@os.inf.tu-dresden.de>
6 * economic rights: Technische Universität Dresden (Germany)
7 * This file is part of TUD:OS and distributed under the terms of the
8 * GNU Lesser General Public License 2.1.
9 * Please see the COPYING-LGPL-2.1 file for details.
12 #include <l4/l4re_vfs/backend>
13 #include <l4/cxx/string>
14 #include <l4/cxx/avl_tree>
17 #include <sys/ioctl.h>
24 using namespace L4Re::Vfs;
30 File_data() : _buf(0), _size(0) {}
32 unsigned long put(unsigned long offset,
33 unsigned long bufsize, void *srcbuf);
34 unsigned long get(unsigned long offset,
35 unsigned long bufsize, void *dstbuf);
37 unsigned long size(unsigned long offset);
38 unsigned long size() const { return _size; }
40 ~File_data() throw() { free(_buf); }
48 File_data::put(unsigned long offset, unsigned long bufsize, void *srcbuf)
50 if (offset + bufsize > _size)
51 size(offset + bufsize);
56 memcpy((char *)_buf + offset, srcbuf, bufsize);
61 File_data::get(unsigned long offset, unsigned long bufsize, void *dstbuf)
63 unsigned long s = bufsize;
68 if (offset + bufsize > _size)
71 memcpy(dstbuf, (char *)_buf + offset, s);
76 File_data::size(unsigned long offset)
81 _buf = realloc(_buf, _size);
90 class Node : public cxx::Avl_tree_node
93 Node(const char *path, mode_t mode)
94 : _ref_cnt(0), _path(strdup(path))
96 memset(&_info, 0, sizeof(_info));
100 const char *path() const { return _path; }
101 struct stat64 *info() { return &_info; }
103 void add_ref() throw() { ++_ref_cnt; }
104 int remove_ref() throw() { return --_ref_cnt; }
106 bool is_dir() const { return S_ISDIR(_info.st_mode); }
108 virtual ~Node() { free(_path); }
118 typedef cxx::String Key_type;
119 static Key_type key_of(Node const *n)
120 { return n->path(); }
123 struct Path_avl_tree_compare
125 bool operator () (const char *l, const char *r) const
126 { return strcmp(l, r) < 0; }
127 bool operator () (const cxx::String l, const cxx::String r) const
129 int v = strncmp(l.start(), r.start(), cxx::min(l.len(), r.len()));
130 return v < 0 || (v == 0 && l.len() < r.len());
134 class Pers_file : public Node
137 Pers_file(const char *name, mode_t mode)
138 : Node(name, (mode & 0777) | __S_IFREG) {}
139 File_data const &data() const { return _data; }
140 File_data &data() { return _data; }
145 class Pers_dir : public Node
148 typedef cxx::Avl_tree<Node, Node_get_key, Path_avl_tree_compare> Tree;
152 Pers_dir(const char *name, mode_t mode)
153 : Node(name, (mode & 0777) | __S_IFDIR) {}
154 Ref_ptr<Node> find_path(cxx::String);
155 bool add_node(Ref_ptr<Node> const &);
157 typedef Tree::Const_iterator Const_iterator;
158 Const_iterator begin() const { return _tree.begin(); }
159 Const_iterator end() const { return _tree.end(); }
162 Ref_ptr<Node> Pers_dir::find_path(cxx::String path)
164 return cxx::ref_ptr(_tree.find_node(path));
167 bool Pers_dir::add_node(Ref_ptr<Node> const &n)
169 bool e = _tree.insert(n.ptr()).second;
175 class Tmpfs_dir : public Be_file
178 explicit Tmpfs_dir(Ref_ptr<Pers_dir> const &d) throw()
179 : _dir(d), _getdents_state(false) {}
180 int get_entry(const char *, int, mode_t, Ref_ptr<File> *) throw();
181 ssize_t getdents(char *, size_t) throw();
182 int fstat64(struct stat64 *buf) const throw();
183 int utime(const struct utimbuf *) throw();
184 int fchmod(mode_t) throw();
185 int mkdir(const char *, mode_t) throw();
186 int unlink(const char *) throw();
187 int rename(const char *, const char *) throw();
188 int faccessat(const char *, int, int) throw();
191 int walk_path(cxx::String const &_s,
192 Ref_ptr<Node> *ret, cxx::String *remaining = 0);
194 Ref_ptr<Pers_dir> _dir;
195 bool _getdents_state;
196 Pers_dir::Const_iterator _getdents_iter;
199 class Tmpfs_file : public Be_file_pos
202 explicit Tmpfs_file(Ref_ptr<Pers_file> const &f) throw()
203 : Be_file_pos(), _file(f) {}
205 off64_t size() const throw();
206 int fstat64(struct stat64 *buf) const throw();
207 int ftruncate64(off64_t p) throw();
208 int ioctl(unsigned long, va_list) throw();
209 int utime(const struct utimbuf *) throw();
210 int fchmod(mode_t) throw();
213 ssize_t preadv(const struct iovec *v, int iovcnt, off64_t p) throw();
214 ssize_t pwritev(const struct iovec *v, int iovcnt, off64_t p) throw();
215 Ref_ptr<Pers_file> _file;
218 ssize_t Tmpfs_file::preadv(const struct iovec *v, int iovcnt, off64_t p) throw()
224 for (int i = 0; i < iovcnt; ++i)
226 sum += _file->data().get(p, v[i].iov_len, v[i].iov_base);
232 ssize_t Tmpfs_file::pwritev(const struct iovec *v, int iovcnt, off64_t p) throw()
238 for (int i = 0; i < iovcnt; ++i)
240 sum += _file->data().put(p, v[i].iov_len, v[i].iov_base);
246 int Tmpfs_file::fstat64(struct stat64 *buf) const throw()
248 _file->info()->st_size = _file->data().size();
249 memcpy(buf, _file->info(), sizeof(*buf));
253 int Tmpfs_file::ftruncate64(off64_t p) throw()
258 if (_file->data().size(p) == 0)
261 return -EIO; // most likely ENOSPC, but can't report that
264 off64_t Tmpfs_file::size() const throw()
265 { return _file->data().size(); }
268 Tmpfs_file::ioctl(unsigned long v, va_list args) throw()
272 case FIONREAD: // return amount of data still available
273 int *available = va_arg(args, int *);
274 *available = _file->data().size() - pos();
281 Tmpfs_file::utime(const struct utimbuf *times) throw()
283 _file->info()->st_atime = times->actime;
284 _file->info()->st_mtime = times->modtime;
289 Tmpfs_file::fchmod(mode_t m) throw()
291 _file->info()->st_mode = m;
296 Tmpfs_dir::faccessat(const char *path, int mode, int) throw()
299 cxx::String name = path;
301 int err = walk_path(name, &node, &name);
305 if (mode == F_OK) // existence check
308 struct stat64 *stats = node->info();
310 if ((mode & R_OK) && !(stats->st_mode & S_IRUSR))
313 if ((mode & W_OK) && !(stats->st_mode & S_IWUSR))
316 if ((mode & X_OK) && !(stats->st_mode & S_IXUSR))
323 Tmpfs_dir::get_entry(const char *name, int flags, mode_t mode,
324 Ref_ptr<File> *file) throw()
329 *file = cxx::ref_ptr(this);
333 cxx::String n = name;
335 int e = walk_path(n, &path, &n);
340 if (!(flags & O_CREAT) && e < 0)
343 if ((flags & O_CREAT) && e == -ENOENT)
345 Ref_ptr<Node> node(new Pers_file(n.start(), mode));
346 // when ENOENT is return, path is always a directory
347 bool e = cxx::ref_ptr_static_cast<Pers_dir>(path)->add_node(node);
354 *file = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr_static_cast<Pers_dir>(path)));
356 *file = cxx::ref_ptr(new Tmpfs_file(cxx::ref_ptr_static_cast<Pers_file>(path)));
366 Tmpfs_dir::getdents(char *buf, size_t sz) throw()
368 struct dirent64 *d = (struct dirent64 *)buf;
371 if (!_getdents_state)
373 _getdents_iter = _dir->begin();
374 _getdents_state = true;
376 else if (_getdents_iter == _dir->end())
378 _getdents_state = false;
382 for (; _getdents_iter != _dir->end(); ++_getdents_iter)
384 unsigned l = strlen(_getdents_iter->path()) + 1;
385 if (l > sizeof(d->d_name))
386 l = sizeof(d->d_name);
388 unsigned n = offsetof (struct dirent64, d_name) + l;
389 n = (n + sizeof(long) - 1) & ~(sizeof(long) - 1);
396 memcpy(d->d_name, _getdents_iter->path(), l);
401 d = (struct dirent64 *)((unsigned long)d + n);
408 Tmpfs_dir::fstat64(struct stat64 *buf) const throw()
410 memcpy(buf, _dir->info(), sizeof(*buf));
415 Tmpfs_dir::utime(const struct utimbuf *times) throw()
417 _dir->info()->st_atime = times->actime;
418 _dir->info()->st_mtime = times->modtime;
423 Tmpfs_dir::fchmod(mode_t m) throw()
425 _dir->info()->st_mode = m;
430 Tmpfs_dir::walk_path(cxx::String const &_s,
431 Ref_ptr<Node> *ret, cxx::String *remaining)
433 Ref_ptr<Pers_dir> p = _dir;
445 cxx::String::Index sep = s.find("/");
447 if (sep - s.start() == 1 && *s.start() == '.')
449 s = s.substr(s.start() + 2);
453 n = p->find_path(s.head(sep - s.start()));
459 *remaining = s.head(sep - s.start());
473 s = s.substr(sep + 1);
475 p = cxx::ref_ptr_static_cast<Pers_dir>(n);
484 Tmpfs_dir::mkdir(const char *name, mode_t mode) throw()
486 Ref_ptr<Node> node = _dir;
487 cxx::String p = cxx::String(name);
488 cxx::String path, last = p;
489 cxx::String::Index s = p.rfind("/");
491 // trim /'s at the end
492 while (p.len() && s == p.end() - 1)
498 //printf("MKDIR '%s' p=%p %p\n", name, p.start(), s);
503 last = p.substr(s + 1, p.end() - s);
505 int e = walk_path(path, &node);
513 // due to path walking we can end up with an empty name
514 if (p.len() == 0 || p == cxx::String("."))
517 Ref_ptr<Pers_dir> dnode = cxx::ref_ptr_static_cast<Pers_dir>(node);
519 Ref_ptr<Pers_dir> dir(new Pers_dir(last.start(), mode));
520 return dnode->add_node(dir) ? 0 : -EEXIST;
524 Tmpfs_dir::unlink(const char *name) throw()
526 cxx::Ref_ptr<Node> n;
528 int e = walk_path(name, &n);
532 printf("Unimplemented (if file exists): %s(%s)\n", __func__, name);
537 Tmpfs_dir::rename(const char *old, const char *newn) throw()
539 printf("Unimplemented: %s(%s, %s)\n", __func__, old, newn);
545 class Tmpfs_fs : public Be_file_system
548 Tmpfs_fs() : Be_file_system("tmpfs") {}
549 int mount(char const *source, unsigned long mountflags,
550 void const *data, cxx::Ref_ptr<File> *dir) throw()
555 *dir = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr(new Pers_dir("root", 0777))));
562 static Tmpfs_fs _tmpfs L4RE_VFS_FILE_SYSTEM_ATTRIBUTE;