mirror of https://github.com/vitalif/e2fsprogs
1474 lines
43 KiB
C
1474 lines
43 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: fs_ext2.c
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <plugin.h>
|
|
#include <sys/wait.h>
|
|
#include "fsimext2.h"
|
|
|
|
static plugin_record_t *pMyPluginRecord = &ext2_plugrec;
|
|
|
|
/*-------------------------------------------------------------------------------------+
|
|
+ +
|
|
+ Start Of EVMS Plugin Functions +
|
|
+ (exported to engine via function table) +
|
|
+ +
|
|
+-------------------------------------------------------------------------------------*/
|
|
|
|
|
|
#if (EVMS_ABI_CODE >= 120)
|
|
static int fs_setup( engine_functions_t *engine_function_table)
|
|
#else
|
|
static int fs_setup( engine_mode_t mode, engine_functions_t *engine_function_table)
|
|
#endif
|
|
{
|
|
int rc = 0;
|
|
EngFncs = engine_function_table;
|
|
|
|
LOGENTRY();
|
|
|
|
/*
|
|
* We don't really care about the e2fsprogs version, but we leave
|
|
* this here in case we do at a later date....
|
|
*/
|
|
rc = fsim_test_version();
|
|
#if 0
|
|
if ( rc ) {
|
|
LOG_WARNING( "e2fsprogs must be version 1.XXX or later to function properly with this FSIM.\n" );
|
|
LOG_WARNING( "Please get the current version of e2fsprogs from http://e2fsprogs.sourceforge.net\n" );
|
|
rc = ENOSYS;
|
|
}
|
|
#endif
|
|
LOGEXIT();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Free all of the private data item we have left on volumes.
|
|
*/
|
|
static void fs_cleanup()
|
|
{
|
|
int rc = 0;
|
|
dlist_t global_volumes;
|
|
logical_volume_t * volume;
|
|
LOGENTRY();
|
|
|
|
rc = EngFncs->get_volume_list(pMyPluginRecord, &global_volumes);
|
|
if (!rc) {
|
|
while (ExtractObject(global_volumes, sizeof(logical_volume_t), VOLUME_TAG, NULL, (void**)&volume)==0) {
|
|
if (volume->private_data) {
|
|
EngFncs->engine_free(volume->private_data);
|
|
}
|
|
}
|
|
}
|
|
LOGEXIT();
|
|
}
|
|
|
|
|
|
/*
|
|
* Does this FSIM manage the file system on this volume?
|
|
* Return 0 for "yes", else a reason code.
|
|
*/
|
|
static int fs_probe(logical_volume_t * volume)
|
|
{
|
|
int rc = 0;
|
|
struct ext2_super_block *sb_ptr;
|
|
|
|
LOGENTRY();
|
|
|
|
/* allocate space for copy of superblock in private data */
|
|
sb_ptr = EngFncs->engine_alloc( SIZE_OF_SUPER );
|
|
|
|
if ( sb_ptr ) {
|
|
memset( (void *) sb_ptr, 0, SIZE_OF_SUPER );
|
|
|
|
/* get and validate ext2 superblock */
|
|
rc = fsim_get_ext2_superblock( volume, sb_ptr );
|
|
|
|
if ( !rc ) {
|
|
/* store copy of valid EXT2/3 superblock in private data */
|
|
volume->private_data = (void*)sb_ptr;
|
|
} else {
|
|
/* could not get valid EXT2/3 superblock */
|
|
volume->private_data = NULL;
|
|
}
|
|
} else {
|
|
rc = ENOMEM;
|
|
}
|
|
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Can mkfs this volume?
|
|
*/
|
|
static int fs_can_mkfs(logical_volume_t * volume)
|
|
{
|
|
int rc=0;
|
|
|
|
LOGENTRY();
|
|
|
|
/*****************************************************
|
|
* FUTURE - ensure mke2fs exists *
|
|
*****************************************************/
|
|
|
|
if (EVMS_IS_MOUNTED(volume)) {
|
|
/* If mounted, can't format. */
|
|
rc = EBUSY;
|
|
} else if ((volume->vol_size * PBSIZE) < MINEXT2) {
|
|
/* voluem size must be >= MINEXT2 */
|
|
rc = EPERM;
|
|
}
|
|
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Can unmkfs this volume?
|
|
*/
|
|
static int fs_can_unmkfs(logical_volume_t * volume)
|
|
{
|
|
int rc=0;
|
|
|
|
LOGENTRY();
|
|
|
|
if (EVMS_IS_MOUNTED(volume)) {
|
|
/* If mounted, can't unmkfs. */
|
|
rc = EBUSY;
|
|
}
|
|
|
|
LOGEXITRC();
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Can fsck this volume?
|
|
*/
|
|
static int fs_can_fsck(logical_volume_t * volume)
|
|
{
|
|
int rc=0;
|
|
|
|
LOGENTRY();
|
|
|
|
/*****************************************************
|
|
* FUTURE - ensure e2fsck exists *
|
|
* match version with available functions *
|
|
*****************************************************/
|
|
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the current size of this volume
|
|
*/
|
|
static int fs_get_fs_size( logical_volume_t * volume,
|
|
sector_count_t * size )
|
|
{
|
|
int rc = EINVAL;
|
|
struct ext2_super_block *sb = (struct ext2_super_block *)volume->private_data;
|
|
|
|
LOGENTRY();
|
|
|
|
if (!sb) {
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
/* get and validate current ext2/3 superblock */
|
|
rc = fsim_get_ext2_superblock( volume, sb );
|
|
|
|
if (!rc && sb) {
|
|
*size = sb->s_blocks_count << (1 + sb->s_log_block_size);
|
|
rc = 0;
|
|
}
|
|
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the size limits for this volume.
|
|
*/
|
|
static int fs_get_fs_limits( logical_volume_t * volume,
|
|
sector_count_t * fs_min_size,
|
|
sector_count_t * fs_max_size,
|
|
sector_count_t * vol_max_size)
|
|
{
|
|
int rc = EINVAL;
|
|
struct ext2_super_block *sb_ptr = (struct ext2_super_block *) volume->private_data;
|
|
|
|
LOGENTRY();
|
|
|
|
if (!sb_ptr) {
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
/* get and validate current ext2 superblock */
|
|
rc = fsim_get_ext2_superblock( volume, sb_ptr );
|
|
|
|
if ( !rc ) {
|
|
rc = fsim_get_volume_limits( sb_ptr, fs_min_size, fs_max_size, vol_max_size);
|
|
LOG_EXTRA("volume:%s, min:%lld, max:%lld\n",EVMS_GET_DEVNAME(volume), *fs_min_size, *fs_max_size);
|
|
LOG_EXTRA("fssize:%lld, vol_size:%lld\n",volume->fs_size,volume->vol_size );
|
|
|
|
if (*fs_min_size > volume->vol_size) {
|
|
LOG_ERROR("EXT2 FSIM returned min size > volume size, setting min size to volume size\n");
|
|
*fs_min_size = volume->vol_size;
|
|
}
|
|
}
|
|
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Expand the volume to new_size. If the volume is not expanded exactly to
|
|
* new_size, set new_sie to the new_size of the volume.
|
|
*/
|
|
static int fs_expand( logical_volume_t * volume,
|
|
sector_count_t * new_size )
|
|
{
|
|
struct ext2_super_block *sb;
|
|
int rc = 0;
|
|
char *argv[7];
|
|
pid_t pidf;
|
|
int status;
|
|
int fds1[2]; /* pipe for stdin 0=read 1=write */
|
|
int fds2[2]; /* pipe for stderr and stdout 0=-read,1=write */
|
|
int bytes_read;
|
|
char *buffer = NULL;
|
|
int banner = 0;
|
|
|
|
LOGENTRY();
|
|
|
|
/* get and validate current ext2/3 superblock */
|
|
sb = (struct ext2_super_block *) volume->private_data;
|
|
rc = fsim_get_ext2_superblock( volume, sb );
|
|
if (rc) {
|
|
goto errout;
|
|
}
|
|
if ((sb->s_lastcheck < sb->s_mtime) ||
|
|
(sb->s_state & EXT2_ERROR_FS) ||
|
|
((sb->s_state & EXT2_VALID_FS) == 0)) {
|
|
MESSAGE("Running fsck before expanding volume");
|
|
rc = fsim_fsck(volume, NULL, &status );
|
|
if (rc) {
|
|
MESSAGE("Attempt to execute fsck failed (%d)", rc);
|
|
MESSAGE("Aborting volume expand");
|
|
goto errout;
|
|
}
|
|
if (status >= 4) {
|
|
MESSAGE("Aborting volume expand");
|
|
rc = status;
|
|
goto errout;
|
|
}
|
|
}
|
|
|
|
/* don't expand if mounted */
|
|
if (EVMS_IS_MOUNTED(volume)) {
|
|
rc = EBUSY;
|
|
goto errout;
|
|
}
|
|
|
|
if (pipe(fds1)) {
|
|
rc = errno;
|
|
goto errout;
|
|
}
|
|
if (pipe(fds2)) {
|
|
rc = errno;
|
|
goto errout;
|
|
}
|
|
if (!(buffer = EngFncs->engine_alloc(MAX_USER_MESSAGE_LEN))) {
|
|
rc = ENOMEM;
|
|
goto errout;
|
|
}
|
|
|
|
/* Fork and execute the correct program. */
|
|
switch (pidf = fork()) {
|
|
|
|
/* error */
|
|
case -1:
|
|
return EIO;
|
|
|
|
/* child */
|
|
case 0:
|
|
argv[0] = "resize2fs";
|
|
SET_STRING_FIELD(argv[1], EVMS_GET_DEVNAME(volume));
|
|
argv[2] = NULL;
|
|
|
|
dup2(fds1[0],0); /* fds1[0] replaces stdin */
|
|
dup2(fds2[1],1); /* fds2[1] replaces stdout */
|
|
dup2(fds2[1],2); /* fds2[1] replaces stderr */
|
|
close(fds2[0]); /* don't need this here */
|
|
close(fds1[1]); /* don't need this here */
|
|
|
|
rc = execvp( argv[0], argv );
|
|
|
|
/* using exit() can hang GUI, use _exit */
|
|
_exit(errno);
|
|
|
|
/* parent */
|
|
default:
|
|
/*
|
|
* WARNING: Do Not close read handle of stdin or
|
|
* you will cause a SIGPIPE if you write after the
|
|
* child process has gone away.
|
|
*/
|
|
/* close(fds1[0]); */
|
|
close(fds2[1]);
|
|
|
|
/* wait for child to complete */
|
|
fcntl(fds2[0], F_SETFL, fcntl(fds2[0], F_GETFL,0) | O_NONBLOCK);
|
|
while (!(waitpid( pidf, &status, WNOHANG ))) {
|
|
bytes_read = read(fds2[0],buffer,MAX_USER_MESSAGE_LEN);
|
|
if (bytes_read > 0) {
|
|
if (!banner)
|
|
MESSAGE("expand 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("expand output:");
|
|
MESSAGE("%s",buffer);
|
|
}
|
|
if ( WIFEXITED(status) ) {
|
|
/* get expand exit code */
|
|
rc = WEXITSTATUS(status);
|
|
if (rc)
|
|
LOG("Expand completed successfully\n");
|
|
else
|
|
LOG("Expand completed with rc = %d\n", status);
|
|
} else {
|
|
if (WIFSIGNALED(status))
|
|
LOG("Expand died with signal %d",
|
|
WTERMSIG(status));
|
|
rc = EINTR;
|
|
}
|
|
}
|
|
if (buffer) {
|
|
EngFncs->engine_free(buffer);
|
|
}
|
|
fs_get_fs_size(volume, new_size);
|
|
errout:
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* "unmkfs" the volume
|
|
*/
|
|
static int fs_unmkfs(logical_volume_t * volume)
|
|
{
|
|
int rc = EINVAL;
|
|
LOGENTRY();
|
|
|
|
if (EVMS_IS_MOUNTED(volume)) {
|
|
/* If mounted, can't unmkfs. */
|
|
rc = EBUSY;
|
|
} else if ( (rc = fsim_unmkfs(volume)) == FSIM_SUCCESS ){
|
|
volume->private_data = NULL;
|
|
}
|
|
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Shrink the volume to new_size. If the volume is not expanded exactly to
|
|
* new_size, set new_size to the new_size of the volume.
|
|
*/
|
|
static int fs_shrink( logical_volume_t * volume,
|
|
sector_count_t requested_size,
|
|
sector_count_t * new_size )
|
|
{
|
|
int rc = 0;
|
|
char *argv[7];
|
|
pid_t pidf;
|
|
int status;
|
|
int fds1[2]; /* pipe for stdin 0=read 1=write */
|
|
int fds2[2]; /* pipe for stderr and stdout 0=-read,1=write */
|
|
int bytes_read;
|
|
char *buffer = NULL;
|
|
char size_buf[128];
|
|
struct ext2_super_block *sb;
|
|
int banner = 0;
|
|
|
|
LOGENTRY();
|
|
|
|
/* don't shrink if mounted */
|
|
if (EVMS_IS_MOUNTED(volume)) {
|
|
LOGEXITRC();
|
|
return EBUSY;
|
|
}
|
|
|
|
/* get and validate current ext2/3 superblock */
|
|
sb = (struct ext2_super_block *) volume->private_data;
|
|
rc = fsim_get_ext2_superblock( volume, sb );
|
|
if (rc) {
|
|
goto errout;
|
|
}
|
|
|
|
requested_size = requested_size >> (1 + sb->s_log_block_size);
|
|
if ((sb->s_lastcheck < sb->s_mtime) ||
|
|
(sb->s_state & EXT2_ERROR_FS) ||
|
|
((sb->s_state & EXT2_VALID_FS) == 0)) {
|
|
MESSAGE("Running fsck before shrinking volume");
|
|
rc = fsim_fsck(volume, NULL, &status );
|
|
if (rc) {
|
|
MESSAGE("Attempt to execute fsck failed (%d)", rc);
|
|
MESSAGE("Aborting volume shrink");
|
|
goto errout;
|
|
}
|
|
if (status >= 4) {
|
|
MESSAGE("Aborting volume shrink");
|
|
rc = status;
|
|
goto errout;
|
|
}
|
|
}
|
|
|
|
if (pipe(fds1)) {
|
|
rc = errno;
|
|
goto errout;
|
|
}
|
|
if (pipe(fds2)) {
|
|
rc = errno;
|
|
goto errout;
|
|
}
|
|
if (!(buffer = EngFncs->engine_alloc(MAX_USER_MESSAGE_LEN))) {
|
|
rc = ENOMEM;
|
|
goto errout;
|
|
}
|
|
|
|
/* Fork and execute the correct program. */
|
|
switch (pidf = fork()) {
|
|
|
|
/* error */
|
|
case -1:
|
|
return EIO;
|
|
|
|
/* child */
|
|
case 0:
|
|
argv[0] = "resize2fs";
|
|
SET_STRING_FIELD(argv[1], EVMS_GET_DEVNAME(volume));
|
|
sprintf(size_buf,"%lld", (sector_count_t)requested_size);
|
|
argv[2] = size_buf;
|
|
argv[3] = NULL;
|
|
|
|
dup2(fds1[0],0); /* fds1[0] replaces stdin */
|
|
dup2(fds2[1],1); /* fds2[1] replaces stdout */
|
|
dup2(fds2[1],2); /* fds2[1] replaces stderr */
|
|
close(fds2[0]); /* don't need this here */
|
|
close(fds1[1]); /* don't need this here */
|
|
|
|
rc = execvp( argv[0], argv );
|
|
|
|
/* using exit() can hang GUI, use _exit */
|
|
_exit(errno);
|
|
|
|
/* parent */
|
|
default:
|
|
/*
|
|
* WARNING: Do Not close read handle of stdin or you
|
|
* will cause a SIGPIPE if you write after the child
|
|
* process has gone away.
|
|
*/
|
|
/* close(fds1[0]); */
|
|
close(fds2[1]);
|
|
write(fds1[1], "Yes\n",4);
|
|
|
|
fcntl(fds2[0], F_SETFL, fcntl(fds2[0], F_GETFL,0) | O_NONBLOCK);
|
|
/* wait for child to complete */
|
|
while (!(waitpid( pidf, &status, WNOHANG ))) {
|
|
bytes_read = read(fds2[0],buffer,MAX_USER_MESSAGE_LEN);
|
|
if (bytes_read > 0) {
|
|
if (!banner)
|
|
MESSAGE("Shrink 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("Shrink output:");
|
|
MESSAGE("%s",buffer);
|
|
}
|
|
if ( WIFEXITED(status) ) {
|
|
/* get shrink exit code */
|
|
rc = WEXITSTATUS(status);
|
|
if (rc)
|
|
LOG("Shrink completed successfully\n");
|
|
else
|
|
LOG("Shrink completed with rc = %d\n",status);
|
|
} else {
|
|
if (WIFSIGNALED(status))
|
|
LOG("Shrink died with signal %d",
|
|
WTERMSIG(status));
|
|
rc = EINTR;
|
|
}
|
|
}
|
|
if (buffer) {
|
|
EngFncs->engine_free(buffer);
|
|
}
|
|
fs_get_fs_size(volume, new_size);
|
|
errout:
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Format the volume.
|
|
*/
|
|
static int fs_mkfs(logical_volume_t * volume, option_array_t * options )
|
|
{
|
|
int rc = 0;
|
|
|
|
LOGENTRY();
|
|
|
|
/* don't format if mounted */
|
|
if (EVMS_IS_MOUNTED(volume)) {
|
|
rc = EBUSY;
|
|
goto errout;
|
|
}
|
|
|
|
rc = fsim_mkfs(volume, options);
|
|
|
|
/* probe to set up private data */
|
|
if (!rc) {
|
|
rc = fs_probe(volume);
|
|
}
|
|
|
|
errout:
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Run fsck on the volume.
|
|
*/
|
|
static int fs_fsck(logical_volume_t * volume, option_array_t * options )
|
|
{
|
|
int rc = EINVAL;
|
|
int status;
|
|
|
|
LOGENTRY();
|
|
|
|
rc = fsim_fsck( volume, options, &status );
|
|
if (rc)
|
|
goto errout;
|
|
|
|
/*
|
|
* If the volume is mounted, e2fsck checked READ ONLY
|
|
* regardless of options specified. If the check was READ
|
|
* ONLY and errors were found, let the user know how to fix
|
|
* them.
|
|
*/
|
|
if (EVMS_IS_MOUNTED(volume) && (status & FSCK_ERRORS_UNCORRECTED)) {
|
|
MESSAGE( "%s is mounted.", EVMS_GET_DEVNAME(volume) );
|
|
MESSAGE( "e2fsck checked the volume READ ONLY and found, but did not fix, errors." );
|
|
MESSAGE( "Unmount %s and run e2fsck again to repair the file system.", EVMS_GET_DEVNAME(volume) );
|
|
}
|
|
if (status > 4) {
|
|
MESSAGE( "e2fsck exited with status code %d.", status);
|
|
}
|
|
|
|
errout:
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the total number of supported options for the specified task.
|
|
*/
|
|
static int fs_get_option_count(task_context_t * context)
|
|
{
|
|
int count = 0;
|
|
|
|
LOGENTRY();
|
|
|
|
switch(context->action) {
|
|
case EVMS_Task_mkfs:
|
|
count = MKFS_EXT2_OPTIONS_COUNT;
|
|
break;
|
|
case EVMS_Task_fsck:
|
|
count = FSCK_EXT2_OPTIONS_COUNT;
|
|
break;
|
|
default:
|
|
count = -1;
|
|
break;
|
|
}
|
|
|
|
LOGEXIT();
|
|
return count;
|
|
}
|
|
|
|
|
|
/*
|
|
* Fill in the initial list of acceptable objects. Fill in the minimum and
|
|
* maximum nuber of objects that must/can be selected. Set up all initial
|
|
* values in the option_descriptors in the context record for the given
|
|
* task. Some fields in the option_descriptor may be dependent on a
|
|
* selected object. Leave such fields blank for now, and fill in during the
|
|
* set_objects call.
|
|
*/
|
|
static int fs_init_task( task_context_t * context )
|
|
{
|
|
dlist_t global_volumes;
|
|
logical_volume_t * volume;
|
|
void* waste;
|
|
int size, tag;
|
|
int rc = 0;
|
|
option_descriptor_t *opt;
|
|
|
|
LOGENTRY();
|
|
|
|
context->min_selected_objects = 1;
|
|
context->max_selected_objects = 1;
|
|
context->option_descriptors->count = 0;
|
|
|
|
/* Parameter check */
|
|
if (!context) {
|
|
rc = EFAULT;
|
|
goto errout;
|
|
}
|
|
|
|
rc = EngFncs->get_volume_list(NULL, &global_volumes);
|
|
|
|
while (!(rc = BlindExtractObject(global_volumes, &size, (TAG *)&tag, NULL, (void **)&volume))) {
|
|
|
|
switch (context->action) {
|
|
case EVMS_Task_mkfs:
|
|
/* only mkfs unformatted volumes */
|
|
if ((volume->file_system_manager == NULL) &&
|
|
!EVMS_IS_MOUNTED(volume) &&
|
|
((volume->vol_size * PBSIZE) > MINEXT2)) {
|
|
rc = InsertObject(context->acceptable_objects, sizeof(logical_volume_t), volume, VOLUME_TAG, NULL, InsertAtStart, TRUE, (void **)&waste);
|
|
}
|
|
break;
|
|
|
|
case EVMS_Task_fsck:
|
|
/* only fsck our stuff */
|
|
if (volume->file_system_manager == &ext2_plugrec) {
|
|
rc = InsertObject(context->acceptable_objects, sizeof(logical_volume_t), volume, VOLUME_TAG, NULL, InsertAtStart, TRUE, (void **)&waste);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
rc = EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rc == DLIST_EMPTY || rc == DLIST_END_OF_LIST) {
|
|
rc = 0;
|
|
}
|
|
|
|
switch (context->action) {
|
|
|
|
case EVMS_Task_mkfs:
|
|
|
|
context->option_descriptors->count = MKFS_EXT2_OPTIONS_COUNT;
|
|
|
|
/* check for bad blocks option */
|
|
opt = &context->option_descriptors->option[MKFS_CHECKBB_INDEX];
|
|
SET_STRING(opt->name, "badblocks" );
|
|
SET_STRING(opt->title, "Check For Bad Blocks" );
|
|
SET_STRING(opt->tip, "Check the volume for bad blocks before building the file system." );
|
|
opt->help = NULL;
|
|
opt->type = EVMS_Type_Boolean;
|
|
opt->unit = EVMS_Unit_None;
|
|
#if (EVMS_ABI_CODE == 100)
|
|
opt->size = 0;
|
|
#endif
|
|
opt->flags = EVMS_OPTION_FLAGS_NOT_REQUIRED;
|
|
opt->constraint_type = EVMS_Collection_None;
|
|
opt->value.bool = FALSE;
|
|
|
|
/* check for r/w bad blocks option */
|
|
opt = &context->option_descriptors->option[MKFS_CHECKRW_INDEX];
|
|
SET_STRING(opt->name, "badblocks_rw" );
|
|
SET_STRING(opt->title, "RW Check For Bad Blocks" );
|
|
SET_STRING(opt->tip, "Do a read/write check for bad blocks before building the file system." );
|
|
opt->help = NULL;
|
|
opt->type = EVMS_Type_Boolean;
|
|
opt->unit = EVMS_Unit_None;
|
|
#if (EVMS_ABI_CODE == 100)
|
|
opt->size = 0;
|
|
#endif
|
|
opt->flags = EVMS_OPTION_FLAGS_NOT_REQUIRED;
|
|
opt->constraint_type = EVMS_Collection_None;
|
|
opt->value.bool = FALSE;
|
|
|
|
/* Set Volume Label option */
|
|
opt = &context->option_descriptors->option[MKFS_SETVOL_INDEX];
|
|
SET_STRING(opt->name, "vollabel" );
|
|
SET_STRING(opt->title, "Volume Label" );
|
|
SET_STRING(opt->tip, "Set the volume label for the file system." );
|
|
opt->help = NULL;
|
|
opt->type = EVMS_Type_String;
|
|
opt->unit = EVMS_Unit_None;
|
|
#if (EVMS_ABI_CODE == 100)
|
|
opt->size = 16;
|
|
#else
|
|
opt->min_len = 0;
|
|
opt->max_len = 16;
|
|
#endif
|
|
opt->flags = EVMS_OPTION_FLAGS_NOT_REQUIRED | EVMS_OPTION_FLAGS_NO_INITIAL_VALUE;
|
|
opt->constraint_type = EVMS_Collection_None;
|
|
opt->value.s = EngFncs->engine_alloc(17);
|
|
if (opt->value.s == NULL) {
|
|
LOGEXIT();
|
|
return ENOMEM;
|
|
}
|
|
|
|
/* create ext3 journal option */
|
|
opt = &context->option_descriptors->option[MKFS_JOURNAL_INDEX];
|
|
SET_STRING(opt->name, "journal" );
|
|
SET_STRING(opt->title, "Create Ext3 Journal" );
|
|
SET_STRING(opt->tip, "Create a journal for use with the ext3 file system." );
|
|
opt->help = NULL;
|
|
opt->type = EVMS_Type_Boolean;
|
|
opt->unit = EVMS_Unit_None;
|
|
#if (EVMS_ABI_CODE == 100)
|
|
opt->size = 0;
|
|
#endif
|
|
opt->flags = EVMS_OPTION_FLAGS_NOT_REQUIRED;
|
|
opt->constraint_type = EVMS_Collection_None;
|
|
opt->value.bool = TRUE;
|
|
|
|
break;
|
|
|
|
case EVMS_Task_fsck:
|
|
|
|
context->option_descriptors->count = FSCK_EXT2_OPTIONS_COUNT;
|
|
|
|
/* force check option */
|
|
opt = &context->option_descriptors->option[FSCK_FORCE_INDEX];
|
|
SET_STRING(opt->name, "force" );
|
|
SET_STRING(opt->title, "Force Check" );
|
|
SET_STRING(opt->tip, "Force complete file system check." );
|
|
opt->help = NULL;
|
|
opt->type = EVMS_Type_Boolean;
|
|
opt->unit = EVMS_Unit_None;
|
|
#if (EVMS_ABI_CODE == 100)
|
|
opt->size = 0;
|
|
#endif
|
|
opt->flags = EVMS_OPTION_FLAGS_NOT_REQUIRED;
|
|
opt->constraint_type = EVMS_Collection_None;
|
|
opt->value.bool = FALSE;
|
|
|
|
/* read-only check option */
|
|
opt = &context->option_descriptors->option[FSCK_READONLY_INDEX];
|
|
SET_STRING(opt->name, "readonly" );
|
|
SET_STRING(opt->title, "Check Read-Only" );
|
|
SET_STRING(opt->tip, "Check the file system READ ONLY. Report but do not correct errors." );
|
|
opt->help = NULL;
|
|
opt->type = EVMS_Type_Boolean;
|
|
opt->unit = EVMS_Unit_None;
|
|
#if (EVMS_ABI_CODE == 100)
|
|
opt->size = 0;
|
|
#endif
|
|
opt->flags = EVMS_OPTION_FLAGS_NOT_REQUIRED;
|
|
opt->constraint_type = EVMS_Collection_None;
|
|
/* if volume is mounted, only possible fsck.ext2 options is READONLY */
|
|
if (EVMS_IS_MOUNTED(context->volume)) {
|
|
opt->value.bool = TRUE;
|
|
} else {
|
|
opt->value.bool = FALSE;
|
|
}
|
|
|
|
/* check for bad blocks option */
|
|
opt = &context->option_descriptors->option[FSCK_CHECKBB_INDEX];
|
|
SET_STRING(opt->name, "badblocks" );
|
|
SET_STRING(opt->title, "Check For Bad Blocks" );
|
|
SET_STRING(opt->tip, "Check for bad blocks and mark them as busy." );
|
|
opt->help = NULL;
|
|
#if (EVMS_ABI_CODE == 100)
|
|
opt->size = 0;
|
|
#endif
|
|
opt->type = EVMS_Type_Boolean;
|
|
opt->unit = EVMS_Unit_None;
|
|
if (EVMS_IS_MOUNTED(context->volume)) {
|
|
opt->flags = EVMS_OPTION_FLAGS_INACTIVE;
|
|
} else {
|
|
opt->flags = EVMS_OPTION_FLAGS_NOT_REQUIRED;
|
|
}
|
|
opt->constraint_type = EVMS_Collection_None;
|
|
opt->value.bool = FALSE;
|
|
|
|
/* check for r/w bad blocks option */
|
|
opt = &context->option_descriptors->option[FSCK_CHECKRW_INDEX];
|
|
SET_STRING(opt->name, "badblocks_rw" );
|
|
SET_STRING(opt->title, "RW Check For Bad Blocks" );
|
|
SET_STRING(opt->tip, "Do a read/write check for bad blocks and mark them as busy." );
|
|
opt->help = NULL;
|
|
opt->type = EVMS_Type_Boolean;
|
|
opt->unit = EVMS_Unit_None;
|
|
#if (EVMS_ABI_CODE == 100)
|
|
opt->size = 0;
|
|
#endif
|
|
if (EVMS_IS_MOUNTED(context->volume)) {
|
|
opt->flags = EVMS_OPTION_FLAGS_INACTIVE;
|
|
} else {
|
|
opt->flags = EVMS_OPTION_FLAGS_NOT_REQUIRED;
|
|
}
|
|
opt->constraint_type = EVMS_Collection_None;
|
|
opt->value.bool = FALSE;
|
|
|
|
/* timing option */
|
|
opt = &context->option_descriptors->option[FSCK_TIMING_INDEX];
|
|
SET_STRING(opt->name, "timing" );
|
|
SET_STRING(opt->title, "Timing Statistics" );
|
|
SET_STRING(opt->tip, "Print timing statistics." );
|
|
opt->help = NULL;
|
|
opt->type = EVMS_Type_Boolean;
|
|
opt->unit = EVMS_Unit_None;
|
|
#if (EVMS_ABI_CODE == 100)
|
|
opt->size = 0;
|
|
#endif
|
|
opt->flags = EVMS_OPTION_FLAGS_NOT_REQUIRED | EVMS_OPTION_FLAGS_INACTIVE;
|
|
opt->constraint_type = EVMS_Collection_None;
|
|
opt->value.bool = FALSE;
|
|
break;
|
|
|
|
default:
|
|
rc = EINVAL;
|
|
break;
|
|
}
|
|
|
|
errout:
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Examine the specified value, and determine if it is valid for the task
|
|
* and option_descriptor index. If it is acceptable, set that value in the
|
|
* appropriate entry in the option_descriptor. The value may be adjusted
|
|
* if necessary/allowed. If so, set the effect return value accordingly.
|
|
*/
|
|
static int fs_set_option( task_context_t * context,
|
|
u_int32_t index,
|
|
value_t * value,
|
|
task_effect_t * effect )
|
|
{
|
|
int rc= 0, other;
|
|
|
|
LOGENTRY();
|
|
|
|
/* Parameter check */
|
|
if (!context || !value || !effect) {
|
|
rc = EFAULT;
|
|
goto errout;
|
|
}
|
|
|
|
*effect = 0;
|
|
|
|
switch (context->action) {
|
|
|
|
case EVMS_Task_mkfs:
|
|
switch (index) {
|
|
|
|
case MKFS_CHECKBB_INDEX:
|
|
case MKFS_CHECKRW_INDEX:
|
|
/* Conflicts with each other */
|
|
if (index == MKFS_CHECKBB_INDEX)
|
|
other = MKFS_CHECKRW_INDEX;
|
|
else
|
|
other = MKFS_CHECKBB_INDEX;
|
|
if (context->option_descriptors->option[other].value.bool) {
|
|
context->option_descriptors->option[other].value.bool = FALSE;
|
|
*effect = EVMS_Effect_Reload_Options;
|
|
}
|
|
/* Fall through */
|
|
|
|
case MKFS_JOURNAL_INDEX:
|
|
context->option_descriptors->option[index].value.bool = value->bool;
|
|
break;
|
|
|
|
case MKFS_SETVOL_INDEX:
|
|
/* 'set volume label' option set? */
|
|
strncpy(context->option_descriptors->option[index].value.s, value->s, 16);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case EVMS_Task_fsck:
|
|
switch (index) {
|
|
|
|
case FSCK_READONLY_INDEX:
|
|
/* 'check read only' option set? */
|
|
context->option_descriptors->option[index].value.bool = value->bool;
|
|
|
|
/* If mounted, only allow 'yes' for check read only */
|
|
if (EVMS_IS_MOUNTED(context->volume) && !value->bool) {
|
|
context->option_descriptors->option[index].value.bool = TRUE;
|
|
*effect = EVMS_Effect_Reload_Options;
|
|
}
|
|
|
|
/* If read-only, we can't check for bad blocks */
|
|
if (context->option_descriptors->option[FSCK_CHECKBB_INDEX].value.bool ||
|
|
context->option_descriptors->option[FSCK_CHECKRW_INDEX].value.bool) {
|
|
context->option_descriptors->option[FSCK_CHECKBB_INDEX].value.bool = FALSE;
|
|
context->option_descriptors->option[FSCK_CHECKRW_INDEX].value.bool = FALSE;
|
|
*effect = EVMS_Effect_Reload_Options;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case FSCK_CHECKBB_INDEX:
|
|
case FSCK_CHECKRW_INDEX:
|
|
if (EVMS_IS_MOUNTED(context->volume) && value->bool) {
|
|
MESSAGE("Can't check for bad blocks when the volume is mounted.");
|
|
context->option_descriptors->option[index].value.bool = FALSE;
|
|
*effect = EVMS_Effect_Reload_Options;
|
|
break;
|
|
}
|
|
|
|
/* Conflicts with each other */
|
|
if (index == FSCK_CHECKBB_INDEX)
|
|
other = FSCK_CHECKRW_INDEX;
|
|
else
|
|
other = FSCK_CHECKBB_INDEX;
|
|
if (context->option_descriptors->option[other].value.bool) {
|
|
context->option_descriptors->option[other].value.bool = FALSE;
|
|
*effect = EVMS_Effect_Reload_Options;
|
|
}
|
|
|
|
/* Conflicts with read-only option */
|
|
if (context->option_descriptors->option[FSCK_READONLY_INDEX].value.bool) {
|
|
context->option_descriptors->option[FSCK_READONLY_INDEX].value.bool = FALSE;
|
|
*effect = EVMS_Effect_Reload_Options;
|
|
}
|
|
|
|
/* Fall Through */
|
|
|
|
case FSCK_FORCE_INDEX:
|
|
case FSCK_TIMING_INDEX:
|
|
context->option_descriptors->option[index].value.bool = value->bool;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
errout:
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Validate the volumes in the selected_objects dlist in the task context.
|
|
* Remove from the selected objects lists any volumes which are not
|
|
* acceptable. For unacceptable volumes, create a declined_handle_t
|
|
* structure with the reason why it is not acceptable, and add it to the
|
|
* declined_volumes dlist. Modify the accepatble_objects dlist in the task
|
|
* context as necessary based on the selected objects and the current
|
|
* settings of the options. Modify any option settings as necessary based
|
|
* on the selected objects. Return the appropriate task_effect_t settings
|
|
* if the object list(s), minimum or maximum objects selected, or option
|
|
* settings have changed.
|
|
*/
|
|
static int fs_set_volumes( task_context_t * context,
|
|
dlist_t declined_volumes, /* of type declined_handle_t */
|
|
task_effect_t * effect )
|
|
{
|
|
int rc = 0;
|
|
logical_volume_t * vol;
|
|
|
|
LOGENTRY();
|
|
|
|
if (effect)
|
|
*effect = 0;
|
|
|
|
if (context->action == EVMS_Task_mkfs) {
|
|
|
|
/* get the selected volume */
|
|
rc = GetObject(context->selected_objects,sizeof(logical_volume_t),VOLUME_TAG,NULL,FALSE,(ADDRESS *) &vol);
|
|
|
|
if (!rc) {
|
|
if (EVMS_IS_MOUNTED(vol)) {
|
|
/* If mounted, can't mkfs.ext2. */
|
|
rc = EBUSY;
|
|
} else {
|
|
if ( (vol->vol_size * PBSIZE) < MINEXT2) {
|
|
|
|
/*****************************************************
|
|
* FUTURE - move this volume to unacceptable list *
|
|
*****************************************************/
|
|
|
|
MESSAGE( "The size of volume %s is %d bytes.", EVMS_GET_DEVNAME(vol), vol->vol_size * PBSIZE );
|
|
MESSAGE( "mke2fs requires a minimum of %u bytes to build the ext2/3 file system.", MINEXT2 );
|
|
rc = EPERM;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return any additional information that you wish to provide about the
|
|
* volume. The Engine privides an external API to get the information
|
|
* stored in the logical_volume_t. This call is to get any other
|
|
* information about the volume that is not specified in the
|
|
* logical_volume_t. Any piece of information you wish to provide must be
|
|
* in an extended_info_t structure. Use the Engine's engine_alloc() to
|
|
* allocate the memory for the extended_info_t. Also use engine_alloc() to
|
|
* allocate any strings that may go into the extended_info_t. Then use
|
|
* engine_alloc() to allocate an extended_info_array_t with enough entries
|
|
* for the number of exteneded_info_t structures you are returning. Fill
|
|
* in the array and return it in *info.
|
|
* If you have extended_info_t descriptors that themselves may have more
|
|
* extended information, set the EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag
|
|
* in the extended_info_t flags field. If the caller wants more information
|
|
* about a particular extended_info_t item, this API will be called with a
|
|
* pointer to the sotrage_object_t and with a pointer to the name of the
|
|
* extended_info_t item. In that case, return an extended_info_array_t with
|
|
* further information about the item. Each of those items may have the
|
|
* EVMS_EINFO_FLAGS_MORE_INFO_AVAILABLE flag set if you desire. It is your
|
|
* resposibility to give the items unique names so that you know which item
|
|
* the caller is asking additional information for. If info_name is NULL,
|
|
* the caller just wants top level information about the object.
|
|
*/
|
|
static int fs_get_volume_info( logical_volume_t * volume,
|
|
char * info_name,
|
|
extended_info_array_t * * info )
|
|
{
|
|
int rc = EINVAL;
|
|
extended_info_array_t *Info;
|
|
struct ext2_super_block *sb_ptr = (struct ext2_super_block *)volume->private_data;
|
|
|
|
|
|
LOGENTRY();
|
|
|
|
if (!sb_ptr) {
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
/* get and validate current ext2 superblock */
|
|
rc = fsim_get_ext2_superblock( volume, sb_ptr );
|
|
|
|
if (info_name || rc) {
|
|
rc = EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
/* reset limits. */
|
|
fs_get_fs_limits( volume, &volume->min_fs_size,
|
|
&volume->max_fs_size, &volume->max_vol_size);
|
|
|
|
Info = EngFncs->engine_alloc( sizeof(extended_info_array_t) + ( 5 * sizeof(extended_info_t) ) );
|
|
|
|
if (!Info) {
|
|
rc = ENOMEM;
|
|
goto errout;
|
|
}
|
|
|
|
Info->count = 5;
|
|
|
|
SET_STRING_FIELD( Info->info[0].name, "Version" );
|
|
SET_STRING_FIELD( Info->info[0].title, "Ext2 Revision Number" );
|
|
SET_STRING_FIELD( Info->info[0].desc, "Ext2 Revision Number.");
|
|
Info->info[0].type = EVMS_Type_Unsigned_Int32;
|
|
Info->info[0].unit = EVMS_Unit_None;
|
|
Info->info[0].value.ui32 = sb_ptr->s_rev_level;
|
|
Info->info[0].collection_type = EVMS_Collection_None;
|
|
memset( &Info->info[0].group, 0, sizeof(group_info_t));
|
|
|
|
SET_STRING_FIELD( Info->info[1].name, "State" );
|
|
SET_STRING_FIELD( Info->info[1].title, "Ext2 State" );
|
|
SET_STRING_FIELD( Info->info[1].desc, "The state of Ext2.");
|
|
Info->info[1].type = EVMS_Type_String;
|
|
Info->info[1].unit = EVMS_Unit_None;
|
|
if (sb_ptr->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
|
|
SET_STRING_FIELD(Info->info[1].value.s, "Needs journal replay");
|
|
} else if (sb_ptr->s_state & EXT2_ERROR_FS) {
|
|
SET_STRING_FIELD(Info->info[1].value.s, "Had errors");
|
|
} else if (sb_ptr->s_state & EXT2_VALID_FS) {
|
|
SET_STRING_FIELD(Info->info[1].value.s, "Clean");
|
|
} else {
|
|
SET_STRING_FIELD(Info->info[1].value.s, "Dirty");
|
|
}
|
|
Info->info[1].collection_type = EVMS_Collection_None;
|
|
memset( &Info->info[1].group, 0, sizeof(group_info_t));
|
|
|
|
SET_STRING_FIELD( Info->info[2].name, "VolLabel" );
|
|
SET_STRING_FIELD( Info->info[2].title, "Volume Label" );
|
|
SET_STRING_FIELD( Info->info[2].desc, "File system volume label.");
|
|
Info->info[2].type = EVMS_Type_String;
|
|
Info->info[2].unit = EVMS_Unit_None;
|
|
|
|
Info->info[2].value.s = EngFncs->engine_alloc(17);
|
|
if (!Info->info[2].value.s)
|
|
return -ENOMEM;
|
|
Info->info[2].value.s[16] = 0;
|
|
memcpy(Info->info[2].value.s, sb_ptr->s_volume_name, 16);
|
|
Info->info[2].collection_type = EVMS_Collection_None;
|
|
memset( &Info->info[2].group, 0, sizeof(group_info_t));
|
|
|
|
SET_STRING_FIELD( Info->info[3].name, "Size" );
|
|
SET_STRING_FIELD( Info->info[3].title, "File System Size" );
|
|
SET_STRING_FIELD( Info->info[3].desc, "Size of the file system.");
|
|
Info->info[3].type = EVMS_Type_Unsigned_Int64;
|
|
Info->info[3].unit = EVMS_Unit_Sectors;
|
|
Info->info[3].value.ui64 = sb_ptr->s_blocks_count <<
|
|
(1 + sb_ptr->s_log_block_size);
|
|
Info->info[3].collection_type = EVMS_Collection_None;
|
|
memset( &Info->info[3].group, 0, sizeof(group_info_t));
|
|
|
|
SET_STRING_FIELD( Info->info[4].name, "FreeSpace" );
|
|
SET_STRING_FIELD( Info->info[4].title, "Free File System Space" );
|
|
SET_STRING_FIELD( Info->info[4].desc, "Amount of unused space in the file system.");
|
|
Info->info[4].type = EVMS_Type_Unsigned_Int64;
|
|
Info->info[4].unit = EVMS_Unit_Sectors;
|
|
Info->info[4].value.ui64 = sb_ptr->s_free_blocks_count <<
|
|
(1 + sb_ptr->s_log_block_size);
|
|
Info->info[3].collection_type = EVMS_Collection_None;
|
|
memset( &Info->info[3].group, 0, sizeof(group_info_t));
|
|
|
|
*info = Info;
|
|
|
|
rc = 0;
|
|
|
|
errout:
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns Plugin specific information ...
|
|
*/
|
|
static int fs_get_plugin_info( char * descriptor_name, extended_info_array_t * * info )
|
|
{
|
|
int rc = EINVAL;
|
|
extended_info_array_t *Info;
|
|
extended_info_t *iptr;
|
|
char version_string[64];
|
|
#if (EVMS_ABI_CODE == 100)
|
|
char required_version_string[64];
|
|
#else
|
|
char required_engine_api_version_string[64];
|
|
char required_fsim_api_version_string[64];
|
|
#endif
|
|
|
|
LOGENTRY();
|
|
|
|
if (info) {
|
|
|
|
if (descriptor_name == NULL) {
|
|
*info = NULL; /* init to no info returned */
|
|
|
|
Info = EngFncs->engine_alloc( sizeof(extended_info_array_t) + (9*sizeof(extended_info_t)) );
|
|
if (Info) {
|
|
|
|
Info->count = 0;
|
|
|
|
sprintf(version_string, "%d.%d.%d",
|
|
MAJOR_VERSION,
|
|
MINOR_VERSION,
|
|
PATCH_LEVEL );
|
|
|
|
#if (EVMS_ABI_CODE == 100)
|
|
sprintf(required_version_string, "%d.%d.%d",
|
|
pMyPluginRecord->required_api_version.major,
|
|
pMyPluginRecord->required_api_version.minor,
|
|
pMyPluginRecord->required_api_version.patchlevel );
|
|
#else
|
|
sprintf(required_engine_api_version_string, "%d.%d.%d",
|
|
pMyPluginRecord->required_engine_api_version.major,
|
|
pMyPluginRecord->required_engine_api_version.minor,
|
|
pMyPluginRecord->required_engine_api_version.patchlevel );
|
|
|
|
sprintf(required_fsim_api_version_string, "%d.%d.%d",
|
|
pMyPluginRecord->required_plugin_api_version.fsim.major,
|
|
pMyPluginRecord->required_plugin_api_version.fsim.minor,
|
|
pMyPluginRecord->required_plugin_api_version.fsim.patchlevel );
|
|
#endif
|
|
|
|
iptr = &Info->info[Info->count++];
|
|
SET_STRING_FIELD( iptr->name, "Short Name" );
|
|
SET_STRING_FIELD( iptr->title, "Short Name" );
|
|
SET_STRING_FIELD( iptr->desc, "A short name given to this plugin.");
|
|
iptr->type = EVMS_Type_String;
|
|
iptr->unit = EVMS_Unit_None;
|
|
SET_STRING_FIELD( iptr->value.s, pMyPluginRecord->short_name );
|
|
iptr->collection_type = EVMS_Collection_None;
|
|
memset( &iptr->group, 0, sizeof(group_info_t));
|
|
|
|
iptr = &Info->info[Info->count++];
|
|
SET_STRING_FIELD( iptr->name, "Long Name" );
|
|
SET_STRING_FIELD( iptr->title, "Long Name" );
|
|
SET_STRING_FIELD( iptr->desc, "A long name given to this plugin.");
|
|
iptr->type = EVMS_Type_String;
|
|
iptr->unit = EVMS_Unit_None;
|
|
SET_STRING_FIELD( iptr->value.s, pMyPluginRecord->long_name );
|
|
iptr->collection_type = EVMS_Collection_None;
|
|
memset( &iptr->group, 0, sizeof(group_info_t));
|
|
|
|
iptr = &Info->info[Info->count++];
|
|
SET_STRING_FIELD( iptr->name, "Type" );
|
|
SET_STRING_FIELD( iptr->title, "Plugin Type" );
|
|
SET_STRING_FIELD( iptr->desc, "There are various types of plugins; each responsible for some kind of storage object.");
|
|
iptr->type = EVMS_Type_String;
|
|
iptr->unit = EVMS_Unit_None;
|
|
SET_STRING_FIELD( iptr->value.s, "File System Interface Module" );
|
|
iptr->collection_type = EVMS_Collection_None;
|
|
memset( &iptr->group, 0, sizeof(group_info_t));
|
|
|
|
iptr = &Info->info[Info->count++];
|
|
SET_STRING_FIELD( iptr->name, "Version" );
|
|
SET_STRING_FIELD( iptr->title, "Plugin Version" );
|
|
SET_STRING_FIELD( iptr->desc, "This is the version number of the plugin.");
|
|
iptr->type = EVMS_Type_String;
|
|
iptr->unit = EVMS_Unit_None;
|
|
SET_STRING_FIELD( iptr->value.s, version_string );
|
|
iptr->collection_type = EVMS_Collection_None;
|
|
memset( &iptr->group, 0, sizeof(group_info_t));
|
|
|
|
#if (EVMS_ABI_CODE == 100)
|
|
iptr = &Info->info[Info->count++];
|
|
SET_STRING_FIELD( iptr->name, "Required Version" );
|
|
SET_STRING_FIELD( iptr->title, "Required Engine Version" );
|
|
SET_STRING_FIELD( iptr->desc, "This is the version of the engine that the plugin requires. It will not run on older versions of the Engine.");
|
|
iptr->type = EVMS_Type_String;
|
|
iptr->unit = EVMS_Unit_None;
|
|
SET_STRING_FIELD( iptr->value.s, required_version_string );
|
|
iptr->collection_type = EVMS_Collection_None;
|
|
memset( &iptr->group, 0, sizeof(group_info_t));
|
|
#else
|
|
iptr = &Info->info[Info->count++];
|
|
SET_STRING_FIELD( iptr->name, "Required Engine Services Version" );
|
|
SET_STRING_FIELD( iptr->title, "Required Engine Services Version" );
|
|
SET_STRING_FIELD( iptr->desc, "This is the version of the Engine services that this plug-in requires. It will not run on older versions of the Engine services.");
|
|
iptr->type = EVMS_Type_String;
|
|
iptr->unit = EVMS_Unit_None;
|
|
SET_STRING_FIELD( iptr->value.s, required_engine_api_version_string );
|
|
iptr->collection_type = EVMS_Collection_None;
|
|
memset( &iptr->group, 0, sizeof(group_info_t));
|
|
|
|
iptr = &Info->info[Info->count++];
|
|
SET_STRING_FIELD( iptr->name, "Required Engine FSIM API Version" );
|
|
SET_STRING_FIELD( iptr->title, "Required Engine FSIM API Version" );
|
|
SET_STRING_FIELD( iptr->desc, "This is the version of the Engine FSIM API that this plug-in requires. It will not run on older versions of the Engine FSIM API.");
|
|
iptr->type = EVMS_Type_String;
|
|
iptr->unit = EVMS_Unit_None;
|
|
SET_STRING_FIELD( iptr->value.s, required_fsim_api_version_string );
|
|
iptr->collection_type = EVMS_Collection_None;
|
|
memset( &iptr->group, 0, sizeof(group_info_t));
|
|
#endif
|
|
|
|
#if defined(PACKAGE) && defined(VERSION)
|
|
iptr = &Info->info[Info->count++];
|
|
SET_STRING_FIELD( iptr->name, "E2fsprogs Version" );
|
|
SET_STRING_FIELD( iptr->title, "E2fsprogs Version" );
|
|
SET_STRING_FIELD( iptr->desc, "This is the version of the e2fsprogs that this plugin was shipped with.");
|
|
iptr->type = EVMS_Type_String;
|
|
iptr->unit = EVMS_Unit_None;
|
|
SET_STRING_FIELD( iptr->value.s, VERSION );
|
|
iptr->collection_type = EVMS_Collection_None;
|
|
memset( &iptr->group, 0, sizeof(group_info_t));
|
|
#endif
|
|
|
|
*info = Info;
|
|
|
|
rc = 0;
|
|
} else {
|
|
rc = ENOMEM;
|
|
}
|
|
|
|
} else {
|
|
/* There is no more information on any of the descriptors. */
|
|
rc = EINVAL;
|
|
}
|
|
}
|
|
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* How much can file system expand?
|
|
*/
|
|
static int fs_can_expand_by(logical_volume_t * volume,
|
|
sector_count_t * delta)
|
|
{
|
|
int rc = 0;
|
|
|
|
LOGENTRY();
|
|
if (EVMS_IS_MOUNTED(volume)) {
|
|
rc = EBUSY; /* If mounted, can't expand */
|
|
goto errout;
|
|
}
|
|
fs_get_fs_limits( volume, /* reset limits */
|
|
&volume->min_fs_size,
|
|
&volume->max_fs_size,
|
|
&volume->max_vol_size);
|
|
if (volume->fs_size + *delta > volume->max_fs_size) {
|
|
*delta = volume->max_fs_size - volume->fs_size;
|
|
}
|
|
errout:
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* How much can file system shrink?
|
|
*/
|
|
static int fs_can_shrink_by(logical_volume_t * volume,
|
|
sector_count_t * delta)
|
|
{
|
|
int rc = 0;
|
|
|
|
LOGENTRY();
|
|
if (EVMS_IS_MOUNTED(volume)) {
|
|
rc = EBUSY; /* If mounted, can't shrink */
|
|
goto errout;
|
|
}
|
|
fs_get_fs_limits( volume, /* reset limits */
|
|
&volume->min_fs_size,
|
|
&volume->max_fs_size,
|
|
&volume->max_vol_size);
|
|
if (volume->fs_size - *delta < volume->min_fs_size) {
|
|
*delta = volume->fs_size - volume->min_fs_size;
|
|
}
|
|
if (volume->min_fs_size >= volume->vol_size) {
|
|
rc = ENOSPC;
|
|
}
|
|
errout:
|
|
LOGEXITRC();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------+
|
|
+ +
|
|
+ PLUGIN FUNCTION TABLE +
|
|
+ +
|
|
+--------------------------------------------------------------------------------------*/
|
|
static fsim_functions_t fsim_ops = {
|
|
|
|
setup_evms_plugin: fs_setup,
|
|
cleanup_evms_plugin:fs_cleanup,
|
|
is_this_yours: fs_probe,
|
|
can_mkfs: fs_can_mkfs,
|
|
can_unmkfs: fs_can_unmkfs,
|
|
can_fsck: fs_can_fsck,
|
|
get_fs_size: fs_get_fs_size,
|
|
get_fs_limits: fs_get_fs_limits,
|
|
can_expand_by: fs_can_expand_by,
|
|
can_shrink_by: fs_can_shrink_by,
|
|
expand: fs_expand,
|
|
shrink: fs_shrink,
|
|
mkfs: fs_mkfs,
|
|
fsck: fs_fsck,
|
|
unmkfs: fs_unmkfs,
|
|
get_option_count: fs_get_option_count,
|
|
init_task: fs_init_task,
|
|
set_option: fs_set_option,
|
|
set_volumes: fs_set_volumes,
|
|
get_volume_info: fs_get_volume_info,
|
|
get_plugin_info: fs_get_plugin_info
|
|
};
|
|
|
|
|
|
/*-------------------------------------------------------------------------------------+
|
|
+ +
|
|
+ PLUGIN RECORD +
|
|
+ +
|
|
+-------------------------------------------------------------------------------------*/
|
|
|
|
plugin_record_t ext2_plugrec = {
|
|
id: SetPluginID(EVMS_OEM_IBM, EVMS_FILESYSTEM_INTERFACE_MODULE, FS_TYPE_EXT2 ),
|
|
version: {MAJOR_VERSION, MINOR_VERSION, PATCH_LEVEL},
|
|
#if (EVMS_ABI_CODE == 100)
|
|
required_api_version: {ENGINE_PLUGIN_API_MAJOR_VERION,
|
|
ENGINE_PLUGIN_API_MINOR_VERION,
|
|
ENGINE_PLUGIN_API_PATCH_LEVEL},
|
|
#else
|
|
required_engine_api_version: {ENGINE_SERVICES_API_MAJOR_VERION,
|
|
ENGINE_SERVICES_API_MINOR_VERION,
|
|
ENGINE_SERVICES_API_PATCH_LEVEL},
|
|
required_plugin_api_version: {fsim: {ENGINE_FSIM_API_MAJOR_VERION,
|
|
ENGINE_FSIM_API_MINOR_VERION,
|
|
ENGINE_FSIM_API_PATCH_LEVEL} },
|
|
#endif
|
|
short_name: "Ext2/3",
|
|
long_name: "Ext2/3 File System Interface Module",
|
|
oem_name: "IBM",
|
|
functions: {fsim: &fsim_ops},
|
|
container_functions: NULL
|
|
|
|
};
|
|
|