1.1 --- a/libfsserver/lib/generic/pager.cc Fri May 20 00:40:27 2022 +0200
1.2 +++ b/libfsserver/lib/generic/pager.cc Fri May 20 22:54:36 2022 +0200
1.3 @@ -1,7 +1,7 @@
1.4 /*
1.5 * Generic pager functionality.
1.6 *
1.7 - * Copyright (C) 2021 Paul Boddie <paul@boddie.org.uk>
1.8 + * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk>
1.9 *
1.10 * This program is free software; you can redistribute it and/or
1.11 * modify it under the terms of the GNU General Public License as
1.12 @@ -23,13 +23,16 @@
1.13 #include "ipc.h"
1.14 #include "pager.h"
1.15
1.16 +#include <string.h>
1.17 +
1.18
1.19
1.20 /* Initialise the pager with a page mapper and the given flags controlling
1.21 access to a file. */
1.22
1.23 Pager::Pager(PageMapper *mapper, map_flags_t flags)
1.24 -: _start(0), _size(0), _mapper(mapper), _flags(flags)
1.25 +: _start(0), _size(0), _mapper(mapper), _flags(flags),
1.26 + _is_masked(false), _start_visible(0), _end_visible(0)
1.27 {
1.28 }
1.29
1.30 @@ -61,8 +64,8 @@
1.31
1.32 /* Expose a region of the file. */
1.33
1.34 -long Pager::mmap(offset_t position, offset_t length, offset_t *start_pos,
1.35 - offset_t *end_pos, offset_t *size)
1.36 +long Pager::mmap(offset_t position, offset_t length, int masked,
1.37 + offset_t *start_pos, offset_t *end_pos, offset_t *size)
1.38 {
1.39 /* Define region characteristics. */
1.40
1.41 @@ -75,6 +78,17 @@
1.42 *end_pos = _start + _size;
1.43 *size = _mapper->get_data_size();
1.44
1.45 + /* Permit masking of mapped regions. */
1.46 +
1.47 + if (masked)
1.48 + {
1.49 + _start_visible = position;
1.50 + _end_visible = position + length;
1.51 + _is_masked = (*start_pos != _start_visible) || (*end_pos != _end_visible);
1.52 + }
1.53 + else
1.54 + _is_masked = false;
1.55 +
1.56 return L4_EOK;
1.57 }
1.58
1.59 @@ -87,17 +101,23 @@
1.60 offset_t file_offset = _start + offset;
1.61 offset_t max_offset = _start + _size;
1.62
1.63 - /* Prevent access beyond that defined by the pager. */
1.64 + /* Prevent access beyond that defined by the pager.
1.65 + NOTE: Permitting executable requests here. This needs to be configured
1.66 + when opening the pager or by another means. */
1.67
1.68 if (flags & (~(_flags | L4RE_DS_F_X)))
1.69 return -L4_EACCESS;
1.70
1.71 Flexpage *flexpage = _mapper->get(file_offset, flags);
1.72
1.73 + /* Determine if the flexpage should be masked. */
1.74 +
1.75 + Flexpage *issued_flexpage = get_masked_flexpage(flexpage);
1.76 +
1.77 /* Issue the flexpage via the IPC system. */
1.78
1.79 - long err = ipc_prepare_flexpage(flexpage, file_offset, max_offset, hot_spot,
1.80 - flags, region);
1.81 + long err = ipc_prepare_flexpage(issued_flexpage, file_offset, max_offset,
1.82 + hot_spot, flags, region);
1.83
1.84 if (!err)
1.85 err = complete_Dataspace_map(*region);
1.86 @@ -119,4 +139,93 @@
1.87 return _mapper->get_data_size();
1.88 }
1.89
1.90 +/* Detect flexpages with masked content, introducing separate flexpages
1.91 + offering masked content from the same region. */
1.92 +
1.93 +Flexpage *Pager::get_masked_flexpage(Flexpage *flexpage)
1.94 +{
1.95 + /* If not masking content, return the original flexpage. */
1.96 +
1.97 + if (!_is_masked)
1.98 + return flexpage;
1.99 +
1.100 + /* Determine whether the flexpage involves the limits of the visible
1.101 + region. */
1.102 +
1.103 + bool has_start = flexpage->supports_position(_start_visible) &&
1.104 + flexpage->base_offset != _start_visible;
1.105 + bool has_end = flexpage->supports_position(_end_visible);
1.106 + bool has_zero = (flexpage->base_offset >= _end_visible) ||
1.107 + (flexpage->base_offset + flexpage->size < _start_visible);
1.108 +
1.109 + /* Return the original flexpage within the visible limits. */
1.110 +
1.111 + if (!has_start && !has_end && !has_zero)
1.112 + return flexpage;
1.113 +
1.114 + /* Allocate and populate a region in one of the preallocated flexpages for
1.115 + masked content.
1.116 + NOTE: A general copy-on-write solution will maintain a collection of
1.117 + flexpages. */
1.118 +
1.119 + Flexpage &masked = has_start ? _start_flexpage :
1.120 + has_end ? _end_flexpage :
1.121 + _zero_flexpage;
1.122 +
1.123 + allocate_region(flexpage, masked);
1.124 + populate_region(flexpage, masked, has_start, has_end);
1.125 +
1.126 + /* Associate the preallocated flexpage with the original flexpage. */
1.127 +
1.128 + flexpage->associate(&masked);
1.129 + return &masked;
1.130 +}
1.131 +
1.132 +void Pager::allocate_region(Flexpage *flexpage, Flexpage &masked)
1.133 +{
1.134 + offset_t needed = flexpage->region->size();
1.135 +
1.136 + /* Attempt to re-use an existing region. */
1.137 +
1.138 + if (masked.region != NULL)
1.139 + {
1.140 + if (masked.region->size() == needed)
1.141 + return;
1.142 + else
1.143 + _memory.release(masked.region);
1.144 + }
1.145 +
1.146 + /* Set the region in the flexpage. */
1.147 +
1.148 + masked.set_region(_memory.region(needed));
1.149 + masked.reset(flexpage->base_offset);
1.150 +}
1.151 +
1.152 +void Pager::populate_region(Flexpage *flexpage, Flexpage &masked,
1.153 + bool has_start, bool has_end)
1.154 +{
1.155 + /* Without any limits involved, provide a zero flexpage. */
1.156 +
1.157 + if (!has_start && !has_end)
1.158 + {
1.159 + memset((void *) masked.region->start, 0, masked.size);
1.160 + return;
1.161 + }
1.162 +
1.163 + /* Determine the content limits given start and/or end offsets. */
1.164 +
1.165 + offset_t start_offset = has_start ? _start_visible - masked.base_offset : 0;
1.166 + offset_t end_offset = has_end ? _end_visible - masked.base_offset : masked.size;
1.167 +
1.168 + if (has_start)
1.169 + memset((void *) masked.region->start, 0, start_offset);
1.170 +
1.171 + memcpy((void *) (masked.region->start + start_offset),
1.172 + (const void *) (flexpage->region->start + start_offset),
1.173 + end_offset - start_offset);
1.174 +
1.175 + if (has_end)
1.176 + memset((void *) (masked.region->start + end_offset), 0, masked.size - end_offset);
1.177 +}
1.178 +
1.179 // vim: tabstop=4 expandtab shiftwidth=4