1 /* 2 * blockserver_io.c --- L4Re block server I/O manager. 3 * 4 * Copyright (C) 2019, 2020, 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library 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 GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 * 02110-1301 USA 20 */ 21 22 #include <l4/re/env.h> 23 24 #include <stdio.h> 25 #include <stdlib.h> 26 27 #include <ext2fs/ext2fs.h> 28 #include <fsclient/client.h> 29 #include <systypes/env.h> 30 31 32 33 /* Specific data for interacting with block devices. */ 34 35 struct blockserver_private_data 36 { 37 int magic; 38 int flags; 39 file_t *file; 40 }; 41 42 io_manager blockserver_io_manager; /* defined below */ 43 44 const char *blockserver_default_cap = ENV_FILESYSTEM_SERVER_NAME; 45 46 47 48 /* Allocate and initialise a channel for the indicated block device. */ 49 50 static errcode_t blockserver_open_channel(const char *name, 51 file_t *file, int flags, 52 io_channel *channel, 53 io_manager io_mgr) 54 { 55 io_channel io = NULL; 56 struct blockserver_private_data *data = NULL; 57 errcode_t retval; 58 59 /* Allocate memory for the channel. */ 60 61 retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); 62 if (retval) 63 goto free_io; 64 65 /* Initialise the channel. */ 66 67 memset(io, 0, sizeof(struct struct_io_channel)); 68 69 io->magic = EXT2_ET_MAGIC_IO_CHANNEL; 70 io->manager = io_mgr; 71 io->block_size = 1024; 72 io->read_error = 0; 73 io->write_error = 0; 74 io->refcount = 1; 75 76 /* Allocate memory for the name and store it. */ 77 78 retval = ext2fs_get_mem(strlen(name)+1, &io->name); 79 if (retval) 80 goto free_io; 81 82 strcpy(io->name, name); 83 84 /* Allocate memory for the private data. */ 85 86 retval = ext2fs_get_mem(sizeof(struct blockserver_private_data), &data); 87 if (retval) 88 goto free_data; 89 90 /* Initialise the private data. */ 91 92 io->private_data = data; 93 94 memset(data, 0, sizeof(struct blockserver_private_data)); 95 96 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; 97 data->flags = flags; 98 data->file = file; 99 100 /* Return the allocated channel. */ 101 102 *channel = io; 103 return 0; 104 105 /* Deallocation upon failure. */ 106 107 free_data: 108 if (data) 109 ext2fs_free_mem(&data); 110 111 free_io: 112 if (io) 113 { 114 if (io->name) 115 ext2fs_free_mem(&io->name); 116 ext2fs_free_mem(&io); 117 } 118 119 return retval; 120 } 121 122 123 124 /* Open a block device using the named capability. */ 125 126 static errcode_t blockserver_open(const char *name, int flags, io_channel *channel) 127 { 128 l4_cap_idx_t server = l4re_env_get_cap(blockserver_default_cap); 129 130 /* Open a device via the named capability. */ 131 132 file_t *file = client_open_using(name, flags, server); 133 134 /* NOTE: May want a more appropriate error code. */ 135 136 if (!client_opened(file)) 137 return EXT2_ET_BAD_DEVICE_NAME; 138 139 /* NOTE: Map the entire block device to avoid peculiar region mapping issues 140 when detaching and attaching the underlying dataspace. */ 141 142 client_mmap(file, 0, file->size, 0, 0, file_region_flags(file->flags)); 143 144 return blockserver_open_channel(name, file, flags, channel, blockserver_io_manager); 145 } 146 147 148 149 /* Close the block device associated with the given channel. */ 150 151 static errcode_t blockserver_close(io_channel channel) 152 { 153 struct blockserver_private_data *data; 154 155 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 156 data = (struct blockserver_private_data *) channel->private_data; 157 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 158 159 client_close(data->file); 160 return 0; 161 } 162 163 /* Update the block size in use. */ 164 165 static errcode_t blockserver_set_blksize(io_channel channel, int blksize) 166 { 167 struct blockserver_private_data *data; 168 169 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 170 data = (struct blockserver_private_data *) channel->private_data; 171 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 172 173 if (channel->block_size != blksize) 174 channel->block_size = blksize; 175 176 return 0; 177 } 178 179 /* Flush unwritten data. Without a cache, this does nothing. */ 180 181 static errcode_t blockserver_flush(io_channel channel) 182 { 183 (void) channel; 184 return 0; 185 } 186 187 static errcode_t blockserver_set_option(io_channel channel, const char *option, const char *arg) 188 { 189 (void) channel; (void) option; (void) arg; 190 return 0; 191 } 192 193 static errcode_t blockserver_get_stats(io_channel channel, io_stats *stats) 194 { 195 (void) channel; (void) stats; 196 return 0; 197 } 198 199 static errcode_t blockserver_read_blk64(io_channel channel, unsigned long long block, int count, void *buf) 200 { 201 /* Negative count values indicate a precise amount to read. */ 202 203 unsigned long long to_read = count < 0 ? -count : count * channel->block_size; 204 offset_t read; 205 struct blockserver_private_data *data; 206 errcode_t retval = 0; 207 208 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 209 data = (struct blockserver_private_data *) channel->private_data; 210 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 211 212 /* Perform the read at the given location. */ 213 214 if (client_seek(data->file, block * channel->block_size, SEEK_SET) != block * channel->block_size) 215 { 216 read = 0; 217 retval = EXT2_ET_LLSEEK_FAILED; 218 } 219 else 220 { 221 read = client_read(data->file, buf, to_read); 222 223 /* Check for a short read. */ 224 225 if (read < to_read) 226 retval = EXT2_ET_SHORT_READ; 227 } 228 229 /* Handle read errors generally. */ 230 231 if (retval) 232 if (channel->read_error) 233 retval = (channel->read_error)(channel, block, count, buf, 234 to_read, read, retval); 235 236 return retval; 237 } 238 239 static errcode_t blockserver_write_blk64(io_channel channel, unsigned long long block, int count, const void *buf) 240 { 241 /* Negative count values indicate a precise amount to write. */ 242 243 unsigned long long to_write = count < 0 ? -count : count * channel->block_size; 244 offset_t written; 245 struct blockserver_private_data *data; 246 errcode_t retval = 0; 247 248 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 249 data = (struct blockserver_private_data *) channel->private_data; 250 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 251 252 /* Perform the write at the given location. */ 253 254 if (client_seek(data->file, block * channel->block_size, SEEK_SET) != block * channel->block_size) 255 { 256 retval = EXT2_ET_LLSEEK_FAILED; 257 written = 0; 258 } 259 else 260 { 261 written = client_write(data->file, buf, to_write); 262 263 /* Check for a short write. */ 264 265 if (written < to_write) 266 retval = EXT2_ET_SHORT_WRITE; 267 } 268 269 /* Handle write errors generally. */ 270 271 if (retval) 272 if (channel->write_error) 273 retval = (channel->write_error)(channel, block, count, buf, 274 to_write, written, retval); 275 276 return retval; 277 } 278 279 static errcode_t blockserver_read_blk(io_channel channel, unsigned long block, int count, void *buf) 280 { 281 return blockserver_read_blk64(channel, block, count, buf); 282 } 283 284 static errcode_t blockserver_write_blk(io_channel channel, unsigned long block, int count, const void *buf) 285 { 286 return blockserver_write_blk64(channel, block, count, buf); 287 } 288 289 /* Unsupported operations are omitted: see io_manager.c for optional operation 290 details. */ 291 292 static struct struct_io_manager struct_blockserver_manager = { 293 .magic = EXT2_ET_MAGIC_IO_MANAGER, 294 .name = "L4Re blockserver I/O Manager", 295 .open = blockserver_open, 296 .close = blockserver_close, 297 .set_blksize = blockserver_set_blksize, 298 .read_blk = blockserver_read_blk, 299 .write_blk = blockserver_write_blk, 300 .flush = blockserver_flush, 301 .write_byte = 0, 302 .set_option = blockserver_set_option, 303 .get_stats = blockserver_get_stats, 304 .read_blk64 = blockserver_read_blk64, 305 .write_blk64 = blockserver_write_blk64, 306 .discard = 0, 307 .cache_readahead = 0, 308 .zeroout = 0, 309 }; 310 311 io_manager blockserver_io_manager = &struct_blockserver_manager;