1 /* 2 * blockserver_io.c --- L4Re block server I/O manager. 3 * 4 * Copyright (C) 2019, 2020, 2021 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 ssize_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 client_seek(data->file, block * channel->block_size, SEEK_SET); 209 read = client_read(data->file, buf, to_read); 210 211 /* Check for a short read. */ 212 213 if ((read < 0) || ((size_t) read < to_read)) 214 retval = EXT2_ET_SHORT_READ; 215 216 /* Handle read errors generally. */ 217 218 if (retval) 219 if (channel->read_error) 220 retval = (channel->read_error)(channel, block, count, buf, 221 to_read, read, retval); 222 223 return retval; 224 } 225 226 static errcode_t blockserver_write_blk64(io_channel channel, unsigned long long block, int count, const void *buf) 227 { 228 /* Negative count values indicate a precise amount to write. */ 229 230 unsigned long long to_write = count < 0 ? -count : count * channel->block_size; 231 ssize_t written; 232 struct blockserver_private_data *data; 233 errcode_t retval = 0; 234 235 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 236 data = (struct blockserver_private_data *) channel->private_data; 237 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 238 239 /* Perform the write at the given location. */ 240 241 client_seek(data->file, block * channel->block_size, SEEK_SET); 242 written = client_write(data->file, buf, to_write); 243 244 /* Check for a short write. */ 245 246 if ((written < 0) || ((size_t) written < to_write)) 247 retval = EXT2_ET_SHORT_WRITE; 248 249 /* Handle write errors generally. */ 250 251 if (retval) 252 if (channel->write_error) 253 retval = (channel->write_error)(channel, block, count, buf, 254 to_write, written, retval); 255 256 return retval; 257 } 258 259 static errcode_t blockserver_read_blk(io_channel channel, unsigned long block, int count, void *buf) 260 { 261 return blockserver_read_blk64(channel, block, count, buf); 262 } 263 264 static errcode_t blockserver_write_blk(io_channel channel, unsigned long block, int count, const void *buf) 265 { 266 return blockserver_write_blk64(channel, block, count, buf); 267 } 268 269 /* Unsupported operations are omitted: see io_manager.c for optional operation 270 details. */ 271 272 static struct struct_io_manager struct_blockserver_manager = { 273 .magic = EXT2_ET_MAGIC_IO_MANAGER, 274 .name = "L4Re blockserver I/O Manager", 275 .open = blockserver_open, 276 .close = blockserver_close, 277 .set_blksize = blockserver_set_blksize, 278 .read_blk = blockserver_read_blk, 279 .write_blk = blockserver_write_blk, 280 .flush = blockserver_flush, 281 .write_byte = 0, 282 .set_option = blockserver_set_option, 283 .get_stats = blockserver_get_stats, 284 .read_blk64 = blockserver_read_blk64, 285 .write_blk64 = blockserver_write_blk64, 286 .discard = 0, 287 .cache_readahead = 0, 288 .zeroout = 0, 289 }; 290 291 io_manager blockserver_io_manager = &struct_blockserver_manager;