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