/* * * 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 #include #include #include #include #include #include #include #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 };