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 : Pager(provider->mapper(), file_region_flags(flags)), 38 _provider(provider), _flags(flags), fileid(fileid) 39 { 40 } 41 42 ipc_server_default_config_type FilePager::config() 43 { 44 return config_MappedFileObject; 45 } 46 47 48 49 /* Close the pager, removing the provider from the registry if appropriate. */ 50 51 void FilePager::close() 52 { 53 /* Notify other users of the file and unsubscribe. */ 54 55 _provider->notify_others(_notifier, NOTIFY_PEER_CLOSED, NOTIFY_VALUES_NULL); 56 unsubscribe(); 57 58 /* Detach the pager, potentially removing the file provider. */ 59 60 _provider->registry()->detach(fileid, _provider); 61 } 62 63 64 65 /* File-specific operations. */ 66 67 long FilePager::flush(offset_t position, offset_t *size) 68 { 69 long err = Pager::flush(position, size); 70 71 if (_resized) 72 { 73 _provider->notify_others(_notifier, NOTIFY_CONTENT_AVAILABLE, NOTIFY_VALUES_NULL); 74 _resized = false; 75 } 76 77 return err; 78 } 79 80 long FilePager::reopen(flags_t flags, offset_t *size, l4_cap_idx_t *file, 81 object_flags_t *object_flags) 82 { 83 Resource *resource; 84 long err = _provider->make_resource(flags, size, object_flags, &resource); 85 86 if (err) 87 return err; 88 89 _provider->notify_all(NOTIFY_FILE_OPENED, NOTIFY_VALUES_NULL); 90 91 return ResourceServer(resource).start_thread(file); 92 } 93 94 long FilePager::resize(offset_t *size) 95 { 96 offset_t old_size = get_data_size(); 97 long err = Pager::resize(size); 98 99 /* If the size has changed, notify other users, but only after data has been 100 flushed. */ 101 102 if (old_size < get_data_size()) 103 _resized = true; 104 105 return err; 106 } 107 108 long FilePager::mmap(offset_t position, offset_t length, 109 offset_t start_visible, offset_t end_visible, 110 offset_t *start_pos, offset_t *end_pos, offset_t *size) 111 { 112 /* Set the limits of the paged region. */ 113 114 long err = Pager::mmap(position, length, start_visible, end_visible, start_pos, 115 end_pos, size); 116 117 if (err) 118 return err; 119 120 /* Impose copy-on-write semantics where appropriate. */ 121 122 if ((_mapper != _mapper_copied) && copy_on_write()) 123 { 124 _mapper_copied = new CopiedPageMapper(_mapper); 125 _mapper = _mapper_copied; 126 } 127 128 return L4_EOK; 129 } 130 131 /* Return whether the pager should employ copy-on-write semantics. */ 132 133 bool FilePager::copy_on_write() 134 { 135 return (_flags == O_RDONLY) && (_map_flags & L4RE_DS_F_W); 136 } 137 138 139 140 /* Generic pager operations. */ 141 142 long FilePager::map(offset_t offset, map_address_t hot_spot, map_flags_t map_flags, l4_snd_fpage_t *region) 143 { 144 return Pager::map(offset, hot_spot, map_flags, region); 145 } 146 147 148 149 /* Subscribe to notifications. 150 Readers can subscribe to new data (flush) and file closed events. 151 Writers can subscribe to file closed events. */ 152 153 long FilePager::subscribe(l4_cap_idx_t notifier, notify_flags_t flags) 154 { 155 /* A single notifier is recorded so that it may be unsubscribed when the 156 file is closed. */ 157 158 unsubscribe(); 159 160 _notifier = notifier; 161 return _provider->subscribe(_notifier, flags); 162 } 163 164 /* Unsubscribe from notifications. */ 165 166 long FilePager::unsubscribe() 167 { 168 if (l4_is_valid_cap(_notifier)) 169 { 170 long err = _provider->unsubscribe(_notifier); 171 _notifier = L4_INVALID_CAP; 172 return err; 173 } 174 else 175 return L4_EOK; 176 } 177 178 // vim: tabstop=4 expandtab shiftwidth=4