]> 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(unsigned long offset);
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     size(offset + bufsize);
52
53   if (!_buf)
54     return 0;
55
56   memcpy((char *)_buf + offset, srcbuf, bufsize);
57   return bufsize;
58 }
59
60 unsigned long
61 File_data::get(unsigned long offset, unsigned long bufsize, void *dstbuf)
62 {
63   unsigned long s = bufsize;
64
65   if (offset > _size)
66     return 0;
67
68   if (offset + bufsize > _size)
69     s = _size - offset;
70
71   memcpy(dstbuf, (char *)_buf + offset, s);
72   return s;
73 }
74
75 unsigned long
76 File_data::size(unsigned long offset)
77 {
78   if (offset != _size)
79     {
80       _size = offset;
81       _buf = realloc(_buf, _size);
82     }
83
84   if (_buf)
85     return 0;
86   return -ENOSPC;
87 }
88
89
90 class Node : public cxx::Avl_tree_node
91 {
92 public:
93   Node(const char *path, mode_t mode)
94     : _ref_cnt(0), _path(strdup(path))
95   {
96     memset(&_info, 0, sizeof(_info));
97     _info.st_mode = mode;
98   }
99
100   const char *path() const { return _path; }
101   struct stat64 *info() { return &_info; }
102
103   void add_ref() throw() { ++_ref_cnt; }
104   int remove_ref() throw() { return --_ref_cnt; }
105
106   bool is_dir() const { return S_ISDIR(_info.st_mode); }
107
108   virtual ~Node() { free(_path); }
109
110 private:
111   int           _ref_cnt;
112   char         *_path;
113   struct stat64 _info;
114 };
115
116 struct Node_get_key
117 {
118   typedef cxx::String Key_type;
119   static Key_type key_of(Node const *n)
120   { return n->path(); }
121 };
122
123 struct Path_avl_tree_compare
124 {
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
128   {
129     int v = strncmp(l.start(), r.start(), cxx::min(l.len(), r.len()));
130     return v < 0 || (v == 0 && l.len() < r.len());
131   }
132 };
133
134 class Pers_file : public Node
135 {
136 public:
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; }
141 private:
142   File_data     _data;
143 };
144
145 class Pers_dir : public Node
146 {
147 private:
148   typedef cxx::Avl_tree<Node, Node_get_key, Path_avl_tree_compare> Tree;
149   Tree _tree;
150
151 public:
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 &);
156
157   typedef Tree::Const_iterator Const_iterator;
158   Const_iterator begin() const { return _tree.begin(); }
159   Const_iterator end() const { return _tree.end(); }
160 };
161
162 Ref_ptr<Node> Pers_dir::find_path(cxx::String path)
163 {
164   return cxx::ref_ptr(_tree.find_node(path));
165 }
166
167 bool Pers_dir::add_node(Ref_ptr<Node> const &n)
168 {
169   bool e = _tree.insert(n.ptr()).second;
170   if (e)
171     n->add_ref();
172   return e;
173 }
174
175 class Tmpfs_dir : public Be_file
176 {
177 public:
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();
189
190 private:
191   int walk_path(cxx::String const &_s,
192                 Ref_ptr<Node> *ret, cxx::String *remaining = 0);
193
194   Ref_ptr<Pers_dir> _dir;
195   bool _getdents_state;
196   Pers_dir::Const_iterator _getdents_iter;
197 };
198
199 class Tmpfs_file : public Be_file_pos
200 {
201 public:
202   explicit Tmpfs_file(Ref_ptr<Pers_file> const &f) throw()
203     : Be_file_pos(), _file(f) {}
204
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();
211
212 private:
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;
216 };
217
218 ssize_t Tmpfs_file::preadv(const struct iovec *v, int iovcnt, off64_t p) 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().get(p, v[i].iov_len, v[i].iov_base);
227       p += v[i].iov_len;
228     }
229   return sum;
230 }
231
232 ssize_t Tmpfs_file::pwritev(const struct iovec *v, int iovcnt, off64_t p) throw()
233 {
234   if (iovcnt < 0)
235     return -EINVAL;
236
237   ssize_t sum = 0;
238   for (int i = 0; i < iovcnt; ++i)
239     {
240       sum  += _file->data().put(p, v[i].iov_len, v[i].iov_base);
241       p += v[i].iov_len;
242     }
243   return sum;
244 }
245
246 int Tmpfs_file::fstat64(struct stat64 *buf) const throw()
247 {
248   _file->info()->st_size = _file->data().size();
249   memcpy(buf, _file->info(), sizeof(*buf));
250   return 0;
251 }
252
253 int Tmpfs_file::ftruncate64(off64_t p) throw()
254 {
255   if (p < 0)
256       return -EINVAL;
257
258   if (_file->data().size(p) == 0)
259       return 0;
260
261   return -EIO; // most likely ENOSPC, but can't report that
262 }
263
264 off64_t Tmpfs_file::size() const throw()
265 { return _file->data().size(); }
266
267 int
268 Tmpfs_file::ioctl(unsigned long v, va_list args) throw()
269 {
270   switch (v)
271     {
272     case FIONREAD: // return amount of data still available
273       int *available = va_arg(args, int *);
274       *available = _file->data().size() - pos();
275       return 0;
276     };
277   return -EINVAL;
278 }
279
280 int
281 Tmpfs_file::utime(const struct utimbuf *times) throw()
282 {
283   _file->info()->st_atime = times->actime;
284   _file->info()->st_mtime = times->modtime;
285   return 0;
286 }
287
288 int
289 Tmpfs_file::fchmod(mode_t m) throw()
290 {
291   _file->info()->st_mode = m;
292   return 0;
293 }
294
295 int
296 Tmpfs_dir::faccessat(const char *path, int mode, int) throw()
297 {
298   Ref_ptr<Node> node;
299   cxx::String name = path;
300
301   int err = walk_path(name, &node, &name);
302   if (err < 0)
303     return err;
304
305   if (mode == F_OK) // existence check
306     return 0;
307
308   struct stat64 *stats = node->info();
309
310   if ((mode & R_OK) && !(stats->st_mode & S_IRUSR))
311     return -EACCES;
312
313   if ((mode & W_OK) && !(stats->st_mode & S_IWUSR))
314     return -EACCES;
315
316   if ((mode & X_OK) && !(stats->st_mode & S_IXUSR))
317     return -EACCES;
318
319   return 0;
320 }
321
322 int
323 Tmpfs_dir::get_entry(const char *name, int flags, mode_t mode,
324                      Ref_ptr<File> *file) throw()
325 {
326   Ref_ptr<Node> path;
327   if (!*name)
328     {
329       *file = cxx::ref_ptr(this);
330       return 0;
331     }
332
333   cxx::String n = name;
334
335   int e = walk_path(n, &path, &n);
336
337   if (e == -ENOTDIR)
338     return e;
339
340   if (!(flags & O_CREAT) && e < 0)
341     return e;
342
343   if ((flags & O_CREAT) && e == -ENOENT)
344     {
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);
348       if (!e)
349         return -ENOMEM;
350       path = node;
351     }
352
353   if (path->is_dir())
354     *file = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr_static_cast<Pers_dir>(path)));
355   else
356     *file = cxx::ref_ptr(new Tmpfs_file(cxx::ref_ptr_static_cast<Pers_file>(path)));
357
358   if (!*file)
359     return -ENOMEM;
360
361
362   return 0;
363 }
364
365 ssize_t
366 Tmpfs_dir::getdents(char *buf, size_t sz) throw()
367 {
368   struct dirent64 *d = (struct dirent64 *)buf;
369   ssize_t ret = 0;
370
371   if (!_getdents_state)
372     {
373       _getdents_iter = _dir->begin();
374       _getdents_state = true;
375     }
376   else if (_getdents_iter == _dir->end())
377     {
378       _getdents_state = false;
379       return 0;
380     }
381
382   for (; _getdents_iter != _dir->end(); ++_getdents_iter)
383     {
384       unsigned l = strlen(_getdents_iter->path()) + 1;
385       if (l > sizeof(d->d_name))
386         l = sizeof(d->d_name);
387
388       unsigned n = offsetof (struct dirent64, d_name) + l;
389       n = (n + sizeof(long) - 1) & ~(sizeof(long) - 1);
390
391       if (n > sz)
392         break;
393
394       d->d_ino = 1;
395       d->d_off = 0;
396       memcpy(d->d_name, _getdents_iter->path(), l);
397       d->d_reclen = n;
398       d->d_type   = DT_REG;
399       ret += n;
400       sz  -= n;
401       d    = (struct dirent64 *)((unsigned long)d + n);
402     }
403
404   return ret;
405 }
406
407 int
408 Tmpfs_dir::fstat64(struct stat64 *buf) const throw()
409 {
410   memcpy(buf, _dir->info(), sizeof(*buf));
411   return 0;
412 }
413
414 int
415 Tmpfs_dir::utime(const struct utimbuf *times) throw()
416 {
417   _dir->info()->st_atime = times->actime;
418   _dir->info()->st_mtime = times->modtime;
419   return 0;
420 }
421
422 int
423 Tmpfs_dir::fchmod(mode_t m) throw()
424 {
425   _dir->info()->st_mode = m;
426   return 0;
427 }
428
429 int
430 Tmpfs_dir::walk_path(cxx::String const &_s,
431                      Ref_ptr<Node> *ret, cxx::String *remaining)
432 {
433   Ref_ptr<Pers_dir> p = _dir;
434   cxx::String s = _s;
435   Ref_ptr<Node> n;
436
437   while (1)
438     {
439       if (s.len() == 0)
440         {
441           *ret = p;
442           return 0;
443         }
444
445       cxx::String::Index sep = s.find("/");
446
447       if (sep - s.start() == 1 && *s.start() == '.')
448         {
449           s = s.substr(s.start() + 2);
450           continue;
451         }
452
453       n = p->find_path(s.head(sep - s.start()));
454
455       if (!n)
456         {
457           *ret = p;
458           if (remaining)
459             *remaining = s.head(sep - s.start());
460           return -ENOENT;
461         }
462
463
464       if (sep == s.end())
465         {
466           *ret = n;
467           return 0;
468         }
469
470       if (!n->is_dir())
471         return -ENOTDIR;
472
473       s = s.substr(sep + 1);
474
475       p = cxx::ref_ptr_static_cast<Pers_dir>(n);
476     }
477
478   *ret = n;
479
480   return 0;
481 }
482
483 int
484 Tmpfs_dir::mkdir(const char *name, mode_t mode) throw()
485 {
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("/");
490
491   // trim /'s at the end
492   while (p.len() && s == p.end() - 1)
493     {
494       p.len(p.len() - 1);
495       s = p.rfind("/");
496     }
497
498   //printf("MKDIR '%s' p=%p %p\n", name, p.start(), s);
499
500   if (s != p.end())
501     {
502       path = p.head(s);
503       last = p.substr(s + 1, p.end() - s);
504
505       int e = walk_path(path, &node);
506       if (e < 0)
507         return e;
508     }
509
510   if (!node->is_dir())
511     return -ENOTDIR;
512
513   // due to path walking we can end up with an empty name
514   if (p.len() == 0 || p == cxx::String("."))
515     return 0;
516
517   Ref_ptr<Pers_dir> dnode = cxx::ref_ptr_static_cast<Pers_dir>(node);
518
519   Ref_ptr<Pers_dir> dir(new Pers_dir(last.start(), mode));
520   return dnode->add_node(dir) ? 0 : -EEXIST;
521 }
522
523 int
524 Tmpfs_dir::unlink(const char *name) throw()
525 {
526   cxx::Ref_ptr<Node> n;
527
528   int e = walk_path(name, &n);
529   if (e < 0)
530     return -ENOENT;
531
532   printf("Unimplemented (if file exists): %s(%s)\n", __func__, name); 
533   return -ENOMEM;
534 }
535
536 int
537 Tmpfs_dir::rename(const char *old, const char *newn) throw()
538 {
539   printf("Unimplemented: %s(%s, %s)\n", __func__, old, newn); 
540   return -ENOMEM;
541 }
542
543
544
545 class Tmpfs_fs : public Be_file_system
546 {
547 public:
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()
551   {
552     (void)mountflags;
553     (void)source;
554     (void)data;
555     *dir = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr(new Pers_dir("root", 0777))));
556     if (!*dir)
557       return -ENOMEM;
558     return 0;
559   }
560 };
561
562 static Tmpfs_fs _tmpfs L4RE_VFS_FILE_SYSTEM_ATTRIBUTE;
563
564 }