]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/tmpfs/lib/src/fs.cc
9fd893962dc7227b0592c8ced56664356df38f78
[l4.git] / l4 / pkg / tmpfs / lib / src / fs.cc
1 /**
2  */
3 /*
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.
10  */
11
12 #include <l4/l4re_vfs/backend>
13 #include <l4/cxx/string>
14 #include <l4/cxx/avl_tree>
15
16 #include <sys/stat.h>
17 #include <sys/ioctl.h>
18 #include <dirent.h>
19
20 #include <cstdio>
21
22
23 namespace {
24
25 using namespace L4Re::Vfs;
26 using cxx::Ref_ptr;
27
28 class File_data
29 {
30 public:
31   File_data() : _buf(0), _size(0) {}
32
33   unsigned long put(unsigned long offset,
34                     unsigned long bufsize, void *srcbuf);
35   unsigned long get(unsigned long offset,
36                     unsigned long bufsize, void *dstbuf);
37
38   unsigned long size() const { return _size; }
39
40   ~File_data() throw() { free(_buf); }
41
42 private:
43   void *_buf;
44   unsigned long _size;
45 };
46
47 unsigned long
48 File_data::put(unsigned long offset, unsigned long bufsize, void *srcbuf)
49 {
50   if (offset + bufsize > _size)
51     {
52       _size = offset + bufsize;
53       _buf = realloc(_buf, _size);
54     }
55
56   if (!_buf)
57     return 0;
58
59   memcpy((char *)_buf + offset, srcbuf, bufsize);
60   return bufsize;
61 }
62
63 unsigned long
64 File_data::get(unsigned long offset, unsigned long bufsize, void *dstbuf)
65 {
66   unsigned long s = bufsize;
67
68   if (offset > _size)
69     return 0;
70
71   if (offset + bufsize > _size)
72     s = _size - offset;
73
74   memcpy(dstbuf, (char *)_buf + offset, s);
75   return s;
76 }
77
78
79 class Node : public cxx::Avl_tree_node
80 {
81 public:
82   Node(const char *path, mode_t mode)
83     : _ref_cnt(0), _path(strdup(path))
84   {
85     memset(&_info, 0, sizeof(_info));
86     _info.st_mode = mode;
87   }
88
89   const char *path() const { return _path; }
90   struct stat64 *info() { return &_info; }
91
92   void add_ref() throw() { ++_ref_cnt; }
93   int remove_ref() throw() { return --_ref_cnt; }
94
95   bool is_dir() const { return S_ISDIR(_info.st_mode); }
96
97   virtual ~Node() { free(_path); }
98
99 private:
100   int           _ref_cnt;
101   char         *_path;
102   struct stat64 _info;
103 };
104
105 struct Node_get_key
106 {
107   typedef cxx::String Key_type;
108   static Key_type key_of(Node const *n)
109   { return n->path(); }
110 };
111
112 struct Path_avl_tree_compare
113 {
114   bool operator () (const char *l, const char *r) const
115   { return strcmp(l, r) < 0; }
116   bool operator () (const cxx::String l, const cxx::String r) const
117   { return strncmp(l.start(), r.start(), cxx::min(l.len(), r.len())) < 0; }
118 };
119
120 class Pers_file : public Node
121 {
122 public:
123   Pers_file(const char *name, mode_t mode)
124     : Node(name, (mode & 0777) | __S_IFREG) {}
125   File_data const &data() const { return _data; }
126   File_data &data() { return _data; }
127 private:
128   File_data     _data;
129 };
130
131 class Pers_dir : public Node
132 {
133 private:
134   typedef cxx::Avl_tree<Node, Node_get_key, Path_avl_tree_compare> Tree;
135   Tree _tree;
136
137 public:
138   Pers_dir(const char *name, mode_t mode)
139     : Node(name, (mode & 0777) | __S_IFDIR) {}
140   Ref_ptr<Node> find_path(cxx::String);
141   int add_node(Ref_ptr<Node> const &);
142
143   typedef Tree::Const_iterator Const_iterator;
144   Const_iterator begin() const { return _tree.begin(); }
145   Const_iterator end() const { return _tree.end(); }
146 };
147
148 Ref_ptr<Node> Pers_dir::find_path(cxx::String path)
149 {
150   return cxx::ref_ptr(_tree.find_node(path));
151 }
152
153 int Pers_dir::add_node(Ref_ptr<Node> const &n)
154 {
155   int e = _tree.insert(n.ptr()).second;
156   if (!e)
157     n->add_ref();
158   return e;
159 }
160
161 class Tmpfs_dir : public Be_file
162 {
163 public:
164   explicit Tmpfs_dir(Ref_ptr<Pers_dir> const &d) throw()
165     : _dir(d), _getdents_state(false) {}
166   int get_entry(const char *, int, mode_t, Ref_ptr<File> *) throw();
167   ssize_t getdents(char *, size_t) throw();
168   int fstat64(struct stat64 *buf) const throw();
169   int utime(const struct utimbuf *) throw();
170   int fchmod(mode_t) throw();
171   int mkdir(const char *, mode_t) throw();
172   int unlink(const char *) throw();
173   int rename(const char *, const char *) throw();
174
175 private:
176   int walk_path(cxx::String const &_s,
177                 Ref_ptr<Node> *ret, cxx::String *remaining = 0);
178
179   Ref_ptr<Pers_dir> _dir;
180   bool _getdents_state;
181   Pers_dir::Const_iterator _getdents_iter;
182 };
183
184 class Tmpfs_file : public Be_file
185 {
186 public:
187   explicit Tmpfs_file(Ref_ptr<Pers_file> const &f) throw()
188     : Be_file(), _file(f), _pos(0) {}
189
190   ssize_t readv(const struct iovec*, int iovcnt) throw();
191   ssize_t writev(const struct iovec*, int iovcnt) throw();
192   off64_t lseek64(off64_t, int) throw();
193   int fstat64(struct stat64 *buf) const throw();
194   int ioctl(unsigned long, va_list) throw();
195   int utime(const struct utimbuf *) throw();
196   int fchmod(mode_t) throw();
197
198 private:
199   Ref_ptr<Pers_file> _file;
200   off64_t _pos;
201 };
202
203 ssize_t Tmpfs_file::readv(const struct iovec *v, int iovcnt) throw()
204 {
205   if (iovcnt < 0)
206     return -EINVAL;
207
208
209   ssize_t sum = 0;
210   for (int i = 0; i < iovcnt; ++i)
211     {
212       sum  += _file->data().get(_pos, v[i].iov_len, v[i].iov_base);
213       _pos += v[i].iov_len;
214     }
215   return sum;
216 }
217
218 ssize_t Tmpfs_file::writev(const struct iovec *v, int iovcnt) throw()
219 {
220   if (iovcnt < 0)
221     return -EINVAL;
222
223   ssize_t sum = 0;
224   for (int i = 0; i < iovcnt; ++i)
225     {
226       sum  += _file->data().put(_pos, v[i].iov_len, v[i].iov_base);
227       _pos += v[i].iov_len;
228     }
229   return sum;
230 }
231
232 int Tmpfs_file::fstat64(struct stat64 *buf) const throw()
233 {
234   _file->info()->st_size = _file->data().size();
235   memcpy(buf, _file->info(), sizeof(*buf));
236   return 0;
237 }
238
239 off64_t Tmpfs_file::lseek64(off64_t offset, int whence) throw()
240 {
241   switch (whence)
242     {
243     case SEEK_SET: _pos = offset; break;
244     case SEEK_CUR: _pos += offset; break;
245     case SEEK_END: _pos = _file->data().size() + offset; break;
246     default: return -EINVAL;
247     };
248
249   if (_pos < 0)
250     return -EINVAL;
251
252   return _pos;
253 }
254
255 int
256 Tmpfs_file::ioctl(unsigned long v, va_list args) throw()
257 {
258   switch (v)
259     {
260     case FIONREAD: // return amount of data still available
261       int *available = va_arg(args, int *);
262       *available = _file->data().size() - _pos;
263       return 0;
264     };
265   return -EINVAL;
266 }
267
268 int
269 Tmpfs_file::utime(const struct utimbuf *times) throw()
270 {
271   _file->info()->st_atime = times->actime;
272   _file->info()->st_mtime = times->modtime;
273   return 0;
274 }
275
276 int
277 Tmpfs_file::fchmod(mode_t m) throw()
278 {
279   _file->info()->st_mode = m;
280   return 0;
281 }
282
283
284 int
285 Tmpfs_dir::get_entry(const char *name, int flags, mode_t mode,
286                      Ref_ptr<File> *file) throw()
287 {
288   Ref_ptr<Node> path;
289   if (!*name)
290     {
291       *file = this;
292       return 0;
293     }
294
295   cxx::String n = name;
296
297   int e = walk_path(n, &path, &n);
298
299   if (e == -ENOTDIR)
300     return e;
301
302   if (!(flags & O_CREAT) && e < 0)
303     return e;
304
305   if ((flags & O_CREAT) && e == -ENOENT)
306     {
307       Ref_ptr<Node> node(new Pers_file(n.start(), mode));
308       // when ENOENT is return, path is always a directory
309       int e = cxx::ref_ptr_static_cast<Pers_dir>(path)->add_node(node);
310       if (e)
311         return e;
312       path = node;
313     }
314
315   if (path->is_dir())
316     *file = new Tmpfs_dir(cxx::ref_ptr_static_cast<Pers_dir>(path));
317   else
318     *file = new Tmpfs_file(cxx::ref_ptr_static_cast<Pers_file>(path));
319
320   if (!*file)
321     return -ENOMEM;
322
323
324   return 0;
325 }
326
327 ssize_t
328 Tmpfs_dir::getdents(char *buf, size_t sz) throw()
329 {
330   struct dirent64 *d = (struct dirent64 *)buf;
331   ssize_t ret = 0;
332
333   if (!_getdents_state)
334     {
335       _getdents_iter = _dir->begin();
336       _getdents_state = true;
337     }
338   else if (_getdents_iter == _dir->end())
339     {
340       _getdents_state = false;
341       return 0;
342     }
343
344   for (; _getdents_iter != _dir->end(); ++_getdents_iter)
345     {
346       unsigned l = strlen(_getdents_iter->path()) + 1;
347       if (l > sizeof(d->d_name))
348         l = sizeof(d->d_name);
349
350       unsigned n = offsetof (struct dirent64, d_name) + l;
351
352       if (n > sz)
353         break;
354
355       d->d_ino = 1;
356       d->d_off = 0;
357       memcpy(d->d_name, _getdents_iter->path(), l);
358       d->d_reclen = n;
359       d->d_type   = DT_REG;
360       ret += n;
361       sz  -= n;
362       d    = (struct dirent64 *)((unsigned long)d + n);
363     }
364
365   return ret;
366 }
367
368 int
369 Tmpfs_dir::fstat64(struct stat64 *buf) const throw()
370 {
371   memcpy(buf, _dir->info(), sizeof(*buf));
372   return 0;
373 }
374
375 int
376 Tmpfs_dir::utime(const struct utimbuf *times) throw()
377 {
378   _dir->info()->st_atime = times->actime;
379   _dir->info()->st_mtime = times->modtime;
380   return 0;
381 }
382
383 int
384 Tmpfs_dir::fchmod(mode_t m) throw()
385 {
386   _dir->info()->st_mode = m;
387   return 0;
388 }
389
390 int
391 Tmpfs_dir::walk_path(cxx::String const &_s,
392                      Ref_ptr<Node> *ret, cxx::String *remaining)
393 {
394   Ref_ptr<Pers_dir> p = _dir;
395   cxx::String s = _s;
396   Ref_ptr<Node> n;
397
398   while (1)
399     {
400       cxx::String::Index sep = s.find("/");
401
402       n = p->find_path(s.head(sep - s.start()));
403
404       if (!n)
405         {
406           *ret = p;
407           if (remaining)
408             *remaining = s.head(sep - s.start());
409           return -ENOENT;
410         }
411
412
413       if (sep == s.end())
414         {
415           *ret = n;
416           return 0;
417         }
418
419       if (!n->is_dir())
420         return -ENOTDIR;
421
422       s = s.substr(sep + 1);
423
424       p = cxx::ref_ptr_static_cast<Pers_dir>(n);
425     }
426
427   *ret = n;
428
429   return 0;
430 }
431
432 int
433 Tmpfs_dir::mkdir(const char *name, mode_t mode) throw()
434 {
435   Ref_ptr<Node> node = _dir;
436   cxx::String p = cxx::String(name);
437   cxx::String path, last = p;
438   cxx::String::Index s = p.rfind("/");
439   if (s != p.end())
440     {
441       path = p.head(s);
442       last = p.substr(s + 1, p.end() - s);
443
444       int e = walk_path(path, &node);
445       if (e < 0)
446         return e;
447     }
448
449   if (!node->is_dir())
450     return -ENOTDIR;
451
452   Ref_ptr<Pers_dir> dnode = cxx::ref_ptr_static_cast<Pers_dir>(node);
453
454   Ref_ptr<Pers_dir> dir(new Pers_dir(last.start(), mode));
455   return dnode->add_node(dir);
456 }
457
458 int
459 Tmpfs_dir::unlink(const char *name) throw()
460 {
461   cxx::Ref_ptr<Node> n;
462
463   int e = walk_path(name, &n);
464   if (e < 0)
465     return -ENOENT;
466
467   printf("Unimplemented (if file exists): %s(%s)\n", __func__, name); 
468   return -ENOMEM;
469 }
470
471 int
472 Tmpfs_dir::rename(const char *old, const char *newn) throw()
473 {
474   printf("Unimplemented: %s(%s, %s)\n", __func__, old, newn); 
475   return -ENOMEM;
476 }
477
478
479
480 class Tmpfs_fs : public Be_file_system
481 {
482 public:
483   Tmpfs_fs() : Be_file_system("tmpfs") {}
484   int mount(char const *source, unsigned long mountflags,
485             void const *data, cxx::Ref_ptr<File> *dir) throw()
486   {
487     (void)mountflags;
488     (void)source;
489     (void)data;
490     *dir = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr(new Pers_dir("root", 0777))));
491     if (!*dir)
492       return -ENOMEM;
493     return 0;
494   }
495 };
496
497 static Tmpfs_fs _tmpfs;
498
499 }