]> 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 // for internal Vcon_streams we want to have a placement new operator, so
73 // inherit and add one
74 class Std_stream : public L4Re::Core::Vcon_stream
75 {
76 public:
77   inline void *operator new (size_t, void *p) throw() { return p; }
78   Std_stream(L4::Cap<L4::Vcon> c) : L4Re::Core::Vcon_stream(c) {}
79 };
80
81 Fd_store::Fd_store() throw()
82 {
83   // use this strange way to prevent deletion of the stdio object
84   // this depends on Fd_store to being a singleton !!!
85   static char m[sizeof(Std_stream)] __attribute__((aligned(sizeof(long))));
86   Std_stream *s = new (m) Std_stream(L4Re::Env::env()->log());
87   // make sure that we never delete the static io stream thing
88   s->add_ref();
89   set(0, cxx::ref_ptr(s)); // stdin
90   set(1, cxx::ref_ptr(s)); // stdout
91   set(2, cxx::ref_ptr(s)); // stderr
92 }
93
94 class Root_mount_tree : public L4Re::Vfs::Mount_tree
95 {
96 public:
97   Root_mount_tree() : L4Re::Vfs::Mount_tree(0) {}
98   void operator delete (void *) {}
99 };
100
101 class Vfs : public L4Re::Vfs::Ops
102 {
103 private:
104   bool _early_oom;
105
106 public:
107   void *operator new (size_t, void *p) throw() { return p; }
108   Vfs()
109   : _early_oom(true), _root_mount(), _root(L4Re::Env::env()),
110     _annon_size(0x10000000)
111   {
112     _root_mount.add_ref();
113     _root.add_ref();
114     _root_mount.mount(cxx::ref_ptr(&_root));
115     _cwd = &_root;
116
117 #if 0
118     Ref_ptr<L4Re::Vfs::File> rom;
119     _root.openat("rom", 0, 0, &rom);
120
121     _root_mount.create_tree("lib/foo", rom);
122
123     _root.openat("lib", 0, 0, &_cwd);
124
125 #endif
126   }
127
128   int alloc_fd(Ref_ptr<L4Re::Vfs::File> const &f) throw();
129   Ref_ptr<L4Re::Vfs::File> free_fd(int fd) throw();
130   Ref_ptr<L4Re::Vfs::File> get_root() throw();
131   Ref_ptr<L4Re::Vfs::File> get_cwd() throw();
132   void set_cwd(Ref_ptr<L4Re::Vfs::File> const &dir) throw();
133   Ref_ptr<L4Re::Vfs::File> get_file(int fd) throw();
134   Ref_ptr<L4Re::Vfs::File> set_fd(int fd, Ref_ptr<L4Re::Vfs::File> const &f = Ref_ptr<>::Nil) throw();
135   L4Re::Cap_alloc *cap_alloc() throw();
136
137   int mmap2(void *start, size_t len, int prot, int flags, int fd,
138             off_t offset, void **ptr) throw();
139
140   int munmap(void *start, size_t len) throw();
141   int mremap(void *old, size_t old_sz, size_t new_sz, int flags,
142              void **new_adr) throw();
143   int mprotect(const void *a, size_t sz, int prot) throw();
144   int msync(void *addr, size_t len, int flags) throw();
145   int madvise(void *addr, size_t len, int advice) throw();
146
147   int register_file_system(L4Re::Vfs::File_system *f) throw();
148   int unregister_file_system(L4Re::Vfs::File_system *f) throw();
149   L4Re::Vfs::File_system *get_file_system(char const *fstype) throw();
150
151   void operator delete (void *) {}
152
153 private:
154   Root_mount_tree _root_mount;
155   L4Re::Core::Env_dir _root;
156   Ref_ptr<L4Re::Vfs::File> _cwd;
157   Fd_store fds;
158
159   L4Re::Vfs::File_system *_fs_registry;
160
161   l4_addr_t _annon_size;
162   l4_addr_t _annon_offset;
163   L4::Cap<L4Re::Dataspace> _annon_ds;
164
165   int alloc_ds(unsigned long size, L4::Cap<L4Re::Dataspace> *ds);
166   int alloc_anon_mem(l4_umword_t size, L4::Cap<L4Re::Dataspace> *ds,
167                      l4_addr_t *offset);
168 };
169
170 static inline bool strequal(char const *a, char const *b)
171 {
172   for (;*a && *a == *b; ++a, ++b)
173     ;
174   return *a == *b;
175 }
176
177 int
178 Vfs::register_file_system(L4Re::Vfs::File_system *f) throw()
179 {
180   using L4Re::Vfs::File_system;
181
182   if (!f)
183     return -EINVAL;
184
185   for (File_system *c = _fs_registry; c; c = c->next())
186     if (strequal(c->type(), f->type()))
187       return -EEXIST;
188
189   f->next(_fs_registry);
190   _fs_registry = f;
191
192   return 0;
193 }
194
195 int
196 Vfs::unregister_file_system(L4Re::Vfs::File_system *f) throw()
197 {
198   using L4Re::Vfs::File_system;
199
200   if (!f)
201     return -EINVAL;
202
203   File_system **p = &_fs_registry;
204
205   for (; *p; p = &(*p)->next())
206     if (*p == f)
207       {
208         *p = f->next();
209         f->next() = 0;
210         return 0;
211       }
212
213   return -ENOENT;
214 }
215
216 L4Re::Vfs::File_system *
217 Vfs::get_file_system(char const *fstype) throw()
218 {
219   bool try_dynamic = true;
220   for (;;)
221     {
222       using L4Re::Vfs::File_system;
223       for (File_system *c = _fs_registry; c; c = c->next())
224         if (strequal(c->type(), fstype))
225           return c;
226
227       if (!try_dynamic)
228         return 0;
229
230       // try to load a file system module dynamically
231       int res = Vfs_config::load_module(fstype);
232
233       if (res < 0)
234         return 0;
235
236       try_dynamic = false;
237     }
238 }
239
240 int
241 Vfs::alloc_fd(Ref_ptr<L4Re::Vfs::File> const &f) throw()
242 {
243   int fd = fds.alloc();
244   if (fd < 0)
245     return -EMFILE;
246
247   if (f)
248     fds.set(fd, f);
249
250   return fd;
251 }
252
253 Ref_ptr<L4Re::Vfs::File>
254 Vfs::free_fd(int fd) throw()
255 {
256   Ref_ptr<L4Re::Vfs::File> f = fds.get(fd);
257
258   if (!f)
259     return Ref_ptr<>::Nil;
260
261   fds.free(fd);
262   return f;
263 }
264
265
266 Ref_ptr<L4Re::Vfs::File>
267 Vfs::get_root() throw()
268 {
269   return cxx::ref_ptr(&_root);
270 }
271
272 Ref_ptr<L4Re::Vfs::File>
273 Vfs::get_cwd() throw()
274 {
275   return _cwd;
276 }
277
278 void
279 Vfs::set_cwd(Ref_ptr<L4Re::Vfs::File> const &dir) throw()
280 {
281   // FIXME: check for is dir
282   if (dir)
283     _cwd = dir;
284 }
285
286 Ref_ptr<L4Re::Vfs::File>
287 Vfs::get_file(int fd) throw()
288 {
289   return fds.get(fd);
290 }
291
292 Ref_ptr<L4Re::Vfs::File>
293 Vfs::set_fd(int fd, Ref_ptr<L4Re::Vfs::File> const &f) throw()
294 {
295   Ref_ptr<L4Re::Vfs::File> old = fds.get(fd);
296   fds.set(fd, f);
297   return old;
298 }
299
300 L4Re::Cap_alloc *
301 Vfs::cap_alloc() throw()
302 {
303   return L4Re::Core::cap_alloc();
304 }
305
306
307
308 #define GET_FILE_DBG(fd, err) \
309   Ref_ptr<L4Re::Vfs::File> fi = fds.get(fd); \
310   if (!fi)                           \
311     {                                \
312       return -err;                   \
313     }
314
315 #define GET_FILE(fd, err) \
316   Ref_ptr<L4Re::Vfs::File> fi = fds.get(fd); \
317   if (!fi)                           \
318     return -err;
319
320
321 int
322 Vfs::munmap(void *start, size_t len) L4_NOTHROW
323 {
324   using namespace L4;
325   using namespace L4Re;
326
327   int err;
328   Cap<Dataspace> ds;
329   Cap<Rm> r = Env::env()->rm();
330
331   while (1)
332     {
333       DEBUG_LOG(debug_mmap, {
334           outstring("DETACH: ");
335           outhex32(l4_addr_t(start));
336           outstring(" ");
337           outhex32(len);
338           outstring("\n");
339       });
340       err = r->detach(l4_addr_t(start), len, &ds, This_task);
341       if (err < 0)
342         return err;
343
344       switch (err & Rm::Detach_result_mask)
345         {
346         case Rm::Split_ds:
347           if (ds.is_valid())
348             ds->take();
349           return 0;
350         case Rm::Detached_ds:
351           if (ds.is_valid())
352             L4Re::Core::release_ds(ds);
353           break;
354         default:
355           break;
356         }
357
358       if (!(err & Rm::Detach_again))
359         return 0;
360     }
361 }
362
363 int
364 Vfs::alloc_ds(unsigned long size, L4::Cap<L4Re::Dataspace> *ds)
365 {
366   *ds = Vfs_config::cap_alloc.alloc<L4Re::Dataspace>();
367
368   if (!ds->is_valid())
369     return -ENOMEM;
370
371   int err;
372   if ((err = Vfs_config::allocator()->alloc(size, *ds)) < 0)
373     return err;
374
375   DEBUG_LOG(debug_mmap, {
376       outstring("ANNON DS ALLOCATED: size=");
377       outhex32(size);
378       outstring("  cap=");
379       outhex32(ds->cap());
380       outstring("\n");
381       });
382
383   return 0;
384 }
385
386 int
387 Vfs::alloc_anon_mem(l4_umword_t size, L4::Cap<L4Re::Dataspace> *ds,
388                     l4_addr_t *offset)
389 {
390 #ifdef USE_BIG_ANON_DS
391   if (!_annon_ds.is_valid() || _annon_offset + size >= _annon_size)
392     {
393       if (_annon_ds.is_valid())
394         L4Re::Core::release_ds(_annon_ds);
395
396       int err;
397       if ((err = alloc_ds(_annon_size, ds)) < 0)
398         return err;
399
400       _annon_offset = 0;
401       _annon_ds = *ds;
402     }
403   else
404     *ds = _annon_ds;
405
406   (*ds)->take();
407
408   if (_early_oom)
409     {
410       if (int err = (*ds)->allocate(_annon_offset, size))
411         return err;
412     }
413
414   *offset = _annon_offset;
415   _annon_offset += size;
416 #else
417   int err;
418   if ((err = alloc_ds(size, ds)) < 0)
419     return err;
420
421   if (_early_oom)
422     {
423       if ((err = (*ds)->allocate(0, size)))
424         return err;
425     }
426   *offset = 0;
427 #endif
428   return 0;
429 }
430
431 int
432 Vfs::mmap2(void *start, size_t len, int prot, int flags, int fd, off_t _offset,
433            void **resptr) L4_NOTHROW
434 {
435   using namespace L4Re;
436   off64_t offset = _offset << 12;
437
438   start = (void*)l4_trunc_page(l4_addr_t(start));
439   len   = l4_round_page(len);
440   l4_umword_t size = (len + L4_PAGESIZE-1) & ~(L4_PAGESIZE-1);
441
442   // special code to just reserve an area of the virtual address space
443   if (flags & 0x1000000)
444     {
445       int err;
446       L4::Cap<Rm> r = Env::env()->rm();
447       l4_addr_t area = (l4_addr_t)start;
448       err = r->reserve_area(&area, size, L4Re::Rm::Search_addr);
449       if (err < 0)
450         return err;
451       *resptr = (void*)area;
452       DEBUG_LOG(debug_mmap, {
453           outstring("MMAP reserved area: ");
454           outhex32(area);
455           outstring("  size=");
456           outhex32(size);
457           outstring("\n");
458       });
459       return 0;
460     }
461
462   L4::Cap<L4Re::Dataspace> ds;
463   l4_addr_t annon_offset = 0;
464   unsigned rm_flags = 0;
465
466   if (flags & (MAP_ANONYMOUS | MAP_PRIVATE))
467     {
468       rm_flags |= L4Re::Rm::Detach_free;
469
470       int err = alloc_anon_mem(size, &ds, &annon_offset);
471       if (err)
472         return err;
473
474       DEBUG_LOG(debug_mmap, {
475           outstring("USE ANNON MEM: ");
476           outhex32(ds.cap());
477           outstring(" offs=");
478           outhex32(annon_offset);
479           outstring("\n");
480       });
481     }
482
483   if (!(flags & MAP_ANONYMOUS))
484     {
485       Ref_ptr<L4Re::Vfs::File> fi = fds.get(fd);
486       if (!fi)
487         {
488           return -EBADF;
489         }
490
491       L4::Cap<L4Re::Dataspace> fds = fi->data_space();
492
493       if (!fds.is_valid())
494         {
495           return -EINVAL;
496         }
497
498       if (size + offset > l4_round_page(fds->size()))
499         {
500           return -EINVAL;
501         }
502
503       if (flags & MAP_PRIVATE)
504         {
505           DEBUG_LOG(debug_mmap, outstring("COW\n"););
506           ds->copy_in(annon_offset, fds, l4_trunc_page(offset), l4_round_page(size));
507           offset = annon_offset;
508         }
509       else
510         {
511           ds = fds;
512           ds->take();
513         }
514     }
515   else
516     offset = annon_offset;
517
518
519   if (!(flags & MAP_FIXED) && start == 0)
520     start = (void*)L4_PAGESIZE;
521
522   int err;
523   char *data = (char *)start;
524   L4::Cap<Rm> r = Env::env()->rm();
525   l4_addr_t overmap_area = L4_INVALID_ADDR;
526
527   if (flags & MAP_FIXED)
528     {
529       overmap_area = l4_addr_t(start);
530
531       err = r->reserve_area(&overmap_area, size);
532       if (err < 0)
533         overmap_area = L4_INVALID_ADDR;
534
535       rm_flags |= Rm::In_area;
536
537       err = munmap(start, len);
538       if (err && err != -ENOENT)
539         return err;
540     }
541
542   if (!(flags & MAP_FIXED))  rm_flags |= Rm::Search_addr;
543   if (!(prot & PROT_WRITE))  rm_flags |= Rm::Read_only;
544
545   err = r->attach(&data, size, rm_flags, ds, offset);
546
547   DEBUG_LOG(debug_mmap, {
548       outstring("  MAPPED: ");
549       outhex32(ds.cap());
550       outstring("  addr: ");
551       outhex32(l4_addr_t(data));
552       outstring("  bytes: ");
553       outhex32(size);
554       outstring("  offset: ");
555       outhex32(offset);
556       outstring("  err=");
557       outdec(err);
558       outstring("\n");
559   });
560
561
562   if (overmap_area != L4_INVALID_ADDR)
563     r->free_area(overmap_area);
564
565   if (err < 0)
566     return err;
567
568
569   if (start && !data)
570     return -EINVAL;
571
572   *resptr = data;
573
574   return 0;
575 }
576
577 namespace {
578   class Auto_area
579   {
580   public:
581     L4::Cap<L4Re::Rm> r;
582     l4_addr_t a;
583
584     explicit Auto_area(L4::Cap<L4Re::Rm> r, l4_addr_t a = L4_INVALID_ADDR)
585     : r(r), a(a) {}
586
587     int reserve(l4_addr_t _a, l4_size_t sz, unsigned flags)
588     {
589       a = _a;
590       int e = r->reserve_area(&a, sz, flags);
591       if (e)
592         a = L4_INVALID_ADDR;
593       return e;
594     }
595
596     void free()
597     {
598       if (a != L4_INVALID_ADDR)
599         {
600           r->free_area(a);
601           a = L4_INVALID_ADDR;
602         }
603     }
604
605     ~Auto_area() { free(); }
606   };
607 }
608
609 int
610 Vfs::mremap(void *old_addr, size_t old_size, size_t new_size, int flags,
611             void **new_addr) L4_NOTHROW
612 {
613   using namespace L4Re;
614
615   if (flags & MREMAP_FIXED && !(flags & MREMAP_MAYMOVE))
616     return -EINVAL;
617
618   L4::Cap<Rm> r = Env::env()->rm();
619
620   // sanitize input parameters to multiples of pages
621   l4_addr_t oa = l4_trunc_page((l4_addr_t)old_addr);
622   old_size = l4_round_page(old_size);
623   new_size = l4_round_page(new_size);
624
625   l4_addr_t na;
626
627   if (new_size < old_size)
628     {
629       *new_addr = old_addr;
630       return munmap((void*)(oa + new_size), old_size - new_size);
631     }
632
633   if (new_size == old_size)
634     {
635       *new_addr = old_addr;
636       return 0;
637     }
638
639   Auto_area area(r);
640
641   if (!(flags & MREMAP_FIXED))
642     na = oa;
643   else
644     na = l4_trunc_page((l4_addr_t)new_addr);
645
646   int err;
647
648   // check if the current virtual memory area can be expanded
649   err = area.reserve(oa, new_size, 0);
650   if (err)
651     return err;
652
653   l4_addr_t ta = oa + old_size;
654   unsigned long ts = new_size - old_size;
655   l4_addr_t toffs;
656   unsigned tflags;
657   L4::Cap<L4Re::Dataspace> tds;
658
659   err = r->find(&ta, &ts, &toffs, &tflags, &tds);
660
661   // there is enough space to expand the mapping in place
662   if (!(err == -ENOENT || (err == 0 && (tflags & Rm::In_area))))
663     {
664       if ((flags & (MREMAP_FIXED | MREMAP_MAYMOVE)) != MREMAP_MAYMOVE)
665         return -EINVAL;
666
667       // free our old reserved area, used for blocking the old memory region
668       area.free();
669
670       // move
671       err = area.reserve(0, new_size, Rm::Search_addr);
672       if (err)
673         return err;
674
675       na = area.a;
676
677       // move all the old regions to the new place ...
678       Auto_area block_area(r);
679       err = block_area.reserve(oa, old_size, 0);
680       if (err)
681         return err;
682
683       while (1)
684         {
685           ta = oa;
686           ts = old_size;
687
688           err = r->find(&ta, &ts, &toffs, &tflags, &tds);
689           if (err == -ENOENT || (err == 0 && (tflags & Rm::In_area)))
690             break;
691
692           if (err)
693             return err;
694
695           if (ta < oa)
696             {
697               toffs += oa - ta;
698               ts -= oa - ta;
699               ta = oa;
700             }
701
702           l4_addr_t n = na + (ta - oa);
703           unsigned long max_s = old_size - (ta - oa);
704
705           if (ts > max_s)
706             ts = max_s;
707
708           err = r->attach(&n, ts, tflags | Rm::In_area, tds, toffs);
709           if (err)
710             return err;
711
712           tds->take();
713
714           err = r->detach(ta, ts, &tds, This_task);
715           if (err < 0)
716             return err;
717
718           switch (err & Rm::Detach_result_mask)
719             {
720             case Rm::Split_ds:
721               if (tds.is_valid())
722                 tds->take();
723               break;
724             case Rm::Detached_ds:
725               if (tds.is_valid())
726                 L4Re::Core::release_ds(tds);
727               break;
728             default:
729               break;
730             }
731         }
732     }
733
734   err = alloc_anon_mem(new_size - old_size, &tds, &toffs);
735   if (err)
736     return err;
737
738   *new_addr = (void *)na;
739   na = na + old_size;
740   err = r->attach(&na, new_size - old_size, Rm::In_area, tds, toffs);
741
742   return err;
743 }
744
745 int
746 Vfs::mprotect(const void *a, size_t sz, int prot) L4_NOTHROW
747 {
748   (void)a;
749   (void)sz;
750   return (prot & PROT_WRITE) ? -1 : 0;
751 }
752
753 int
754 Vfs::msync(void *, size_t, int) L4_NOTHROW
755 { return 0; }
756
757 int
758 Vfs::madvise(void *, size_t, int) L4_NOTHROW
759 { return 0; }
760
761 namespace {
762 // use this container construct here to prevent a destructor for
763 // our VFS main object is ever called!
764 static char vfs_cnt[sizeof(Vfs)] __attribute__((aligned(sizeof(long))));
765 static void init_vfs() { new (vfs_cnt) Vfs(); }
766 L4_DECLARE_CONSTRUCTOR(init_vfs, INIT_PRIO_EARLY);
767 }
768
769 }
770
771 //L4Re::Vfs::Ops *__ldso_posix_vfs_ops = &vfs;
772 void *__rtld_l4re_env_posix_vfs_ops = &vfs_cnt;
773 extern void *l4re_env_posix_vfs_ops __attribute__((alias("__rtld_l4re_env_posix_vfs_ops"), visibility("default")));
774
775
776 #undef DEBUG_LOG
777 #undef GET_FILE_DBG
778 #undef GET_FILE
779