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