unix_io.c revision 9f8046fc6dfc13eee2f5c363214e60b533872cac
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#define _LARGEFILE_SOURCE 15#define _LARGEFILE64_SOURCE 16 17#include <stdio.h> 18#include <string.h> 19#if HAVE_UNISTD_H 20#include <unistd.h> 21#endif 22#if HAVE_ERRNO_H 23#include <errno.h> 24#endif 25#include <fcntl.h> 26#include <time.h> 27#if HAVE_SYS_STAT_H 28#include <sys/stat.h> 29#endif 30#if HAVE_SYS_TYPES_H 31#include <sys/types.h> 32#endif 33 34#include "ext2_fs.h" 35#include "ext2fs.h" 36 37/* 38 * For checking structure magic numbers... 39 */ 40 41#define EXT2_CHECK_MAGIC(struct, code) \ 42 if ((struct)->magic != (code)) return (code) 43 44struct unix_cache { 45 char *buf; 46 unsigned long block; 47 int access_time; 48 int dirty:1; 49 int in_use:1; 50}; 51 52#define CACHE_SIZE 8 53#define WRITE_VIA_CACHE_SIZE 4 /* Must be smaller than CACHE_SIZE */ 54 55struct unix_private_data { 56 int magic; 57 int dev; 58 int flags; 59 int access_time; 60 struct unix_cache cache[CACHE_SIZE]; 61}; 62 63static errcode_t unix_open(const char *name, int flags, io_channel *channel); 64static errcode_t unix_close(io_channel channel); 65static errcode_t unix_set_blksize(io_channel channel, int blksize); 66static errcode_t unix_read_blk(io_channel channel, unsigned long block, 67 int count, void *data); 68static errcode_t unix_write_blk(io_channel channel, unsigned long block, 69 int count, const void *data); 70static errcode_t unix_flush(io_channel channel); 71static errcode_t unix_write_byte(io_channel channel, unsigned long offset, 72 int size, const void *data); 73 74static struct struct_io_manager struct_unix_manager = { 75 EXT2_ET_MAGIC_IO_MANAGER, 76 "Unix I/O Manager", 77 unix_open, 78 unix_close, 79 unix_set_blksize, 80 unix_read_blk, 81 unix_write_blk, 82 unix_flush, 83 unix_write_byte 84}; 85 86io_manager unix_io_manager = &struct_unix_manager; 87 88/* 89 * Here are the raw I/O functions 90 */ 91static errcode_t raw_read_blk(io_channel channel, 92 struct unix_private_data *data, 93 unsigned long block, 94 int count, void *buf) 95{ 96 errcode_t retval; 97 size_t size; 98 ext2_loff_t location; 99 int actual = 0; 100 101 size = (count < 0) ? -count : count * channel->block_size; 102 location = (ext2_loff_t) block * channel->block_size; 103 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { 104 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; 105 goto error_out; 106 } 107 actual = read(data->dev, buf, size); 108 if (actual != size) { 109 if (actual < 0) 110 actual = 0; 111 retval = EXT2_ET_SHORT_READ; 112 goto error_out; 113 } 114 return 0; 115 116error_out: 117 memset((char *) buf+actual, 0, size-actual); 118 if (channel->read_error) 119 retval = (channel->read_error)(channel, block, count, buf, 120 size, actual, retval); 121 return retval; 122} 123 124static errcode_t raw_write_blk(io_channel channel, 125 struct unix_private_data *data, 126 unsigned long block, 127 int count, const void *buf) 128{ 129 size_t size; 130 ext2_loff_t location; 131 int actual = 0; 132 errcode_t retval; 133 134 if (count == 1) 135 size = channel->block_size; 136 else { 137 if (count < 0) 138 size = -count; 139 else 140 size = count * channel->block_size; 141 } 142 143 location = (ext2_loff_t) block * channel->block_size; 144 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { 145 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; 146 goto error_out; 147 } 148 149 actual = write(data->dev, buf, size); 150 if (actual != size) { 151 retval = EXT2_ET_SHORT_WRITE; 152 goto error_out; 153 } 154 return 0; 155 156error_out: 157 if (channel->write_error) 158 retval = (channel->write_error)(channel, block, count, buf, 159 size, actual, retval); 160 return retval; 161} 162 163 164/* 165 * Here we implement the cache functions 166 */ 167 168/* Allocate the cache buffers */ 169static errcode_t alloc_cache(io_channel channel, 170 struct unix_private_data *data) 171{ 172 errcode_t retval; 173 struct unix_cache *cache; 174 int i; 175 176 data->access_time = 0; 177 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { 178 cache->block = 0; 179 cache->access_time = 0; 180 cache->dirty = 0; 181 cache->in_use = 0; 182 if ((retval = ext2fs_get_mem(channel->block_size, 183 (void **) &cache->buf))) 184 return retval; 185 } 186 return 0; 187} 188 189/* Free the cache buffers */ 190static void free_cache(io_channel channel, 191 struct unix_private_data *data) 192{ 193 struct unix_cache *cache; 194 int i; 195 196 data->access_time = 0; 197 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { 198 cache->block = 0; 199 cache->access_time = 0; 200 cache->dirty = 0; 201 cache->in_use = 0; 202 if (cache->buf) 203 ext2fs_free_mem((void **) &cache->buf); 204 cache->buf = 0; 205 } 206} 207 208/* 209 * Try to find a block in the cache. If get_cache is non-zero, then 210 * if the block isn't in the cache, evict the oldest block in the 211 * cache and create a new cache entry for the requested block. 212 */ 213static struct unix_cache *find_cached_block(io_channel channel, 214 struct unix_private_data *data, 215 unsigned long block, 216 int get_cache) 217{ 218 struct unix_cache *cache, *unused_cache, *oldest_cache; 219 int i; 220 221 unused_cache = oldest_cache = 0; 222 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { 223 if (!cache->in_use) { 224 unused_cache = cache; 225 continue; 226 } 227 if (cache->block == block) { 228 cache->access_time = ++data->access_time; 229 return cache; 230 } 231 if (!oldest_cache || 232 (cache->access_time < oldest_cache->access_time)) 233 oldest_cache = cache; 234 } 235 if (!get_cache) 236 return 0; 237 238 /* 239 * Try to allocate cache slot. 240 */ 241 if (unused_cache) 242 cache = unused_cache; 243 else { 244 cache = oldest_cache; 245 if (cache->dirty) 246 raw_write_blk(channel, data, 247 cache->block, 1, cache->buf); 248 } 249 cache->in_use = 1; 250 cache->block = block; 251 cache->access_time = ++data->access_time; 252 return cache; 253} 254 255/* 256 * Flush all of the blocks in the cache 257 */ 258static errcode_t flush_cached_blocks(io_channel channel, 259 struct unix_private_data *data, 260 int invalidate) 261 262{ 263 struct unix_cache *cache; 264 errcode_t retval, retval2; 265 int i; 266 267 retval2 = 0; 268 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { 269 if (!cache->in_use) 270 continue; 271 272 if (invalidate) 273 cache->in_use = 0; 274 275 if (!cache->dirty) 276 continue; 277 278 retval = raw_write_blk(channel, data, 279 cache->block, 1, cache->buf); 280 if (retval) 281 retval2 = retval; 282 else 283 cache->dirty = 0; 284 } 285 return retval2; 286} 287 288 289 290static errcode_t unix_open(const char *name, int flags, io_channel *channel) 291{ 292 io_channel io = NULL; 293 struct unix_private_data *data = NULL; 294 errcode_t retval; 295 int open_flags; 296 297 if (name == 0) 298 return EXT2_ET_BAD_DEVICE_NAME; 299 retval = ext2fs_get_mem(sizeof(struct struct_io_channel), 300 (void **) &io); 301 if (retval) 302 return retval; 303 memset(io, 0, sizeof(struct struct_io_channel)); 304 io->magic = EXT2_ET_MAGIC_IO_CHANNEL; 305 retval = ext2fs_get_mem(sizeof(struct unix_private_data), 306 (void **) &data); 307 if (retval) 308 goto cleanup; 309 310 io->manager = unix_io_manager; 311 retval = ext2fs_get_mem(strlen(name)+1, (void **) &io->name); 312 if (retval) 313 goto cleanup; 314 315 strcpy(io->name, name); 316 io->private_data = data; 317 io->block_size = 1024; 318 io->read_error = 0; 319 io->write_error = 0; 320 io->refcount = 1; 321 322 memset(data, 0, sizeof(struct unix_private_data)); 323 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; 324 325 if ((retval = alloc_cache(io, data))) 326 goto cleanup; 327 328 open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; 329#ifdef HAVE_OPEN64 330 data->dev = open64(name, open_flags); 331#else 332 data->dev = open(name, open_flags); 333#endif 334 if (data->dev < 0) { 335 retval = errno; 336 goto cleanup; 337 } 338 *channel = io; 339 return 0; 340 341cleanup: 342 if (data) { 343 free_cache(io, data); 344 ext2fs_free_mem((void **) &data); 345 } 346 if (io) 347 ext2fs_free_mem((void **) &io); 348 return retval; 349} 350 351static errcode_t unix_close(io_channel channel) 352{ 353 struct unix_private_data *data; 354 errcode_t retval = 0; 355 356 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 357 data = (struct unix_private_data *) channel->private_data; 358 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 359 360 if (--channel->refcount > 0) 361 return 0; 362 363 retval = flush_cached_blocks(channel, data, 0); 364 365 if (close(data->dev) < 0) 366 retval = errno; 367 free_cache(channel, data); 368 if (channel->private_data) 369 ext2fs_free_mem((void **) &channel->private_data); 370 if (channel->name) 371 ext2fs_free_mem((void **) &channel->name); 372 ext2fs_free_mem((void **) &channel); 373 return retval; 374} 375 376static errcode_t unix_set_blksize(io_channel channel, int blksize) 377{ 378 struct unix_private_data *data; 379 errcode_t retval; 380 381 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 382 data = (struct unix_private_data *) channel->private_data; 383 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 384 385 if (channel->block_size != blksize) { 386 if ((retval = flush_cached_blocks(channel, data, 0))) 387 return retval; 388 389 channel->block_size = blksize; 390 free_cache(channel, data); 391 if ((retval = alloc_cache(channel, data))) 392 return retval; 393 } 394 return 0; 395} 396 397 398static errcode_t unix_read_blk(io_channel channel, unsigned long block, 399 int count, void *buf) 400{ 401 struct unix_private_data *data; 402 struct unix_cache *cache; 403 errcode_t retval; 404 char *cp; 405 int i, j; 406 407 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 408 data = (struct unix_private_data *) channel->private_data; 409 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 410 411 /* 412 * If we're doing an odd-sized read, flush out the cache and 413 * then do a direct read. 414 */ 415 if (count < 0) { 416 if ((retval = flush_cached_blocks(channel, data, 0))) 417 return retval; 418 return raw_read_blk(channel, data, block, count, buf); 419 } 420 421 cp = buf; 422 while (count > 0) { 423 /* If it's in the cache, use it! */ 424 if ((cache = find_cached_block(channel, data, block, 0))) { 425#ifdef DEBUG 426 printf("Using cached block %d\n", block); 427#endif 428 memcpy(cp, cache->buf, channel->block_size); 429 count--; 430 block++; 431 cp += channel->block_size; 432 continue; 433 } 434 /* 435 * Find the number of uncached blocks so we can do a 436 * single read request 437 */ 438 for (i=1; i < count; i++) 439 if (find_cached_block(channel, data, block+i, 0)) 440 break; 441#ifdef DEBUG 442 printf("Reading %d blocks starting at %d\n", i, block); 443#endif 444 if ((retval = raw_read_blk(channel, data, block, i, cp))) 445 return retval; 446 447 /* Save the results in the cache */ 448 for (j=0; j < i; j++) { 449 count--; 450 cache = find_cached_block(channel, data, block++, 1); 451 if (cache) 452 memcpy(cache->buf, cp, channel->block_size); 453 cp += channel->block_size; 454 } 455 } 456 return 0; 457} 458 459static errcode_t unix_write_blk(io_channel channel, unsigned long block, 460 int count, const void *buf) 461{ 462 struct unix_private_data *data; 463 struct unix_cache *cache; 464 errcode_t retval = 0, retval2; 465 const char *cp; 466 int writethrough; 467 468 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 469 data = (struct unix_private_data *) channel->private_data; 470 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 471 472 /* 473 * If we're doing an odd-sized write or a very large write, 474 * flush out the cache completely and then do a direct write. 475 */ 476 if (count < 0 || count > WRITE_VIA_CACHE_SIZE) { 477 if ((retval = flush_cached_blocks(channel, data, 1))) 478 return retval; 479 return raw_write_blk(channel, data, block, count, buf); 480 } 481 482 /* 483 * For a moderate-sized multi-block write, first force a write 484 * if we're in write-through cache mode, and then fill the 485 * cache with the blocks. 486 */ 487 writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH; 488 if (writethrough) 489 retval = raw_write_blk(channel, data, block, count, buf); 490 491 cp = buf; 492 while (count > 0) { 493 cache = find_cached_block(channel, data, block, 1); 494 if (!cache) { 495 /* 496 * Oh shit, we couldn't get cache descriptor. 497 * Force the write directly. 498 */ 499 if ((retval2 = raw_write_blk(channel, data, block, 500 1, cp))) 501 retval = retval2; 502 } else { 503 memcpy(cache->buf, cp, channel->block_size); 504 cache->dirty = !writethrough; 505 } 506 count--; 507 block++; 508 cp += channel->block_size; 509 } 510 return retval; 511} 512 513static errcode_t unix_write_byte(io_channel channel, unsigned long offset, 514 int size, const void *buf) 515{ 516 struct unix_private_data *data; 517 errcode_t retval = 0; 518 size_t actual; 519 520 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 521 data = (struct unix_private_data *) channel->private_data; 522 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 523 524 /* 525 * Flush out the cache completely 526 */ 527 if ((retval = flush_cached_blocks(channel, data, 1))) 528 return retval; 529 530 if (lseek(data->dev, offset, SEEK_SET) < 0) 531 return errno; 532 533 actual = write(data->dev, buf, size); 534 if (actual != size) 535 return EXT2_ET_SHORT_WRITE; 536 537 return 0; 538} 539 540/* 541 * Flush data buffers to disk. 542 */ 543static errcode_t unix_flush(io_channel channel) 544{ 545 struct unix_private_data *data; 546 errcode_t retval = 0; 547 548 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 549 data = (struct unix_private_data *) channel->private_data; 550 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 551 552 retval = flush_cached_blocks(channel, data, 0); 553 fsync(data->dev); 554 return retval; 555} 556 557