diff --git a/base-linux/include/linux_dataspace/client.h b/base-linux/include/linux_dataspace/client.h index 658bcddf11..bcca8099dd 100644 --- a/base-linux/include/linux_dataspace/client.h +++ b/base-linux/include/linux_dataspace/client.h @@ -40,7 +40,8 @@ namespace Genode { ** Linux-specific dataspace interface ** ****************************************/ - Filename fname() { return call(); } + Filename fname() { return call(); } + Untyped_capability fd() { return call(); } }; } diff --git a/base-linux/include/linux_dataspace/linux_dataspace.h b/base-linux/include/linux_dataspace/linux_dataspace.h index 3bf3a90e5e..f50989805f 100644 --- a/base-linux/include/linux_dataspace/linux_dataspace.h +++ b/base-linux/include/linux_dataspace/linux_dataspace.h @@ -24,23 +24,31 @@ namespace Genode { struct Linux_dataspace : Dataspace { enum { FNAME_LEN = 32 }; - struct Filename { char buf[FNAME_LEN]; }; virtual ~Linux_dataspace() { } /** * Request name of file that represents the dataspace on Linux + * + * This function is used for calling execve on files passed as ROM + * dataspaces. */ virtual Filename fname() = 0; + /** + * Request file descriptor of the dataspace + */ + virtual Untyped_capability fd() = 0; + /********************* ** RPC declaration ** *********************/ GENODE_RPC(Rpc_fname, Filename, fname); - GENODE_RPC_INTERFACE_INHERIT(Dataspace, Rpc_fname); + GENODE_RPC(Rpc_fd, Untyped_capability, fd); + GENODE_RPC_INTERFACE_INHERIT(Dataspace, Rpc_fname, Rpc_fd); }; } diff --git a/base-linux/src/base/env/rm_session_mmap.cc b/base-linux/src/base/env/rm_session_mmap.cc index 83d12156cf..af134a1b1b 100644 --- a/base-linux/src/base/env/rm_session_mmap.cc +++ b/base-linux/src/base/env/rm_session_mmap.cc @@ -46,29 +46,30 @@ static void *map_local(Dataspace_capability ds, Genode::size_t size, addr_t offset, bool use_local_addr, addr_t local_addr, bool executable) { - Linux_dataspace::Filename fname = Linux_dataspace_client(ds).fname(); - fname.buf[sizeof(fname.buf) - 1] = 0; + int const fd = Linux_dataspace_client(ds).fd().dst().socket; + bool const writable = Dataspace_client(ds).writable(); - bool writable = Dataspace_client(ds).writable(); - int fd = lx_open(fname.buf, (writable ? O_RDWR : O_RDONLY) | LX_O_CLOEXEC); - if (fd < 0) { - PERR("map_local: Could not open file \"%s\"", fname.buf); - throw Rm_session::Invalid_dataspace(); - } - - int flags = MAP_SHARED | (use_local_addr ? MAP_FIXED : 0); - int prot = PROT_READ | (writable ? PROT_WRITE : 0) | (executable ? PROT_EXEC : 0); - void *addr = lx_mmap(use_local_addr ? (void*)local_addr : 0, size, - prot, flags, fd, offset); + int const flags = MAP_SHARED | (use_local_addr ? MAP_FIXED : 0); + int const prot = PROT_READ + | (writable ? PROT_WRITE : 0) + | (executable ? PROT_EXEC : 0); + void * const addr_in = use_local_addr ? (void*)local_addr : 0; + void * const addr_out = lx_mmap(addr_in, size, prot, flags, fd, offset); + /* + * We can close the file after calling mmap. The Linux kernel will still + * keep the file mapped. By immediately closing the file descriptor, we + * won't need to keep track of dataspace file descriptors within the + * process. + */ lx_close(fd); - if (((long)addr < 0) && ((long)addr > -4095)) { - PERR("map_local: return value of mmap is %ld", (long)addr); + if (((long)addr_out < 0) && ((long)addr_out > -4095)) { + PERR("map_local: return value of mmap is %ld", (long)addr_out); throw Rm_session::Region_conflict(); } - return addr; + return addr_out; } diff --git a/base-linux/src/core/include/dataspace_component.h b/base-linux/src/core/include/dataspace_component.h index 836a39dd65..200184ed7c 100644 --- a/base-linux/src/core/include/dataspace_component.h +++ b/base-linux/src/core/include/dataspace_component.h @@ -39,6 +39,7 @@ namespace Genode { size_t _size; /* size of dataspace in bytes */ addr_t _addr; /* meaningless on linux */ Filename _fname; /* filename for mmap */ + int _fd; /* file descriptor */ bool _writable; /* false if read-only */ /* Holds the dataspace owner if a distinction between owner and @@ -53,14 +54,14 @@ namespace Genode { Dataspace_component(size_t size, addr_t addr, bool /* write_combined */, bool writable, Dataspace_owner * owner) - : _size(size), _addr(addr), _writable(writable), + : _size(size), _addr(addr), _fd(-1), _writable(writable), _owner(owner) { } /** * Default constructor returns invalid dataspace */ - Dataspace_component() : _size(0), _addr(0), _writable(false), - _owner(0) { } + Dataspace_component() + : _size(0), _addr(0), _fd(-1), _writable(false), _owner(0) { } /** * This constructor is only provided for compatibility @@ -69,20 +70,29 @@ namespace Genode { Dataspace_component(size_t size, addr_t core_local_addr, addr_t phys_addr, bool write_combined, bool writable, Dataspace_owner * _owner) - : _size(size), _addr(phys_addr), _owner(_owner) + : + _size(size), _addr(phys_addr), _fd(-1), _owner(_owner) { PWRN("Should only be used for IOMEM and not within Linux."); + _fname.buf[0] = 0; } /** - * Define/request corresponding filename of dataspace + * Define corresponding filename of dataspace * - * To use dataspaces as shared memory objects on Linux, we have to - * assign a file to each dataspace. This way, multiple Linux process - * can mmap this file. + * The file name is only relevant for ROM dataspaces that should + * be executed via execve. */ void fname(const char *fname) { strncpy(_fname.buf, fname, sizeof(_fname.buf)); } + /** + * Assign file descriptor to dataspace + * + * The file descriptor assigned to the dataspace will be enable + * processes outside of core to mmap the dataspace. + */ + void fd(int fd) { _fd = fd; } + /** * Check if dataspace is owned by a specified object */ @@ -102,6 +112,13 @@ namespace Genode { ****************************************/ Filename fname() { return _fname; } + + Untyped_capability fd() + { + typedef Untyped_capability::Dst Dst; + enum { DUMMY_LOCAL_NAME = 0 }; + return Untyped_capability(Dst(_fd), DUMMY_LOCAL_NAME); + } }; } diff --git a/base-linux/src/core/ram_session_support.cc b/base-linux/src/core/ram_session_support.cc index b2d75f7376..fdc2710028 100644 --- a/base-linux/src/core/ram_session_support.cc +++ b/base-linux/src/core/ram_session_support.cc @@ -32,24 +32,32 @@ static int ram_ds_cnt = 0; /* counter for creating unique dataspace IDs */ void Ram_session_component::_export_ram_ds(Dataspace_component *ds) { - char fname_buf[Linux_dataspace::FNAME_LEN]; + char fname[Linux_dataspace::FNAME_LEN]; - /* assign filename to dataspace */ - snprintf(fname_buf, sizeof(fname_buf), "%s/ds-%d", lx_rpath(), ram_ds_cnt++); - - ds->fname(fname_buf); - - /* create new file representing the dataspace */ - lx_unlink(fname_buf); - int fd = lx_open(fname_buf, O_CREAT | O_RDWR | O_TRUNC | LX_O_CLOEXEC, S_IRWXU); + /* create file using a unique file name in 'lx_rpath' */ + snprintf(fname, sizeof(fname), "%s/ds-%d", lx_rpath(), ram_ds_cnt++); + lx_unlink(fname); + int const fd = lx_open(fname, O_CREAT|O_RDWR|O_TRUNC|LX_O_CLOEXEC, S_IRWXU); lx_ftruncate(fd, ds->size()); - lx_close(fd); + + /* remember file descriptor in dataspace component object */ + ds->fd(fd); + + /* + * Wipe the file from the Linux file system. The kernel will still keep the + * then unnamed file around until the last reference to the file will be + * gone (i.e., an open file descriptor referring to the file). A process + * w/o the right file descriptor won't be able to open and access the file. + */ + lx_unlink(fname); } void Ram_session_component::_revoke_ram_ds(Dataspace_component *ds) { - lx_unlink(ds->fname().buf); + int const fd = ds->fd().dst().socket; + if (fd != -1) + lx_close(fd); } diff --git a/base-linux/src/core/rom_session_component.cc b/base-linux/src/core/rom_session_component.cc index 2c65b74fbb..fde020d677 100644 --- a/base-linux/src/core/rom_session_component.cc +++ b/base-linux/src/core/rom_session_component.cc @@ -45,15 +45,15 @@ Rom_session_component::Rom_session_component(Rom_fs *rom_fs, : _ds_ep(ds_ep) { /* extract filename from session arguments */ - char fname_buf[Linux_dataspace::FNAME_LEN]; - Arg_string::find_arg(args, "filename").string(fname_buf, sizeof(fname_buf), ""); + char fname[Linux_dataspace::FNAME_LEN]; + Arg_string::find_arg(args, "filename").string(fname, sizeof(fname), ""); /* only files inside the current working directory are allowed */ - for (const char *c = fname_buf; *c; c++) + for (const char *c = fname; *c; c++) if (*c == '/') throw Root::Invalid_args(); - Genode::size_t fsize = file_size(fname_buf); + Genode::size_t const fsize = file_size(fname); /* use invalid capability as default value */ _ds_cap = Rom_dataspace_capability(); @@ -62,8 +62,11 @@ Rom_session_component::Rom_session_component(Rom_fs *rom_fs, if (fsize == 0) throw Root::Invalid_args(); + int const fd = lx_open(fname, O_RDONLY | LX_O_CLOEXEC, S_IRUSR | S_IXUSR); + _ds = Dataspace_component(fsize, 0, false, false, 0); - _ds.fname(fname_buf); + _ds.fd(fd); + _ds.fname(fname); Dataspace_capability ds_cap = _ds_ep->manage(&_ds); _ds_cap = static_cap_cast(ds_cap); @@ -73,4 +76,8 @@ Rom_session_component::Rom_session_component(Rom_fs *rom_fs, Rom_session_component::~Rom_session_component() { _ds_ep->dissolve(&_ds); + + int const fd = _ds.fd().dst().socket; + if (fd != -1) + lx_close(fd); } diff --git a/base-linux/src/platform/linux_syscalls.h b/base-linux/src/platform/linux_syscalls.h index 0b824861c1..844907db7c 100644 --- a/base-linux/src/platform/linux_syscalls.h +++ b/base-linux/src/platform/linux_syscalls.h @@ -38,6 +38,7 @@ #include #include #include +#include /* Genode includes */ #include