Beispiel #1
0
/**
 * main function - for clone or restore data
 */
int main(int argc, char **argv) {
#ifdef MEMTRACE
	setenv("MALLOC_TRACE", "partclone_mtrace.log", 1);
	mtrace();
#endif
	char*			source;			/// source data
	char*			target;			/// target data
	int			dfr, dfw;		/// file descriptor for source and target
	int			r_size, w_size;		/// read and write size
	unsigned		cs_size = 0;		/// checksum_size
	int			cs_reseed = 1;
	int			start, stop;		/// start, range, stop number for progress bar
	unsigned long *bitmap = NULL;		/// the point for bitmap data
	int			debug = 0;		/// debug level
	int			tui = 0;		/// text user interface
	int			pui = 0;		/// progress mode(default text)
	
	int			flag;
	int			pres = 0;
	pthread_t		prog_thread;
	void			*p_result;

	static const char *const bad_sectors_warning_msg =
		"*************************************************************************\n"
		"* WARNING: The disk has bad sectors. This means physical damage on the  *\n"
		"* disk surface caused by deterioration, manufacturing faults, or        *\n"
		"* another reason. The reliability of the disk may remain stable or      *\n"
		"* degrade quickly. Use the --rescue option to efficiently save as much  *\n"
		"* data as possible!                                                     *\n"
		"*************************************************************************\n";

	file_system_info fs_info;   /// description of the file system
	image_options    img_opt;

	init_fs_info(&fs_info);
	init_image_options(&img_opt);

	/**
	 * get option and assign to opt structure
	 * check parameter and read from argv
	 */
	parse_options(argc, argv, &opt);

	/**
	 * if "-d / --debug" given
	 * open debug file in "/var/log/partclone.log" for log message 
	 */
	memset(&fs_opt, 0, sizeof(fs_cmd_opt));
	debug = opt.debug;
	fs_opt.debug = debug;
	fs_opt.ignore_fschk = opt.ignore_fschk;

	//if(opt.debug)
	open_log(opt.logfile);

	/**
	 * using Text User Interface
	 */
	if (opt.ncurses) {
		pui = NCURSES;
		log_mesg(1, 0, 0, debug, "Using Ncurses User Interface mode.\n");
	} else
		pui = TEXT;

	tui = open_pui(pui, opt.fresh);
	if ((opt.ncurses) && (!tui)) {
		opt.ncurses = 0;
		pui = TEXT;
		log_mesg(1, 0, 0, debug, "Open Ncurses User Interface Error.\n");
	}

	/// print partclone info
	print_partclone_info(opt);

#ifndef CHKIMG
	if (geteuid() != 0)
		log_mesg(0, 1, 1, debug, "You are not logged as root. You may have \"access denied\" errors when working.\n");
	else
		log_mesg(1, 0, 0, debug, "UID is root.\n");
#endif

	/// ignore crc check
	if (opt.ignore_crc)
		log_mesg(1, 0, 1, debug, "Ignore CRC errors\n");

	/**
	 * open source and target
	 * clone mode, source is device and target is image file/stdout
	 * restore mode, source is image file/stdin and target is device
	 * dd mode, source is device and target is device !!not complete
	 */
	source = opt.source;
	target = opt.target;
	log_mesg(1, 0, 0, debug, "source=%s, target=%s \n", source, target);
	dfr = open_source(source, &opt);
	if (dfr == -1) {
		log_mesg(0, 1, 1, debug, "Error exit\n");
	}

#ifndef CHKIMG
	dfw = open_target(target, &opt);
	if (dfw == -1) {
		log_mesg(0, 1, 1, debug, "Error exit\n");
	}
#else
	dfw = -1;
#endif

	/**
	 * get partition information like super block, bitmap from device or image file.
	 */
	if (opt.clone) {

		log_mesg(1, 0, 0, debug, "Initiate image options - version %s\n", IMAGE_VERSION_CURRENT);

		img_opt.checksum_mode = opt.checksum_mode;
		img_opt.checksum_size = get_checksum_size(opt.checksum_mode, opt.debug);
		img_opt.blocks_per_checksum = opt.blocks_per_checksum;
		img_opt.reseed_checksum = opt.reseed_checksum;

		cs_size = img_opt.checksum_size;
		cs_reseed = img_opt.reseed_checksum;

		log_mesg(1, 0, 0, debug, "Initial image hdr - get Super Block from partition\n");
		log_mesg(0, 0, 1, debug, "Reading Super Block\n");

		/// get Super Block information from partition
		read_super_blocks(source, &fs_info);

		if (img_opt.checksum_mode != CSM_NONE && img_opt.blocks_per_checksum == 0) {

			const unsigned int buffer_capacity = opt.buffer_size > fs_info.block_size
				? opt.buffer_size / fs_info.block_size : 1; // in blocks

			img_opt.blocks_per_checksum = buffer_capacity;

		}
		log_mesg(1, 0, 0, debug, "%u blocks per checksum\n", img_opt.blocks_per_checksum);

		check_mem_size(fs_info, img_opt, opt);

		/// alloc a memory to store bitmap
		bitmap = pc_alloc_bitmap(fs_info.totalblock);
		if (bitmap == NULL) {
			log_mesg(0, 1, 1, debug, "%s, %i, not enough memory\n", __func__, __LINE__);
		}

		log_mesg(2, 0, 0, debug, "initial main bitmap pointer %p\n", bitmap);
		log_mesg(1, 0, 0, debug, "Initial image hdr - read bitmap table\n");

		/// read and check bitmap from partition
		log_mesg(0, 0, 1, debug, "Calculating bitmap... Please wait... \n");
		read_bitmap(source, fs_info, bitmap, pui);
		update_used_blocks_count(&fs_info, bitmap);

		if (opt.check) {

			unsigned long long needed_space = 0;

			needed_space += sizeof(image_head) + sizeof(file_system_info) + sizeof(image_options);
			needed_space += get_bitmap_size_on_disk(&fs_info, &img_opt, &opt);
			needed_space += cnv_blocks_to_bytes(0, fs_info.usedblocks, fs_info.block_size, &img_opt);

			check_free_space(&dfw, needed_space);
		}

		log_mesg(2, 0, 0, debug, "check main bitmap pointer %p\n", bitmap);
		log_mesg(1, 0, 0, debug, "Writing super block and bitmap...\n");

		write_image_desc(&dfw, fs_info, img_opt, &opt);
		write_image_bitmap(&dfw, fs_info, img_opt, bitmap, &opt);

		log_mesg(0, 0, 1, debug, "done!\n");

	} else if (opt.restore) {

		image_head_v2 img_head;

		log_mesg(1, 0, 0, debug, "restore image hdr - get information from image file\n");
		log_mesg(1, 0, 1, debug, "Reading Super Block\n");

		/// get image information from image file
		load_image_desc(&dfr, &opt, &img_head, &fs_info, &img_opt);
		cs_size = img_opt.checksum_size;
		cs_reseed = img_opt.reseed_checksum;

		check_mem_size(fs_info, img_opt, opt);

		/// alloc a memory to restore bitmap
		bitmap = pc_alloc_bitmap(fs_info.totalblock);
		if (bitmap == NULL) {
			log_mesg(0, 1, 1, debug, "%s, %i, not enough memory\n", __func__, __LINE__);
		}

		log_mesg(2, 0, 0, debug, "initial main bitmap pointer %p\n", bitmap);
		log_mesg(1, 0, 0, debug, "Initial image hdr - read bitmap table\n");

		/// read and check bitmap from image file
		log_mesg(0, 0, 1, debug, "Calculating bitmap... Please wait...\n");
		load_image_bitmap(&dfr, opt, fs_info, img_opt, bitmap);

#ifndef CHKIMG
		/// check the dest partition size.
		if (opt.restore_raw_file)
			check_free_space(&dfw, fs_info.device_size);
		else if (opt.check)
			check_size(&dfw, fs_info.device_size);
#endif

		log_mesg(2, 0, 0, debug, "check main bitmap pointer %p\n", bitmap);
		log_mesg(0, 0, 1, debug, "done!\n");

	} else if (opt.dd || opt.domain) {

		log_mesg(1, 0, 0, debug, "Initiate image options - version %s\n", IMAGE_VERSION_CURRENT);
		img_opt.checksum_mode = opt.checksum_mode;
		img_opt.checksum_size = get_checksum_size(opt.checksum_mode, opt.debug);
		img_opt.blocks_per_checksum = opt.blocks_per_checksum;
		img_opt.reseed_checksum = opt.reseed_checksum;
		log_mesg(1, 0, 0, debug, "Initial image hdr - get Super Block from partition\n");
		log_mesg(1, 0, 1, debug, "Reading Super Block\n");

		/// get Super Block information from partition
		read_super_blocks(source, &fs_info);

		check_mem_size(fs_info, img_opt, opt);

		/// alloc a memory to restore bitmap
		bitmap = pc_alloc_bitmap(fs_info.totalblock);
		if (bitmap == NULL) {
			log_mesg(0, 1, 1, debug, "%s, %i, not enough memory\n", __func__, __LINE__);
		}

		log_mesg(2, 0, 0, debug, "initial main bitmap pointer %p\n", bitmap);
		log_mesg(1, 0, 0, debug, "Initial image hdr - read bitmap table\n");

		/// read and check bitmap from partition
		log_mesg(0, 0, 1, debug, "Calculating bitmap... Please wait... ");
		read_bitmap(source, fs_info, bitmap, pui);

		/// check the dest partition size.
		if (opt.dd && opt.check) {
			check_size(&dfw, fs_info.device_size);
		}

		log_mesg(2, 0, 0, debug, "check main bitmap pointer %p\n", bitmap);
		log_mesg(0, 0, 1, debug, "done!\n");
	} else if (opt.ddd){

		if (dfr != 0)
		    read_super_blocks(source, &fs_info);
		else
		    read_super_blocks(target, &fs_info);

		img_opt.checksum_mode = opt.checksum_mode;
		img_opt.checksum_size = get_checksum_size(opt.checksum_mode, opt.debug);
		img_opt.blocks_per_checksum = opt.blocks_per_checksum;
		check_mem_size(fs_info, img_opt, opt);

		/// alloc a memory to restore bitmap
		bitmap = pc_alloc_bitmap(fs_info.totalblock);
		if (bitmap == NULL) {
			log_mesg(0, 1, 1, debug, "%s, %i, not enough memory\n", __func__, __LINE__);
		}

		log_mesg(2, 0, 0, debug, "initial main bitmap pointer %p\n", bitmap);
		log_mesg(1, 0, 0, debug, "Initial image hdr - read bitmap table\n");

		/// read and check bitmap from partition
		log_mesg(0, 0, 1, debug, "Calculating bitmap... Please wait... ");
		read_bitmap(source, fs_info, bitmap, pui);

		/// check the dest partition size.
		if (opt.check) {

		    struct stat target_stat;
		    if ((stat(opt.target, &target_stat) != -1) && (strcmp(opt.target, "-") != 0)) {
			if (S_ISBLK(target_stat.st_mode)) 
			    check_size(&dfw, fs_info.device_size);
			else {
			    unsigned long long needed_space = 0;

			    needed_space += sizeof(image_head) + sizeof(file_system_info) + sizeof(image_options);
			    needed_space += get_bitmap_size_on_disk(&fs_info, &img_opt, &opt);
			    needed_space += cnv_blocks_to_bytes(0, fs_info.usedblocks, fs_info.block_size, &img_opt);

			    check_free_space(&dfw, needed_space);
			}
		    }
		}

		log_mesg(2, 0, 0, debug, "check main bitmap pointer %p\n", bitmap);
		log_mesg(0, 0, 1, debug, "done!\n");
    
	}

	log_mesg(1, 0, 0, debug, "print image information\n");

	/// print option to log file
	if (debug)
		print_opt(opt);

	print_file_system_info(fs_info, opt);

	/**
	 * initial progress bar
	 */
	start = 0;				/// start number of progress bar
	stop = (fs_info.usedblocks);		/// get the end of progress number, only used block
	log_mesg(1, 0, 0, debug, "Initial Progress bar\n");
	/// Initial progress bar
	if (opt.no_block_detail)
		flag = NO_BLOCK_DETAIL;
	else
		flag = IO;
	progress_init(&prog, start, stop, fs_info.totalblock, flag, fs_info.block_size);
	copied = 0;				/// initial number is 0

	/**
	 * thread to print progress
	 */
	pres = pthread_create(&prog_thread, NULL, thread_update_pui, NULL);
	if(pres)
	    log_mesg(0, 1, 1, debug, "%s, %i, thread create error\n", __func__, __LINE__);


	/**
	 * start read and write data between source and destination
	 */
	if (opt.clone) {

		const unsigned long long blocks_total = fs_info.totalblock;
		const unsigned int block_size = fs_info.block_size;
		const unsigned int buffer_capacity = opt.buffer_size > block_size ? opt.buffer_size / block_size : 1; // in blocks
		unsigned char checksum[cs_size];
		unsigned int blocks_in_cs, blocks_per_cs, write_size;
		char *read_buffer, *write_buffer;

		blocks_per_cs = img_opt.blocks_per_checksum;

		log_mesg(1, 0, 0, debug, "#\nBuffer capacity = %u, Blocks per cs = %u\n#\n", buffer_capacity, blocks_per_cs);

		write_size = cnv_blocks_to_bytes(0, buffer_capacity, block_size, &img_opt);

		read_buffer = (char*)malloc(buffer_capacity * block_size);
		write_buffer = (char*)malloc(write_size + cs_size);

		if (read_buffer == NULL || write_buffer == NULL) {
			log_mesg(0, 1, 1, debug, "%s, %i, not enough memory\n", __func__, __LINE__);
		}

		/// read data from the first block
		if (lseek(dfr, 0, SEEK_SET) == (off_t)-1)
			log_mesg(0, 1, 1, debug, "source seek ERROR:%s\n", strerror(errno));

		log_mesg(0, 0, 0, debug, "Total block %llu\n", blocks_total);

		/// start clone partition to image file
		log_mesg(1, 0, 0, debug, "start backup data...\n");

		blocks_in_cs = 0;
		init_checksum(img_opt.checksum_mode, checksum, debug);

		block_id = 0;
		do {
			/// scan bitmap
			unsigned long long i, blocks_skip, blocks_read;
			unsigned int cs_added = 0, write_offset = 0;
			off_t offset;

			/// skip unused blocks
			for (blocks_skip = 0;
			     block_id + blocks_skip < blocks_total &&
			     !pc_test_bit(block_id + blocks_skip, bitmap);
			     blocks_skip++);
			if (block_id + blocks_skip == blocks_total)
				break;

			if (blocks_skip)
				block_id += blocks_skip;

			/// read blocks
			for (blocks_read = 0;
			     block_id + blocks_read < blocks_total && blocks_read < buffer_capacity &&
			     pc_test_bit(block_id + blocks_read, bitmap);
			     ++blocks_read);
			if (!blocks_read)
				break;

			offset = (off_t)(block_id * block_size);
			if (lseek(dfr, offset, SEEK_SET) == (off_t)-1)
				log_mesg(0, 1, 1, debug, "source seek ERROR:%s\n", strerror(errno));

			r_size = read_all(&dfr, read_buffer, blocks_read * block_size, &opt);
			if (r_size != (int)(blocks_read * block_size)) {
				if ((r_size == -1) && (errno == EIO)) {
					if (opt.rescue) {
						memset(read_buffer, 0, blocks_read * block_size);
						for (r_size = 0; r_size < blocks_read * block_size; r_size += PART_SECTOR_SIZE)
							rescue_sector(&dfr, offset + r_size, read_buffer + r_size, &opt);
					} else
						log_mesg(0, 1, 1, debug, "%s", bad_sectors_warning_msg);
				} else
					log_mesg(0, 1, 1, debug, "read error: %s\n", strerror(errno));
			}

			/// calculate checksum
			log_mesg(2, 0, 0, debug, "blocks_read = %i\n", blocks_read);
			for (i = 0; i < blocks_read; ++i) {

				memcpy(write_buffer + write_offset,
					read_buffer + i * block_size, block_size);

				write_offset += block_size;

				update_checksum(checksum, read_buffer + i * block_size, block_size);

				if (blocks_per_cs > 0 && ++blocks_in_cs == blocks_per_cs) {
				    log_mesg(3, 0, 0, debug, "CRC = %x%x%x%x \n", checksum[0], checksum[1], checksum[2], checksum[3]);

					memcpy(write_buffer + write_offset, checksum, cs_size);

					++cs_added;
					write_offset += cs_size;

					blocks_in_cs = 0;
					if (cs_reseed)
						init_checksum(img_opt.checksum_mode, checksum, debug);
				}
			}

			/// write buffer to target
			w_size = write_all(&dfw, write_buffer, write_offset, &opt);
			if (w_size != write_offset)
				log_mesg(0, 1, 1, debug, "image write ERROR:%s\n", strerror(errno));

			/// count copied block
			copied += blocks_read;
			log_mesg(2, 0, 0, debug, "copied = %lld\n", copied);

			/// next block
			block_id += blocks_read;

			/// read or write error
			if (r_size + cs_added * cs_size != w_size)
				log_mesg(0, 1, 1, debug, "read(%i) and write(%i) different\n", r_size, w_size);

		} while (1);

		if (blocks_in_cs > 0) {

			// Write the checksum for the latest blocks
			log_mesg(1, 0, 0, debug, "Write the checksum for the latest blocks. size = %i\n", cs_size);
			log_mesg(3, 0, 0, debug, "CRC = %x%x%x%x \n", checksum[0], checksum[1], checksum[2], checksum[3]);
			w_size = write_all(&dfw, (char*)checksum, cs_size, &opt);
			if (w_size != cs_size)
				log_mesg(0, 1, 1, debug, "image write ERROR:%s\n", strerror(errno));
		}

		free(write_buffer);
		free(read_buffer);

	// check only the size when the image does not contains checksums and does not
	// comes from a pipe
	} else if (opt.chkimg && img_opt.checksum_mode == CSM_NONE
		&& strcmp(opt.source, "-") != 0) {

		unsigned long long total_offset = (fs_info.usedblocks - 1) * fs_info.block_size;
		char last_block[fs_info.block_size];
		off_t partial_offset = INT32_MAX;

		while (total_offset) {

			if (partial_offset > total_offset)
				partial_offset = total_offset;

			if (lseek(dfr, partial_offset, SEEK_CUR) == (off_t)-1)
				log_mesg(0, 1, 1, debug, "source seek ERROR: %s\n", strerror(errno));

			total_offset -= partial_offset;
		}

		if (read_all(&dfr, last_block, fs_info.block_size, &opt) != fs_info.block_size)
			log_mesg(0, 1, 1, debug, "ERROR: source image too short\n");

	} else if (opt.restore) {

		const unsigned long long blocks_total = fs_info.totalblock;
		const unsigned int block_size = fs_info.block_size;
		const unsigned int buffer_capacity = opt.buffer_size > block_size ? opt.buffer_size / block_size : 1; // in blocks
		const unsigned int blocks_per_cs = img_opt.blocks_per_checksum;
		unsigned long long blocks_used = fs_info.usedblocks;
		unsigned int blocks_in_cs, buffer_size, read_offset;
		unsigned char checksum[cs_size];
		char *read_buffer, *write_buffer;
		unsigned long long blocks_used_fix = 0, test_block = 0;

		log_mesg(1, 0, 0, debug, "#\nBuffer capacity = %u, Blocks per cs = %u\n#\n", buffer_capacity, blocks_per_cs);

		// fix some super block record incorrect
		for (test_block = 0; test_block < blocks_total; ++test_block)
			if (pc_test_bit(test_block, bitmap))
				blocks_used_fix++;

		if (blocks_used_fix != blocks_used) {
			blocks_used = blocks_used_fix;
			log_mesg(1, 0, 0, debug, "info: fixed used blocks count\n");
		}
		buffer_size = cnv_blocks_to_bytes(0, buffer_capacity, block_size, &img_opt);

		if (img_opt.image_version != 0x0001)
			read_buffer = (char*)malloc(buffer_size);
		else {
			// Allocate more memory in case the image is affected by the 64 bits bug
			read_buffer = (char*)malloc(buffer_size + buffer_capacity * cs_size);
		}
		write_buffer = (char*)malloc(buffer_capacity * block_size);
		if (read_buffer == NULL || write_buffer == NULL) {
			log_mesg(0, 1, 1, debug, "%s, %i, not enough memory\n", __func__, __LINE__);
		}

#ifndef CHKIMG
		/// seek to the first
		if (lseek(dfw, opt.offset, SEEK_SET) == (off_t)-1)
			log_mesg(0, 1, 1, debug, "target seek ERROR:%s\n", strerror(errno));
#endif

		/// start restore image file to partition
		log_mesg(1, 0, 0, debug, "start restore data...\n");

		blocks_in_cs = 0;
		if (!opt.ignore_crc)
			init_checksum(img_opt.checksum_mode, checksum, debug);

		block_id = 0;
		do {
			unsigned int i;
			unsigned long long blocks_written, bytes_skip;
			unsigned int read_size;
			// max chunk to read using one read(2) syscall
			int blocks_read = copied + buffer_capacity < blocks_used ?
				buffer_capacity : blocks_used - copied;
			if (!blocks_read)
				break;

			log_mesg(1, 0, 0, debug, "blocks_read = %d and copied = %lld\n", blocks_read, copied);
			read_size = cnv_blocks_to_bytes(copied, blocks_read, block_size, &img_opt);

			// increase read_size to make room for the oversized checksum
			if (blocks_per_cs && blocks_read < buffer_capacity &&
					(blocks_read % blocks_per_cs) && (blocks_used % blocks_per_cs)) {
				/// it is the last read and there is a partial chunk at the end
				log_mesg(1, 0, 0, debug, "# PARTIAL CHUNK\n");
				read_size += cs_size;
			}

			// read chunk from image
			log_mesg(1, 0, 0, debug, "read more: ");

			r_size = read_all(&dfr, read_buffer, read_size, &opt);
			if (r_size != read_size)
				log_mesg(0, 1, 1, debug, "read ERROR:%s\n", strerror(errno));

			// read buffer is the follows:
			// <blocks_per_cs><cs1><blocks_per_cs><cs2>...

			// write buffer should be the following:
			// <block1><block2>...

			read_offset = 0;
			for (i = 0; i < blocks_read; ++i) {

				memcpy(write_buffer + i * block_size,
					read_buffer + read_offset, block_size);

				if (opt.ignore_crc) {
					read_offset += block_size;
					if (++blocks_in_cs == blocks_per_cs)
						read_offset += cs_size;
					continue;
				}

				update_checksum(checksum, read_buffer + read_offset, block_size);

				if (++blocks_in_cs == blocks_per_cs) {

				    unsigned char checksum_orig[cs_size];
				    memcpy(checksum_orig, read_buffer + read_offset + block_size, cs_size);
				    log_mesg(3, 0, 0, debug, "CRC = %x%x%x%x \n", checksum[0], checksum[1], checksum[2], checksum[3]);
				    log_mesg(3, 0, 0, debug, "CRC.orig = %x%x%x%x \n", checksum_orig[0], checksum_orig[1], checksum_orig[2], checksum_orig[3]);
					if (memcmp(read_buffer + read_offset + block_size, checksum, cs_size)) {
					    log_mesg(0, 1, 1, debug, "CRC error, block_id=%llu...\n ", block_id + i);
					}

					read_offset += cs_size;

					blocks_in_cs = 0;
					if (cs_reseed)
						init_checksum(img_opt.checksum_mode, checksum, debug);
				}

				read_offset += block_size;
			}
			if (blocks_in_cs && blocks_per_cs && blocks_read < buffer_capacity &&
					(blocks_read % blocks_per_cs)) {

			    log_mesg(1, 0, 0, debug, "check latest chunk's checksum covering %u blocks\n", blocks_in_cs);
			    if (memcmp(read_buffer + read_offset, checksum, cs_size)){
				unsigned char checksum_orig[cs_size];
				memcpy(checksum_orig, read_buffer + read_offset, cs_size);
				log_mesg(1, 0, 0, debug, "CRC = %x%x%x%x \n", checksum[0], checksum[1], checksum[2], checksum[3]);
				log_mesg(1, 0, 0, debug, "CRC.orig = %x%x%x%x \n", checksum_orig[0], checksum_orig[1], checksum_orig[2], checksum_orig[3]);
				log_mesg(0, 1, 1, debug, "CRC error, block_id=%llu...\n ", block_id + i);
			    }

			}


			blocks_written = 0;
			do {
				int blocks_write;

				/// count bytes to skip
				for (bytes_skip = 0;
				     block_id < blocks_total &&
				     !pc_test_bit(block_id, bitmap);
				     block_id++, bytes_skip += block_size);

#ifndef CHKIMG
				/// skip empty blocks
				if (bytes_skip > 0 && lseek(dfw, (off_t)bytes_skip, SEEK_CUR) == (off_t)-1)
					log_mesg(0, 1, 1, debug, "target seek ERROR:%s\n", strerror(errno));
#endif

				/// blocks to write
				for (blocks_write = 0;
				     block_id + blocks_write < blocks_total &&
				     blocks_written + blocks_write < blocks_read &&
				     pc_test_bit(block_id + blocks_write, bitmap);
				     blocks_write++);

#ifndef CHKIMG
				// write blocks
				if (blocks_write > 0) {
					w_size = write_all(&dfw, write_buffer + blocks_written * block_size,
						blocks_write * block_size, &opt);
					if (w_size != blocks_write * block_size) {
						if (!opt.skip_write_error)
							log_mesg(0, 1, 1, debug, "write block %llu ERROR:%s\n", block_id + blocks_written, strerror(errno));
						else
							log_mesg(0, 0, 1, debug, "skip write block %llu error:%s\n", block_id + blocks_written, strerror(errno));
					}
				}
#endif

				blocks_written += blocks_write;
				block_id += blocks_write;
				copied += blocks_write;
			} while (blocks_written < blocks_read);

		} while(1);
		free(write_buffer);
		free(read_buffer);

#ifndef CHKIMG
		/// restore_raw_file option
		if (opt.restore_raw_file && !pc_test_bit(blocks_total - 1, bitmap)) {
			if (ftruncate(dfw, (off_t)(blocks_total * block_size)) == -1)
				log_mesg(0, 0, 1, debug, "ftruncate ERROR:%s\n", strerror(errno));
		}
#endif

	} else if (opt.dd) {

		char *buffer;
		int block_size = fs_info.block_size;
		unsigned long long blocks_total = fs_info.totalblock;
		int buffer_capacity = block_size < opt.buffer_size ? opt.buffer_size / block_size : 1;

		buffer = (char*)malloc(buffer_capacity * block_size);
		if (buffer == NULL) {
			log_mesg(0, 1, 1, debug, "%s, %i, not enough memory\n", __func__, __LINE__);
		}

		block_id = 0;

		if (lseek(dfr, 0, SEEK_SET) == (off_t)-1)
			log_mesg(0, 1, 1, debug, "source seek ERROR:%d\n", strerror(errno));

		log_mesg(0, 0, 0, debug, "Total block %llu\n", blocks_total);

		/// start clone partition to partition
		log_mesg(1, 0, 0, debug, "start backup data device-to-device...\n");
		do {
			/// scan bitmap
			unsigned long long blocks_skip, blocks_read;
			off_t offset;

			/// skip unused blocks
			for (blocks_skip = 0;
			     block_id + blocks_skip < blocks_total &&
			     !pc_test_bit(block_id + blocks_skip, bitmap);
			     blocks_skip++);

			if (block_id + blocks_skip == blocks_total)
				break;

			if (blocks_skip)
				block_id += blocks_skip;

			/// read chunk from source
			for (blocks_read = 0;
			     block_id + blocks_read < blocks_total && blocks_read < buffer_capacity &&
			     pc_test_bit(block_id + blocks_read, bitmap);
			     ++blocks_read);

			if (!blocks_read)
				break;

			offset = (off_t)(block_id * block_size);
			if (lseek(dfr, offset, SEEK_SET) == (off_t)-1)
				log_mesg(0, 1, 1, debug, "source seek ERROR:%s\n", strerror(errno));
			if (lseek(dfw, offset + opt.offset, SEEK_SET) == (off_t)-1)
				log_mesg(0, 1, 1, debug, "target seek ERROR:%s\n", strerror(errno));

			r_size = read_all(&dfr, buffer, blocks_read * block_size, &opt);
			if (r_size != (int)(blocks_read * block_size)) {
				if ((r_size == -1) && (errno == EIO)) {
					if (opt.rescue) {
						memset(buffer, 0, blocks_read * block_size);
						for (r_size = 0; r_size < blocks_read * block_size; r_size += PART_SECTOR_SIZE)
							rescue_sector(&dfr, offset + r_size, buffer + r_size, &opt);
					} else
						log_mesg(0, 1, 1, debug, "%s", bad_sectors_warning_msg);
				} else
					log_mesg(0, 1, 1, debug, "source read ERROR %s\n", strerror(errno));
			}

			/// write buffer to target
			w_size = write_all(&dfw, buffer, blocks_read * block_size, &opt);
			if (w_size != (int)(blocks_read * block_size)) {
				if (opt.skip_write_error)
					log_mesg(0, 0, 1, debug, "skip write block %lli error:%s\n", block_id, strerror(errno));
				else
					log_mesg(0, 1, 1, debug, "write block %lli ERROR:%s\n", block_id, strerror(errno));
			}

			/// count copied block
			copied += blocks_read;

			/// next block
			block_id += blocks_read;

			/// read or write error
			if (r_size != w_size) {
				if (opt.skip_write_error)
					log_mesg(0, 0, 1, debug, "read and write different\n");
				else
					log_mesg(0, 1, 1, debug, "read and write different\n");
			}
		} while (1);

		free(buffer);

		/// restore_raw_file option
		if (opt.restore_raw_file && !pc_test_bit(blocks_total - 1, bitmap)) {
			if (ftruncate(dfw, (off_t)(blocks_total * block_size)) == -1)
				log_mesg(0, 0, 1, debug, "ftruncate ERROR:%s\n", strerror(errno));
		}

	} else if (opt.domain) {

		int cmp, nx_current = 0;
		unsigned long long next_block_id = 0;
		log_mesg(0, 0, 0, debug, "Total block %i\n", fs_info.totalblock);
		log_mesg(1, 0, 0, debug, "start writing domain log...\n");
		// write domain log comment and status line
		dprintf(dfw, "# Domain logfile created by %s v%s\n", get_exec_name(), VERSION);
		dprintf(dfw, "# Source: %s\n", opt.source);
		dprintf(dfw, "# Offset: 0x%08llX\n", opt.offset_domain);
		dprintf(dfw, "# current_pos  current_status\n");
		dprintf(dfw, "0x%08llX     ?\n", opt.offset_domain + (fs_info.totalblock * fs_info.block_size));
		dprintf(dfw, "#      pos        size  status\n");
		// start logging the used/unused areas
		cmp = pc_test_bit(0, bitmap);
		for (block_id = 0; block_id <= fs_info.totalblock; block_id++) {
			if (block_id < fs_info.totalblock) {
				nx_current = pc_test_bit(block_id, bitmap);
				if (nx_current)
					copied++;
			} else
				nx_current = -1;
			if (nx_current != cmp) {
				dprintf(dfw, "0x%08llX  0x%08llX  %c\n",
					opt.offset_domain + (next_block_id * fs_info.block_size),
					(block_id - next_block_id) * fs_info.block_size,
					cmp ? '+' : '?');
				next_block_id = block_id;
				cmp = nx_current;
			}
			// don't bother updating progress
		} /// end of for
	} else if (opt.ddd) {

		char *buffer;
		int block_size = fs_info.block_size;
		unsigned long long blocks_total = fs_info.totalblock;
		int blocks_in_buffer = block_size < opt.buffer_size ? opt.buffer_size / block_size : 1;

		buffer = (char*)malloc(blocks_in_buffer * block_size);
		if (buffer == NULL) {
			log_mesg(0, 1, 1, debug, "%s, %i, not enough memory\n", __func__, __LINE__);
		}

		block_id = 0;


		log_mesg(0, 0, 0, debug, "Total block %llu\n", blocks_total);

		/// start clone partition to partition
		log_mesg(1, 0, 0, debug, "start backup data device-to-device...\n");
		do {
			/// scan bitmap
			unsigned long long blocks_read;

			/// read chunk from source
			for (blocks_read = 0;
			     block_id + blocks_read < blocks_total && blocks_read < blocks_in_buffer &&
			     pc_test_bit(block_id + blocks_read, bitmap);
			     blocks_read++);

			if (!blocks_read)
				break;

			r_size = read_all(&dfr, buffer, blocks_read * block_size, &opt);
			if (r_size != (int)(blocks_read * block_size)) {
				if ((r_size == -1) && (errno == EIO)) {
					if (opt.rescue) {
						memset(buffer, 0, blocks_read * block_size);
						for (r_size = 0; r_size < blocks_read * block_size; r_size += PART_SECTOR_SIZE)
							rescue_sector(&dfr, r_size, buffer + r_size, &opt);
					} else
						log_mesg(0, 1, 1, debug, "%s", bad_sectors_warning_msg);
				} else if (r_size == 0){ // done for ddd
				    /// write buffer to target
				    w_size = write_all(&dfw, buffer, rescue_write_size, &opt);
				    break;
				} else
					log_mesg(0, 1, 1, debug, "source read ERROR %s\n", strerror(errno));
			}

			/// write buffer to target
			w_size = write_all(&dfw, buffer, blocks_read * block_size, &opt);
			if (w_size != (int)(blocks_read * block_size)) {
				if (opt.skip_write_error)
					log_mesg(0, 0, 1, debug, "skip write block %lli error:%s\n", block_id, strerror(errno));
				else
					log_mesg(0, 1, 1, debug, "write block %lli ERROR:%s\n", block_id, strerror(errno));
			}

			/// count copied block
			copied += blocks_read;

			/// next block
			block_id += blocks_read;

			/// read or write error
			if (r_size != w_size) {
				if (opt.skip_write_error)
					log_mesg(0, 0, 1, debug, "read and write different\n");
				else
					log_mesg(0, 1, 1, debug, "read and write different\n");
			}
		} while (1);

		free(buffer);

		/// restore_raw_file option
		if (opt.restore_raw_file && !pc_test_bit(blocks_total - 1, bitmap)) {
			if (ftruncate(dfw, (off_t)(blocks_total * block_size)) == -1)
				log_mesg(0, 0, 1, debug, "ftruncate ERROR:%s\n", strerror(errno));
		}



	}

	done = 1;
	pres = pthread_join(prog_thread, &p_result);
	if(pres)
	    log_mesg(0, 1, 1, debug, "%s, %i, thread join error\n", __func__, __LINE__);
	update_pui(&prog, copied, block_id, done);
#ifndef CHKIMG
	sync_data(dfw, &opt);
#endif
	print_finish_info(opt);

	/// close source
	close(dfr);
	/// close target
	if (dfw != -1)
		close(dfw);
	/// free bitmp
	free(bitmap);
	close_pui(pui);
#ifndef CHKIMG
	fprintf(stderr, "Cloned successfully.\n");
#else
	printf("Checked successfully.\n");
#endif
	if (opt.debug)
		close_log();
#ifdef MEMTRACE
	muntrace();
#endif
	return 0;      /// finish
}
Beispiel #2
0
/**
 * main functiom - for colne or restore data
 */
