]> 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 Technische Universität Dresden
5  * This file is part of TUD:OS and distributed under the terms of the
6  * GNU Lesser General Public License 2.1.
7  * Please see the COPYING-LGPL-2.1 file for details.
8  */
9
10 #include <l4/l4re_vfs/backend>
11 #include <l4/cxx/string>
12 #include <l4/cxx/avl_tree>
13
14 #include <sys/stat.h>
15 #include <sys/ioctl.h>
16 #include <errno.h>
17
18 #include <cstdio>
19
20
21 namespace {
22
23 using namespace L4Re::Vfs;
24 using cxx::Ref_ptr;
25
26 class File_data
27 {
28 public:
29   File_data() : _buf(0), _size(0) {}
30
31   unsigned long put(unsigned long offset,
32                     unsigned long bufsize, void *srcbuf);
33   unsigned long get(unsigned long offset,
34                     unsigned long bufsize, void *dstbuf);
35
36   unsigned long size() const { return _size; }
37
38   ~File_data() throw() { free(_buf); }
39
40 private:
41   void *_buf;
42   unsigned long _size;
43 };
44
45 unsigned long
46 File_data::put(unsigned long offset, unsigned long bufsize, void *srcbuf)
47 {
48   if (offset + bufsize > _size)
49     {
50       _size = offset + bufsize;
51       _buf = realloc(_buf, _size);
52     }
53
54   if (!_buf)
55     return 0;
56
57   memcpy((char *)_buf + offset, srcbuf, bufsize);
58   return bufsize;
59 }
60
61 unsigned long
62 File_data::get(unsigned long offset, unsigned long bufsize, void *dstbuf)
63 {
64   unsigned long s = bufsize;
65
66   if (offset > _size)
67     return 0;
68
69   if (offset + bufsize > _size)
70     s = _size - offset;
71
72   memcpy(dstbuf, (char *)_buf + offset, s);
73   return s;
74 }
75
76
77 class Node : public cxx::Avl_tree_node
78 {
79 public:
80   Node(const char *path, mode_t mode)
81     : _ref_cnt(0), _path(strdup(path))
82   { _info.st_mode = mode; }
83
84   const char *path() const { return _path; }
85   struct stat64 *info() { return &_info; }
86
87   void add_ref() throw() { ++_ref_cnt; }
88   int remove_ref() throw() { return --_ref_cnt; }
89
90   bool is_dir() const { return S_ISDIR(_info.st_mode); }
91
92   virtual ~Node() { free(_path); }
93
94 private:
95   int           _ref_cnt;
96   char         *_path;
97   struct stat64 _info;
98 };
99
100 struct Node_get_key
101 {
102   typedef cxx::String Key_type;
103   static Key_type key_of(Node const *n)
104   { return n->path(); }
105 };
106
107 struct Path_avl_tree_compare
108 {
109   bool operator () (const char *l, const char *r) const
110   { return strcmp(l, r) < 0; }
111   bool operator () (const cxx::String l, const cxx::String r) const
112   { return strncmp(l.start(), r.start(), cxx::min(l.len(), r.len())) < 0; }
113 };
114
115 class Pers_file : public Node
116 {
117 public:
118   Pers_file(const char *name, mode_t mode)
119     : Node(name, (mode & 0777) | __S_IFREG) {}
120   File_data const &data() const { return _data; }
121   File_data &data() { return _data; }
122 private:
123   File_data     _data;
124 };
125
126 class Pers_dir : public Node
127 {
128 public:
129   Pers_dir(const char *name, mode_t mode)
130     : Node(name, (mode & 0777) | __S_IFDIR) {}
131   Ref_ptr<Node> find_path(cxx::String);
132   int add_node(Ref_ptr<Node> const &);
133
134 private:
135   typedef cxx::Avl_tree<Node, Node_get_key, Path_avl_tree_compare> Tree;
136   Tree _tree;
137 };
138
139 Ref_ptr<Node> Pers_dir::find_path(cxx::String path)
140 {
141   return cxx::ref_ptr(_tree.find_node(path));
142 }
143
144 int Pers_dir::add_node(Ref_ptr<Node> const &n)
145 {
146   int e;
147   e = _tree.insert(n.ptr()).second;
148   if (!e)
149     n->add_ref();
150   return e;
151 }
152
153 class Tmpfs_dir : public Be_file
154 {
155 public:
156   explicit Tmpfs_dir(Ref_ptr<Pers_dir> const &d) throw()
157     : _dir(d) {}
158   int get_entry(const char *, int, mode_t, Ref_ptr<File> *) throw();
159   int fstat64(struct stat64 *buf) const throw();
160   int mkdir(const char *, mode_t) throw();
161   int unlink(const char *) throw();
162   int rename(const char *, const char *) throw();
163
164 private:
165   int walk_path(cxx::String const &_s,
166                 Ref_ptr<Node> *ret, cxx::String *remaining = 0);
167
168   Ref_ptr<Pers_dir> _dir;
169 };
170
171 class Tmpfs_file : public Be_file
172 {
173 public:
174   explicit Tmpfs_file(Ref_ptr<Pers_file> const &f) throw()
175     : Be_file(), _file(f), _pos(0) {}
176
177   ssize_t readv(const struct iovec*, int iovcnt) throw();
178   ssize_t writev(const struct iovec*, int iovcnt) throw();
179   off64_t lseek64(off64_t, int) throw();
180   int fstat64(struct stat64 *buf) const throw();
181   int ioctl(unsigned long, va_list) throw();
182
183 private:
184   Ref_ptr<Pers_file> _file;
185   off64_t _pos;
186 };
187
188 ssize_t Tmpfs_file::readv(const struct iovec *v, int iovcnt) throw()
189 {
190   if (iovcnt < 0)
191     return -EINVAL;
192
193
194   ssize_t sum = 0;
195   for (int i = 0; i < iovcnt; ++i)
196     {
197       sum  += _file->data().get(_pos, v[i].iov_len, v[i].iov_base);
198       _pos += v[i].iov_len;
199     }
200   return sum;
201 }
202
203 ssize_t Tmpfs_file::writev(const struct iovec *v, int iovcnt) throw()
204 {
205   if (iovcnt < 0)
206     return -EINVAL;
207
208   ssize_t sum = 0;
209   for (int i = 0; i < iovcnt; ++i)
210     {
211       sum  += _file->data().put(_pos, v[i].iov_len, v[i].iov_base);
212       _pos += v[i].iov_len;
213     }
214   return sum;
215 }
216
217 int Tmpfs_file::fstat64(struct stat64 *buf) const throw()
218 {
219   memcpy(buf, _file->info(), sizeof(*buf));
220   return 0;
221 }
222
223 off64_t Tmpfs_file::lseek64(off64_t offset, int whence) throw()
224 {
225   switch (whence)
226     {
227     case SEEK_SET: _pos = offset; break;
228     case SEEK_CUR: _pos += offset; break;
229     case SEEK_END: _pos = _file->data().size() + offset; break;
230     default: return -EINVAL;
231     };
232
233   if (_pos < 0)
234     return -EINVAL;
235
236   return _pos;
237 }
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
253
254
255 int
256 Tmpfs_dir::get_entry(const char *name, int flags, mode_t mode,
257                      Ref_ptr<File> *file) throw()
258 {
259   Ref_ptr<Node> path;
260   if (!*name)
261     {
262       *file = this;
263       return 0;
264     }
265
266   cxx::String n = name;
267
268   int e = walk_path(n, &path, &n);
269
270   if (e == -ENOTDIR)
271     return e;
272
273   if (!(flags & O_CREAT) && e < 0)
274     return e;
275
276   if ((flags & O_CREAT) && e == -ENOENT)
277     {
278       Ref_ptr<Node> node(new Pers_file(n.start(), mode));
279       // when ENOENT is return, path is always a directory
280       int e = cxx::ref_ptr_static_cast<Pers_dir>(path)->add_node(node);
281       if (e)
282         return e;
283       path = node;
284     }
285
286   if (path->is_dir())
287     *file = new Tmpfs_dir(cxx::ref_ptr_static_cast<Pers_dir>(path));
288   else
289     *file = new Tmpfs_file(cxx::ref_ptr_static_cast<Pers_file>(path));
290
291   if (!*file)
292     return -ENOMEM;
293
294
295   return 0;
296 }
297
298 int
299 Tmpfs_dir::fstat64(struct stat64 *buf) const throw()
300 {
301   memcpy(buf, _dir->info(), sizeof(*buf));
302   return 0;
303 }
304
305 int
306 Tmpfs_dir::walk_path(cxx::String const &_s,
307                      Ref_ptr<Node> *ret, cxx::String *remaining)
308 {
309   Ref_ptr<Pers_dir> p = _dir;
310   cxx::String s = _s;
311   Ref_ptr<Node> n;
312
313   while (1)
314     {
315       cxx::String::Index sep = s.find("/");
316
317       n = p->find_path(s.head(sep - s.start()));
318
319       if (!n)
320         {
321           *ret = p;
322           if (remaining)
323             *remaining = s.head(sep - s.start());
324           return -ENOENT;
325         }
326
327
328       if (sep == s.end())
329         {
330           *ret = n;
331           return 0;
332         }
333
334       if (!n->is_dir())
335         return -ENOTDIR;
336
337       s = s.substr(sep + 1);
338
339       p = cxx::ref_ptr_static_cast<Pers_dir>(n);
340     }
341
342   *ret = n;
343
344   return 0;
345 }
346
347 int
348 Tmpfs_dir::mkdir(const char *name, mode_t mode) throw()
349 {
350   Ref_ptr<Node> node = _dir;
351   cxx::String p = cxx::String(name);
352   cxx::String path, last = p;
353   cxx::String::Index s = p.rfind("/");
354   if (s != p.end())
355     {
356       path = p.head(s);
357       last = p.substr(s + 1, p.end() - s);
358
359       int e = walk_path(path, &node);
360       if (e < 0)
361         return e;
362     }
363
364   if (!node->is_dir())
365     return -ENOTDIR;
366
367   Ref_ptr<Pers_dir> dnode = cxx::ref_ptr_static_cast<Pers_dir>(node);
368
369   Ref_ptr<Pers_dir> dir(new Pers_dir(last.start(), mode));
370   return dnode->add_node(dir);
371 }
372
373 int
374 Tmpfs_dir::unlink(const char *name) throw()
375 {
376   printf("Unimplemented: %s(%s)\n", __func__, name); 
377   return -ENOMEM;
378 }
379
380 int
381 Tmpfs_dir::rename(const char *old, const char *newn) throw()
382 {
383   printf("Unimplemented: %s(%s, %s)\n", __func__, old, newn); 
384   return -ENOMEM;
385 }
386
387
388
389 class Tmpfs_fs : public Be_file_system
390 {
391 public:
392   Tmpfs_fs() : Be_file_system("tmpfs") {}
393   int mount(char const *source, unsigned long mountflags,
394             void const *data, cxx::Ref_ptr<File> *dir) throw()
395   {
396     (void)mountflags;
397     (void)source;
398     (void)data;
399     *dir = cxx::ref_ptr(new Tmpfs_dir(cxx::ref_ptr(new Pers_dir("root", 0777))));
400     if (!*dir)
401       return -ENOMEM;
402     return 0;
403   }
404 };
405
406 static Tmpfs_fs _tmpfs;
407
408 }