| /* |
| * |
| * 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 <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) + |
| + + |
| +-------------------------------------------------------------------------------------*/ |
| |
| |
| static int fs_setup( engine_mode_t mode, engine_functions_t *engine_function_table) |
| { |
| 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 ) { |
| MESSAGE( "e2fsprogs must be version 1.XXX or later to function properly with this FSIM." ); |
| MESSAGE( "Please get the current version of e2fsprogs from http://e2fsprogs.sourceforge.net" ); |
| 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 * min_size, |
| sector_count_t * max_volume_size, |
| sector_count_t * max_object_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, min_size, max_volume_size, max_object_size); |
| LOG_EXTRA("volume:%s, min:%lld, max:%lld\n",EVMS_GET_DEVNAME(volume), *min_size, *max_volume_size); |
| LOG_EXTRA("fssize:%lld, vol_size:%lld\n",volume->fs_size,volume->vol_size ); |
| |
| if (*min_size > volume->vol_size) { |
| LOG_ERROR("EXT2 FSIM returned min size > volume size, setting min size to volume size\n"); |
| *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 ((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 ); |
| if (rc) |
| goto errout; |
| } |
| |
| /* don't expand if mounted */ |
| if (EngFncs->is_mounted(volume->name, NULL)) { |
| 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 */ |
| while (!(pidf = 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 */ |
| LOG("Expand completed with rc = %d \n",status); |
| rc = WEXITSTATUS(status); |
| } |
| } |
| 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 ); |
| 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 ); |
| if (rc) |
| 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); |
| |
| /* wait for child to complete */ |
| while (!(pidf = 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 */ |
| LOG("Shrink completed with rc = %d \n",status); |
| rc = WEXITSTATUS(status); |
| } |
| } |
| 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)) { |
| return EBUSY; |
| } |
| |
| rc = fsim_mkfs(volume, options); |
| |
| /* probe to set up private data */ |
| if ( !rc ) { |
| rc = fs_probe(volume); |
| } |
| |
| LOGEXITRC(); |
| return rc; |
| } |
| |
| |
| /* |
| * Run fsck on the volume. |
| */ |
| static int fs_fsck(logical_volume_t * volume, option_array_t * options ) |
| { |
| int rc = EINVAL; |
| |
| LOGENTRY(); |
| |
| rc = fsim_fsck( volume, options ); |
| |
| /* |
| * If fsck.ext2 returns FSCK_CORRECTED, the |
| * file system is clean, so return FSCK_OK. |
| */ |
| if ( rc == FSCK_CORRECTED ) { |
| rc = FSCK_OK; |
| /* |
| * The value of FSCK_CORRECTED is the same as |
| * EPERM, so fsim_fsck will return -1 for EPERM. |
| */ |
| } else if (rc == -1) { |
| rc = EPERM; |
| } |
| |
| /* |
| * 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) && rc) { |
| 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) ); |
| } |
| |
| 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) { |
| return EFAULT; |
| } |
| |
| 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 > 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 = ENOSYS; |
| 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; |
| #ifdef ABI_EVMS_1_0 |
| 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; |
| #ifdef ABI_EVMS_1_0 |
| 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; |
| #ifdef ABI_EVMS_1_0 |
| 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; |
| #ifdef ABI_EVMS_1_0 |
| 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; |
| #ifdef ABI_EVMS_1_0 |
| 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; |
| #ifdef ABI_EVMS_1_0 |
| 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; |
| #ifdef ABI_EVMS_1_0 |
| 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; |
| #ifdef ABI_EVMS_1_0 |
| 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; |
| #ifdef ABI_EVMS_1_0 |
| 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: |
| break; |
| } |
| |
| 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) { |
| return EFAULT; |
| } |
| |
| *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; |
| } |
| |
| 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_vol_size, &volume->max_fs_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.ui64 = 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]; |
| #ifdef ABI_EVMS_1_0 |
| 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 ); |
| |
| #ifdef ABI_EVMS_1_0 |
| 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)); |
| |
| #ifdef ABI_EVMS_1_0 |
| 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_vol_size, |
| &volume->max_fs_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_vol_size, |
| &volume->max_fs_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}, |
| #ifdef ABI_EVMS_1_0 |
| required_api_version: {ENGINE_PLUGIN_API_MAJOR_VERION, |
| ENGINE_PLUGIN_API_MINOR_VERION, |
| ENGINE_PLUGIN_API_PATCH_LEVEL}, |
| #else |
| required_engine_api_version: {8, |
| 0, |
| 0}, |
| required_plugin_api_version: {fsim: {8, |
| 0, |
| 0} }, |
| #endif |
| short_name: "Ext2/3", |
| long_name: "Ext2 File System Interface Module", |
| oem_name: "IBM", |
| functions: {fsim: &fsim_ops}, |
| container_functions: NULL |
| |
| }; |
| |