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