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