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