unix_io.c revision c4e749abd8451f02418fe552b2af14f226f7bd1e
1/* 2 * unix_io.c --- This is the Unix I/O interface to the I/O manager. 3 * 4 * Implements a one-block write-through cache. 5 * 6 * Copyright (C) 1993, 1994, 1995 Theodore Ts'o. 7 * 8 * %Begin-Header% 9 * This file may be redistributed under the terms of the GNU Public 10 * License. 11 * %End-Header% 12 */ 13 14#include <stdio.h> 15#include <string.h> 16#if HAVE_UNISTD_H 17#include <unistd.h> 18#endif 19#if HAVE_ERRNO_H 20#include <errno.h> 21#endif 22#include <fcntl.h> 23#include <time.h> 24#if HAVE_SYS_STAT_H 25#include <sys/stat.h> 26#endif 27#if HAVE_SYS_TYPES_H 28#include <sys/types.h> 29#endif 30 31#if EXT2_FLAT_INCLUDES 32#include "ext2_fs.h" 33#else 34#include <linux/ext2_fs.h> 35#endif 36 37#include "ext2fs.h" 38 39/* 40 * For checking structure magic numbers... 41 */ 42 43#define EXT2_CHECK_MAGIC(struct, code) \ 44 if ((struct)->magic != (code)) return (code) 45 46struct unix_private_data { 47 int magic; 48 int dev; 49 int flags; 50 char *buf; 51 int buf_block_nr; 52}; 53 54static errcode_t unix_open(const char *name, int flags, io_channel *channel); 55static errcode_t unix_close(io_channel channel); 56static errcode_t unix_set_blksize(io_channel channel, int blksize); 57static errcode_t unix_read_blk(io_channel channel, unsigned long block, 58 int count, void *data); 59static errcode_t unix_write_blk(io_channel channel, unsigned long block, 60 int count, const void *data); 61static errcode_t unix_flush(io_channel channel); 62 63static struct struct_io_manager struct_unix_manager = { 64 EXT2_ET_MAGIC_IO_MANAGER, 65 "Unix I/O Manager", 66 unix_open, 67 unix_close, 68 unix_set_blksize, 69 unix_read_blk, 70 unix_write_blk, 71 unix_flush 72}; 73 74io_manager unix_io_manager = &struct_unix_manager; 75 76static errcode_t unix_open(const char *name, int flags, io_channel *channel) 77{ 78 io_channel io = NULL; 79 struct unix_private_data *data = NULL; 80 errcode_t retval; 81 82 if (name == 0) 83 return EXT2_ET_BAD_DEVICE_NAME; 84 retval = ext2fs_get_mem(sizeof(struct struct_io_channel), 85 (void **) &io); 86 if (retval) 87 return retval; 88 memset(io, 0, sizeof(struct struct_io_channel)); 89 io->magic = EXT2_ET_MAGIC_IO_CHANNEL; 90 retval = ext2fs_get_mem(sizeof(struct unix_private_data), 91 (void **) &data); 92 if (retval) 93 goto cleanup; 94 95 io->manager = unix_io_manager; 96 retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name); 97 if (retval) 98 goto cleanup; 99 100 strcpy(io->name, name); 101 io->private_data = data; 102 io->block_size = 1024; 103 io->read_error = 0; 104 io->write_error = 0; 105 io->refcount = 1; 106 107 memset(data, 0, sizeof(struct unix_private_data)); 108 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; 109 retval = ext2fs_get_mem(io->block_size, (void **) &data->buf); 110 data->buf_block_nr = -1; 111 if (retval) 112 goto cleanup; 113 114 data->dev = open(name, (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY); 115 if (data->dev < 0) { 116 retval = errno; 117 goto cleanup; 118 } 119 *channel = io; 120 return 0; 121 122cleanup: 123 if (io) 124 ext2fs_free_mem((void **) &io); 125 if (data) { 126 if (data->buf) 127 ext2fs_free_mem((void **) &data->buf); 128 ext2fs_free_mem((void **) &data); 129 } 130 return retval; 131} 132 133static errcode_t unix_close(io_channel channel) 134{ 135 struct unix_private_data *data; 136 errcode_t retval = 0; 137 138 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 139 data = (struct unix_private_data *) channel->private_data; 140 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 141 142 if (--channel->refcount > 0) 143 return 0; 144 145 if (close(data->dev) < 0) 146 retval = errno; 147 if (data->buf) 148 ext2fs_free_mem((void **) &data->buf); 149 if (channel->private_data) 150 ext2fs_free_mem((void **) &channel->private_data); 151 if (channel->name) 152 ext2fs_free_mem((void **) &channel->name); 153 ext2fs_free_mem((void **) &channel); 154 return retval; 155} 156 157static errcode_t unix_set_blksize(io_channel channel, int blksize) 158{ 159 struct unix_private_data *data; 160 errcode_t retval; 161 162 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 163 data = (struct unix_private_data *) channel->private_data; 164 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 165 166 if (channel->block_size != blksize) { 167 channel->block_size = blksize; 168 ext2fs_free_mem((void **) &data->buf); 169 retval = ext2fs_get_mem(blksize, (void **) &data->buf); 170 if (retval) 171 return retval; 172 data->buf_block_nr = -1; 173 } 174 return 0; 175} 176 177 178static errcode_t unix_read_blk(io_channel channel, unsigned long block, 179 int count, void *buf) 180{ 181 struct unix_private_data *data; 182 errcode_t retval; 183 size_t size; 184 ext2_loff_t location; 185 int actual = 0; 186 187 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 188 data = (struct unix_private_data *) channel->private_data; 189 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 190 191 /* 192 * If it's in the cache, use it! 193 */ 194 if ((count == 1) && (block == data->buf_block_nr)) { 195 memcpy(buf, data->buf, channel->block_size); 196 return 0; 197 } 198#if 0 199 printf("read_block %lu (%d)\n", block, count); 200#endif 201 size = (count < 0) ? -count : count * channel->block_size; 202 location = (ext2_loff_t) block * channel->block_size; 203 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { 204 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; 205 goto error_out; 206 } 207 actual = read(data->dev, buf, size); 208 if (actual != size) { 209 if (actual < 0) 210 actual = 0; 211 retval = EXT2_ET_SHORT_READ; 212 goto error_out; 213 } 214 if (count == 1) { 215 data->buf_block_nr = block; 216 memcpy(data->buf, buf, size); /* Update the cache */ 217 } 218 return 0; 219 220error_out: 221 memset((char *) buf+actual, 0, size-actual); 222 if (channel->read_error) 223 retval = (channel->read_error)(channel, block, count, buf, 224 size, actual, retval); 225 return retval; 226} 227 228static errcode_t unix_write_blk(io_channel channel, unsigned long block, 229 int count, const void *buf) 230{ 231 struct unix_private_data *data; 232 size_t size; 233 ext2_loff_t location; 234 int actual = 0; 235 errcode_t retval; 236 237 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 238 data = (struct unix_private_data *) channel->private_data; 239 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 240 241 if (count == 1) 242 size = channel->block_size; 243 else { 244 data->buf_block_nr = -1; /* Invalidate the cache */ 245 if (count < 0) 246 size = -count; 247 else 248 size = count * channel->block_size; 249 } 250 251 location = (ext2_loff_t) block * channel->block_size; 252 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { 253 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; 254 goto error_out; 255 } 256 257 actual = write(data->dev, buf, size); 258 if (actual != size) { 259 retval = EXT2_ET_SHORT_WRITE; 260 goto error_out; 261 } 262 263 if ((count == 1) && (block == data->buf_block_nr)) 264 memcpy(data->buf, buf, size); /* Update the cache */ 265 266 return 0; 267 268error_out: 269 if (channel->write_error) 270 retval = (channel->write_error)(channel, block, count, buf, 271 size, actual, retval); 272 return retval; 273} 274 275/* 276 * Flush data buffers to disk. 277 */ 278static errcode_t unix_flush(io_channel channel) 279{ 280 struct unix_private_data *data; 281 282 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 283 data = (struct unix_private_data *) channel->private_data; 284 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 285 286 fsync(data->dev); 287 return 0; 288} 289 290