1/* 2 * unix_io.c --- This is the Unix (well, really POSIX) implementation 3 * of the I/O manager. 4 * 5 * Implements a one-block write-through cache. 6 * 7 * Includes support for Windows NT support under Cygwin. 8 * 9 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 10 * 2002 by Theodore Ts'o. 11 * 12 * %Begin-Header% 13 * This file may be redistributed under the terms of the GNU Library 14 * General Public License, version 2. 15 * %End-Header% 16 */ 17 18#define _LARGEFILE_SOURCE 19#define _LARGEFILE64_SOURCE 20#define _GNU_SOURCE 21 22#include <stdio.h> 23#include <string.h> 24#if HAVE_UNISTD_H 25#include <unistd.h> 26#endif 27#if HAVE_ERRNO_H 28#include <errno.h> 29#endif 30#include <fcntl.h> 31#include <time.h> 32#ifdef __linux__ 33#include <sys/utsname.h> 34#endif 35#ifdef HAVE_SYS_IOCTL_H 36#include <sys/ioctl.h> 37#endif 38#ifdef HAVE_SYS_MOUNT_H 39#include <sys/mount.h> 40#endif 41#if HAVE_SYS_STAT_H 42#include <sys/stat.h> 43#endif 44#if HAVE_SYS_TYPES_H 45#include <sys/types.h> 46#endif 47#if HAVE_SYS_RESOURCE_H 48#include <sys/resource.h> 49#endif 50 51#if defined(__linux__) && defined(_IO) && !defined(BLKROGET) 52#define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */ 53#endif 54 55#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET) 56#define BLKSSZGET _IO(0x12,104)/* get block device sector size */ 57#endif 58 59#undef ALIGN_DEBUG 60 61#include "ext2_fs.h" 62#include "ext2fs.h" 63 64/* 65 * For checking structure magic numbers... 66 */ 67 68#define EXT2_CHECK_MAGIC(struct, code) \ 69 if ((struct)->magic != (code)) return (code) 70 71struct unix_cache { 72 char *buf; 73 unsigned long block; 74 int access_time; 75 unsigned dirty:1; 76 unsigned in_use:1; 77}; 78 79#define CACHE_SIZE 8 80#define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */ 81#define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */ 82 83struct unix_private_data { 84 int magic; 85 int dev; 86 int flags; 87 int align; 88 int access_time; 89 ext2_loff_t offset; 90 struct unix_cache cache[CACHE_SIZE]; 91 void *bounce; 92 struct struct_io_stats io_stats; 93}; 94 95#define IS_ALIGNED(n, align) ((((unsigned long) n) & \ 96 ((unsigned long) ((align)-1))) == 0) 97 98static errcode_t unix_open(const char *name, int flags, io_channel *channel); 99static errcode_t unix_close(io_channel channel); 100static errcode_t unix_set_blksize(io_channel channel, int blksize); 101static errcode_t unix_read_blk(io_channel channel, unsigned long block, 102 int count, void *data); 103static errcode_t unix_write_blk(io_channel channel, unsigned long block, 104 int count, const void *data); 105static errcode_t unix_flush(io_channel channel); 106static errcode_t unix_write_byte(io_channel channel, unsigned long offset, 107 int size, const void *data); 108static errcode_t unix_set_option(io_channel channel, const char *option, 109 const char *arg); 110static errcode_t unix_get_stats(io_channel channel, io_stats *stats) 111; 112static void reuse_cache(io_channel channel, struct unix_private_data *data, 113 struct unix_cache *cache, unsigned long long block); 114static errcode_t unix_read_blk64(io_channel channel, unsigned long long block, 115 int count, void *data); 116static errcode_t unix_write_blk64(io_channel channel, unsigned long long block, 117 int count, const void *data); 118 119static struct struct_io_manager struct_unix_manager = { 120 EXT2_ET_MAGIC_IO_MANAGER, 121 "Unix I/O Manager", 122 unix_open, 123 unix_close, 124 unix_set_blksize, 125 unix_read_blk, 126 unix_write_blk, 127 unix_flush, 128 unix_write_byte, 129 unix_set_option, 130 unix_get_stats, 131 unix_read_blk64, 132 unix_write_blk64, 133}; 134 135io_manager unix_io_manager = &struct_unix_manager; 136 137static errcode_t unix_get_stats(io_channel channel, io_stats *stats) 138{ 139 errcode_t retval = 0; 140 141 struct unix_private_data *data; 142 143 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 144 data = (struct unix_private_data *) channel->private_data; 145 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 146 147 if (stats) 148 *stats = &data->io_stats; 149 150 return retval; 151} 152 153/* 154 * Here are the raw I/O functions 155 */ 156static errcode_t raw_read_blk(io_channel channel, 157 struct unix_private_data *data, 158 unsigned long long block, 159 int count, void *buf) 160{ 161 errcode_t retval; 162 ssize_t size; 163 ext2_loff_t location; 164 int actual = 0; 165 166 size = (count < 0) ? -count : count * channel->block_size; 167 data->io_stats.bytes_read += size; 168 location = ((ext2_loff_t) block * channel->block_size) + data->offset; 169 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { 170 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; 171 goto error_out; 172 } 173 if ((data->align == 0) || 174 ((IS_ALIGNED(buf, data->align)) && IS_ALIGNED(size, data->align))) { 175 actual = read(data->dev, buf, size); 176 if (actual != size) { 177 short_read: 178 if (actual < 0) 179 actual = 0; 180 retval = EXT2_ET_SHORT_READ; 181 goto error_out; 182 } 183 return 0; 184 } 185 186#ifdef ALIGN_DEBUG 187 printf("raw_read_blk: O_DIRECT fallback: %p %lu\n", buf, 188 (unsigned long) size); 189#endif 190 191 /* 192 * The buffer or size which we're trying to read isn't aligned 193 * to the O_DIRECT rules, so we need to do this the hard way... 194 */ 195 while (size > 0) { 196 actual = read(data->dev, data->bounce, channel->block_size); 197 if (actual != channel->block_size) 198 goto short_read; 199 actual = size; 200 if (size > channel->block_size) 201 actual = channel->block_size; 202 memcpy(buf, data->bounce, actual); 203 size -= actual; 204 buf += actual; 205 } 206 return 0; 207 208error_out: 209 memset((char *) buf+actual, 0, size-actual); 210 if (channel->read_error) 211 retval = (channel->read_error)(channel, block, count, buf, 212 size, actual, retval); 213 return retval; 214} 215 216static errcode_t raw_write_blk(io_channel channel, 217 struct unix_private_data *data, 218 unsigned long long block, 219 int count, const void *buf) 220{ 221 ssize_t size; 222 ext2_loff_t location; 223 int actual = 0; 224 errcode_t retval; 225 226 if (count == 1) 227 size = channel->block_size; 228 else { 229 if (count < 0) 230 size = -count; 231 else 232 size = count * channel->block_size; 233 } 234 data->io_stats.bytes_written += size; 235 236 location = ((ext2_loff_t) block * channel->block_size) + data->offset; 237 if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { 238 retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; 239 goto error_out; 240 } 241 242 if ((data->align == 0) || 243 ((IS_ALIGNED(buf, data->align)) && IS_ALIGNED(size, data->align))) { 244 actual = write(data->dev, buf, size); 245 if (actual != size) { 246 short_write: 247 retval = EXT2_ET_SHORT_WRITE; 248 goto error_out; 249 } 250 return 0; 251 } 252 253#ifdef ALIGN_DEBUG 254 printf("raw_write_blk: O_DIRECT fallback: %p %lu\n", buf, 255 (unsigned long) size); 256#endif 257 /* 258 * The buffer or size which we're trying to write isn't aligned 259 * to the O_DIRECT rules, so we need to do this the hard way... 260 */ 261 while (size > 0) { 262 if (size < channel->block_size) { 263 actual = read(data->dev, data->bounce, 264 channel->block_size); 265 if (actual != channel->block_size) { 266 retval = EXT2_ET_SHORT_READ; 267 goto error_out; 268 } 269 } 270 actual = size; 271 if (size > channel->block_size) 272 actual = channel->block_size; 273 memcpy(data->bounce, buf, actual); 274 actual = write(data->dev, data->bounce, channel->block_size); 275 if (actual != channel->block_size) 276 goto short_write; 277 size -= actual; 278 buf += actual; 279 } 280 return 0; 281 282error_out: 283 if (channel->write_error) 284 retval = (channel->write_error)(channel, block, count, buf, 285 size, actual, retval); 286 return retval; 287} 288 289 290/* 291 * Here we implement the cache functions 292 */ 293 294/* Allocate the cache buffers */ 295static errcode_t alloc_cache(io_channel channel, 296 struct unix_private_data *data) 297{ 298 errcode_t retval; 299 struct unix_cache *cache; 300 int i; 301 302 data->access_time = 0; 303 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { 304 cache->block = 0; 305 cache->access_time = 0; 306 cache->dirty = 0; 307 cache->in_use = 0; 308 if (cache->buf) 309 ext2fs_free_mem(&cache->buf); 310 retval = ext2fs_get_memalign(channel->block_size, 311 data->align, &cache->buf); 312 if (retval) 313 return retval; 314 } 315 if (data->align) { 316 if (data->bounce) 317 ext2fs_free_mem(&data->bounce); 318 retval = ext2fs_get_memalign(channel->block_size, data->align, 319 &data->bounce); 320 } 321 return retval; 322} 323 324/* Free the cache buffers */ 325static void free_cache(struct unix_private_data *data) 326{ 327 struct unix_cache *cache; 328 int i; 329 330 data->access_time = 0; 331 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { 332 cache->block = 0; 333 cache->access_time = 0; 334 cache->dirty = 0; 335 cache->in_use = 0; 336 if (cache->buf) 337 ext2fs_free_mem(&cache->buf); 338 } 339 if (data->bounce) 340 ext2fs_free_mem(&data->bounce); 341} 342 343#ifndef NO_IO_CACHE 344/* 345 * Try to find a block in the cache. If the block is not found, and 346 * eldest is a non-zero pointer, then fill in eldest with the cache 347 * entry to that should be reused. 348 */ 349static struct unix_cache *find_cached_block(struct unix_private_data *data, 350 unsigned long long block, 351 struct unix_cache **eldest) 352{ 353 struct unix_cache *cache, *unused_cache, *oldest_cache; 354 int i; 355 356 unused_cache = oldest_cache = 0; 357 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { 358 if (!cache->in_use) { 359 if (!unused_cache) 360 unused_cache = cache; 361 continue; 362 } 363 if (cache->block == block) { 364 cache->access_time = ++data->access_time; 365 return cache; 366 } 367 if (!oldest_cache || 368 (cache->access_time < oldest_cache->access_time)) 369 oldest_cache = cache; 370 } 371 if (eldest) 372 *eldest = (unused_cache) ? unused_cache : oldest_cache; 373 return 0; 374} 375 376/* 377 * Reuse a particular cache entry for another block. 378 */ 379static void reuse_cache(io_channel channel, struct unix_private_data *data, 380 struct unix_cache *cache, unsigned long long block) 381{ 382 if (cache->dirty && cache->in_use) 383 raw_write_blk(channel, data, cache->block, 1, cache->buf); 384 385 cache->in_use = 1; 386 cache->dirty = 0; 387 cache->block = block; 388 cache->access_time = ++data->access_time; 389} 390 391/* 392 * Flush all of the blocks in the cache 393 */ 394static errcode_t flush_cached_blocks(io_channel channel, 395 struct unix_private_data *data, 396 int invalidate) 397 398{ 399 struct unix_cache *cache; 400 errcode_t retval, retval2; 401 int i; 402 403 retval2 = 0; 404 for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { 405 if (!cache->in_use) 406 continue; 407 408 if (invalidate) 409 cache->in_use = 0; 410 411 if (!cache->dirty) 412 continue; 413 414 retval = raw_write_blk(channel, data, 415 cache->block, 1, cache->buf); 416 if (retval) 417 retval2 = retval; 418 else 419 cache->dirty = 0; 420 } 421 return retval2; 422} 423#endif /* NO_IO_CACHE */ 424 425static errcode_t unix_open(const char *name, int flags, io_channel *channel) 426{ 427 io_channel io = NULL; 428 struct unix_private_data *data = NULL; 429 errcode_t retval; 430 int open_flags; 431 int f_nocache = 0; 432 struct stat st; 433#ifdef __linux__ 434 struct utsname ut; 435#endif 436 437 if (name == 0) 438 return EXT2_ET_BAD_DEVICE_NAME; 439 retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); 440 if (retval) 441 return retval; 442 memset(io, 0, sizeof(struct struct_io_channel)); 443 io->magic = EXT2_ET_MAGIC_IO_CHANNEL; 444 retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data); 445 if (retval) 446 goto cleanup; 447 448 io->manager = unix_io_manager; 449 retval = ext2fs_get_mem(strlen(name)+1, &io->name); 450 if (retval) 451 goto cleanup; 452 453 strcpy(io->name, name); 454 io->private_data = data; 455 io->block_size = 1024; 456 io->read_error = 0; 457 io->write_error = 0; 458 io->refcount = 1; 459 460 memset(data, 0, sizeof(struct unix_private_data)); 461 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; 462 data->io_stats.num_fields = 2; 463 464 open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; 465 if (flags & IO_FLAG_EXCLUSIVE) 466 open_flags |= O_EXCL; 467 if (flags & IO_FLAG_DIRECT_IO) 468#if !defined(O_DIRECT) && defined(F_NOCACHE) 469 f_nocache = F_NOCACHE; 470#else 471 open_flags |= O_DIRECT; 472#endif 473 data->flags = flags; 474 475#ifdef HAVE_OPEN64 476 data->dev = open64(io->name, open_flags); 477#else 478 data->dev = open(io->name, open_flags); 479#endif 480 if (data->dev < 0) { 481 retval = errno; 482 goto cleanup; 483 } 484 485 if (f_nocache) { 486 if (fcntl(data->dev, f_nocache, 1) < 0) { 487 retval = errno; 488 goto cleanup; 489 } 490 } 491 492#ifdef BLKSSZGET 493 if (flags & IO_FLAG_DIRECT_IO) { 494 if (ioctl(data->dev, BLKSSZGET, &data->align) != 0) 495 data->align = io->block_size; 496 } 497#endif 498 499#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 500 /* 501 * Some operating systems require that the buffers be aligned, 502 * regardless of O_DIRECT 503 */ 504 data->align = 512; 505#endif 506 507 508 if ((retval = alloc_cache(io, data))) 509 goto cleanup; 510 511#ifdef BLKROGET 512 if (flags & IO_FLAG_RW) { 513 int error; 514 int readonly = 0; 515 516 /* Is the block device actually writable? */ 517 error = ioctl(data->dev, BLKROGET, &readonly); 518 if (!error && readonly) { 519 close(data->dev); 520 retval = EPERM; 521 goto cleanup; 522 } 523 } 524#endif 525 526#ifdef __linux__ 527#undef RLIM_INFINITY 528#if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4))) 529#define RLIM_INFINITY ((unsigned long)(~0UL>>1)) 530#else 531#define RLIM_INFINITY (~0UL) 532#endif 533 /* 534 * Work around a bug in 2.4.10-2.4.18 kernels where writes to 535 * block devices are wrongly getting hit by the filesize 536 * limit. This workaround isn't perfect, since it won't work 537 * if glibc wasn't built against 2.2 header files. (Sigh.) 538 * 539 */ 540 if ((flags & IO_FLAG_RW) && 541 (uname(&ut) == 0) && 542 ((ut.release[0] == '2') && (ut.release[1] == '.') && 543 (ut.release[2] == '4') && (ut.release[3] == '.') && 544 (ut.release[4] == '1') && (ut.release[5] >= '0') && 545 (ut.release[5] < '8')) && 546 (fstat(data->dev, &st) == 0) && 547 (S_ISBLK(st.st_mode))) { 548 struct rlimit rlim; 549 550 rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; 551 setrlimit(RLIMIT_FSIZE, &rlim); 552 getrlimit(RLIMIT_FSIZE, &rlim); 553 if (((unsigned long) rlim.rlim_cur) < 554 ((unsigned long) rlim.rlim_max)) { 555 rlim.rlim_cur = rlim.rlim_max; 556 setrlimit(RLIMIT_FSIZE, &rlim); 557 } 558 } 559#endif 560 *channel = io; 561 return 0; 562 563cleanup: 564 if (data) { 565 free_cache(data); 566 ext2fs_free_mem(&data); 567 } 568 if (io) 569 ext2fs_free_mem(&io); 570 return retval; 571} 572 573static errcode_t unix_close(io_channel channel) 574{ 575 struct unix_private_data *data; 576 errcode_t retval = 0; 577 578 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 579 data = (struct unix_private_data *) channel->private_data; 580 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 581 582 if (--channel->refcount > 0) 583 return 0; 584 585#ifndef NO_IO_CACHE 586 retval = flush_cached_blocks(channel, data, 0); 587#endif 588 589 if (close(data->dev) < 0) 590 retval = errno; 591 free_cache(data); 592 593 ext2fs_free_mem(&channel->private_data); 594 if (channel->name) 595 ext2fs_free_mem(&channel->name); 596 ext2fs_free_mem(&channel); 597 return retval; 598} 599 600static errcode_t unix_set_blksize(io_channel channel, int blksize) 601{ 602 struct unix_private_data *data; 603 errcode_t retval; 604 605 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 606 data = (struct unix_private_data *) channel->private_data; 607 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 608 609 if (channel->block_size != blksize) { 610#ifndef NO_IO_CACHE 611 if ((retval = flush_cached_blocks(channel, data, 0))) 612 return retval; 613#endif 614 615 channel->block_size = blksize; 616 free_cache(data); 617 if ((retval = alloc_cache(channel, data))) 618 return retval; 619 } 620 return 0; 621} 622 623 624static errcode_t unix_read_blk64(io_channel channel, unsigned long long block, 625 int count, void *buf) 626{ 627 struct unix_private_data *data; 628 struct unix_cache *cache, *reuse[READ_DIRECT_SIZE]; 629 errcode_t retval; 630 char *cp; 631 int i, j; 632 633 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 634 data = (struct unix_private_data *) channel->private_data; 635 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 636 637#ifdef NO_IO_CACHE 638 return raw_read_blk(channel, data, block, count, buf); 639#else 640 /* 641 * If we're doing an odd-sized read or a very large read, 642 * flush out the cache and then do a direct read. 643 */ 644 if (count < 0 || count > WRITE_DIRECT_SIZE) { 645 if ((retval = flush_cached_blocks(channel, data, 0))) 646 return retval; 647 return raw_read_blk(channel, data, block, count, buf); 648 } 649 650 cp = buf; 651 while (count > 0) { 652 /* If it's in the cache, use it! */ 653 if ((cache = find_cached_block(data, block, &reuse[0]))) { 654#ifdef DEBUG 655 printf("Using cached block %lu\n", block); 656#endif 657 memcpy(cp, cache->buf, channel->block_size); 658 count--; 659 block++; 660 cp += channel->block_size; 661 continue; 662 } 663 if (count == 1) { 664 /* 665 * Special case where we read directly into the 666 * cache buffer; important in the O_DIRECT case 667 */ 668 cache = reuse[0]; 669 reuse_cache(channel, data, cache, block); 670 if ((retval = raw_read_blk(channel, data, block, 1, 671 cache->buf))) { 672 cache->in_use = 0; 673 return retval; 674 } 675 memcpy(cp, cache->buf, channel->block_size); 676 return 0; 677 } 678 679 /* 680 * Find the number of uncached blocks so we can do a 681 * single read request 682 */ 683 for (i=1; i < count; i++) 684 if (find_cached_block(data, block+i, &reuse[i])) 685 break; 686#ifdef DEBUG 687 printf("Reading %d blocks starting at %lu\n", i, block); 688#endif 689 if ((retval = raw_read_blk(channel, data, block, i, cp))) 690 return retval; 691 692 /* Save the results in the cache */ 693 for (j=0; j < i; j++) { 694 count--; 695 cache = reuse[j]; 696 reuse_cache(channel, data, cache, block++); 697 memcpy(cache->buf, cp, channel->block_size); 698 cp += channel->block_size; 699 } 700 } 701 return 0; 702#endif /* NO_IO_CACHE */ 703} 704 705static errcode_t unix_read_blk(io_channel channel, unsigned long block, 706 int count, void *buf) 707{ 708 return unix_read_blk64(channel, block, count, buf); 709} 710 711static errcode_t unix_write_blk64(io_channel channel, unsigned long long block, 712 int count, const void *buf) 713{ 714 struct unix_private_data *data; 715 struct unix_cache *cache, *reuse; 716 errcode_t retval = 0; 717 const char *cp; 718 int writethrough; 719 720 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 721 data = (struct unix_private_data *) channel->private_data; 722 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 723 724#ifdef NO_IO_CACHE 725 return raw_write_blk(channel, data, block, count, buf); 726#else 727 /* 728 * If we're doing an odd-sized write or a very large write, 729 * flush out the cache completely and then do a direct write. 730 */ 731 if (count < 0 || count > WRITE_DIRECT_SIZE) { 732 if ((retval = flush_cached_blocks(channel, data, 1))) 733 return retval; 734 return raw_write_blk(channel, data, block, count, buf); 735 } 736 737 /* 738 * For a moderate-sized multi-block write, first force a write 739 * if we're in write-through cache mode, and then fill the 740 * cache with the blocks. 741 */ 742 writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH; 743 if (writethrough) 744 retval = raw_write_blk(channel, data, block, count, buf); 745 746 cp = buf; 747 while (count > 0) { 748 cache = find_cached_block(data, block, &reuse); 749 if (!cache) { 750 cache = reuse; 751 reuse_cache(channel, data, cache, block); 752 } 753 memcpy(cache->buf, cp, channel->block_size); 754 cache->dirty = !writethrough; 755 count--; 756 block++; 757 cp += channel->block_size; 758 } 759 return retval; 760#endif /* NO_IO_CACHE */ 761} 762 763static errcode_t unix_write_blk(io_channel channel, unsigned long block, 764 int count, const void *buf) 765{ 766 return unix_write_blk64(channel, block, count, buf); 767} 768 769static errcode_t unix_write_byte(io_channel channel, unsigned long offset, 770 int size, const void *buf) 771{ 772 struct unix_private_data *data; 773 errcode_t retval = 0; 774 ssize_t actual; 775 776 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 777 data = (struct unix_private_data *) channel->private_data; 778 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 779 780 if (data->align != 0) { 781#ifdef ALIGN_DEBUG 782 printf("unix_write_byte: O_DIRECT fallback\n"); 783#endif 784 return EXT2_ET_UNIMPLEMENTED; 785 } 786 787#ifndef NO_IO_CACHE 788 /* 789 * Flush out the cache completely 790 */ 791 if ((retval = flush_cached_blocks(channel, data, 1))) 792 return retval; 793#endif 794 795 if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0) 796 return errno; 797 798 actual = write(data->dev, buf, size); 799 if (actual != size) 800 return EXT2_ET_SHORT_WRITE; 801 802 return 0; 803} 804 805/* 806 * Flush data buffers to disk. 807 */ 808static errcode_t unix_flush(io_channel channel) 809{ 810 struct unix_private_data *data; 811 errcode_t retval = 0; 812 813 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 814 data = (struct unix_private_data *) channel->private_data; 815 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 816 817#ifndef NO_IO_CACHE 818 retval = flush_cached_blocks(channel, data, 0); 819#endif 820 fsync(data->dev); 821 return retval; 822} 823 824static errcode_t unix_set_option(io_channel channel, const char *option, 825 const char *arg) 826{ 827 struct unix_private_data *data; 828 unsigned long long tmp; 829 char *end; 830 831 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 832 data = (struct unix_private_data *) channel->private_data; 833 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 834 835 if (!strcmp(option, "offset")) { 836 if (!arg) 837 return EXT2_ET_INVALID_ARGUMENT; 838 839 tmp = strtoull(arg, &end, 0); 840 if (*end) 841 return EXT2_ET_INVALID_ARGUMENT; 842 data->offset = tmp; 843 if (data->offset < 0) 844 return EXT2_ET_INVALID_ARGUMENT; 845 return 0; 846 } 847 return EXT2_ET_INVALID_ARGUMENT; 848} 849