int main(int argc, char **argv) {
#ifdef MEMTRACE
    setenv("MALLOC_TRACE", "partclone_mtrace.log", 1);
    mtrace();
#endif
    char*		source;			/// source data
    char*		target;			/// target data
    char*		buffer;			/// buffer data for malloc used
    char*		buffer2;			/// buffer data for malloc used
    int			dfr, dfw;		/// file descriptor for source and target
    int			r_size, w_size;		/// read and write size
    //unsigned long long	block_id, copied = 0;	/// block_id is every block in partition
    /// copied is copied block count
    off_t		offset = 0, sf = 0;	/// seek postition, lseek result
    int			start, stop;		/// start, range, stop number for progress bar
    unsigned long long	total_write = 0;	/// the copied size
    unsigned long long	needed_size = 0;	/// the copied size
    unsigned long long	needed_mem  = 0;	/// the copied size
    char		bitmagic[8] = "BiTmAgIc";// only for check postition
    char		bitmagic_r[8]="00000000";/// read magic string from image
    int			cmp;			/// compare magic string
    unsigned long	*bitmap;		/// the point for bitmap data
    int			debug = 0;		/// debug or not
    unsigned long	crc = 0xffffffffL;	/// CRC32 check code for writint to image
    unsigned long	crc_ck = 0xffffffffL;	/// CRC32 check code for checking
    unsigned long	crc_ck2 = 0xffffffffL;	/// CRC32 check code for checking
    int			c_size;			/// CRC32 code size
    int			n_crc_size = CRC_SIZE;
    char*		crc_buffer;		/// buffer data for malloc crc code
    //int			done = 0;
    int			s_count = 0;
    int			rescue_num = 0;
    unsigned long long			rescue_pos = 0;
    unsigned long long			main_pos = 0;
    int			tui = 0;		/// text user interface
    int			pui = 0;		/// progress mode(default text)
    int                 next=1,next_int=1,next_max_count=7,next_count=7,i;
    unsigned long long  next_block_id;
    char*               cache_buffer;
    int                 nx_current=0;
    char                bbuffer[4096];
    int flag;
    int pres;
    pthread_t prog_thread;
    void *p_result;

    char *bad_sectors_warning_msg =
        "*************************************************************************\n"
        "* WARNING: The disk has bad sector. This means physical damage on the   *\n"
        "* disk surface caused by deterioration, manufacturing faults or other   *\n"
        "* reason. The reliability of the disk may stay stable or degrade fast.  *\n"
        "* Use the --rescue option to efficiently save as much data as possible! *\n"
        "*************************************************************************\n";

    image_head		image_hdr;		/// image_head structure defined in partclone.h
    memset(&image_hdr, 0, sizeof(image_hdr));

    /**
     * get option and assign to opt structure
     * check parameter and read from argv
     */
    parse_options(argc, argv, &opt);

    /**
     * if "-d / --debug" given
     * open debug file in "/var/log/partclone.log" for log message
     */
    memset(&fs_opt, 0, sizeof(fs_cmd_opt));
    debug = opt.debug;
    fs_opt.debug = debug;
    fs_opt.ignore_fschk = opt.ignore_fschk;

    next_max_count = opt.max_block_cache-1;
    next_count = opt.max_block_cache-1;

    //if(opt.debug)
    open_log(opt.logfile);

    /**
     * using Text User Interface
     */
    if (opt.ncurses) {
        pui = NCURSES;
        log_mesg(1, 0, 0, debug, "Using Ncurses User Interface mode.\n");
    } else
        pui = TEXT;

    tui = open_pui(pui, opt.fresh);
    if ((opt.ncurses) && (tui == 0)) {
        opt.ncurses = 0;
        log_mesg(1, 0, 0, debug, "Open Ncurses User Interface Error.\n");
    }

    /// print partclone info
    print_partclone_info(opt);

    if (geteuid() != 0)
        log_mesg(0, 1, 1, debug, "You are not logged as root. You may have \"access denied\" errors when working.\n");
    else
        log_mesg(1, 0, 0, debug, "UID is root.\n");

    /// ignore crc check
    if(opt.ignore_crc)
        log_mesg(1, 0, 1, debug, "Ignore CRC error\n");

    /**
     * open source and target
     * clone mode, source is device and target is image file/stdout
     * restore mode, source is image file/stdin and target is device
     * dd mode, source is device and target is device !!not complete
     */
#ifdef _FILE_OFFSET_BITS
    log_mesg(1, 0, 0, debug, "enable _FILE_OFFSET_BITS %i\n", _FILE_OFFSET_BITS);
#endif
    source = opt.source;
    target = opt.target;
    dfr = open_source(source, &opt);
    if (dfr == -1) {
        log_mesg(0, 1, 1, debug, "Erro EXIT.\n");
    }

    dfw = open_target(target, &opt);
    if (dfw == -1) {
        log_mesg(0, 1, 1, debug, "Error Exit.\n");
    }

    /**
     * get partition information like super block, image_head, bitmap
     * from device or image file.
     */
    if (opt.clone) {

        log_mesg(1, 0, 0, debug, "Initial image hdr - get Super Block from partition\n");
        log_mesg(0, 0, 1, debug, "Reading Super Block\n");

        /// get Super Block information from partition
        initial_image_hdr(source, &image_hdr);

        /// check memory size
        if (check_mem_size(image_hdr, opt, &needed_mem) == -1)
            log_mesg(0, 1, 1, debug, "Ther is no enough free memory, partclone suggests you should have %lld bytes memory\n", needed_mem);

        strncpy(image_hdr.version, IMAGE_VERSION, VERSION_SIZE);

        /// alloc a memory to restore bitmap
        bitmap = (unsigned long*)calloc(sizeof(unsigned long), LONGS(image_hdr.totalblock));
        if(bitmap == NULL) {
            log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
        }

        log_mesg(2, 0, 0, debug, "initial main bitmap pointer %i\n", bitmap);
        log_mesg(1, 0, 0, debug, "Initial image hdr - read bitmap table\n");

        /// read and check bitmap from partition
        log_mesg(0, 0, 1, debug, "Calculating bitmap... Please wait... ");
        readbitmap(source, image_hdr, bitmap, pui);

        needed_size = (unsigned long long)(((image_hdr.block_size+sizeof(unsigned long))*image_hdr.usedblocks)+sizeof(image_hdr)+sizeof(char)*image_hdr.totalblock);
        if (opt.check)
            check_free_space(&dfw, needed_size);

        log_mesg(2, 0, 0, debug, "check main bitmap pointer %i\n", bitmap);

        log_mesg(1, 0, 0, debug, "Writing super block and bitmap... ");
        // write image_head to image file
        w_size = write_all(&dfw, (char *)&image_hdr, sizeof(image_head), &opt);
        if(w_size == -1)
            log_mesg(0, 1, 1, debug, "write image_hdr to image error\n");

        // write bitmap information to image file
        for (i = 0; i < image_hdr.totalblock; i++) {
            if (pc_test_bit(i, bitmap)) {
                bbuffer[i % sizeof(bbuffer)] = 1;
            } else {
                bbuffer[i % sizeof(bbuffer)] = 0;
            }
            if (i % sizeof(bbuffer) == sizeof(bbuffer) - 1 || i == image_hdr.totalblock - 1) {
                w_size = write_all(&dfw, bbuffer, 1 + (i % sizeof(bbuffer)), &opt);
                if(w_size == -1)
                    log_mesg(0, 1, 1, debug, "write bitmap to image error\n");
            }
        }
        log_mesg(0, 0, 1, debug, "done!\n");
    } else if (opt.restore) {

        log_mesg(1, 0, 0, debug, "restore image hdr - get image_head from image file\n");
        log_mesg(1, 0, 1, debug, "Reading Super Block\n");
        /// get image information from image file
        restore_image_hdr(&dfr, &opt, &image_hdr);

        /// check memory size
        if (check_mem_size(image_hdr, opt, &needed_mem) == -1)
            log_mesg(0, 1, 1, debug, "Ther is no enough free memory, partclone suggests you should have %lld bytes memory\n", needed_mem);

        /// alloc a memory to restore bitmap
        bitmap = (unsigned long*)calloc(sizeof(unsigned long), LONGS(image_hdr.totalblock));
        if(bitmap == NULL) {
            log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
        }

        /// check the image magic
        if (memcmp(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) != 0)
            log_mesg(0, 1, 1, debug, "This is not partclone image.\n");

        /// check the file system
        //if (strcmp(image_hdr.fs, FS) != 0)
        //    log_mesg(0, 1, 1, debug, "%s can't restore from the image which filesystem is %s not %s\n", argv[0], image_hdr.fs, FS);

        log_mesg(2, 0, 0, debug, "initial main bitmap pointer %lli\n", bitmap);
        log_mesg(1, 0, 0, debug, "Initial image hdr - read bitmap table\n");

        /// read and check bitmap from image file
        log_mesg(0, 0, 1, debug, "Calculating bitmap... Please wait... ");
        get_image_bitmap(&dfr, opt, image_hdr, bitmap);

        /// check the dest partition size.
        if (opt.restore_row_file)
            check_free_space(&dfw, image_hdr.device_size);
        else if(opt.check)
            check_size(&dfw, image_hdr.device_size);

        log_mesg(2, 0, 0, debug, "check main bitmap pointer %i\n", bitmap);
        log_mesg(0, 0, 1, debug, "done!\n");
    } else if (opt.dd) {
        log_mesg(1, 0, 0, debug, "Initial image hdr - get Super Block from partition\n");
        log_mesg(1, 0, 1, debug, "Reading Super Block\n");

        /// get Super Block information from partition
        initial_image_hdr(source, &image_hdr);

        /// check memory size
        if (check_mem_size(image_hdr, opt, &needed_mem) == -1)
            log_mesg(0, 1, 1, debug, "Ther is no enough free memory, partclone suggests you should have %lld bytes memory\n", needed_mem);

        strncpy(image_hdr.version, IMAGE_VERSION, VERSION_SIZE);

        /// alloc a memory to restore bitmap
        bitmap = (unsigned long*)calloc(sizeof(unsigned long), LONGS(image_hdr.totalblock));
        if(bitmap == NULL) {
            log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
        }

        log_mesg(2, 0, 0, debug, "initial main bitmap pointer %i\n", bitmap);
        log_mesg(1, 0, 0, debug, "Initial image hdr - read bitmap table\n");

        /// read and check bitmap from partition
        log_mesg(0, 0, 1, debug, "Calculating bitmap... Please wait... ");
        readbitmap(source, image_hdr, bitmap, pui);

        /// check the dest partition size.
        if(opt.check) {
            check_size(&dfw, image_hdr.device_size);
        }

        log_mesg(2, 0, 0, debug, "check main bitmap pointer %i\n", bitmap);
        log_mesg(0, 0, 1, debug, "done!\n");
    } else if (opt.domain) {
        log_mesg(1, 0, 0, debug, "Initial image hdr - get Super Block from partition\n");
        log_mesg(1, 0, 1, debug, "Reading Super Block\n");

        /// get Super Block information from partition
        initial_image_hdr(source, &image_hdr);

        /// check memory size
        if (check_mem_size(image_hdr, opt, &needed_mem) == -1)
            log_mesg(0, 1, 1, debug, "Ther is no enough free memory, partclone suggests you should have %lld bytes memory\n", needed_mem);

        strncpy(image_hdr.version, IMAGE_VERSION, VERSION_SIZE);

        /// alloc a memory to restore bitmap
        bitmap = (unsigned long*)calloc(sizeof(unsigned long), LONGS(image_hdr.totalblock));
        if(bitmap == NULL) {
            log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
        }

        log_mesg(2, 0, 0, debug, "initial main bitmap pointer %i\n", bitmap);
        log_mesg(1, 0, 0, debug, "Initial image hdr - read bitmap table\n");

        /// read and check bitmap from partition
        log_mesg(0, 0, 1, debug, "Calculating bitmap... Please wait... ");
        readbitmap(source, image_hdr, bitmap, pui);

        log_mesg(2, 0, 0, debug, "check main bitmap pointer %i\n", bitmap);
        log_mesg(0, 0, 1, debug, "done!\n");
    }

    log_mesg(1, 0, 0, debug, "print image_head\n");

    /// print option to log file
    if (debug)
        print_opt(opt);

    /// print image_head
    print_image_hdr_info(image_hdr, opt);

    /**
     * initial progress bar
     */
    //progress_bar	prog;		/// progress_bar structure defined in progress.h
    start = 0;				/// start number of progress bar
    stop = (image_hdr.usedblocks);	/// get the end of progress number, only used block
    log_mesg(1, 0, 0, debug, "Initial Progress bar\n");
    /// Initial progress bar
    if (opt.no_block_detail)
        flag = NO_BLOCK_DETAIL;
    else
        flag = IO;
    progress_init(&prog, start, stop, image_hdr.totalblock, flag, image_hdr.block_size);
    copied = 0;				/// initial number is 0

    /**
     * thread to print progress
     */
    pres = pthread_create(&prog_thread, NULL, thread_update_pui, NULL);

    /**
     * start read and write data between device and image file
     */
    if (opt.clone) {

        w_size = write_all(&dfw, bitmagic, 8, &opt); /// write a magic string

        /// read data from the first block and log the offset
        sf = lseek(dfr, 0, SEEK_SET);
        log_mesg(1, 0, 0, debug, "seek %lli for reading data string\n",sf);
        if (sf == (off_t)-1)
            log_mesg(0, 1, 1, debug, "seek set %lli\n", sf);

        buffer = (char*)malloc(image_hdr.block_size); ///alloc a memory to copy data
        if(buffer == NULL) {
            log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
        }

        log_mesg(0, 0, 0, debug, "Total block %i\n", image_hdr.totalblock);


        /// start clone partition to image file
        log_mesg(1, 0, 0, debug, "start backup data...\n");
        for( block_id = 0; block_id < image_hdr.totalblock; block_id++ ) {

            r_size = 0;
            w_size = 0;

            main_pos = lseek(dfr, 0, SEEK_CUR);
            log_mesg(3, 0, 0, debug, "man pos = %lli\n", main_pos);

            if (pc_test_bit(block_id, bitmap)) {
                /// if the block is used
                log_mesg(1, 0, 0, debug, "block_id=%lli, ",block_id);
                log_mesg(2, 0, 0, debug, "bitmap=%i, ",pc_test_bit(block_id, bitmap));

                offset = (off_t)(block_id * image_hdr.block_size);
#ifdef _FILE_OFFSET_BITS
                sf = lseek(dfr, offset, SEEK_SET);
                if (sf == -1)
                    log_mesg(0, 1, 1, debug, "source seek error = %lli, ",sf);
#endif
                /// read data from source to buffer
                memset(buffer, 0, image_hdr.block_size);
                rescue_pos = lseek(dfr, 0, SEEK_CUR);
                r_size = read_all(&dfr, buffer, image_hdr.block_size, &opt);
                log_mesg(3, 0, 0, debug, "bs=%i and r=%i, ",image_hdr.block_size, r_size);
                if (r_size != (int)image_hdr.block_size) {

                    if ((r_size == -1) && (errno == EIO)) {
                        if (opt.rescue) {
                            r_size = 0;
                            for (rescue_num = 0; rescue_num < image_hdr.block_size; rescue_num += SECTOR_SIZE) {
                                rescue_sector(&dfr, rescue_pos + rescue_num, buffer + rescue_num, &opt);
                                r_size+=SECTOR_SIZE;
                            }
                        } else
                            log_mesg(0, 1, 1, debug, "%s", bad_sectors_warning_msg);

                    } else
                        log_mesg(0, 1, 1, debug, "read error: %s(%i) \n", strerror(errno), errno);
                }

                /// write buffer to target
                w_size = write_all(&dfw, buffer, image_hdr.block_size, &opt);
                log_mesg(3, 0, 0, debug, "bs=%i and w=%i, ",image_hdr.block_size, w_size);
                if (w_size != (int)image_hdr.block_size)
                    log_mesg(0, 1, 1, debug, "write error %i \n", w_size);

                /// generate crc32 code and write it.
                crc_buffer = (char*)malloc(CRC_SIZE); ///alloc a memory to copy data
                if(crc_buffer == NULL) {
                    log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
                }
                crc = crc32(crc, buffer, w_size);
                memcpy(crc_buffer, &crc, CRC_SIZE);
                c_size = write_all(&dfw, crc_buffer, CRC_SIZE, &opt);

                /// free buffer
                free(crc_buffer);

                copied++;					/// count copied block
                total_write += (unsigned long long)(w_size);	/// count copied size
                log_mesg(3, 0, 0, debug, "total=%lli, ", total_write);

                /// read or write error
                if (r_size != w_size)
                    log_mesg(0, 1, 1, debug, "read(%i) and write(%i) different\n", r_size, w_size);
                log_mesg(2, 0, 0, debug, "end\n");
            } else {
#ifndef _FILE_OFFSET_BITS
                /// if the block is not used, I just skip it.
                log_mesg(2, 0, 0, debug, "block_id=%lli, ",block_id);
                sf = lseek(dfr, image_hdr.block_size, SEEK_CUR);
                log_mesg(2, 0, 0, debug, "skip seek=%lli, ",sf);
                if (sf == (off_t)-1)
                    log_mesg(0, 1, 1, debug, "clone seek error %lli errno=%i\n", (long long)offset, (int)errno);

                log_mesg(2, 0, 0, debug, "end\n");
#endif
            }
        } /// end of for
        free(buffer);
    } else if (opt.restore) {

        /**
         * read magic string from image file
         * and check it.
         */
        r_size = read_all(&dfr, bitmagic_r, 8, &opt); /// read a magic string
        cmp = memcmp(bitmagic, bitmagic_r, 8);
        if(cmp != 0)
            log_mesg(0, 1, 1, debug, "bitmagic error %i\n", cmp);

        /// seek to the first
        sf = lseek(dfw, 0, SEEK_SET);
        log_mesg(1, 0, 0, debug, "seek %lli for writing dtat string\n",sf);
        if (sf == (off_t)-1)
            log_mesg(0, 1, 1, debug, "seek set %lli\n", sf);

        cache_buffer = (char*)malloc(image_hdr.block_size * (next_max_count+1));
        if(cache_buffer == NULL) {
            log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
        }

        buffer = (char*)malloc(image_hdr.block_size); ///alloc a memory to copy data
        if(buffer == NULL) {
            log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
        }

        /// start restore image file to partition
        log_mesg(1, 0, 0, debug, "start restore data...\n");

        for( block_id = 0; block_id < image_hdr.totalblock; block_id++ ) {

            r_size = 0;
            w_size = 0;


            if (pc_test_bit(block_id, bitmap)) {
                /// The block is used
                log_mesg(1, 0, 0, debug, "block_id=%lli, ",block_id);
                log_mesg(2, 0, 0, debug, "bitmap=%i, ",pc_test_bit(block_id, bitmap));

                memset(buffer, 0, image_hdr.block_size);
                r_size = read_all(&dfr, buffer, image_hdr.block_size, &opt);
                log_mesg(3, 0, 0, debug, "bs=%i and r=%i, ",image_hdr.block_size, r_size);
                if (r_size <0)
                    log_mesg(0, 1, 1, debug, "read errno = %i \n", errno);

                /// read crc32 code and check it.
                crc_ck = crc32(crc_ck, buffer, r_size);
                crc_buffer = (char*)malloc(CRC_SIZE); ///alloc a memory to copy data
                if(crc_buffer == NULL) {
                    log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
                }
                c_size = read_all(&dfr, crc_buffer, CRC_SIZE, &opt);
                if (c_size < CRC_SIZE)
                    log_mesg(0, 1, 1, debug, "read CRC error: %s, please check your image file. \n", strerror(errno));

                memcpy(&crc, crc_buffer, CRC_SIZE);
                /*FIX: 64bit image can't ignore crc error*/
                if ((memcmp(&crc, &crc_ck, CRC_SIZE) != 0) && (!opt.ignore_crc)) {
                    log_mesg(1, 0, 0, debug, "CRC Check error. 64bit bug before v0.1.0 (Rev:250M), enlarge crc size and recheck again....\n ");
                    /// check again
                    buffer2 = (char*)malloc(image_hdr.block_size+CRC_SIZE); ///alloc a memory to copy data
                    if(buffer2 == NULL) {
                        log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
                    }
                    memcpy(buffer2, buffer, image_hdr.block_size);
                    memcpy(buffer2+image_hdr.block_size, crc_buffer, CRC_SIZE);
                    memcpy(buffer, buffer2+CRC_SIZE, image_hdr.block_size);

                    crc_ck2 = crc32(crc_ck2, buffer, r_size);
                    c_size = read_all(&dfr, crc_buffer, CRC_SIZE, &opt);
                    if (c_size < CRC_SIZE)
                        log_mesg(0, 1, 1, debug, "read CRC error: %s, please check your image file. \n", strerror(errno));
                    memcpy(&crc, crc_buffer, CRC_SIZE);
                    if ((memcmp(&crc, &crc_ck2, CRC_SIZE) != 0 )&& (!opt.ignore_crc)) {
                        log_mesg(0, 1, 1, debug, "CRC error again at %i...\n ", sf);
                    } else {
                        crc_ck = crc_ck2;
                    }
                    free(buffer2);
                } else {
                    crc_ck2 = crc_ck;
                }

                if(next != next_count) {
                    memset(cache_buffer, 0, image_hdr.block_size*next_max_count);
                    for (next_int = 1; next_int <= next_max_count; next_int++)
                    {
                        next_block_id = block_id+next_int;
                        if (pc_test_bit(next_block_id, bitmap)) {
                            next++;
                        } else {
                            next_count = next;
                            break;
                        }
                        next_count = next;
                    }
                    log_mesg(1, 0, 0, debug, "next = %i\n",next);
                }

                if ((next == next_count) &&(nx_current < next)) {
                    memcpy(cache_buffer+(image_hdr.block_size*nx_current), buffer, image_hdr.block_size);
                    w_size = 0;
                    nx_current++;
                }

                if ((next == next_count) && (nx_current == next)) {
#ifdef _FILE_OFFSET_BITS
                    offset = (off_t)((block_id-next+1) * image_hdr.block_size);
                    sf = lseek(dfw, offset, SEEK_SET);
                    if (sf == -1)
                        log_mesg(0, 1, 1, debug, "target seek error = %lli, ",sf);
#endif
                    /// write block from buffer to partition
                    w_size = write_all(&dfw, cache_buffer, (image_hdr.block_size*nx_current), &opt);
                    log_mesg(1, 0, 0, debug, "bs=%i and w=%i, ",(image_hdr.block_size*nx_current), w_size);
                    if (w_size != (int)image_hdr.block_size*nx_current)
                        log_mesg(0, 1, 1, debug, "write error %i \n", w_size);
                    next = 1;
                    next_count = next_max_count;
                    nx_current=0;
                }

                /// free buffer
                free(crc_buffer);
                copied++;					/// count copied block
                total_write += (unsigned long long) w_size;	/// count copied size

                /// read or write error
                //if ((r_size != w_size) || (r_size != image_hdr.block_size))
                //	log_mesg(0, 1, 1, debug, "read and write different\n");
                log_mesg(1, 0, 0, debug, "end\n");
            } else {
                /// for restore to row file, mount -o loop used.
                if ((block_id == (image_hdr.totalblock-1)) && (opt.restore_row_file)) {
                    write_last_block(&dfw, image_hdr.block_size, block_id, &opt);

                } else {
#ifndef _FILE_OFFSET_BITS
                    /// if the block is not used, I just skip it.
                    log_mesg(2, 0, 0, debug, "block_id=%lli, ",block_id);
                    sf = lseek(dfw, image_hdr.block_size, SEEK_CUR);
                    log_mesg(2, 0, 0, debug, "seek=%lli, ",sf);
                    if (sf == (off_t)-1)
                        log_mesg(0, 1, 1, debug, "seek error %lli errno=%i\n", (long long)offset, (int)errno);
                    log_mesg(2, 0, 0, debug, "end\n");
#endif
                }
            }
        } // end of for
        free(buffer);
    } else if (opt.dd) {
        sf = lseek(dfr, 0, SEEK_SET);
        log_mesg(1, 0, 0, debug, "seek %lli for reading data string\n",sf);
        if (sf == (off_t)-1)
            log_mesg(0, 1, 1, debug, "seek set %lli\n", sf);

        main_pos = lseek(dfr, 0, SEEK_CUR);
        log_mesg(1, 0, 0, debug, "man pos = %lli\n", main_pos);

        log_mesg(0, 0, 0, debug, "Total block %i\n", image_hdr.totalblock);

        buffer = (char*)malloc(image_hdr.block_size); ///alloc a memory to copy data
        if(buffer == NULL) {
            log_mesg(0, 1, 1, debug, "%s, %i, ERROR:%s", __func__, __LINE__, strerror(errno));
        }

        /// start clone partition to image file
        log_mesg(1, 0, 0, debug, "start backup data device-to-device...\n");
        for( block_id = 0; block_id < image_hdr.totalblock; block_id++ ) {
            r_size = 0;
            w_size = 0;

            if (pc_test_bit(block_id, bitmap)) {
                /// if the block is used

                log_mesg(1, 0, 0, debug, "block_id=%lli, ",block_id);
                log_mesg(2, 0, 0, debug, "bitmap=%i, ",pc_test_bit(block_id, bitmap));
                offset = (off_t)(block_id * image_hdr.block_size);
#ifdef _FILE_OFFSET_BITS
                sf = lseek(dfr, offset, SEEK_SET);
                if (sf == -1)
                    log_mesg(0, 1, 1, debug, "source seek error = %lli, ",sf);
                sf = lseek(dfw, offset, SEEK_SET);
                if (sf == -1)
                    log_mesg(0, 1, 1, debug, "target seek error = %lli, ",sf);
#endif
                /// read data from source to buffer
                memset(buffer, 0, image_hdr.block_size);
                rescue_pos = lseek(dfr, 0, SEEK_CUR);
                r_size = read_all(&dfr, buffer, image_hdr.block_size, &opt);
                log_mesg(3, 0, 0, debug, "bs=%i and r=%i, ",image_hdr.block_size, r_size);
                if (r_size != (int)image_hdr.block_size) {
                    if ((r_size == -1) && (errno == EIO)) {
                        if (opt.rescue) {
                            r_size = 0;
                            for (rescue_num = 0; rescue_num < image_hdr.block_size; rescue_num += SECTOR_SIZE) {
                                rescue_sector(&dfr, rescue_pos + rescue_num, buffer + rescue_num, &opt);
                                r_size+=SECTOR_SIZE;
                            }
                        } else
                            log_mesg(0, 1, 1, debug, "%s", bad_sectors_warning_msg);

                    } else
                        log_mesg(0, 1, 1, debug, "read error: %s(%i) \n", strerror(errno), errno);
                }

                /// write buffer to target
                w_size = write_all(&dfw, buffer, image_hdr.block_size, &opt);
                log_mesg(3, 0, 0, debug, "bs=%i and w=%i, ",image_hdr.block_size, w_size);
                if (w_size != (int)image_hdr.block_size)
                    log_mesg(0, 1, 1, debug, "write error %i \n", w_size);

                copied++;                                       /// count copied block
                total_write += (unsigned long long)(w_size);    /// count copied size
                log_mesg(2, 0, 0, debug, "total=%lli, ", total_write);
                /// read or write error
                if (r_size != w_size)
                    log_mesg(0, 1, 1, debug, "read and write different\n");
                log_mesg(1, 0, 0, debug, "end\n");
            } else {
#ifndef _FILE_OFFSET_BITS
                /// if the block is not used, I just skip it.
                log_mesg(2, 0, 0, debug, "block_id=%lli, ",block_id);
                sf = lseek(dfr, image_hdr.block_size, SEEK_CUR);
                log_mesg(2, 0, 0, debug, "skip source seek=%lli, ",sf);
                sf = lseek(dfw, image_hdr.block_size, SEEK_CUR);
                log_mesg(2, 0, 0, debug, "skip target seek=%lli, ",sf);
                if (sf == (off_t)-1)
                    log_mesg(0, 1, 1, debug, "clone seek error %lli errno=%i\n", (long long)offset, (int)errno);
#endif
            }
        } /// end of for
        free(buffer);
    } else if (opt.domain) {
        log_mesg(0, 0, 0, debug, "Total block %i\n", image_hdr.totalblock);
        log_mesg(1, 0, 0, debug, "start writing domain log...\n");
        // write domain log comment and status line
        dprintf(dfw, "# Domain logfile created by %s v%s\n", EXECNAME, VERSION);
        dprintf(dfw, "# Source: %s\n", opt.source);
        dprintf(dfw, "# Offset: 0x%08llX\n", opt.offset_domain);
        dprintf(dfw, "# current_pos  current_status\n");
        dprintf(dfw, "0x%08llX     ?\n",
                opt.offset_domain + (image_hdr.totalblock * image_hdr.block_size));
        dprintf(dfw, "#      pos        size  status\n");
        // start logging the used/unused areas
        next_block_id = 0;
        cmp = pc_test_bit(0, bitmap);
        for( block_id = 0; block_id <= image_hdr.totalblock; block_id++ ) {
            if (block_id < image_hdr.totalblock) {
                nx_current = pc_test_bit(block_id, bitmap);
                if (nx_current == 1)
                    copied++;
            } else
                nx_current = -1;
            if (nx_current != cmp) {
                dprintf(dfw, "0x%08llX  0x%08llX  %c\n",
                        opt.offset_domain + (next_block_id * image_hdr.block_size),
                        (block_id - next_block_id) * image_hdr.block_size,
                        cmp ? '+' : '?');
                next_block_id = block_id;
                cmp = nx_current;
            }
            // don't bother updating progress
        } /// end of for
    }

    done = 1;
    pres = pthread_join(prog_thread, &p_result);
    update_pui(&prog, copied, block_id, done);
    sync_data(dfw, &opt);
    print_finish_info(opt);

    close (dfr);    /// close source
    close (dfw);    /// close target
    free(bitmap);   /// free bitmp
    close_pui(pui);
    printf("Cloned successfully.\n");
    if(opt.debug)
        close_log();
#ifdef MEMTRACE
    muntrace();
#endif
    return 0;	    /// finish
}