1 /* 2 * A page mapper providing memory pages to satisfy file accesses, masking the 3 * limits of a visible region of the file's contents. 4 * 5 * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA 21 */ 22 23 #include <string.h> 24 25 #include "masked_page_mapper.h" 26 27 28 29 /* Provide mapped pages populated using the given 'mapper', with a visible 30 region defined that causes the limits of this region to be masked. */ 31 32 MaskedPageMapper::MaskedPageMapper(GenericPageMapper *mapper, 33 offset_t start_visible, offset_t end_visible) 34 : _mapper(mapper), _start_visible(start_visible), _end_visible(end_visible) 35 { 36 _size = _mapper->get_data_size(); 37 } 38 39 MaskedPageMapper::~MaskedPageMapper() 40 { 41 if (_start_flexpage.region != NULL) 42 _memory.release(_start_flexpage.region); 43 44 if (_end_flexpage.region != NULL) 45 _memory.release(_end_flexpage.region); 46 47 if (_zero_flexpage.region != NULL) 48 _memory.release(_zero_flexpage.region); 49 } 50 51 /* Interface for the pager. */ 52 53 /* Return a flexpage providing access to the indicated file 'offset'. 54 55 The returned flexpage will either provide direct access to the underlying 56 file content or be a completely new flexpage containing masked content. */ 57 58 Flexpage *MaskedPageMapper::get(offset_t offset, map_flags_t flags) 59 { 60 Flexpage *f = _mapper->get(offset, flags); 61 Flexpage *mf = get_masked_flexpage(f); 62 63 if (f != mf) 64 _mapper->queue(f); 65 66 return mf; 67 } 68 69 /* Queue the given 'flexpage'. */ 70 71 void MaskedPageMapper::queue(Flexpage *flexpage) 72 { 73 if (dynamic_cast<MaskedFlexpage *>(flexpage) == NULL) 74 _mapper->queue(flexpage); 75 } 76 77 /* Flush pages in the given range from 'start' with 'size'. */ 78 79 void MaskedPageMapper::flush_all(offset_t start, offset_t size) 80 { 81 /* NOTE: This might be superfluous since masked regions probably should be 82 read-only or copy-on-write. */ 83 84 _mapper->flush_all(start, size); 85 } 86 87 /* Return the maximum extent of the mapped resource. */ 88 89 offset_t MaskedPageMapper::get_data_size() 90 { 91 return _size; 92 } 93 94 /* Set the maximum extent of the mapped resource. */ 95 96 void MaskedPageMapper::set_data_size(offset_t size) 97 { 98 _size = size; 99 } 100 101 /* Internal flexpage retrieval methods. */ 102 103 /* Detect flexpages with masked content, introducing separate flexpages 104 offering masked content from the same region. */ 105 106 Flexpage *MaskedPageMapper::get_masked_flexpage(Flexpage *flexpage) 107 { 108 /* Determine whether the flexpage involves the limits of the visible 109 region or is beyond such limits. */ 110 111 bool has_start = flexpage->supports_position(_start_visible) && 112 flexpage->base_offset != _start_visible; 113 bool has_end = flexpage->supports_position(_end_visible); 114 bool has_zero = (flexpage->base_offset >= _end_visible) || 115 (flexpage->base_offset + flexpage->size < _start_visible); 116 117 /* Return the original flexpage within the visible limits. */ 118 119 if (!has_start && !has_end && !has_zero) 120 return flexpage; 121 122 /* Allocate and populate a region in one of the preallocated flexpages for 123 masked content. */ 124 125 Flexpage &masked = has_start ? _start_flexpage : 126 has_end ? _end_flexpage : 127 _zero_flexpage; 128 129 allocate_region(flexpage, masked); 130 populate_region(flexpage, masked, has_start, has_end); 131 132 /* Associate the preallocated flexpage with the original flexpage. */ 133 134 flexpage->associate(&masked); 135 136 return &masked; 137 } 138 139 void MaskedPageMapper::allocate_region(Flexpage *flexpage, Flexpage &masked) 140 { 141 offset_t needed = flexpage->region->size(); 142 143 /* Attempt to re-use an existing region. */ 144 145 if (masked.region != NULL) 146 { 147 if (masked.region->size() == needed) 148 return; 149 else 150 _memory.release(masked.region); 151 } 152 153 /* Set the region in the flexpage. */ 154 155 masked.set_region(_memory.region(needed)); 156 masked.reset(flexpage->base_offset); 157 } 158 159 void MaskedPageMapper::populate_region(Flexpage *flexpage, Flexpage &masked, 160 bool has_start, bool has_end) 161 { 162 /* Without any limits involved, provide a zero flexpage. */ 163 164 if (!has_start && !has_end) 165 { 166 memset((void *) masked.region->start, 0, masked.size); 167 return; 168 } 169 170 /* Determine the content limits given start and/or end offsets. */ 171 172 offset_t start_offset = has_start ? _start_visible - masked.base_offset : 0; 173 offset_t end_offset = has_end ? _end_visible - masked.base_offset : masked.size; 174 175 if (has_start) 176 memset((void *) masked.region->start, 0, start_offset); 177 178 memcpy((void *) (masked.region->start + start_offset), 179 (const void *) (flexpage->region->start + start_offset), 180 end_offset - start_offset); 181 182 if (has_end) 183 memset((void *) (masked.region->start + end_offset), 0, masked.size - end_offset); 184 } 185 186 187 188 /* Masked flexpage constructor. */ 189 190 MaskedFlexpage::MaskedFlexpage(Region *region) 191 : Flexpage(region) 192 { 193 } 194 195 // vim: tabstop=4 expandtab shiftwidth=4