From 39cc87feb2eee82f289480357c0b11859126d9b3 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 28 May 2002 23:16:10 -0400 Subject: [PATCH] Add support for expanding and contracting filesystems. Fix a few minor bugs since the last commit. --- lib/evms/Makefile.in | 11 ++- lib/evms/fs_ext2.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++--- lib/evms/fsimext2.c | 95 +++++++++++------- lib/evms/fsimext2.h | 11 +-- 4 files changed, 323 insertions(+), 61 deletions(-) diff --git a/lib/evms/Makefile.in b/lib/evms/Makefile.in index 5ea517f..cd3ced9 100644 --- a/lib/evms/Makefile.in +++ b/lib/evms/Makefile.in @@ -5,7 +5,12 @@ top_builddir = ../.. my_dir = lib/evms INSTALL = @INSTALL@ -XTRA_CFLAGS= -I@srcdir@ +MAJOR_VERSION = 1 +MINOR_VERSION = 0 +PATCH_LEVEL = 0 +EXTRAVERSION = + +XTRA_CFLAGS= -I@srcdir@ -DMAJOR_VERSION=$(MAJOR_VERSION) -DMINOR_VERSION=$(MINOR_VERSION) -DPATCH_LEVEL=$(PATCH_LEVEL) @MCONFIG@ @@ -17,8 +22,8 @@ SRCS= $(srcdir)/fs_ext2.c $(srcdir)/fsimext2.c LIBRARY= libext2fsim LIBDIR= evms -ELF_VERSION = 1.0.0 -ELF_SO_VERSION = 1 +ELF_VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_LEVEL) +ELF_SO_VERSION = $(MAJOR_VERSION) ELF_IMAGE = libe2fsim ELF_MYDIR = evms ELF_INSTALL_DIR = $(root_libdir) diff --git a/lib/evms/fs_ext2.c b/lib/evms/fs_ext2.c index baa954f..16c2a2f 100644 --- a/lib/evms/fs_ext2.c +++ b/lib/evms/fs_ext2.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "fsimext2.h" static plugin_record_t *pMyPluginRecord = &ext2_plugrec; @@ -50,12 +51,13 @@ static int fs_setup( engine_mode_t mode, engine_functions_t *engine_function_tab * 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; } @@ -257,10 +259,114 @@ static int fs_get_fs_limits( logical_volume_t * volume, static int fs_expand( logical_volume_t * volume, sector_count_t * new_size ) { - /* unsupported at this time */ - int rc = ENOSYS; + 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; } @@ -294,10 +400,119 @@ static int fs_shrink( logical_volume_t * volume, sector_count_t requested_size, sector_count_t * new_size ) { - /* unsupported at this time */ - int rc = ENOSYS; + 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; } @@ -775,9 +990,7 @@ static int fs_set_volumes( task_context_t * context, task_effect_t * effect ) { int rc = 0; - int64_t log_size; - unsigned int max_log_size; - logical_volume_t * vol; + logical_volume_t * vol; LOGENTRY(); @@ -960,7 +1173,7 @@ static int fs_get_plugin_info( char * descriptor_name, extended_info_array_t * * if (info) { if (descriptor_name == NULL) { - *info = NULL; // init to no info returned + *info = NULL; /* init to no info returned */ Info = EngFncs->engine_alloc( sizeof(extended_info_array_t) + (8*sizeof(extended_info_t)) ); if (Info) { @@ -1027,7 +1240,7 @@ static int fs_get_plugin_info( char * descriptor_name, extended_info_array_t * * iptr->collection_type = EVMS_Collection_None; memset( &iptr->group, 0, sizeof(group_info_t)); -#ifdef VERSION +#if defined(PACKAGE) && defined(VERSION) iptr = &Info->info[Info->count++]; SET_STRING_FIELD( iptr->name, "E2fsprogs Version" ); SET_STRING_FIELD( iptr->title, "E2fsprogs Version" ); @@ -1063,13 +1276,23 @@ static int fs_get_plugin_info( char * descriptor_name, extended_info_array_t * * static int fs_can_expand_by(logical_volume_t * volume, sector_count_t * delta) { - /* unsupported at this time */ - int rc = ENOSYS; + 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; - } @@ -1079,10 +1302,24 @@ static int fs_can_expand_by(logical_volume_t * volume, static int fs_can_shrink_by(logical_volume_t * volume, sector_count_t * delta) { - /* unsupported at this time */ - int rc = ENOSYS; + 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; } diff --git a/lib/evms/fsimext2.c b/lib/evms/fsimext2.c index b826dd2..29f38bb 100644 --- a/lib/evms/fsimext2.c +++ b/lib/evms/fsimext2.c @@ -36,7 +36,7 @@ int fsim_rw_diskblocks( int, int64_t, int32_t, void *, int ); void set_mkfs_options( option_array_t *, char **, logical_volume_t *, char * ); void set_fsck_options( option_array_t *, char **, logical_volume_t * ); -// Vector of plugin record ptrs that we export for the EVMS Engine. +/* Vector of plugin record ptrs that we export for the EVMS Engine. */ plugin_record_t *evms_plugin_records[] = { &ext2_plugrec, NULL @@ -55,21 +55,23 @@ static plugin_record_t * pMyPluginRecord = &ext2_plugrec; * Get the size limits for this volume. */ int fsim_get_volume_limits( struct ext2_super_block * sb, - sector_count_t * min_size, - sector_count_t * max_volume_size, - sector_count_t * max_object_size) + sector_count_t * fs_min_size, + sector_count_t * fs_max_size, + sector_count_t * vol_max_size) { int rc = 0; sector_count_t fs_size; + int blk_to_sect; /* * Since ext2/3 does not yet support shrink or expand, * all values are actual file system size. */ - fs_size = sb->s_blocks_count << (1 + sb->s_log_block_size); - *max_volume_size = fs_size; - *max_object_size = fs_size; - *min_size = fs_size; + blk_to_sect = (1 + sb->s_log_block_size); + fs_size = sb->s_blocks_count << blk_to_sect; + *fs_min_size = (sb->s_blocks_count - sb->s_free_blocks_count) << blk_to_sect; + *fs_max_size = (sector_count_t) 1 << (32+blk_to_sect); + *vol_max_size = 0xffffffff; return rc; } @@ -164,7 +166,8 @@ void set_mkfs_options( option_array_t * options, logical_volume_t * volume, char * logsize ) { - int i, opt_count = 2; + int i, bufsize, opt_count = 2; + char *buf; argv[0] = "mke2fs"; @@ -246,16 +249,20 @@ void set_mkfs_options( option_array_t * options, argv[opt_count++] = EVMS_GET_DEVNAME(volume); argv[opt_count] = NULL; - { - FILE *f; - - f = fopen("/var/tmp/evms-log", "a"); - for ( i=0; argv[i]; i++) { - fprintf(f, "'%s' ", argv[i]); - } - fprintf(f, "\n"); - fclose(f); + bufsize = 0; + for (i=0; argv[i]; i++) + bufsize += strlen(argv[i]) + 5; + buf = malloc(bufsize+1); + if (!buf) + return; + buf[0] = 0; + for (i=0; argv[i]; i++) { + strcat(buf, argv[i]); + strcat(buf, " "); } + EngFncs->write_log_entry(DEBUG, pMyPluginRecord, + "mke2fs command: %s\n", buf); + free(buf); return; } @@ -325,16 +332,21 @@ int fsim_fsck(logical_volume_t * volume, option_array_t * options ) if (bytes_read > 0) { /* display e2fsck output */ if (!banner) - MESSAGE("e2fsck output: \n\n%s",buffer); - else - banner = 1; - memset(buffer,0,bytes_read); //clear out message + MESSAGE("e2fsck output:"); + banner = 1; + MESSAGE("%s",buffer); + memset(buffer,0,bytes_read); /* clear out message */ } usleep(10000); /* don't hog all the cpu */ } - /* wait for child to complete */ - pidf = waitpid( pidf, &status, 0 ); + /* do final read, just in case we missed some */ + bytes_read = read(fds2[0],buffer,MAX_USER_MESSAGE_LEN); + if (bytes_read > 0) { + if (!banner) + MESSAGE("e2fsck output:"); + MESSAGE("%s",buffer); + } if ( WIFEXITED(status) ) { /* get e2fsck exit code */ rc = WEXITSTATUS(status); @@ -363,12 +375,21 @@ int fsim_fsck(logical_volume_t * volume, option_array_t * options ) */ void set_fsck_options( option_array_t * options, char ** argv, logical_volume_t * volume ) { - int i, opt_count = 1; + int i, bufsize, num_opts, opt_count = 1; int do_preen = 1; + char *buf; argv[0] = "e2fsck"; - for ( i=0; icount; i++) { + if (options) + num_opts = options->count; + else { + /* No options, assume force (for resizing) */ + argv[opt_count++] = "-f"; + num_opts = 0; + } + + for ( i=0; i < num_opts; i++) { if ( options->option[i].is_number_based ) { @@ -454,16 +475,20 @@ void set_fsck_options( option_array_t * options, char ** argv, logical_volume_t argv[opt_count++] = EVMS_GET_DEVNAME(volume); argv[opt_count] = NULL; - { - FILE *f; - - f = fopen("/var/tmp/evms-log", "a"); - for ( i=0; argv[i]; i++) { - fprintf(f, "'%s' ", argv[i]); - } - fprintf(f, "\n"); - fclose(f); + bufsize = 0; + for (i=0; argv[i]; i++) + bufsize += strlen(argv[i]) + 5; + buf = malloc(bufsize+1); + if (!buf) + return; + buf[0] = 0; + for (i=0; argv[i]; i++) { + strcat(buf, argv[i]); + strcat(buf, " "); } + EngFncs->write_log_entry(DEBUG, pMyPluginRecord, + "fsck command: %s\n", buf); + free(buf); return; } diff --git a/lib/evms/fsimext2.h b/lib/evms/fsimext2.h index dd7d44b..d95aac7 100644 --- a/lib/evms/fsimext2.h +++ b/lib/evms/fsimext2.h @@ -1,8 +1,3 @@ -/* Version number of the ext2 plugin */ -#define MAJOR_VERSION 1 -#define MINOR_VERSION 0 -#define PATCH_LEVEL 0 - /* * * Copyright (c) International Business Machines Corp., 2000 @@ -47,10 +42,10 @@ extern plugin_record_t ext2_plugrec; engine_functions_t *EngFncs; -// file system type ... used by the SetPluginID macro +/* file system type ... used by the SetPluginID macro */ #define FS_TYPE_EXT2 7 -// logging macros +/* logging macros */ #define LOGENTRY() EngFncs->write_log_entry(ENTRY_EXIT, pMyPluginRecord, "%s: Enter.\n", __FUNCTION__) #define LOGEXIT() EngFncs->write_log_entry(ENTRY_EXIT, pMyPluginRecord, "%s: Exit.\n", __FUNCTION__ ) #define LOGEXITRC() EngFncs->write_log_entry(ENTRY_EXIT, pMyPluginRecord, "%s: Exit. RC= %d.\n", __FUNCTION__, rc) @@ -64,7 +59,7 @@ engine_functions_t *EngFncs; #define LOG_DEBUG(msg, args...) EngFncs->write_log_entry(DEBUG, pMyPluginRecord, __FUNCTION__ ": " msg, ## args) #define LOG_EXTRA(msg, args...) EngFncs->write_log_entry(EXTRA, pMyPluginRecord, __FUNCTION__ ": " msg, ## args) -// useful macro for option code +/* useful macro for option code */ #define SET_STRING_FIELD(a,b)\ a = EngFncs->engine_alloc( strlen(b)+1 );\ if (a ) {\ -- 1.8.3.1