1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/libfsserver/lib/mapping/masked_page_mapper.cc Sat May 28 00:59:04 2022 +0200
1.3 @@ -0,0 +1,195 @@
1.4 +/*
1.5 + * A page mapper providing memory pages to satisfy file accesses, masking the
1.6 + * limits of a visible region of the file's contents.
1.7 + *
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 + * published by the Free Software Foundation; either version 2 of
1.13 + * the License, or (at your option) any later version.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful,
1.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.18 + * GNU General Public License for more details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License
1.21 + * along with this program; if not, write to the Free Software
1.22 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.23 + * Boston, MA 02110-1301, USA
1.24 + */
1.25 +
1.26 +#include <string.h>
1.27 +
1.28 +#include "masked_page_mapper.h"
1.29 +
1.30 +
1.31 +
1.32 +/* Provide mapped pages populated using the given 'mapper', with a visible
1.33 + region defined that causes the limits of this region to be masked. */
1.34 +
1.35 +MaskedPageMapper::MaskedPageMapper(GenericPageMapper *mapper,
1.36 + offset_t start_visible, offset_t end_visible)
1.37 +: _mapper(mapper), _start_visible(start_visible), _end_visible(end_visible)
1.38 +{
1.39 + _size = _mapper->get_data_size();
1.40 +}
1.41 +
1.42 +MaskedPageMapper::~MaskedPageMapper()
1.43 +{
1.44 + if (_start_flexpage.region != NULL)
1.45 + _memory.release(_start_flexpage.region);
1.46 +
1.47 + if (_end_flexpage.region != NULL)
1.48 + _memory.release(_end_flexpage.region);
1.49 +
1.50 + if (_zero_flexpage.region != NULL)
1.51 + _memory.release(_zero_flexpage.region);
1.52 +}
1.53 +
1.54 +/* Interface for the pager. */
1.55 +
1.56 +/* Return a flexpage providing access to the indicated file 'offset'.
1.57 +
1.58 + The returned flexpage will either provide direct access to the underlying
1.59 + file content or be a completely new flexpage containing masked content. */
1.60 +
1.61 +Flexpage *MaskedPageMapper::get(offset_t offset, map_flags_t flags)
1.62 +{
1.63 + Flexpage *f = _mapper->get(offset, flags);
1.64 + Flexpage *mf = get_masked_flexpage(f);
1.65 +
1.66 + if (f != mf)
1.67 + _mapper->queue(f);
1.68 +
1.69 + return mf;
1.70 +}
1.71 +
1.72 +/* Queue the given 'flexpage'. */
1.73 +
1.74 +void MaskedPageMapper::queue(Flexpage *flexpage)
1.75 +{
1.76 + if (dynamic_cast<MaskedFlexpage *>(flexpage) == NULL)
1.77 + _mapper->queue(flexpage);
1.78 +}
1.79 +
1.80 +/* Flush pages in the given range from 'start' with 'size'. */
1.81 +
1.82 +void MaskedPageMapper::flush_all(offset_t start, offset_t size)
1.83 +{
1.84 + /* NOTE: This might be superfluous since masked regions probably should be
1.85 + read-only or copy-on-write. */
1.86 +
1.87 + _mapper->flush_all(start, size);
1.88 +}
1.89 +
1.90 +/* Return the maximum extent of the mapped resource. */
1.91 +
1.92 +offset_t MaskedPageMapper::get_data_size()
1.93 +{
1.94 + return _size;
1.95 +}
1.96 +
1.97 +/* Set the maximum extent of the mapped resource. */
1.98 +
1.99 +void MaskedPageMapper::set_data_size(offset_t size)
1.100 +{
1.101 + _size = size;
1.102 +}
1.103 +
1.104 +/* Internal flexpage retrieval methods. */
1.105 +
1.106 +/* Detect flexpages with masked content, introducing separate flexpages
1.107 + offering masked content from the same region. */
1.108 +
1.109 +Flexpage *MaskedPageMapper::get_masked_flexpage(Flexpage *flexpage)
1.110 +{
1.111 + /* Determine whether the flexpage involves the limits of the visible
1.112 + region or is beyond such limits. */
1.113 +
1.114 + bool has_start = flexpage->supports_position(_start_visible) &&
1.115 + flexpage->base_offset != _start_visible;
1.116 + bool has_end = flexpage->supports_position(_end_visible);
1.117 + bool has_zero = (flexpage->base_offset >= _end_visible) ||
1.118 + (flexpage->base_offset + flexpage->size < _start_visible);
1.119 +
1.120 + /* Return the original flexpage within the visible limits. */
1.121 +
1.122 + if (!has_start && !has_end && !has_zero)
1.123 + return flexpage;
1.124 +
1.125 + /* Allocate and populate a region in one of the preallocated flexpages for
1.126 + masked content. */
1.127 +
1.128 + Flexpage &masked = has_start ? _start_flexpage :
1.129 + has_end ? _end_flexpage :
1.130 + _zero_flexpage;
1.131 +
1.132 + allocate_region(flexpage, masked);
1.133 + populate_region(flexpage, masked, has_start, has_end);
1.134 +
1.135 + /* Associate the preallocated flexpage with the original flexpage. */
1.136 +
1.137 + flexpage->associate(&masked);
1.138 +
1.139 + return &masked;
1.140 +}
1.141 +
1.142 +void MaskedPageMapper::allocate_region(Flexpage *flexpage, Flexpage &masked)
1.143 +{
1.144 + offset_t needed = flexpage->region->size();
1.145 +
1.146 + /* Attempt to re-use an existing region. */
1.147 +
1.148 + if (masked.region != NULL)
1.149 + {
1.150 + if (masked.region->size() == needed)
1.151 + return;
1.152 + else
1.153 + _memory.release(masked.region);
1.154 + }
1.155 +
1.156 + /* Set the region in the flexpage. */
1.157 +
1.158 + masked.set_region(_memory.region(needed));
1.159 + masked.reset(flexpage->base_offset);
1.160 +}
1.161 +
1.162 +void MaskedPageMapper::populate_region(Flexpage *flexpage, Flexpage &masked,
1.163 + bool has_start, bool has_end)
1.164 +{
1.165 + /* Without any limits involved, provide a zero flexpage. */
1.166 +
1.167 + if (!has_start && !has_end)
1.168 + {
1.169 + memset((void *) masked.region->start, 0, masked.size);
1.170 + return;
1.171 + }
1.172 +
1.173 + /* Determine the content limits given start and/or end offsets. */
1.174 +
1.175 + offset_t start_offset = has_start ? _start_visible - masked.base_offset : 0;
1.176 + offset_t end_offset = has_end ? _end_visible - masked.base_offset : masked.size;
1.177 +
1.178 + if (has_start)
1.179 + memset((void *) masked.region->start, 0, start_offset);
1.180 +
1.181 + memcpy((void *) (masked.region->start + start_offset),
1.182 + (const void *) (flexpage->region->start + start_offset),
1.183 + end_offset - start_offset);
1.184 +
1.185 + if (has_end)
1.186 + memset((void *) (masked.region->start + end_offset), 0, masked.size - end_offset);
1.187 +}
1.188 +
1.189 +
1.190 +
1.191 +/* Masked flexpage constructor. */
1.192 +
1.193 +MaskedFlexpage::MaskedFlexpage(Region *region)
1.194 +: Flexpage(region)
1.195 +{
1.196 +}
1.197 +
1.198 +// vim: tabstop=4 expandtab shiftwidth=4