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