]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/tmpfs/lib/src/fs.cc
2820f20b5970eeab561d8d75353c3f26a4c61c45
[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   { _info.st_mode = mode; }
85
86   const char *path() const { return _path; }
87   struct stat64 *info() { return &_info; }
88
89   void add_ref() throw() { ++_ref_cnt; }
90   int remove_ref() throw() { return --_ref_cnt; }
91
92   bool is_dir() const { return S_ISDIR(_info.st_mode); }
93
94   virtual ~Node() { free(_path); }
95
96 private:
97   int           _ref_cnt;
98   char         *_path;
99   struct stat64 _info;
100 };
101
102 struct Node_get_key
103 {
104   typedef cxx::String Key_type;
105   static Key_type key_of(Node const *n)
106   { return n->path(); }
107 };
108
109 struct Path_avl_tree_compare
110 {
111   bool operator () (const char *l, const char *r) const
112   { return strcmp(l, r) < 0; }
113   bool operator () (const cxx::String l, const cxx::String r) const
114   { return strncmp(l.start(), r.start(), cxx::min(l.len(), r.len())) < 0; }
115 };
116
117 class Pers_file : public Node
118 {
119 public:
120   Pers_file(const char *name, mode_t mode)
121     : Node(name, (mode & 0777) | __S_IFREG) {}
122   File_data const &data() const { return _data; }
123   File_data &data() { return _data; }
124 private:
125   File_data     _data;
126 };
127
128 class Pers_dir : public Node
129 {
130 private:
131   typedef cxx::Avl_tree<Node, Node_get_key, Path_avl_tree_compare> Tree;
132   Tree _tree;
133
134 public:
135   Pers_dir(const char *name, mode_t mode)
136     : Node(name, (mode & 0777) | __S_IFDIR) {}
137   Ref_ptr<Node> find_path(cxx::String);
138   int add_node(Ref_ptr<Node> const &);
139
140   typedef Tree::Const_iterator Const_iterator;
141   Const_iterator begin() const { return _tree.begin(); }
142   Const_iterator end() const { return _tree.end(); }
143 };
144
145 Ref_ptr<Node> Pers_dir::find_path(cxx::String path)
146 {
147   return cxx::ref_ptr(_tree.find_node(path));
148 }
149
150 int Pers_dir::add_node(Ref_ptr<Node> const &n)
151 {
152   int e;
153   e = _tree.insert(n.ptr()).second;
154   if (!e)
155     n->add_ref();
156   return e;
157 }
158
159 class Tmpfs_dir : public Be_file
160 {
161 public:
162   explicit Tmpfs_dir(Ref_ptr<Pers_dir> const &d) throw()
163     : _dir(d), _getdents_state(false) {}
164   int get_entry(const char *, int, mode_t, Ref_ptr<File> *) throw();
165   ssize_t getdents(char *, size_t) throw();
166   int fstat64(struct stat64 *buf) const throw();
167   int mkdir(const char *, mode_t) throw();
168   int unlink(const char *) throw();
169   int rename(const char *, const char *) throw();
170
171 private:
172   int walk_path(cxx::String const &_s,
173                 Ref_ptr<Node> *ret, cxx::String *remaining = 0);
174
175   Ref_ptr<Pers_dir> _dir;
176   bool _getdents_state;
177   Pers_dir::Const_iterator _getdents_iter;
178 };
179
180 class Tmpfs_file : public Be_file
181 {
182 public:
183   explicit Tmpfs_file(Ref_ptr<Pers_file> const &f) throw()
184     : Be_file(), _file(f), _pos(0) {}
185
186   ssize_t readv(const struct iovec*, int iovcnt) throw();
187   ssize_t writev(const struct iovec*, int iovcnt) throw();
188   off64_t lseek64(off64_t, int) throw();
189   int fstat64(struct stat64 *buf) const throw();
190   int ioctl(unsigned long, va_list) throw();
191
192 private:
193   Ref_ptr<Pers_file> _file;
194   off64_t _pos;
195 };
196
197 ssize_t Tmpfs_file::readv(const struct iovec *v, int iovcnt) throw()
198 {
199   if (iovcnt < 0)
200     return -EINVAL;
201
202
203   ssize_t sum = 0;
204   for (int i = 0; i < iovcnt; ++i)
205     {
206       sum  += _file->data().get(_pos, v[i].iov_len, v[i].iov_base);
207       _pos += v[i].iov_len;
208     }
209   return sum;
210 }
211
212 ssize_t Tmpfs_file::writev(const struct iovec *v, int iovcnt) throw()
213 {
214   if (iovcnt < 0)
215     return -EINVAL;
216
217   ssize_t sum = 0;
218   for (int i = 0; i < iovcnt; ++i)
219     {
220       sum  += _file->data().put(_pos, v[i].iov_len, v[i].iov_base);
221       _pos += v[i].iov_len;
222     }
223   return sum;
224 }
225
226 int Tmpfs_file::fstat64(struct stat64 *buf) const throw()
227 {
228   _file->info()->st_size = _file->data().size();
229   memcpy(buf, _file->info(), sizeof(*buf));
230   return 0;
231 }
232
233 off64_t Tmpfs_file::lseek64(off64_t offset, int whence) throw()
234 {
235   switch (whence)
236     {
237     case SEEK_SET: _pos = offset; break;
238     case SEEK_CUR: _pos += offset; break;
239     case SEEK_END: _pos = _file->data().size() + offset; break;
240     default: return -EINVAL;
241     };
242
243   if (_pos < 0)
244     return -EINVAL;
245
246   return _pos;
247 }
248
249 int
250 Tmpfs_file::ioctl(unsigned long v, va_list args) throw()
251 {
252   switch (v)
253     {
254     case FIONREAD: // return amount of data still available
255       int *available = va_arg(args, int *);
256       *available = _file->data().size() - _pos;
257       return 0;
258     };
259   return -EINVAL;
260 }
261
262
263
264
265 int
266 Tmpfs_dir::get_entry(const char *name, int flags, mode_t mode,
267                      Ref_ptr<File> *file) throw()
268 {
269   Ref_ptr<Node> path;
270   if (!*name)
271     {
272       *file = this;
273       return 0;
274     }
275
276   cxx::String n = name;
277
278   int e = walk_path(n, &path, &n);
279
280   if (e == -ENOTDIR)
281     return e;
282
283   if (!(flags & O_CREAT) && e < 0)
284     return e;
285
286   if ((flags & O_CREAT) && e == -ENOENT)
287     {
288       Ref_ptr<Node> node(new Pers_file(n.start(), mode));
289       // when ENOENT is return, path is always a directory
290       int e = cxx::ref_ptr_static_cast<Pers_dir>(path)->add_node(node);
291       if (e)
292         return e;
293       path = node;
294     }
295
296   if (path->is_dir())
297     *file = new Tmpfs_dir(cxx::ref_ptr_static_cast<Pers_dir>(path));
298   else
299     *file = new Tmpfs_file(cxx::ref_ptr_static_cast<Pers_file>(path));
300
301   if (!*file)
302     return -ENOMEM;
303
304
305   return 0;
306 }
307
308 ssize_t
309 Tmpfs_dir::getdents(char *buf, size_t sz) throw()
310 {
311   struct dirent64 *d = (struct dirent64 *)buf;
312   ssize_t ret = 0;
313
314   if (!_getdents_state)
315     {
316       _getdents_iter = _dir->begin();
317       _getdents_state = true;
318     }
319   else if (_getdents_iter == _dir->end())
320     {
321       _getdents_state = false;
322       return 0;
323     }
324
325   for (; _getdents_iter != _dir->end(); ++_getdents_iter)
326     {
327       unsigned l = strlen(_getdents_iter->path()) + 1;
328       if (l > sizeof(d->d_name))
329         l = sizeof(d->d_name);
330
331       unsigned n = offsetof (struct dirent64, d_name) + l;
332
333       if (n > sz)
334         break;
335
336       d->d_ino = 1;
337       d->d_off = 0;
338       memcpy(d->d_name, _getdents_iter->path(), l);
339       d->d_reclen = n;
340       d->d_type   = DT_REG;
341       ret += n;
342       sz  -= n;
343       d    = (struct dirent64 *)((unsigned long)d + n);
344     }
345
346   return ret;
347 }
348
349 int
350 Tmpfs_dir::fstat64(struct stat64 *buf) const throw()
351 {
352   memcpy(buf, _dir->info(), sizeof(*buf));
353   return 0;
354 }
355
356 int
357 Tmpfs_dir::walk_path(cxx::String const &_s,
358                      Ref_ptr<Node> *ret, cxx::String *remaining)
359 {
360   Ref_ptr<Pers_dir> p = _dir;
361   cxx::String s = _s;
362   Ref_ptr<Node> n;
363
364   while (1)
365     {
366       cxx::String::Index sep = s.find("/");
367
368       n = p->find_path(s.head(sep - s.start()));
369
370       if (!n)
371         {
372           *ret = p;
373           if (remaining)
374             *remaining = s.head(sep - s.start());
375           return -ENOENT;
376         }
377
378
379       if (sep == s.end())
380         {
381           *ret = n;
382           return 0;
383         }
384
385       if (!n->is_dir())
386         return -ENOTDIR;
387
388       s = s.substr(sep + 1);
389
390       p = cxx::ref_ptr_static_cast<Pers_dir>(n);
391     }
392
393   *ret = n;
394
395   return 0;
396 }
397
398 int
399 Tmpfs_dir::mkdir(const char *name, mode_t mode) throw()
400 {
401   Ref_ptr<Node> node = _dir;
402   cxx::String p = cxx::String(name);
403   cxx::String path, last = p;
404   cxx::String::Index s = p.rfind("/");
405   if (s != p.end())
406     {
407       path = p.head(s);
408       last = p.substr(s + 1, p.end() - s);
409
410       int e = walk_path(path, &node);
411       if (e < 0)
412         return e;
413     }
414
415   if (!node->is_dir())
416     return -ENOTDIR;
417
418   Ref_ptr<Pers_dir> dnode = cxx::ref_ptr_static_cast<Pers_dir>(node);
419
420   Ref_ptr<Pers_dir> dir(new Pers_dir(last.start(), mode));
421   return dnode->add_node(dir);
422 }
423
424 int
425 Tmpfs_dir::unlink(const char *name) throw()
426 {
427   printf("Unimplemented: %s(%s)\n", __func__, name); 
428   return -ENOMEM;
429 }
430
431 int
432 Tmpfs_dir::rename(const char *old, const char *newn) throw()
433 {
434   printf("Unimplemented: %s(%s, %s)\n", __func__, old, newn); 
435   return -ENOMEM;
436 }
437
438
439
440 class Tmpfs_fs : public Be_file_system
441 {
442 public:
443   Tmpfs_fs() : Be_file_system("tmpfs") {}
444   int mount(char const *source, unsigned long mountflags,
445             void const *data, cxx::Ref_ptr<File> *dir) throw()
446   {
447     (void)mountflags;
448     (void)source;
449     (void)data;
450     *dir = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr(new Pers_dir("root", 0777))));
451     if (!*dir)
452       return -ENOMEM;
453     return 0;
454   }
455 };
456
457 static Tmpfs_fs _tmpfs;
458
459 }