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