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