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 (file == NULL) 137 return EXT2_ET_BAD_DEVICE_NAME; 138 139 return blockserver_open_channel(name, file, flags, channel, blockserver_io_manager); 140 } 141 142 143 144 /* Close the block device associated with the given channel. */ 145 146 static errcode_t blockserver_close(io_channel channel) 147 { 148 struct blockserver_private_data *data; 149 150 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 151 data = (struct blockserver_private_data *) channel->private_data; 152 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 153 154 client_close(data->file); 155 return 0; 156 } 157 158 /* Update the block size in use. */ 159 160 static errcode_t blockserver_set_blksize(io_channel channel, int blksize) 161 { 162 struct blockserver_private_data *data; 163 164 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 165 data = (struct blockserver_private_data *) channel->private_data; 166 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 167 168 if (channel->block_size != blksize) 169 channel->block_size = blksize; 170 171 return 0; 172 } 173 174 /* Flush unwritten data. Without a cache, this does nothing. */ 175 176 static errcode_t blockserver_flush(io_channel channel) 177 { 178 (void) channel; 179 return 0; 180 } 181 182 static errcode_t blockserver_set_option(io_channel channel, const char *option, const char *arg) 183 { 184 (void) channel; (void) option; (void) arg; 185 return 0; 186 } 187 188 static errcode_t blockserver_get_stats(io_channel channel, io_stats *stats) 189 { 190 (void) channel; (void) stats; 191 return 0; 192 } 193 194 static errcode_t blockserver_read_blk64(io_channel channel, unsigned long long block, int count, void *buf) 195 { 196 /* Negative count values indicate a precise amount to read. */ 197 198 unsigned long long to_read = count < 0 ? -count : count * channel->block_size; 199 offset_t read; 200 struct blockserver_private_data *data; 201 errcode_t retval = 0; 202 203 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 204 data = (struct blockserver_private_data *) channel->private_data; 205 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 206 207 /* Perform the read at the given location. */ 208 209 if (client_seek(data->file, block * channel->block_size, SEEK_SET) != block * channel->block_size) 210 { 211 read = 0; 212 retval = EXT2_ET_LLSEEK_FAILED; 213 } 214 else 215 { 216 read = client_read(data->file, buf, to_read); 217 218 /* Check for a short read. */ 219 220 if (read < to_read) 221 retval = EXT2_ET_SHORT_READ; 222 } 223 224 /* Handle read errors generally. */ 225 226 if (retval) 227 if (channel->read_error) 228 retval = (channel->read_error)(channel, block, count, buf, 229 to_read, read, retval); 230 231 return retval; 232 } 233 234 static errcode_t blockserver_write_blk64(io_channel channel, unsigned long long block, int count, const void *buf) 235 { 236 /* Negative count values indicate a precise amount to write. */ 237 238 unsigned long long to_write = count < 0 ? -count : count * channel->block_size; 239 offset_t written; 240 struct blockserver_private_data *data; 241 errcode_t retval = 0; 242 243 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 244 data = (struct blockserver_private_data *) channel->private_data; 245 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 246 247 /* Perform the write at the given location. */ 248 249 if (client_seek(data->file, block * channel->block_size, SEEK_SET) != block * channel->block_size) 250 { 251 retval = EXT2_ET_LLSEEK_FAILED; 252 written = 0; 253 } 254 else 255 { 256 written = client_write(data->file, buf, to_write); 257 258 /* Check for a short write. */ 259 260 if (written < to_write) 261 retval = EXT2_ET_SHORT_WRITE; 262 } 263 264 /* Handle write errors generally. */ 265 266 if (retval) 267 if (channel->write_error) 268 retval = (channel->write_error)(channel, block, count, buf, 269 to_write, written, retval); 270 271 return retval; 272 } 273 274 static errcode_t blockserver_read_blk(io_channel channel, unsigned long block, int count, void *buf) 275 { 276 return blockserver_read_blk64(channel, block, count, buf); 277 } 278 279 static errcode_t blockserver_write_blk(io_channel channel, unsigned long block, int count, const void *buf) 280 { 281 return blockserver_write_blk64(channel, block, count, buf); 282 } 283 284 /* Unsupported operations are omitted: see io_manager.c for optional operation 285 details. */ 286 287 static struct struct_io_manager struct_blockserver_manager = { 288 .magic = EXT2_ET_MAGIC_IO_MANAGER, 289 .name = "L4Re blockserver I/O Manager", 290 .open = blockserver_open, 291 .close = blockserver_close, 292 .set_blksize = blockserver_set_blksize, 293 .read_blk = blockserver_read_blk, 294 .write_blk = blockserver_write_blk, 295 .flush = blockserver_flush, 296 .write_byte = 0, 297 .set_option = blockserver_set_option, 298 .get_stats = blockserver_get_stats, 299 .read_blk64 = blockserver_read_blk64, 300 .write_blk64 = blockserver_write_blk64, 301 .discard = 0, 302 .cache_readahead = 0, 303 .zeroout = 0, 304 }; 305 306 io_manager blockserver_io_manager = &struct_blockserver_manager;