mirror of https://github.com/vitalif/e2fsprogs
676 lines
19 KiB
C
676 lines
19 KiB
C
/*
|
|
*
|
|
* Copyright (c) International Business Machines Corp., 2000
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
* the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Module: fsimext2.c
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <plugin.h>
|
|
#include "fsimext2.h"
|
|
|
|
int fsim_rw_diskblocks( int, int64_t, int32_t, void *, int );
|
|
void set_mkfs_options( option_array_t *, char **, logical_volume_t *, char * );
|
|
void set_fsck_options( option_array_t *, char **, logical_volume_t * );
|
|
|
|
/* Vector of plugin record ptrs that we export for the EVMS Engine. */
|
|
plugin_record_t *evms_plugin_records[] = {
|
|
&ext2_plugrec,
|
|
NULL
|
|
};
|
|
|
|
static plugin_record_t * pMyPluginRecord = &ext2_plugrec;
|
|
|
|
/*-------------------------------------------------------------------------------------+
|
|
+ +
|
|
+ Common Routines +
|
|
+ +
|
|
+--------------------------------------------------------------------------------------*/
|
|
|
|
|
|
/*
|
|
* Get the size limits for this volume.
|
|
*/
|
|
int fsim_get_volume_limits( struct ext2_super_block * sb,
|
|
sector_count_t * fs_min_size,
|
|
sector_count_t * fs_max_size,
|
|
sector_count_t * vol_max_size)
|
|
{
|
|
int rc = 0;
|
|
sector_count_t fs_size;
|
|
int blk_to_sect;
|
|
|
|
blk_to_sect = (1 + sb->s_log_block_size);
|
|
fs_size = sb->s_blocks_count << blk_to_sect;
|
|
*fs_min_size = (sb->s_blocks_count - sb->s_free_blocks_count) << blk_to_sect;
|
|
*fs_max_size = (sector_count_t) 1 << (32+blk_to_sect);
|
|
*vol_max_size = 0xffffffffff;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Un-Format the volume.
|
|
*/
|
|
int fsim_unmkfs( logical_volume_t * volume )
|
|
{
|
|
int fd;
|
|
int rc = 0;
|
|
|
|
fd = open(EVMS_GET_DEVNAME(volume), O_RDWR|O_EXCL, 0);
|
|
if (fd < 0) return -1;
|
|
|
|
if ( volume->private_data ) {
|
|
/* clear private data */
|
|
memset( (void *) volume->private_data, 0, SIZE_OF_SUPER );
|
|
/* zero primary superblock */
|
|
rc = fsim_rw_diskblocks( fd, EXT2_SUPER_LOC, SIZE_OF_SUPER,
|
|
volume->private_data, PUT );
|
|
} else {
|
|
rc = ERROR;
|
|
}
|
|
|
|
fd = close(fd);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Format the volume.
|
|
*/
|
|
int fsim_mkfs(logical_volume_t * volume, option_array_t * options )
|
|
{
|
|
int rc = FSIM_ERROR;
|
|
char *argv[MKFS_EXT2_OPTIONS_COUNT + 6];
|
|
char logsize[sizeof(unsigned int) + 1];
|
|
pid_t pidm;
|
|
int status;
|
|
|
|
/* Fork and execute the correct program. */
|
|
switch (pidm = fork()) {
|
|
|
|
/* error */
|
|
case -1:
|
|
return EIO;
|
|
|
|
/* child */
|
|
case 0:
|
|
set_mkfs_options( options, argv, volume, logsize );
|
|
|
|
/* close stderr, stdout to suppress mke2fs output */
|
|
close(1);
|
|
close(2);
|
|
open("/dev/null", O_WRONLY);
|
|
open("/dev/null", O_WRONLY);
|
|
|
|
(void) execvp(argv[0], argv);
|
|
/* using exit() can hang GUI, use _exit */
|
|
_exit(errno);
|
|
|
|
/* parent */
|
|
default:
|
|
/* wait for child to complete */
|
|
pidm = waitpid( pidm, &status, 0 );
|
|
if ( WIFEXITED(status) ) {
|
|
/* get mke2fs exit code */
|
|
rc = WEXITSTATUS(status);
|
|
if (rc)
|
|
LOG("mke2fs exited with status %d", rc);
|
|
} else {
|
|
if (WIFSIGNALED(status))
|
|
LOG("mke2fs died with signal %d",
|
|
WTERMSIG(status));
|
|
rc = EINTR;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* NAME: set_mkfs_options
|
|
*
|
|
* FUNCTION: Build options array (argv) for mkfs.ext2
|
|
*
|
|
* PARAMETERS:
|
|
* options - options array passed from EVMS engine
|
|
* argv - mkfs options array
|
|
* vol_name - volume name on which program will be executed
|
|
*
|
|
*/
|
|
void set_mkfs_options( option_array_t * options,
|
|
char ** argv,
|
|
logical_volume_t * volume,
|
|
char * logsize )
|
|
{
|
|
int i, bufsize, opt_count = 2;
|
|
char *buf;
|
|
|
|
argv[0] = "mke2fs";
|
|
|
|
/* 'quiet' option */
|
|
argv[1] = "-q";
|
|
|
|
/* the following is a big hack to make sure that we don't use a block */
|
|
/* size smaller than hardsector size since this does not work. */
|
|
/* would be nice if the ext2/3 utilities (mkfs) handled this themselves */
|
|
/* also, eventually we will implement this as a user option to manually */
|
|
/* set block size */
|
|
if (volume->object->geometry.bytes_per_sector != EVMS_VSECTOR_SIZE) {
|
|
switch (volume->object->geometry.bytes_per_sector) {
|
|
case 2048:
|
|
argv[2] = "-b2048";
|
|
opt_count++;
|
|
break;
|
|
case 4096:
|
|
argv[2] = "-b4096";
|
|
opt_count++;
|
|
break;
|
|
default:
|
|
/* not one we expect, just skip it */
|
|
}
|
|
}
|
|
|
|
for ( i=0; i<options->count; i++ ) {
|
|
|
|
if ( options->option[i].is_number_based ) {
|
|
|
|
switch (options->option[i].number) {
|
|
|
|
case MKFS_CHECKBB_INDEX:
|
|
/* 'check for bad blocks' option */
|
|
if ( options->option[i].value.bool == TRUE ) {
|
|
argv[opt_count++] = "-c";
|
|
}
|
|
break;
|
|
|
|
case MKFS_CHECKRW_INDEX:
|
|
/* 'check for r/w bad blocks' option */
|
|
if ( options->option[i].value.bool == TRUE ) {
|
|
argv[opt_count++] = "-cc";
|
|
}
|
|
break;
|
|
|
|
case MKFS_JOURNAL_INDEX:
|
|
/* 'create ext3 journal' option */
|
|
if ( options->option[i].value.bool == TRUE ) {
|
|
argv[opt_count++] = "-j";
|
|
}
|
|
break;
|
|
|
|
case MKFS_SETVOL_INDEX:
|
|
/* 'set volume name' option */
|
|
if ( options->option[i].value.s ) {
|
|
argv[opt_count++] = "-L";
|
|
argv[opt_count++] = options->option[i].value.s;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( !strcmp(options->option[i].name, "badblocks") ) {
|
|
/* 'check for bad blocks' option */
|
|
if ( options->option[i].value.bool == TRUE ) {
|
|
argv[opt_count++] = "-c";
|
|
}
|
|
}
|
|
|
|
if ( !strcmp(options->option[i].name, "badblocks_rw") ) {
|
|
/* 'check for r/w bad blocks' option */
|
|
if ( options->option[i].value.bool == TRUE ) {
|
|
argv[opt_count++] = "-cc";
|
|
}
|
|
}
|
|
|
|
if ( !strcmp(options->option[i].name, "journal") ) {
|
|
/* 'create ext3 journal' option */
|
|
if ( options->option[i].value.bool == TRUE ) {
|
|
argv[opt_count++] = "-j";
|
|
}
|
|
}
|
|
|
|
if ( !strcmp(options->option[i].name, "vollabel") ) {
|
|
/* 'check for bad blocks' option */
|
|
if ( options->option[i].value.s ) {
|
|
argv[opt_count++] = "-L";
|
|
argv[opt_count++] = options->option[i].value.s;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
argv[opt_count++] = EVMS_GET_DEVNAME(volume);
|
|
argv[opt_count] = NULL;
|
|
|
|
bufsize = 0;
|
|
for (i=0; argv[i]; i++)
|
|
bufsize += strlen(argv[i]) + 5;
|
|
buf = malloc(bufsize+1);
|
|
if (!buf)
|
|
return;
|
|
buf[0] = 0;
|
|
for (i=0; argv[i]; i++) {
|
|
strcat(buf, argv[i]);
|
|
strcat(buf, " ");
|
|
}
|
|
EngFncs->write_log_entry(DEBUG, pMyPluginRecord,
|
|
"mke2fs command: %s\n", buf);
|
|
free(buf);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Run fsck on the volume.
|
|
*/
|
|
int fsim_fsck(logical_volume_t * volume, option_array_t * options,
|
|
int *ret_status)
|
|
{
|
|
int rc = FSIM_ERROR;
|
|
char *argv[FSCK_EXT2_OPTIONS_COUNT + 3];
|
|
pid_t pidf;
|
|
int status, bytes_read;
|
|
char *buffer = NULL;
|
|
int fds2[2];
|
|
int banner = 0;
|
|
|
|
/* open pipe, alloc buffer for collecting fsck.jfs output */
|
|
rc = pipe(fds2);
|
|
if (rc) {
|
|
return(errno);
|
|
}
|
|
if (!(buffer = EngFncs->engine_alloc(MAX_USER_MESSAGE_LEN))) {
|
|
return(ENOMEM);
|
|
}
|
|
|
|
/* Fork and execute the correct program. */
|
|
switch (pidf = fork()) {
|
|
|
|
/* error */
|
|
case -1:
|
|
return EIO;
|
|
|
|
/* child */
|
|
case 0:
|
|
set_fsck_options( options, argv, volume );
|
|
|
|
/* pipe stderr, stdout */
|
|
dup2(fds2[1],1); /* fds2[1] replaces stdout */
|
|
dup2(fds2[1],2); /* fds2[1] replaces stderr */
|
|
close(fds2[0]); /* don't need this here */
|
|
|
|
execvp( argv[0], argv );
|
|
/* should never get here */
|
|
_exit(8); /* FSCK_ERROR -- operational error */
|
|
|
|
/* parent */
|
|
default:
|
|
close(fds2[1]);
|
|
|
|
/* wait for child to complete */
|
|
while (!(pidf = waitpid( pidf, &status, WNOHANG ))) {
|
|
/* read e2fsck output */
|
|
bytes_read = read(fds2[0],buffer,MAX_USER_MESSAGE_LEN);
|
|
if (bytes_read > 0) {
|
|
/* display e2fsck output */
|
|
if (!banner)
|
|
MESSAGE("e2fsck output:");
|
|
banner = 1;
|
|
MESSAGE("%s",buffer);
|
|
memset(buffer,0,bytes_read); /* clear out message */
|
|
}
|
|
usleep(10000); /* don't hog all the cpu */
|
|
}
|
|
|
|
/* do final read, just in case we missed some */
|
|
bytes_read = read(fds2[0],buffer,MAX_USER_MESSAGE_LEN);
|
|
if (bytes_read > 0) {
|
|
if (!banner)
|
|
MESSAGE("e2fsck output:");
|
|
MESSAGE("%s",buffer);
|
|
}
|
|
if ( WIFEXITED(status) ) {
|
|
/* get e2fsck exit code */
|
|
*ret_status = WEXITSTATUS(status);
|
|
LOG("e2fsck completed with exit code %d\n",
|
|
*ret_status);
|
|
rc = 0;
|
|
} else {
|
|
if (WIFSIGNALED(status))
|
|
LOG("e2fsck died with signal %d",
|
|
WTERMSIG(status));
|
|
rc = EINTR;
|
|
}
|
|
}
|
|
|
|
if (buffer) {
|
|
EngFncs->engine_free(buffer);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* NAME: set_fsck_options
|
|
*
|
|
* FUNCTION: Build options array (argv) for e2fsck
|
|
*
|
|
* PARAMETERS:
|
|
* options - options array passed from EVMS engine
|
|
* argv - fsck options array
|
|
* volume - volume on which program will be executed
|
|
*
|
|
*/
|
|
void set_fsck_options( option_array_t * options, char ** argv, logical_volume_t * volume )
|
|
{
|
|
int i, bufsize, num_opts, opt_count = 1;
|
|
int do_preen = 1;
|
|
char *buf;
|
|
|
|
argv[0] = "e2fsck";
|
|
|
|
if (options)
|
|
num_opts = options->count;
|
|
else {
|
|
/* No options, assume force (for resizing) */
|
|
argv[opt_count++] = "-f";
|
|
num_opts = 0;
|
|
}
|
|
|
|
for ( i=0; i < num_opts; i++) {
|
|
|
|
if ( options->option[i].is_number_based ) {
|
|
|
|
/* 'force check' option */
|
|
if ( (options->option[i].number == FSCK_FORCE_INDEX) &&
|
|
(options->option[i].value.bool == TRUE) ) {
|
|
argv[opt_count++] = "-f";
|
|
}
|
|
|
|
/* 'check read only' option or mounted */
|
|
if ((options->option[i].number == FSCK_READONLY_INDEX) &&
|
|
((options->option[i].value.bool == TRUE) ||
|
|
EVMS_IS_MOUNTED(volume))) {
|
|
argv[opt_count++] = "-n";
|
|
do_preen = 0;
|
|
}
|
|
|
|
/* 'bad blocks check' option and NOT mounted */
|
|
if ( (options->option[i].number == FSCK_CHECKBB_INDEX) &&
|
|
(options->option[i].value.bool == TRUE) &&
|
|
!EVMS_IS_MOUNTED(volume) ) {
|
|
argv[opt_count++] = "-c";
|
|
do_preen = 0;
|
|
}
|
|
|
|
/* 'bad blocks check' option and NOT mounted */
|
|
if ( (options->option[i].number == FSCK_CHECKRW_INDEX) &&
|
|
(options->option[i].value.bool == TRUE) &&
|
|
!EVMS_IS_MOUNTED(volume) ) {
|
|
argv[opt_count++] = "-cc";
|
|
do_preen = 0;
|
|
}
|
|
|
|
/* timing option */
|
|
if ( (options->option[i].number == FSCK_TIMING_INDEX) &&
|
|
(options->option[i].value.bool == TRUE) ) {
|
|
argv[opt_count++] = "-tt";
|
|
}
|
|
|
|
} else {
|
|
|
|
/* 'force check' option selected and NOT mounted */
|
|
if ( !strcmp(options->option[i].name, "force") &&
|
|
(options->option[i].value.bool == TRUE) &&
|
|
!EVMS_IS_MOUNTED(volume) ) {
|
|
argv[opt_count++] = "-f";
|
|
}
|
|
|
|
/* 'check read only' option selected or mounted */
|
|
if ((!strcmp(options->option[i].name, "readonly")) &&
|
|
((options->option[i].value.bool == TRUE) ||
|
|
EVMS_IS_MOUNTED(volume))) {
|
|
argv[opt_count++] = "-n";
|
|
do_preen = 0;
|
|
}
|
|
|
|
/* 'check badblocks' option selected and NOT mounted */
|
|
if (!strcmp(options->option[i].name, "badblocks") &&
|
|
(options->option[i].value.bool == TRUE) &&
|
|
!EVMS_IS_MOUNTED(volume)) {
|
|
argv[opt_count++] = "-c";
|
|
do_preen = 0;
|
|
}
|
|
|
|
/* 'check r/w badblocks' option selected and NOT mounted */
|
|
if (!strcmp(options->option[i].name, "badblocks_rw") &&
|
|
(options->option[i].value.bool == TRUE) &&
|
|
!EVMS_IS_MOUNTED(volume)) {
|
|
argv[opt_count++] = "-cc";
|
|
do_preen = 0;
|
|
}
|
|
|
|
/* 'timing' option selected */
|
|
if (!strcmp(options->option[i].name, "badblocks") &&
|
|
(options->option[i].value.bool == TRUE)) {
|
|
argv[opt_count++] = "-tt";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (do_preen)
|
|
argv[opt_count++] = "-p";
|
|
argv[opt_count++] = EVMS_GET_DEVNAME(volume);
|
|
argv[opt_count] = NULL;
|
|
|
|
bufsize = 0;
|
|
for (i=0; argv[i]; i++)
|
|
bufsize += strlen(argv[i]) + 5;
|
|
buf = malloc(bufsize+1);
|
|
if (!buf)
|
|
return;
|
|
buf[0] = 0;
|
|
for (i=0; argv[i]; i++) {
|
|
strcat(buf, argv[i]);
|
|
strcat(buf, " ");
|
|
}
|
|
EngFncs->write_log_entry(DEBUG, pMyPluginRecord,
|
|
"fsck command: %s\n", buf);
|
|
free(buf);
|
|
|
|
return;
|
|
}
|
|
/*
|
|
* NAME:ext2fs_swap_super
|
|
*
|
|
* FUNCTION: Swap all fields in the super block to CPU format.
|
|
*
|
|
* PARAMETERS:
|
|
* sb - pointer to superblock
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*/
|
|
static void ext2fs_swap_super(struct ext2_super_block * sb)
|
|
{
|
|
sb->s_inodes_count = DISK_TO_CPU32(sb->s_inodes_count);
|
|
sb->s_blocks_count = DISK_TO_CPU32(sb->s_blocks_count);
|
|
sb->s_r_blocks_count = DISK_TO_CPU32(sb->s_r_blocks_count);
|
|
sb->s_free_blocks_count = DISK_TO_CPU32(sb->s_free_blocks_count);
|
|
sb->s_free_inodes_count = DISK_TO_CPU32(sb->s_free_inodes_count);
|
|
sb->s_first_data_block = DISK_TO_CPU32(sb->s_first_data_block);
|
|
sb->s_log_block_size = DISK_TO_CPU32(sb->s_log_block_size);
|
|
sb->s_log_frag_size = DISK_TO_CPU32(sb->s_log_frag_size);
|
|
sb->s_blocks_per_group = DISK_TO_CPU32(sb->s_blocks_per_group);
|
|
sb->s_frags_per_group = DISK_TO_CPU32(sb->s_frags_per_group);
|
|
sb->s_inodes_per_group = DISK_TO_CPU32(sb->s_inodes_per_group);
|
|
sb->s_mtime = DISK_TO_CPU32(sb->s_mtime);
|
|
sb->s_wtime = DISK_TO_CPU32(sb->s_wtime);
|
|
sb->s_mnt_count = DISK_TO_CPU16(sb->s_mnt_count);
|
|
sb->s_max_mnt_count = DISK_TO_CPU16(sb->s_max_mnt_count);
|
|
sb->s_magic = DISK_TO_CPU16(sb->s_magic);
|
|
sb->s_state = DISK_TO_CPU16(sb->s_state);
|
|
sb->s_errors = DISK_TO_CPU16(sb->s_errors);
|
|
sb->s_minor_rev_level = DISK_TO_CPU16(sb->s_minor_rev_level);
|
|
sb->s_lastcheck = DISK_TO_CPU32(sb->s_lastcheck);
|
|
sb->s_checkinterval = DISK_TO_CPU32(sb->s_checkinterval);
|
|
sb->s_creator_os = DISK_TO_CPU32(sb->s_creator_os);
|
|
sb->s_rev_level = DISK_TO_CPU32(sb->s_rev_level);
|
|
sb->s_def_resuid = DISK_TO_CPU16(sb->s_def_resuid);
|
|
sb->s_def_resgid = DISK_TO_CPU16(sb->s_def_resgid);
|
|
sb->s_first_ino = DISK_TO_CPU32(sb->s_first_ino);
|
|
sb->s_inode_size = DISK_TO_CPU16(sb->s_inode_size);
|
|
sb->s_block_group_nr = DISK_TO_CPU16(sb->s_block_group_nr);
|
|
sb->s_feature_compat = DISK_TO_CPU32(sb->s_feature_compat);
|
|
sb->s_feature_incompat = DISK_TO_CPU32(sb->s_feature_incompat);
|
|
sb->s_feature_ro_compat = DISK_TO_CPU32(sb->s_feature_ro_compat);
|
|
sb->s_algorithm_usage_bitmap = DISK_TO_CPU32(sb->s_algorithm_usage_bitmap);
|
|
sb->s_journal_inum = DISK_TO_CPU32(sb->s_journal_inum);
|
|
sb->s_journal_dev = DISK_TO_CPU32(sb->s_journal_dev);
|
|
sb->s_last_orphan = DISK_TO_CPU32(sb->s_last_orphan);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* NAME: fsim_get_ext2_superblock
|
|
*
|
|
* FUNCTION: Get and validate a ext2/3 superblock
|
|
*
|
|
* PARAMETERS:
|
|
* volume - pointer to volume from which to get the superblock
|
|
* sb_ptr - pointer to superblock
|
|
*
|
|
* RETURNS:
|
|
* (0) for success
|
|
* != 0 otherwise
|
|
*
|
|
*/
|
|
int fsim_get_ext2_superblock( logical_volume_t *volume, struct ext2_super_block *sb_ptr )
|
|
{
|
|
int fd;
|
|
int rc = 0;
|
|
|
|
fd = open(EVMS_GET_DEVNAME(volume), O_RDONLY, 0);
|
|
if (fd < 0) return EIO;
|
|
|
|
/* get primary superblock */
|
|
rc = fsim_rw_diskblocks( fd, EXT2_SUPER_LOC, SIZE_OF_SUPER, sb_ptr, GET );
|
|
|
|
if( rc == 0 ) {
|
|
ext2fs_swap_super(sb_ptr);
|
|
/* see if superblock is ext2/3 */
|
|
if (( sb_ptr->s_magic != EXT2_SUPER_MAGIC ) ||
|
|
( sb_ptr->s_rev_level > 1 ))
|
|
rc = FSIM_ERROR;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* NAME: fsim_rw_diskblocks
|
|
*
|
|
* FUNCTION: Read or write specific number of bytes for an opened device.
|
|
*
|
|
* PARAMETERS:
|
|
* dev_ptr - file handle of an opened device to read/write
|
|
* disk_offset - byte offset from beginning of device for start of disk
|
|
* block read/write
|
|
* disk_count - number of bytes to read/write
|
|
* data_buffer - On read this will be filled in with data read from
|
|
* disk; on write this contains data to be written
|
|
* mode - GET (read) or PUT (write)
|
|
*
|
|
* RETURNS:
|
|
* FSIM_SUCCESS (0) for success
|
|
* ERROR (-1) can't lseek
|
|
* EINVAL
|
|
* EIO
|
|
*
|
|
*/
|
|
int fsim_rw_diskblocks( int dev_ptr,
|
|
int64_t disk_offset,
|
|
int32_t disk_count,
|
|
void *data_buffer,
|
|
int mode )
|
|
{
|
|
off_t Actual_Location;
|
|
size_t Bytes_Transferred;
|
|
|
|
Actual_Location = lseek(dev_ptr,disk_offset, SEEK_SET);
|
|
if ( ( Actual_Location < 0 ) || ( Actual_Location != disk_offset ) )
|
|
return ERROR;
|
|
|
|
switch ( mode ) {
|
|
case GET:
|
|
Bytes_Transferred = read(dev_ptr,data_buffer,disk_count);
|
|
break;
|
|
case PUT:
|
|
Bytes_Transferred = write(dev_ptr,data_buffer,disk_count);
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
break;
|
|
}
|
|
|
|
if ( Bytes_Transferred != disk_count ) {
|
|
return EIO;
|
|
}
|
|
|
|
return FSIM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* Test e2fsprogs version.
|
|
*
|
|
* We don't bother since we don't need any special functionality that
|
|
* hasn't been around for *years*
|
|
*/
|
|
int fsim_test_version( )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|