]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/l4re_vfs/include/impl/vfs_impl.h
update
[l4.git] / l4 / pkg / l4re_vfs / include / impl / vfs_impl.h
1 /*
2  * (c) 2008-2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
3  *               Alexander Warg <warg@os.inf.tu-dresden.de>,
4  *               Björn Döbel <doebel@os.inf.tu-dresden.de>
5  *     economic rights: Technische Universität Dresden (Germany)
6  *
7  * This file is part of TUD:OS and distributed under the terms of the
8  * GNU General Public License 2.
9  * Please see the COPYING-GPL-2 file for details.
10  *
11  * As a special exception, you may use this file as part of a free software
12  * library without restriction.  Specifically, if other files instantiate
13  * templates or use macros or inline functions from this file, or you compile
14  * this file and link it with other files to produce an executable, this
15  * file does not by itself cause the resulting executable to be covered by
16  * the GNU General Public License.  This exception does not however
17  * invalidate any other reasons why the executable file might be covered by
18  * the GNU General Public License.
19  */
20
21 #include "ds_util.h"
22 #include "fd_store.h"
23 #include "vcon_stream.h"
24 #include "ns_fs.h"
25 #include "vfs_api.h"
26
27 #include <l4/re/env>
28 #include <l4/re/rm>
29 #include <l4/re/dataspace>
30
31 #include <l4/l4re_vfs/backend>
32
33 #include <unistd.h>
34 #include <cstdarg>
35 #include <errno.h>
36 #include <sys/uio.h>
37
38 //#include <l4/sys/kdebug.h>
39 //static int debug_mmap = 1;
40 //#define DEBUG_LOG(level, dbg...) do { if (level) dbg } while (0)
41
42 #define DEBUG_LOG(level, dbg...) do { } while (0)
43
44 /**
45  * If USE_BIG_ANON_DS is defined the implementation will use a really big
46  * data space for backing anonymous memory. Otherwise each mmap call
47  * with anonymous memory will allocate a separate data space.
48  */
49 #define USE_BIG_ANON_DS
50
51 using L4Re::Rm;
52
53 namespace {
54
55 struct Dl_env_ops
56 {
57   void *(*malloc)(size_t);
58   void (*free)(void*);
59
60   unsigned long (*cap_alloc)();
61   void (*cap_free)(unsigned long);
62 };
63
64 using cxx::Ref_ptr;
65
66 class Fd_store : public L4Re::Core::Fd_store
67 {
68 public:
69   Fd_store() throw();
70 };
71
72
73 Fd_store::Fd_store() throw()
74 {
75   static L4Re::Core::Vcon_stream s(L4Re::Env::env()->log());
76   // make sure that we never delete the static io stream thing
77   s.add_ref();
78   set(0, cxx::ref_ptr(&s)); // stdin
79   set(1, cxx::ref_ptr(&s)); // stdout
80   set(2, cxx::ref_ptr(&s)); // stderr
81 }
82
83 class Root_mount_tree : public L4Re::Vfs::Mount_tree
84 {
85 public:
86   Root_mount_tree() : L4Re::Vfs::Mount_tree(0) {}
87   void operator delete (void *) {}
88 };
89
90 class Vfs : public L4Re::Vfs::Ops
91 {
92 private:
93   bool _early_oom;
94
95 public:
96   Vfs()
97   : _early_oom(true), _root_mount(), _root(L4Re::Env::env()),
98     _annon_size(0x10000000)
99   {
100     _root_mount.add_ref();
101     _root.add_ref();
102     _root_mount.mount(cxx::ref_ptr(&_root));
103     _cwd = &_root;
104
105 #if 0
106     Ref_ptr<L4Re::Vfs::File> rom;
107     _root.openat("rom", 0, 0, &rom);
108
109     _root_mount.create_tree("lib/foo", rom);
110
111     _root.openat("lib", 0, 0, &_cwd);
112
113 #endif
114   }
115
116   int alloc_fd(Ref_ptr<L4Re::Vfs::File> const &f) throw();
117   Ref_ptr<L4Re::Vfs::File> free_fd(int fd) throw();
118   Ref_ptr<L4Re::Vfs::File> get_root() throw();
119   Ref_ptr<L4Re::Vfs::File> get_cwd() throw();
120   void set_cwd(Ref_ptr<L4Re::Vfs::File> const &dir) throw();
121   Ref_ptr<L4Re::Vfs::File> get_file(int fd) throw();
122   Ref_ptr<L4Re::Vfs::File> set_fd(int fd, Ref_ptr<L4Re::Vfs::File> const &f = Ref_ptr<>::Nil) throw();
123   L4Re::Cap_alloc *cap_alloc() throw();
124
125   int mmap2(void *start, size_t len, int prot, int flags, int fd,
126             off_t offset, void **ptr) throw();
127
128   int munmap(void *start, size_t len) throw();
129   int mremap(void *old, size_t old_sz, size_t new_sz, int flags,
130              void **new_adr) throw();
131   int mprotect(const void *a, size_t sz, int prot) throw();
132   int msync(void *addr, size_t len, int flags) throw();
133   int madvise(void *addr, size_t len, int advice) throw();
134
135   int register_file_system(L4Re::Vfs::File_system *f) throw();
136   int unregister_file_system(L4Re::Vfs::File_system *f) throw();
137   L4Re::Vfs::File_system *get_file_system(char const *fstype) throw();
138
139   void operator delete (void *) {}
140
141 private:
142   Root_mount_tree _root_mount;
143   L4Re::Core::Env_dir _root;
144   Ref_ptr<L4Re::Vfs::File> _cwd;
145   Fd_store fds;
146
147   L4Re::Vfs::File_system *_fs_registry;
148
149   l4_addr_t _annon_size;
150   l4_addr_t _annon_offset;
151   L4::Cap<L4Re::Dataspace> _annon_ds;
152
153   int alloc_ds(unsigned long size, L4::Cap<L4Re::Dataspace> *ds);
154   int alloc_anon_mem(l4_umword_t size, L4::Cap<L4Re::Dataspace> *ds,
155                      l4_addr_t *offset);
156 };
157
158 static inline bool strequal(char const *a, char const *b)
159 {
160   for (;*a && *a == *b; ++a, ++b)
161     ;
162   return *a == *b;
163 }
164
165 int
166 Vfs::register_file_system(L4Re::Vfs::File_system *f) throw()
167 {
168   using L4Re::Vfs::File_system;
169
170   if (!f)
171     return -EINVAL;
172
173   for (File_system *c = _fs_registry; c; c = c->next())
174     if (strequal(c->type(), f->type()))
175       return -EEXIST;
176
177   f->next(_fs_registry);
178   _fs_registry = f;
179
180   return 0;
181 }
182
183 int
184 Vfs::unregister_file_system(L4Re::Vfs::File_system *f) throw()
185 {
186   using L4Re::Vfs::File_system;
187
188   if (!f)
189     return -EINVAL;
190
191   File_system **p = &_fs_registry;
192
193   for (; *p; p = &(*p)->next())
194     if (*p == f)
195       {
196         *p = f->next();
197         f->next() = 0;
198         return 0;
199       }
200
201   return -ENOENT;
202 }
203
204 L4Re::Vfs::File_system *
205 Vfs::get_file_system(char const *fstype) throw()
206 {
207   bool try_dynamic = true;
208   for (;;)
209     {
210       using L4Re::Vfs::File_system;
211       for (File_system *c = _fs_registry; c; c = c->next())
212         if (strequal(c->type(), fstype))
213           return c;
214
215       if (!try_dynamic)
216         return 0;
217
218       // try to load a file system module dynamically
219       int res = Vfs_config::load_module(fstype);
220
221       if (res < 0)
222         return 0;
223
224       try_dynamic = false;
225     }
226 }
227
228 int
229 Vfs::alloc_fd(Ref_ptr<L4Re::Vfs::File> const &f) throw()
230 {
231   int fd = fds.alloc();
232   if (fd < 0)
233     return -EMFILE;
234
235   if (f)
236     fds.set(fd, f);
237
238   return fd;
239 }
240
241 Ref_ptr<L4Re::Vfs::File>
242 Vfs::free_fd(int fd) throw()
243 {
244   Ref_ptr<L4Re::Vfs::File> f = fds.get(fd);
245
246   if (!f)
247     return Ref_ptr<>::Nil;
248
249   fds.free(fd);
250   return f;
251 }
252
253
254 Ref_ptr<L4Re::Vfs::File>
255 Vfs::get_root() throw()
256 {
257   return cxx::ref_ptr(&_root);
258 }
259
260 Ref_ptr<L4Re::Vfs::File>
261 Vfs::get_cwd() throw()
262 {
263   return _cwd;
264 }
265
266 void
267 Vfs::set_cwd(Ref_ptr<L4Re::Vfs::File> const &dir) throw()
268 {
269   // FIXME: check for is dir
270   if (dir)
271     _cwd = dir;
272 }
273
274 Ref_ptr<L4Re::Vfs::File>
275 Vfs::get_file(int fd) throw()
276 {
277   return fds.get(fd);
278 }
279
280 Ref_ptr<L4Re::Vfs::File>
281 Vfs::set_fd(int fd, Ref_ptr<L4Re::Vfs::File> const &f) throw()
282 {
283   Ref_ptr<L4Re::Vfs::File> old = fds.get(fd);
284   fds.set(fd, f);
285   return old;
286 }
287
288 L4Re::Cap_alloc *
289 Vfs::cap_alloc() throw()
290 {
291   return L4Re::Core::cap_alloc();
292 }
293
294
295
296 #define GET_FILE_DBG(fd, err) \
297   Ref_ptr<L4Re::Vfs::File> fi = fds.get(fd); \
298   if (!fi)                           \
299     {                                \
300       return -err;                   \
301     }
302
303 #define GET_FILE(fd, err) \
304   Ref_ptr<L4Re::Vfs::File> fi = fds.get(fd); \
305   if (!fi)                           \
306     return -err;
307
308
309 int
310 Vfs::munmap(void *start, size_t len) L4_NOTHROW
311 {
312   using namespace L4;
313   using namespace L4Re;
314
315   int err;
316   Cap<Dataspace> ds;
317   Cap<Rm> r = Env::env()->rm();
318
319   while (1)
320     {
321       DEBUG_LOG(debug_mmap, {
322           outstring("DETACH: ");
323           outhex32(l4_addr_t(start));
324           outstring(" ");
325           outhex32(len);
326           outstring("\n");
327       });
328       err = r->detach(l4_addr_t(start), len, &ds, This_task);
329       if (err < 0)
330         return err;
331
332       switch (err & Rm::Detach_result_mask)
333         {
334         case Rm::Split_ds:
335           if (ds.is_valid())
336             ds->take();
337           return 0;
338         case Rm::Detached_ds:
339           if (ds.is_valid())
340             L4Re::Core::release_ds(ds);
341           break;
342         default:
343           break;
344         }
345
346       if (!(err & Rm::Detach_again))
347         return 0;
348     }
349 }
350
351 int
352 Vfs::alloc_ds(unsigned long size, L4::Cap<L4Re::Dataspace> *ds)
353 {
354   *ds = Vfs_config::cap_alloc.alloc<L4Re::Dataspace>();
355
356   if (!ds->is_valid())
357     return -ENOMEM;
358
359   int err;
360   if ((err = Vfs_config::allocator()->alloc(size, *ds)) < 0)
361     return err;
362
363   DEBUG_LOG(debug_mmap, {
364       outstring("ANNON DS ALLOCATED: size=");
365       outhex32(size);
366       outstring("  cap=");
367       outhex32(ds->cap());
368       outstring("\n");
369       });
370
371   return 0;
372 }
373
374 int
375 Vfs::alloc_anon_mem(l4_umword_t size, L4::Cap<L4Re::Dataspace> *ds,
376                     l4_addr_t *offset)
377 {
378 #ifdef USE_BIG_ANON_DS
379   if (!_annon_ds.is_valid() || _annon_offset + size >= _annon_size)
380     {
381       if (_annon_ds.is_valid())
382         L4Re::Core::release_ds(_annon_ds);
383
384       int err;
385       if ((err = alloc_ds(_annon_size, ds)) < 0)
386         return err;
387
388       _annon_offset = 0;
389       _annon_ds = *ds;
390     }
391   else
392     *ds = _annon_ds;
393
394   (*ds)->take();
395
396   if (_early_oom)
397     {
398       if (int err = (*ds)->allocate(_annon_offset, size))
399         return err;
400     }
401
402   *offset = _annon_offset;
403   _annon_offset += size;
404 #else
405   int err;
406   if ((err = alloc_ds(size, ds)) < 0)
407     return err;
408
409   if (_early_oom)
410     {
411       if ((err = (*ds)->allocate(0, size)))
412         return err;
413     }
414   *offset = 0;
415 #endif
416   return 0;
417 }
418
419 int
420 Vfs::mmap2(void *start, size_t len, int prot, int flags, int fd, off_t _offset,
421            void **resptr) L4_NOTHROW
422 {
423   using namespace L4Re;
424   off64_t offset = _offset << 12;
425
426   start = (void*)l4_trunc_page(l4_addr_t(start));
427   len   = l4_round_page(len);
428   l4_umword_t size = (len + L4_PAGESIZE-1) & ~(L4_PAGESIZE-1);
429
430   // special code to just reserve an area of the virtual address space
431   if (flags & 0x1000000)
432     {
433       int err;
434       L4::Cap<Rm> r = Env::env()->rm();
435       l4_addr_t area = (l4_addr_t)start;
436       err = r->reserve_area(&area, size, L4Re::Rm::Search_addr);
437       if (err < 0)
438         return err;
439       *resptr = (void*)area;
440       DEBUG_LOG(debug_mmap, {
441           outstring("MMAP reserved area: ");
442           outhex32(area);
443           outstring("  size=");
444           outhex32(size);
445           outstring("\n");
446       });
447       return 0;
448     }
449
450   L4::Cap<L4Re::Dataspace> ds;
451   l4_addr_t annon_offset = 0;
452   unsigned rm_flags = 0;
453
454   if (flags & (MAP_ANONYMOUS | MAP_PRIVATE))
455     {
456       rm_flags |= L4Re::Rm::Detach_free;
457
458       int err = alloc_anon_mem(size, &ds, &annon_offset);
459       if (err)
460         return err;
461
462       DEBUG_LOG(debug_mmap, {
463           outstring("USE ANNON MEM: ");
464           outhex32(ds.cap());
465           outstring(" offs=");
466           outhex32(annon_offset);
467           outstring("\n");
468       });
469     }
470
471   if (!(flags & MAP_ANONYMOUS))
472     {
473       Ref_ptr<L4Re::Vfs::File> fi = fds.get(fd);
474       if (!fi)
475         {
476           return -EBADF;
477         }
478
479       L4::Cap<L4Re::Dataspace> fds = fi->data_space();
480
481       if (!fds.is_valid())
482         {
483           return -EINVAL;
484         }
485
486       if (size + offset > l4_round_page(fds->size()))
487         {
488           return -EINVAL;
489         }
490
491       if (flags & MAP_PRIVATE)
492         {
493           DEBUG_LOG(debug_mmap, outstring("COW\n"););
494           ds->copy_in(annon_offset, fds, l4_trunc_page(offset), l4_round_page(size));
495           offset = annon_offset;
496         }
497       else
498         {
499           ds = fds;
500           ds->take();
501         }
502     }
503   else
504     offset = annon_offset;
505
506
507   if (!(flags & MAP_FIXED) && start == 0)
508     start = (void*)L4_PAGESIZE;
509
510   int err;
511   char *data = (char *)start;
512   L4::Cap<Rm> r = Env::env()->rm();
513   l4_addr_t overmap_area = L4_INVALID_ADDR;
514
515   if (flags & MAP_FIXED)
516     {
517       overmap_area = l4_addr_t(start);
518
519       err = r->reserve_area(&overmap_area, size);
520       if (err < 0)
521         overmap_area = L4_INVALID_ADDR;
522
523       rm_flags |= Rm::In_area;
524
525       err = munmap(start, len);
526       if (err && err != -ENOENT)
527         return err;
528     }
529
530   if (!(flags & MAP_FIXED))  rm_flags |= Rm::Search_addr;
531   if (!(prot & PROT_WRITE))  rm_flags |= Rm::Read_only;
532
533   err = r->attach(&data, size, rm_flags, ds, offset);
534
535   DEBUG_LOG(debug_mmap, {
536       outstring("  MAPPED: ");
537       outhex32(ds.cap());
538       outstring("  addr: ");
539       outhex32(l4_addr_t(data));
540       outstring("  bytes: ");
541       outhex32(size);
542       outstring("  offset: ");
543       outhex32(offset);
544       outstring("  err=");
545       outdec(err);
546       outstring("\n");
547   });
548
549
550   if (overmap_area != L4_INVALID_ADDR)
551     r->free_area(overmap_area);
552
553   if (err < 0)
554     return err;
555
556
557   if (start && !data)
558     return -EINVAL;
559
560   *resptr = data;
561
562   return 0;
563 }
564
565 namespace {
566   class Auto_area
567   {
568   public:
569     L4::Cap<L4Re::Rm> r;
570     l4_addr_t a;
571
572     explicit Auto_area(L4::Cap<L4Re::Rm> r, l4_addr_t a = L4_INVALID_ADDR)
573     : r(r), a(a) {}
574
575     int reserve(l4_addr_t _a, l4_size_t sz, unsigned flags)
576     {
577       a = _a;
578       int e = r->reserve_area(&a, sz, flags);
579       if (e)
580         a = L4_INVALID_ADDR;
581       return e;
582     }
583
584     void free()
585     {
586       if (a != L4_INVALID_ADDR)
587         {
588           r->free_area(a);
589           a = L4_INVALID_ADDR;
590         }
591     }
592
593     ~Auto_area() { free(); }
594   };
595 }
596
597 int
598 Vfs::mremap(void *old_addr, size_t old_size, size_t new_size, int flags,
599             void **new_addr) L4_NOTHROW
600 {
601   using namespace L4Re;
602
603   if (flags & MREMAP_FIXED && !(flags & MREMAP_MAYMOVE))
604     return -EINVAL;
605
606   L4::Cap<Rm> r = Env::env()->rm();
607
608   // sanitize input parameters to multiples of pages
609   l4_addr_t oa = l4_trunc_page((l4_addr_t)old_addr);
610   old_size = l4_round_page(old_size);
611   new_size = l4_round_page(new_size);
612
613   l4_addr_t na;
614
615   if (new_size < old_size)
616     {
617       *new_addr = old_addr;
618       return munmap((void*)(oa + new_size), old_size - new_size);
619     }
620
621   if (new_size == old_size)
622     {
623       *new_addr = old_addr;
624       return 0;
625     }
626
627   Auto_area area(r);
628
629   if (!(flags & MREMAP_FIXED))
630     na = oa;
631   else
632     na = l4_trunc_page((l4_addr_t)new_addr);
633
634   int err;
635
636   // check if the current virtual memory area can be expanded
637   err = area.reserve(oa, new_size, 0);
638   if (err)
639     return err;
640
641   l4_addr_t ta = oa + old_size;
642   unsigned long ts = new_size - old_size;
643   l4_addr_t toffs;
644   unsigned tflags;
645   L4::Cap<L4Re::Dataspace> tds;
646
647   err = r->find(&ta, &ts, &toffs, &tflags, &tds);
648
649   // there is enough space to expand the mapping in place
650   if (!(err == -ENOENT || (err == 0 && (tflags & Rm::In_area))))
651     {
652       if ((flags & (MREMAP_FIXED | MREMAP_MAYMOVE)) != MREMAP_MAYMOVE)
653         return -EINVAL;
654
655       // free our old reserved area, used for blocking the old memory region
656       area.free();
657
658       // move
659       err = area.reserve(0, new_size, Rm::Search_addr);
660       if (err)
661         return err;
662
663       na = area.a;
664
665       // move all the old regions to the new place ...
666       Auto_area block_area(r);
667       err = block_area.reserve(oa, old_size, 0);
668       if (err)
669         return err;
670
671       while (1)
672         {
673           ta = oa;
674           ts = old_size;
675
676           err = r->find(&ta, &ts, &toffs, &tflags, &tds);
677           if (err == -ENOENT || (err == 0 && (tflags & Rm::In_area)))
678             break;
679
680           if (err)
681             return err;
682
683           if (ta < oa)
684             {
685               toffs += oa - ta;
686               ts -= oa - ta;
687               ta = oa;
688             }
689
690           l4_addr_t n = na + (ta - oa);
691           unsigned long max_s = old_size - (ta - oa);
692
693           if (ts > max_s)
694             ts = max_s;
695
696           err = r->attach(&n, ts, tflags | Rm::In_area, tds, toffs);
697           if (err)
698             return err;
699
700           tds->take();
701
702           err = r->detach(ta, ts, &tds, This_task);
703           if (err < 0)
704             return err;
705
706           switch (err & Rm::Detach_result_mask)
707             {
708             case Rm::Split_ds:
709               if (tds.is_valid())
710                 tds->take();
711               break;
712             case Rm::Detached_ds:
713               if (tds.is_valid())
714                 L4Re::Core::release_ds(tds);
715               break;
716             default:
717               break;
718             }
719         }
720     }
721
722   err = alloc_anon_mem(new_size - old_size, &tds, &toffs);
723   if (err)
724     return err;
725
726   *new_addr = (void *)na;
727   na = na + old_size;
728   err = r->attach(&na, new_size - old_size, Rm::In_area, tds, toffs);
729
730   return err;
731 }
732
733 int
734 Vfs::mprotect(const void *a, size_t sz, int prot) L4_NOTHROW
735 {
736   (void)a;
737   (void)sz;
738   return (prot & PROT_WRITE) ? -1 : 0;
739 }
740
741 int
742 Vfs::msync(void *, size_t, int) L4_NOTHROW
743 { return 0; }
744
745 int
746 Vfs::madvise(void *, size_t, int) L4_NOTHROW
747 { return 0; }
748
749 static Vfs vfs __attribute__((init_priority(1000)));
750
751 }
752
753 //L4Re::Vfs::Ops *__ldso_posix_vfs_ops = &vfs;
754 void *__rtld_l4re_env_posix_vfs_ops = &vfs;
755 extern void *l4re_env_posix_vfs_ops __attribute__((alias("__rtld_l4re_env_posix_vfs_ops"), visibility("default")));
756
757
758 #undef DEBUG_LOG
759 #undef GET_FILE_DBG
760 #undef GET_FILE
761