1 /* 2 * File-specific pager functionality. 3 * 4 * Copyright (C) 2021, 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <fsclient/file.h> /* file_region_flags */ 23 #include <resource/resource_server.h> 24 #include <systypes/fcntl.h> 25 26 #include "copied_page_mapper.h" 27 #include "file_pager.h" 28 #include "mapped_file_object_server.h" 29 30 31 32 /* Initialise a pager for a file with a unique file identifier, file provider, 33 opening flags and a file registry. The provider offers a shared page mapper 34 for moderating access to loaded pages. */ 35 36 FilePager::FilePager(fileid_t fileid, FileProvider *provider, flags_t flags, 37 object_flags_t object_flags) 38 : Pager(provider->mapper(), file_region_flags(flags)), 39 _provider(provider), _flags(flags), _object_flags(object_flags), 40 fileid(fileid) 41 { 42 /* Initialise any recorded position in the mapped region for this 43 endpoint. */ 44 45 _data_current = 0; 46 } 47 48 ipc_server_default_config_type FilePager::config() 49 { 50 return config_MappedFileObject; 51 } 52 53 54 55 /* Close the pager, removing the provider from the registry if appropriate. */ 56 57 void FilePager::close() 58 { 59 /* Notify other users of the file and unsubscribe. */ 60 61 _provider->notify_others(_notifier, NOTIFY_PEER_CLOSED, NOTIFY_VALUES_NULL); 62 unsubscribe(); 63 64 /* Detach the pager, potentially removing the file provider. */ 65 66 _provider->registry()->detach(fileid, _provider); 67 } 68 69 70 71 /* Flush operations. */ 72 73 long FilePager::flush(offset_t position, offset_t *size) 74 { 75 long err = Pager::flush(position, size); 76 77 _data_current = position; 78 79 if (_resized) 80 { 81 _provider->notify_others(_notifier, NOTIFY_CONTENT_AVAILABLE, NOTIFY_VALUES_NULL); 82 _resized = false; 83 } 84 85 return err; 86 } 87 88 long FilePager::refresh(offset_t *position, offset_t *start_pos, offset_t *end_pos, 89 offset_t *size, object_flags_t *object_flags) 90 { 91 *position = _data_current; 92 *start_pos = _start; 93 *end_pos = _start + _size; 94 *size = _mapper->get_data_size(); 95 *object_flags = _object_flags; 96 97 return L4_EOK; 98 } 99 100 101 102 /* File-specific operations. */ 103 104 long FilePager::reopen(flags_t flags, offset_t *size, l4_cap_idx_t *file, 105 object_flags_t *object_flags) 106 { 107 Resource *resource; 108 long err = _provider->make_resource(flags, size, object_flags, &resource); 109 110 if (err) 111 return err; 112 113 _provider->notify_all(NOTIFY_FILE_OPENED, NOTIFY_VALUES_NULL); 114 115 return ResourceServer(resource).start_thread(file); 116 } 117 118 long FilePager::resize(offset_t *size) 119 { 120 offset_t old_size = get_data_size(); 121 long err = Pager::resize(size); 122 123 /* If the size has changed, notify other users, but only after data has been 124 flushed. */ 125 126 if (old_size < get_data_size()) 127 _resized = true; 128 129 return err; 130 } 131 132 long FilePager::mmap(offset_t position, offset_t length, 133 offset_t start_visible, offset_t end_visible, 134 offset_t *start_pos, offset_t *end_pos, offset_t *size) 135 { 136 /* Set the limits of the paged region. */ 137 138 long err = Pager::mmap(position, length, start_visible, end_visible, start_pos, 139 end_pos, size); 140 141 if (err) 142 return err; 143 144 /* Impose copy-on-write semantics where appropriate. */ 145 146 if ((_mapper != _mapper_copied) && copy_on_write()) 147 { 148 _mapper_copied = new CopiedPageMapper(_mapper); 149 _mapper = _mapper_copied; 150 } 151 152 return L4_EOK; 153 } 154 155 /* Return whether the pager should employ copy-on-write semantics. */ 156 157 bool FilePager::copy_on_write() 158 { 159 return (_flags == O_RDONLY) && (_map_flags & L4RE_DS_F_W); 160 } 161 162 163 164 /* Generic pager operations. */ 165 166 long FilePager::map(offset_t offset, map_address_t hot_spot, map_flags_t map_flags, l4_snd_fpage_t *region) 167 { 168 return Pager::map(offset, hot_spot, map_flags, region); 169 } 170 171 172 173 /* Subscribe to notifications. 174 Readers can subscribe to new data (flush) and file closed events. 175 Writers can subscribe to file closed events. */ 176 177 long FilePager::subscribe(l4_cap_idx_t notifier, notify_flags_t flags) 178 { 179 /* A single notifier is recorded so that it may be unsubscribed when the 180 file is closed. */ 181 182 unsubscribe(); 183 184 _notifier = notifier; 185 return _provider->subscribe(_notifier, flags); 186 } 187 188 /* Unsubscribe from notifications. */ 189 190 long FilePager::unsubscribe() 191 { 192 if (l4_is_valid_cap(_notifier)) 193 { 194 long err = _provider->unsubscribe(_notifier); 195 _notifier = L4_INVALID_CAP; 196 return err; 197 } 198 else 199 return L4_EOK; 200 } 201 202 // vim: tabstop=4 expandtab shiftwidth=4