]> 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   {
117     int v = strncmp(l.start(), r.start(), cxx::min(l.len(), r.len()));
118     return v < 0 || (v == 0 && l.len() < r.len());
119   }
120 };
121
122 class Pers_file : public Node
123 {
124 public:
125   Pers_file(const char *name, mode_t mode)
126     : Node(name, (mode & 0777) | __S_IFREG) {}
127   File_data const &data() const { return _data; }
128   File_data &data() { return _data; }
129 private:
130   File_data     _data;
131 };
132
133 class Pers_dir : public Node
134 {
135 private:
136   typedef cxx::Avl_tree<Node, Node_get_key, Path_avl_tree_compare> Tree;
137   Tree _tree;
138
139 public:
140   Pers_dir(const char *name, mode_t mode)
141     : Node(name, (mode & 0777) | __S_IFDIR) {}
142   Ref_ptr<Node> find_path(cxx::String);
143   bool add_node(Ref_ptr<Node> const &);
144
145   typedef Tree::Const_iterator Const_iterator;
146   Const_iterator begin() const { return _tree.begin(); }
147   Const_iterator end() const { return _tree.end(); }
148 };
149
150 Ref_ptr<Node> Pers_dir::find_path(cxx::String path)
151 {
152   return cxx::ref_ptr(_tree.find_node(path));
153 }
154
155 bool Pers_dir::add_node(Ref_ptr<Node> const &n)
156 {
157   bool e = _tree.insert(n.ptr()).second;
158   if (e)
159     n->add_ref();
160   return e;
161 }
162
163 class Tmpfs_dir : public Be_file
164 {
165 public:
166   explicit Tmpfs_dir(Ref_ptr<Pers_dir> const &d) throw()
167     : _dir(d), _getdents_state(false) {}
168   int get_entry(const char *, int, mode_t, Ref_ptr<File> *) throw();
169   ssize_t getdents(char *, size_t) throw();
170   int fstat64(struct stat64 *buf) const throw();
171   int utime(const struct utimbuf *) throw();
172   int fchmod(mode_t) throw();
173   int mkdir(const char *, mode_t) throw();
174   int unlink(const char *) throw();
175   int rename(const char *, const char *) throw();
176
177 private:
178   int walk_path(cxx::String const &_s,
179                 Ref_ptr<Node> *ret, cxx::String *remaining = 0);
180
181   Ref_ptr<Pers_dir> _dir;
182   bool _getdents_state;
183   Pers_dir::Const_iterator _getdents_iter;
184 };
185
186 class Tmpfs_file : public Be_file_pos
187 {
188 public:
189   explicit Tmpfs_file(Ref_ptr<Pers_file> const &f) throw()
190     : Be_file_pos(), _file(f) {}
191
192   off64_t size() const 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   ssize_t preadv(const struct iovec *v, int iovcnt, off64_t p) throw();
200   ssize_t pwritev(const struct iovec *v, int iovcnt, off64_t p) throw();
201   Ref_ptr<Pers_file> _file;
202 };
203
204 ssize_t Tmpfs_file::preadv(const struct iovec *v, int iovcnt, off64_t p) throw()
205 {
206   if (iovcnt < 0)
207     return -EINVAL;
208
209   ssize_t sum = 0;
210   for (int i = 0; i < iovcnt; ++i)
211     {
212       sum  += _file->data().get(p, v[i].iov_len, v[i].iov_base);
213       p += v[i].iov_len;
214     }
215   return sum;
216 }
217
218 ssize_t Tmpfs_file::pwritev(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().put(p, v[i].iov_len, v[i].iov_base);
227       p += 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::size() const throw()
240 { return _file->data().size(); }
241
242 int
243 Tmpfs_file::ioctl(unsigned long v, va_list args) throw()
244 {
245   switch (v)
246     {
247     case FIONREAD: // return amount of data still available
248       int *available = va_arg(args, int *);
249       *available = _file->data().size() - pos();
250       return 0;
251     };
252   return -EINVAL;
253 }
254
255 int
256 Tmpfs_file::utime(const struct utimbuf *times) throw()
257 {
258   _file->info()->st_atime = times->actime;
259   _file->info()->st_mtime = times->modtime;
260   return 0;
261 }
262
263 int
264 Tmpfs_file::fchmod(mode_t m) throw()
265 {
266   _file->info()->st_mode = m;
267   return 0;
268 }
269
270
271 int
272 Tmpfs_dir::get_entry(const char *name, int flags, mode_t mode,
273                      Ref_ptr<File> *file) throw()
274 {
275   Ref_ptr<Node> path;
276   if (!*name)
277     {
278       *file = this;
279       return 0;
280     }
281
282   cxx::String n = name;
283
284   int e = walk_path(n, &path, &n);
285
286   if (e == -ENOTDIR)
287     return e;
288
289   if (!(flags & O_CREAT) && e < 0)
290     return e;
291
292   if ((flags & O_CREAT) && e == -ENOENT)
293     {
294       Ref_ptr<Node> node(new Pers_file(n.start(), mode));
295       // when ENOENT is return, path is always a directory
296       bool e = cxx::ref_ptr_static_cast<Pers_dir>(path)->add_node(node);
297       if (!e)
298         return -ENOMEM;
299       path = node;
300     }
301
302   if (path->is_dir())
303     *file = new Tmpfs_dir(cxx::ref_ptr_static_cast<Pers_dir>(path));
304   else
305     *file = new Tmpfs_file(cxx::ref_ptr_static_cast<Pers_file>(path));
306
307   if (!*file)
308     return -ENOMEM;
309
310
311   return 0;
312 }
313
314 ssize_t
315 Tmpfs_dir::getdents(char *buf, size_t sz) throw()
316 {
317   struct dirent64 *d = (struct dirent64 *)buf;
318   ssize_t ret = 0;
319
320   if (!_getdents_state)
321     {
322       _getdents_iter = _dir->begin();
323       _getdents_state = true;
324     }
325   else if (_getdents_iter == _dir->end())
326     {
327       _getdents_state = false;
328       return 0;
329     }
330
331   for (; _getdents_iter != _dir->end(); ++_getdents_iter)
332     {
333       unsigned l = strlen(_getdents_iter->path()) + 1;
334       if (l > sizeof(d->d_name))
335         l = sizeof(d->d_name);
336
337       unsigned n = offsetof (struct dirent64, d_name) + l;
338       n = (n + sizeof(long) - 1) & ~(sizeof(long) - 1);
339
340       if (n > sz)
341         break;
342
343       d->d_ino = 1;
344       d->d_off = 0;
345       memcpy(d->d_name, _getdents_iter->path(), l);
346       d->d_reclen = n;
347       d->d_type   = DT_REG;
348       ret += n;
349       sz  -= n;
350       d    = (struct dirent64 *)((unsigned long)d + n);
351     }
352
353   return ret;
354 }
355
356 int
357 Tmpfs_dir::fstat64(struct stat64 *buf) const throw()
358 {
359   memcpy(buf, _dir->info(), sizeof(*buf));
360   return 0;
361 }
362
363 int
364 Tmpfs_dir::utime(const struct utimbuf *times) throw()
365 {
366   _dir->info()->st_atime = times->actime;
367   _dir->info()->st_mtime = times->modtime;
368   return 0;
369 }
370
371 int
372 Tmpfs_dir::fchmod(mode_t m) throw()
373 {
374   _dir->info()->st_mode = m;
375   return 0;
376 }
377
378 int
379 Tmpfs_dir::walk_path(cxx::String const &_s,
380                      Ref_ptr<Node> *ret, cxx::String *remaining)
381 {
382   Ref_ptr<Pers_dir> p = _dir;
383   cxx::String s = _s;
384   Ref_ptr<Node> n;
385
386   while (1)
387     {
388       if (s.len() == 0)
389         {
390           *ret = p;
391           return 0;
392         }
393
394       cxx::String::Index sep = s.find("/");
395
396       if (sep - s.start() == 1 && *s.start() == '.')
397         {
398           s = s.substr(s.start() + 2);
399           continue;
400         }
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
440   // trim /'s at the end
441   while (p.len() && s == p.end() - 1)
442     {
443       p.len(p.len() - 1);
444       s = p.rfind("/");
445     }
446
447   //printf("MKDIR '%s' p=%p %p\n", name, p.start(), s);
448
449   if (s != p.end())
450     {
451       path = p.head(s);
452       last = p.substr(s + 1, p.end() - s);
453
454       int e = walk_path(path, &node);
455       if (e < 0)
456         return e;
457     }
458
459   if (!node->is_dir())
460     return -ENOTDIR;
461
462   // due to path walking we can end up with an empty name
463   if (p.len() == 0 || p == cxx::String("."))
464     return 0;
465
466   Ref_ptr<Pers_dir> dnode = cxx::ref_ptr_static_cast<Pers_dir>(node);
467
468   Ref_ptr<Pers_dir> dir(new Pers_dir(last.start(), mode));
469   return dnode->add_node(dir) ? 0 : -EEXIST;
470 }
471
472 int
473 Tmpfs_dir::unlink(const char *name) throw()
474 {
475   cxx::Ref_ptr<Node> n;
476
477   int e = walk_path(name, &n);
478   if (e < 0)
479     return -ENOENT;
480
481   printf("Unimplemented (if file exists): %s(%s)\n", __func__, name); 
482   return -ENOMEM;
483 }
484
485 int
486 Tmpfs_dir::rename(const char *old, const char *newn) throw()
487 {
488   printf("Unimplemented: %s(%s, %s)\n", __func__, old, newn); 
489   return -ENOMEM;
490 }
491
492
493
494 class Tmpfs_fs : public Be_file_system
495 {
496 public:
497   Tmpfs_fs() : Be_file_system("tmpfs") {}
498   int mount(char const *source, unsigned long mountflags,
499             void const *data, cxx::Ref_ptr<File> *dir) throw()
500   {
501     (void)mountflags;
502     (void)source;
503     (void)data;
504     *dir = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr(new Pers_dir("root", 0777))));
505     if (!*dir)
506       return -ENOMEM;
507     return 0;
508   }
509 };
510
511 static Tmpfs_fs _tmpfs L4RE_VFS_FILE_SYSTEM_ATTRIBUTE;
512
513 }