/* * $Id$ * * Copyright (C) 2003-2013 The Bochs Project * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Commits a redolog file to a 'flat' or 'growing' mode base image. */ /* Converts growing mode image to flat and vice versa */ #if defined(WIN32) && !defined(__CYGWIN__) # include #endif #include #include #include #ifndef _MSC_VER #include #else #include #endif #include #include #include #include #include #include "config.h" #include #if !BX_HAVE_SNPRINTF #include /* XXX use real snprintf */ /* if they don't have snprintf, just use sprintf */ int snprintf(char *s, size_t maxlen, const char *format, ...) { va_list arg; int done; va_start(arg, format); done = vsprintf(s, format, arg); va_end(arg); return done; } #endif /* !BX_HAVE_SNPRINTF */ #include "../osdep.h" #include "bswap.h" #define HDIMAGE_HEADERS_ONLY 1 #include "../iodev/hdimage/hdimage.h" #define BXCOMMIT_MODE_COMMIT_UNDOABLE 1 #define BXCOMMIT_MODE_GROWING_TO_FLAT 2 #define BXCOMMIT_MODE_FLAT_TO_GROWING 3 #define BXCOMMIT_BASE_FLAT 1 #define BXCOMMIT_BASE_GROWING 2 typedef struct { int fd; redolog_header_t header; Bit32u *catalog; Bit8u *bitmap; bx_bool bitmap_update; Bit32u extent_index; Bit32u extent_offset; Bit32u extent_next; Bit32u bitmap_blocks; Bit32u extent_blocks; Bit64s imagepos; } redolog_t; int bxcommit_mode; int bx_remove; int bx_interactive; char bx_flat_filename[256]; char bx_redolog_name[256]; Bit8u null_sector[512]; const char *EOF_ERR = "ERROR: End of input"; const char *svnid = "$Id$"; const char *divider = "========================================================================"; const char *main_menu_prompt = "\n" "1. Commit 'undoable' redolog to 'flat' or 'growing' mode base image\n" "2. Create 'flat' disk image from 'growing' disk image\n" "3. Create 'growing' disk image from 'flat' disk image\n" "\n" "0. Quit\n" "\n" "Please choose one "; void myexit(int code) { #if defined(WIN32) && !defined(__CYGWIN__) printf("\nPress any key to continue\n"); getch(); #endif exit(code); } /* stolen from main.cc */ void bx_center_print(FILE *file, const char *line, int maxwidth) { int imax; int i; imax = (maxwidth - strlen(line)) >> 1; for (i=0; imax) { printf("Your choice (%s) was not an integer between %d and %d.\n\n", clean, min, max); } else { // choice is okay *out = n; return 0; } } } int ask_yn(const char *prompt, int the_default, int *out) { char buffer[16]; char *clean; *out = -1; while (1) { printf("%s", prompt); printf("[%s] ", the_default?"yes":"no"); if (!fgets(buffer, sizeof(buffer), stdin)) return -1; clean = clean_string(buffer); if (strlen(clean) < 1) { // empty line, use the default *out = the_default; return 0; } switch (tolower(clean[0])) { case 'y': *out=1; return 0; case 'n': *out=0; return 0; } printf("Please type either yes or no.\n"); } } int ask_string(const char *prompt, char *the_default, char *out) { char buffer[1024]; char *clean; printf("%s", prompt); printf("[%s] ", the_default); if (!fgets(buffer, sizeof(buffer), stdin)) return -1; clean = clean_string(buffer); if (strlen(clean) < 1) { // empty line, use the default strcpy(out, the_default); return 0; } strcpy(out, clean); return 0; } /* helper functions */ int bx_read_image(int fd, Bit64s offset, void *buf, int count) { if (lseek(fd, offset, SEEK_SET) == -1) { return -1; } return read(fd, buf, count); } int bx_write_image(int fd, Bit64s offset, void *buf, int count) { if (lseek(fd, offset, SEEK_SET) == -1) { return -1; } return write(fd, buf, count); } /* Read one sector and check if empty */ int flat_image_read_sector(int fd, Bit8u *buffer, Bit64u offset) { if (bx_read_image(fd, offset, buffer, 512) < 0) return 0; if (memcmp(buffer, null_sector, 512) == 0) return 0; return 1; } /* Create a suited redolog header */ void redolog_make_header(redolog_header_t *header, const char* type, Bit64u size) { Bit32u entries, extent_size, bitmap_size; Bit64u maxsize; // Set standard header values strcpy((char*)header->standard.magic, STANDARD_HEADER_MAGIC); strcpy((char*)header->standard.type, REDOLOG_TYPE); strcpy((char*)header->standard.subtype, type); header->standard.version = htod32(STANDARD_HEADER_VERSION); header->standard.header = htod32(STANDARD_HEADER_SIZE); entries = 512; bitmap_size = 1; // Compute #entries and extent size values do { static Bit32u flip=0; extent_size = 8 * bitmap_size * 512; header->specific.catalog = htod32(entries); header->specific.bitmap = htod32(bitmap_size); header->specific.extent = htod32(extent_size); maxsize = (Bit64u)entries * (Bit64u)extent_size; flip++; if(flip&0x01) bitmap_size *= 2; else entries *= 2; } while (maxsize < size); header->specific.disk = htod64(size); } /* Redolog functions */ int redolog_open(redolog_t *r, int fd, redolog_header_t *header) { int res; Bit32u i; memset(r, 0, sizeof(redolog_t)); r->fd = fd; memcpy(&r->header, header, sizeof(redolog_header_t)); r->catalog = (Bit32u*)malloc(dtoh32(r->header.specific.catalog) * sizeof(Bit32u)); printf("\nReading Catalog: ["); res = bx_read_image(r->fd, dtoh32(r->header.standard.header), r->catalog, dtoh32(r->header.specific.catalog) * sizeof(Bit32u)); if (res != (ssize_t)(dtoh32(r->header.specific.catalog) * sizeof(Bit32u))) { fatal("ERROR: redolog : could not read catalog"); } printf("...] Done."); r->extent_next = 0; for (i = 0; i < dtoh32(r->header.specific.catalog); i++) { if (dtoh32(r->catalog[i]) != REDOLOG_PAGE_NOT_ALLOCATED) { if (dtoh32(r->catalog[i]) >= r->extent_next) r->extent_next = dtoh32(r->catalog[i]) + 1; } } r->bitmap = (Bit8u *)malloc(dtoh32(r->header.specific.bitmap)); r->bitmap_blocks = 1 + (dtoh32(r->header.specific.bitmap) - 1) / 512; r->extent_blocks = 1 + (dtoh32(r->header.specific.extent) - 1) / 512; r->bitmap_update = 1; return 0; } int redolog_close(redolog_t *r) { if (r->fd >= 0) close(r->fd); if (r->catalog != NULL) free(r->catalog); if (r->bitmap != NULL) free(r->bitmap); return 0; } int redolog_check_format(int fd, const char *subtype, redolog_header_t *header) { redolog_header_t temp_header; int res = bx_read_image(fd, 0, &temp_header, sizeof(redolog_header_t)); if (res != STANDARD_HEADER_SIZE) { return HDIMAGE_READ_ERROR; } if (strcmp((char*)temp_header.standard.magic, STANDARD_HEADER_MAGIC) != 0) { return HDIMAGE_NO_SIGNATURE; } if (strcmp((char*)temp_header.standard.type, REDOLOG_TYPE) != 0) { return HDIMAGE_TYPE_ERROR; } if (strcmp((char*)temp_header.standard.subtype, subtype) != 0) { return HDIMAGE_TYPE_ERROR; } if (dtoh32(temp_header.standard.version) != STANDARD_HEADER_VERSION) { return HDIMAGE_VERSION_ERROR; } memcpy(header, &temp_header, sizeof(redolog_header_t)); return HDIMAGE_FORMAT_OK; } Bit64s redolog_lseek(redolog_t *redolog, Bit64s offset, int whence) { char msg[80]; Bit32u old_extent_index; if (whence == SEEK_SET) { redolog->imagepos = offset; } else if (whence == SEEK_CUR) { redolog->imagepos += offset; } else { fatal("redolog: lseek() mode not supported yet"); return -1; } if (redolog->imagepos > (Bit64s)dtoh64(redolog->header.specific.disk)) { sprintf(msg, "redolog : lseek() to byte %ld failed", (long)offset); fatal(msg); return -1; } old_extent_index = redolog->extent_index; redolog->extent_index = (Bit32u)(redolog->imagepos / dtoh32(redolog->header.specific.extent)); if (redolog->extent_index != old_extent_index) { redolog->bitmap_update = 1; } redolog->extent_offset = (Bit32u)((redolog->imagepos % dtoh32(redolog->header.specific.extent)) / 512); return redolog->imagepos; } int redolog_write_sector(redolog_t *redolog, Bit8u *buf, Bit64u offset) { Bit32u i; Bit64s block_offset, bitmap_offset, catalog_offset; ssize_t written; bx_bool update_catalog = 0; char *zerobuffer; char msg[80]; redolog_lseek(redolog, offset, SEEK_SET); if (dtoh32(redolog->catalog[redolog->extent_index]) == REDOLOG_PAGE_NOT_ALLOCATED) { if (redolog->extent_next >= dtoh32(redolog->header.specific.catalog)) { fatal("redolog : can't allocate new extent... catalog is full"); return -1; } // Extent not allocated, allocate new redolog->catalog[redolog->extent_index] = htod32(redolog->extent_next); redolog->extent_next += 1; zerobuffer = (char*)malloc(512); memset(zerobuffer, 0, 512); // Write bitmap bitmap_offset = (Bit64s)STANDARD_HEADER_SIZE + (dtoh32(redolog->header.specific.catalog) * sizeof(Bit32u)); bitmap_offset += (Bit64s)512 * dtoh32(redolog->catalog[redolog->extent_index]) * (redolog->extent_blocks + redolog->bitmap_blocks); lseek(redolog->fd, (off_t)bitmap_offset, SEEK_SET); for (i=0; ibitmap_blocks; i++) { write(redolog->fd, zerobuffer, 512); } // Write extent for (i=0; iextent_blocks; i++) { write(redolog->fd, zerobuffer, 512); } free(zerobuffer); update_catalog = 1; } bitmap_offset = (Bit64s)STANDARD_HEADER_SIZE + (dtoh32(redolog->header.specific.catalog) * sizeof(Bit32u)); bitmap_offset += (Bit64s)512 * dtoh32(redolog->catalog[redolog->extent_index]) * (redolog->extent_blocks + redolog->bitmap_blocks); block_offset = bitmap_offset + ((Bit64s)512 * (redolog->bitmap_blocks + redolog->extent_offset)); // Write block written = bx_write_image(redolog->fd, (off_t)block_offset, (void*)buf, 512); // Write bitmap if (redolog->bitmap_update) { if (bx_read_image(redolog->fd, (off_t)bitmap_offset, redolog->bitmap, dtoh32(redolog->header.specific.bitmap)) != (ssize_t)dtoh32(redolog->header.specific.bitmap)) { sprintf(msg, "redolog : failed to read bitmap for extent %d", redolog->extent_index); fatal(msg); return 0; } redolog->bitmap_update = 0; } // If block does not belong to extent yet if (((redolog->bitmap[redolog->extent_offset/8] >> (redolog->extent_offset%8)) & 0x01) == 0x00) { redolog->bitmap[redolog->extent_offset/8] |= 1 << (redolog->extent_offset%8); bx_write_image(redolog->fd, (off_t)bitmap_offset, redolog->bitmap, dtoh32(redolog->header.specific.bitmap)); } // Write catalog if (update_catalog) { // FIXME if mmap catalog_offset = (Bit64s)STANDARD_HEADER_SIZE + (redolog->extent_index * sizeof(Bit32u)); bx_write_image(redolog->fd, (off_t)catalog_offset, &redolog->catalog[redolog->extent_index], sizeof(Bit32u)); } if (written >= 0) redolog_lseek(redolog, 512, SEEK_CUR); return written; } int convert_flat_image() { int flatfd, redologfd; Bit64u disk_size, offset; redolog_t redolog; Bit32u i; struct stat stat_buf; Bit8u buffer[512]; if (bxcommit_mode == BXCOMMIT_MODE_FLAT_TO_GROWING) { // check if flat file exists flatfd = open(bx_flat_filename, O_RDONLY #ifdef O_BINARY | O_BINARY #endif ); if (flatfd < 0) { fatal("ERROR: flat file not found or not readable"); } if (fstat(flatfd, &stat_buf)) { fatal("fstat() returns error!"); } disk_size = (Bit64u)stat_buf.st_size; memset(null_sector, 0, 512); redologfd = open(bx_redolog_name, O_RDONLY #ifdef O_BINARY | O_BINARY #endif ); if (redologfd >= 0) { close(flatfd); close(redologfd); fatal("ERROR: growing file already exists"); } fprintf(stderr, "\nCreating growing image file: ["); memset(&redolog, 0, sizeof(redolog_t)); redolog.fd = open(bx_redolog_name, O_RDWR | O_CREAT #ifdef O_BINARY | O_BINARY #endif , S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP ); if (redolog.fd < 0) { close(flatfd); fatal("ERROR: redolog file is not writable"); } redolog_make_header(&redolog.header, REDOLOG_SUBTYPE_GROWING, disk_size); if (write(redolog.fd, &redolog.header, sizeof(redolog.header)) < 0) { fatal("ERROR: The disk image is not complete - could not write header!"); } else { lseek(redolog.fd, sizeof(redolog.header), SEEK_SET); } redolog.catalog = (Bit32u*)malloc(dtoh32(redolog.header.specific.catalog) * sizeof(Bit32u)); for (i=0; i 0) { if (redolog_write_sector(&redolog, buffer, offset) <= 0) break; } offset += 512; if (((offset >> 9) % (disk_size >> 15)) == 0) fprintf(stderr, "."); } fprintf(stderr, "]\n"); } else { fatal("ERROR: unknown / unsupported mode"); } close(flatfd); close(redolog.fd); return 0; } /* produce the image file */ int commit_redolog() { int flatfd = -1, redologfd, res = -1; int destmode = BXCOMMIT_BASE_FLAT; redolog_header_t header; redolog_t r1, r2; Bit32u i; Bit8u buffer[512]; if (bxcommit_mode == BXCOMMIT_MODE_COMMIT_UNDOABLE) { // check if flat file exists flatfd = open(bx_flat_filename, O_RDWR #ifdef O_BINARY | O_BINARY #endif ); if (flatfd < 0) { fatal("ERROR: flat file not found or not writable"); } printf("\nMode of base image: "); res = redolog_check_format(flatfd, REDOLOG_SUBTYPE_GROWING, &header); if (res == HDIMAGE_FORMAT_OK) { printf("'growing'\n"); redolog_open(&r1, flatfd, &header); destmode = BXCOMMIT_BASE_GROWING; } else { printf("'flat'\n"); } } // Check if redolog exists printf("\nOpening '%s'\n", bx_redolog_name); redologfd = open(bx_redolog_name, O_RDONLY #ifdef O_BINARY | O_BINARY #endif ); if (redologfd < 0) { fatal("ERROR: redolog file not found"); } printf("\nReading redolog header: ["); if (bxcommit_mode == BXCOMMIT_MODE_COMMIT_UNDOABLE) { res = redolog_check_format(redologfd, REDOLOG_SUBTYPE_UNDOABLE, &header); } else if (bxcommit_mode == BXCOMMIT_MODE_GROWING_TO_FLAT) { res = redolog_check_format(redologfd, REDOLOG_SUBTYPE_GROWING, &header); } else { fatal("\nERROR: unknown bxcommit mode!"); } if (res != HDIMAGE_FORMAT_OK) { switch (res) { case HDIMAGE_READ_ERROR: fatal("\nERROR: while reading redolog header!"); break; case HDIMAGE_NO_SIGNATURE: fatal("\nERROR: bad magic in redolog header!"); break; case HDIMAGE_TYPE_ERROR: fatal("\nERROR: bad type or subtype in redolog header!"); break; case HDIMAGE_VERSION_ERROR: fatal("\nERROR: bad version in redolog header!"); break; } } // Print infos on redlog printf("Type='%s', Subtype='%s', Version=%d.%d] Done.", header.standard.type, header.standard.subtype, dtoh32(header.standard.version)/0x10000, dtoh32(header.standard.version)%0x10000); printf("\nChecking redolog header: ["); printf("#entries=%d, bitmap size=%d, exent size = %d] Done.", dtoh32(header.specific.catalog), dtoh32(header.specific.bitmap), dtoh32(header.specific.extent)); if (bxcommit_mode == BXCOMMIT_MODE_GROWING_TO_FLAT) { flatfd = open(bx_flat_filename, O_RDONLY #ifdef O_BINARY | O_BINARY #endif ); if (flatfd >= 0) { close(flatfd); fatal("ERROR: flat file already exists"); } printf("\nCreating flat image file: ["); flatfd = open(bx_flat_filename, O_WRONLY | O_CREAT #ifdef O_BINARY | O_BINARY #endif , S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP ); if (flatfd < 0) { fatal("ERROR: flat file is not writable"); } lseek(flatfd, dtoh64(header.specific.disk) - 512, SEEK_SET); if (write(flatfd, buffer, 512) != 512) fatal("ERROR: while writing block in flat file !"); printf("...] Done."); } redolog_open(&r2, redologfd, &header); printf("\nCommitting changes to base image file: [ 0%%]"); for(i=0; i