1 /* 2 * File access convenience functions. 3 * 4 * Copyright (C) 2021 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 <ipc/cap_alloc.h> 23 #include <ipc/mem_ipc.h> 24 25 #include <string.h> 26 27 #include "dataspace_client.h" 28 #include "file_client.h" 29 #include "opener_client.h" 30 #include "opener_context_client.h" 31 #include "mapped_file_client.h" 32 33 #include "file.h" 34 35 36 37 /* Release resources for the given file. */ 38 39 void file_close(file_t *file) 40 { 41 if (l4_is_valid_cap(file->ref)) 42 ipc_cap_free_um(file->ref); 43 44 if (file->memory != NULL) 45 ipc_detach_dataspace(file->memory); 46 47 file_init(file); 48 } 49 50 /* Initialise a file structure for a context obtained from the given server. */ 51 52 long file_context(file_t *file, l4_cap_idx_t server) 53 { 54 client_Opener opener(server); 55 unsigned long size, flags; 56 long err; 57 58 file_init(file); 59 60 err = opener.context(&file->ref); 61 if (err) 62 return err; 63 64 client_Dataspace context_ds(file->ref); 65 66 err = context_ds.info(&size, &flags); 67 if (err) 68 return err; 69 70 file->start_pos = 0; 71 file->end_pos = size; 72 73 return ipc_attach_dataspace(file->ref, size, (void **) &file->memory); 74 } 75 76 /* Open a file using the given structure and context. */ 77 78 long file_context_open(file_t *file, file_t *context) 79 { 80 client_OpenerContext openercontext(context->ref); 81 file_init(file); 82 return openercontext.open(L4_FPAGE_RW, &file->size, &file->ref); 83 } 84 85 /* Initialise the given file structure. */ 86 87 void file_init(file_t *file) 88 { 89 file->memory = NULL; 90 file->ref = L4_INVALID_CAP; 91 file->start_pos = 0; 92 file->end_pos = 0; 93 file->data_end = 0; 94 } 95 96 /* Open a file using the given structure, indicating the filename and 97 filesystem server. This is a convenience function invoking file_context and 98 file_context_open. */ 99 100 long file_open(file_t *file, const char *filename, l4_cap_idx_t server) 101 { 102 file_t context; 103 long err; 104 105 err = file_context(&context, server); 106 if (err) 107 return err; 108 109 if (!file_string_set(&context, filename, 0, NULL)) 110 return -L4_ENOMEM; 111 112 err = file_context_open(file, &context); 113 file_close(&context); 114 return err; 115 } 116 117 118 119 /* Map a region of the given file to a memory region. */ 120 121 long file_mmap(file_t *file, offset_t position, offset_t length) 122 { 123 client_MappedFile mapped_file(file->ref); 124 long err = mapped_file.mmap(position, length, &file->start_pos, &file->end_pos, &file->data_end); 125 126 if (err) 127 return err; 128 129 return ipc_attach_dataspace(file->ref, file_span(file), (void **) &file->memory); 130 } 131 132 /* Resize a file. */ 133 134 long file_resize(file_t *file, offset_t size) 135 { 136 client_File _file(file->ref); 137 offset_t file_size = size; 138 long err = _file.resize(&file_size); 139 140 if (!err) 141 { 142 /* Determine the extent of the file in this region. */ 143 144 if (file_size > file->end_pos) 145 file->data_end = file_span(file); 146 else 147 file->data_end = file_size - file->start_pos; 148 149 /* Update the file size locally. */ 150 151 file->size = file_size; 152 } 153 154 return err; 155 } 156 157 158 159 /* Return the amount of data in the mapped region for the given file. */ 160 161 offset_t file_populated_span(file_t *file) 162 { 163 offset_t size = file_span(file); 164 return (file->data_end < size) ? file->data_end : size; 165 } 166 167 /* Return the size of the mapped region for the given file. */ 168 169 offset_t file_span(file_t *file) 170 { 171 return file->end_pos - file->start_pos; 172 } 173 174 175 176 /* Get a pointer to any terminated string at the given offset or NULL if the 177 data from offset is not terminated. */ 178 179 char *file_string_get(file_t *file, offset_t offset) 180 { 181 offset_t limit = file_span(file) - offset; 182 183 if (strnlen(file->memory + offset, limit) < limit) 184 return file->memory + offset; 185 else 186 return NULL; 187 } 188 189 /* Copy a string to the mapped region at the given offset, returning 1 (true) 190 where all characters were copied, 0 (false) otherwise. The precise number of 191 characters copied, excluding the zero terminator is provided via the written 192 parameter if it is not specified as NULL. */ 193 194 int file_string_set(file_t *file, const char *data, offset_t offset, 195 offset_t *written) 196 { 197 offset_t i, pos, limit = file_span(file); 198 199 /* Do not attempt to copy data with an invalid offset. */ 200 201 if (offset >= limit) 202 { 203 if (written != NULL) 204 *written = 0; 205 return 0; 206 } 207 208 /* Copy the data to the given offset, stopping at the end of the region. */ 209 210 for (i = 0, pos = offset; pos < limit; i++, pos++) 211 { 212 file->memory[pos] = data[i]; 213 214 /* Terminator written, can return immediately. */ 215 216 if (!data[i]) 217 { 218 if (written != NULL) 219 *written = pos - offset; 220 return 1; 221 } 222 } 223 224 /* Terminate the incomplete string at the end of the region. */ 225 226 file->memory[limit - 1] = '\0'; 227 if (written != NULL) 228 *written = limit - 1 - offset; 229 return 0; 230 } 231 232 // vim: tabstop=2 expandtab shiftwidth=2