/* * * 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 #include #include #include #include #include #include #include #include #include #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; int blk_to_sect; blk_to_sect = (1 + sb->s_log_block_size); *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; LOGEXITRC(); return rc; } /* * Un-Format the volume. */ int fsim_unmkfs( logical_volume_t * volume ) { int fd; int rc = 0; LOGENTRY(); 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); LOGEXITRC(); 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; LOGENTRY(); /* Fork and execute the correct program. */ switch (pidm = fork()) { /* error */ case -1: rc = 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 */ while (1) { rc = waitpid( pidm, &status, 0 ); if (rc == -1) { if (errno == EINTR) continue; rc = errno; goto reterr; } else break; } if ( WIFEXITED(status) ) { /* get mke2fs exit code */ rc = WEXITSTATUS(status); if (rc) LOG("mke2fs exited with status %d", rc); } else { if (WIFSIGNALED(status)) LOG("mke2fs died with signal %d", WTERMSIG(status)); rc = EINTR; } } reterr: LOGEXITRC(); 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; LOGENTRY(); 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 2048: argv[2] = "-b2048"; opt_count++; break; case 4096: argv[2] = "-b4096"; opt_count++; break; default: /* not one we expect, just skip it */ break; } } for ( i=0; icount; 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); LOGEXIT(); 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; LOGENTRY(); /* 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: rc = 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 */ fcntl(fds2[0], F_SETFL, fcntl(fds2[0], F_GETFL,0) | O_NONBLOCK); while (!(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 { if (WIFSIGNALED(status)) LOG("e2fsck died with signal %d", WTERMSIG(status)); rc = EINTR; } } if (buffer) { EngFncs->engine_free(buffer); } close(fds2[0]); LOGEXITRC(); 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; LOGENTRY(); 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); LOGEXIT(); 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) { LOGENTRY(); 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); LOGEXIT(); } /* * 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; LOGENTRY(); fd = open(EVMS_GET_DEVNAME(volume), O_RDONLY, 0); if (fd < 0) { rc = EIO; LOGEXITRC(); return rc; } /* 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); LOGEXITRC(); 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; int rc = 0; LOGENTRY(); 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: rc = EINVAL; LOGEXITRC(); return rc; break; } if ( Bytes_Transferred != disk_count ) { rc = EIO; LOGEXITRC(); return rc; } LOGEXIT(); 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; }