diff --git a/repos/libports/src/lib/libc/internal/init.h b/repos/libports/src/lib/libc/internal/init.h index b833fd5be8..91d5cde8ec 100644 --- a/repos/libports/src/lib/libc/internal/init.h +++ b/repos/libports/src/lib/libc/internal/init.h @@ -66,7 +66,7 @@ namespace Libc { /** * Virtual file system */ - void init_vfs_plugin(Monitor &); + void init_vfs_plugin(Monitor &, Genode::Region_map &); void init_file_operations(Cwd &); /** diff --git a/repos/libports/src/lib/libc/kernel.cc b/repos/libports/src/lib/libc/kernel.cc index 59177bfe5d..b1bca7c80f 100644 --- a/repos/libports/src/lib/libc/kernel.cc +++ b/repos/libports/src/lib/libc/kernel.cc @@ -534,7 +534,7 @@ Libc::Kernel::Kernel(Genode::Env &env, Genode::Allocator &heap) *file_descriptor_allocator()); init_plugin(*this); init_sleep(*this); - init_vfs_plugin(*this); + init_vfs_plugin(*this, _env.rm()); init_file_operations(*this); init_time(*this, *this); init_select(*this, *this, *this, _signal); diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc index fef94b38bb..35a2e8c681 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.cc +++ b/repos/libports/src/lib/libc/vfs_plugin.cc @@ -46,12 +46,14 @@ #include -static Libc::Monitor *_monitor_ptr; +static Libc::Monitor *_monitor_ptr; +static Genode::Region_map *_region_map_ptr; -void Libc::init_vfs_plugin(Monitor &monitor) +void Libc::init_vfs_plugin(Monitor &monitor, Genode::Region_map &rm) { _monitor_ptr = &monitor; + _region_map_ptr = &rm; } @@ -64,6 +66,15 @@ static Libc::Monitor & monitor() } +static Genode::Region_map ®ion_map() +{ + struct Missing_call_of_init_vfs_plugin : Genode::Exception { }; + if (!_region_map_ptr) + throw Missing_call_of_init_vfs_plugin(); + return *_region_map_ptr; +} + + static Genode::Mutex &vfs_mutex() { static Genode::Mutex mutex; @@ -1634,7 +1645,7 @@ int Libc::Vfs_plugin::rename(char const *from_path, char const *to_path) void *Libc::Vfs_plugin::mmap(void *addr_in, ::size_t length, int prot, int flags, File_descriptor *fd, ::off_t offset) { - if (prot != PROT_READ && !(prot == (PROT_READ | PROT_WRITE) && flags == MAP_PRIVATE)) { + if ((prot != PROT_READ) && (prot != (PROT_READ | PROT_WRITE))) { error("mmap for prot=", Hex(prot), " not supported"); errno = EACCES; return (void *)-1; @@ -1646,34 +1657,57 @@ void *Libc::Vfs_plugin::mmap(void *addr_in, ::size_t length, int prot, int flags return (void *)-1; } - /* - * XXX attempt to obtain memory mapping via - * 'Vfs::Directory_service::dataspace'. - */ + void *addr = nullptr; - void *addr = mem_alloc()->alloc(length, PAGE_SHIFT); - if (addr == (void *)-1) { - errno = ENOMEM; - return (void *)-1; - } + if (flags & MAP_PRIVATE) { - /* copy variables for complete read */ - size_t read_remain = length; - size_t read_offset = offset; - char *read_addr = (char *)addr; + /* + * XXX attempt to obtain memory mapping via + * 'Vfs::Directory_service::dataspace'. + */ - while (read_remain > 0) { - ssize_t length_read = ::pread(fd->libc_fd, read_addr, read_remain, read_offset); - if (length_read < 0) { /* error */ - error("mmap could not obtain file content"); - ::munmap(addr, length); - errno = EACCES; + addr = mem_alloc()->alloc(length, PAGE_SHIFT); + if (addr == (void *)-1) { + error("mmap out of memory"); + errno = ENOMEM; return (void *)-1; - } else if (length_read == 0) /* EOF */ - break; /* done (length can legally be greater than the file length) */ - read_remain -= length_read; - read_offset += length_read; - read_addr += length_read; + } + + /* copy variables for complete read */ + size_t read_remain = length; + size_t read_offset = offset; + char *read_addr = (char *)addr; + + while (read_remain > 0) { + ssize_t length_read = ::pread(fd->libc_fd, read_addr, read_remain, read_offset); + if (length_read < 0) { /* error */ + error("mmap could not obtain file content"); + ::munmap(addr, length); + errno = EACCES; + return (void *)-1; + } else if (length_read == 0) /* EOF */ + break; /* done (length can legally be greater than the file length) */ + read_remain -= length_read; + read_offset += length_read; + read_addr += length_read; + } + } else if (flags & MAP_SHARED) { + + Genode::Dataspace_capability ds_cap; + + Mutex::Guard guard(vfs_mutex()); + monitor().monitor(vfs_mutex(), [&] { + ds_cap = _root_fs.dataspace(fd->fd_path); + return Fn::COMPLETE; + }); + + if (!ds_cap.valid()) { + Genode::error("mmap got invalid dataspace capability"); + errno = ENODEV; + return (void*)-1; + } + + addr = region_map().attach(ds_cap, length, offset); } return addr; @@ -1682,7 +1716,11 @@ void *Libc::Vfs_plugin::mmap(void *addr_in, ::size_t length, int prot, int flags int Libc::Vfs_plugin::munmap(void *addr, ::size_t) { - mem_alloc()->free(addr); + if (mem_alloc()->size_at(addr) > 0) + mem_alloc()->free(addr); + else + region_map().detach(addr); + return 0; }