1 /******************************************************************************
4 * Written by hondza. As far as I'm concerned public domain. Several (GPL'd)
5 * lines (PAGE_ALIGN stuff...) taken from linux kernel source.
7 * Do whatever you wish with it, but don't blame me.
9 *****************************************************************************/
11 #define _FILE_OFFSET_BITS 64
18 #include <stddef.h> /* ptrdiff_t */
20 #include <stdint.h> /* uint64_t */
27 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 #include <linux/fs.h> /* BLKGETSIZE64, BLKFLSBUF */
38 #if __BYTE_ORDER == __BIG_ENDIAN
39 #include <linux/byteorder/big_endian.h>
41 #include <linux/byteorder/little_endian.h>
45 #define SNAP_MAGIC 0x70416e53 /* 'SnAp' */
46 #define SNAPSHOT_DISK_VERSION 1
49 /* doubtful stuff; only used with O_DIRECT */
50 #define MAX_CHUNKSIZE 524288
51 #define PAGE_SIZE 4096
52 #define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
53 #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
54 #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
60 uint32_t chunk_size
=512, *cs
;
63 struct disk_exception
{
69 unsigned char *header
=NULL
, *header_orig
=NULL
, *buf
=NULL
, *buf_orig
=NULL
, *chunk
=NULL
, *chunk_orig
=NULL
;
70 char *input_filename
=NULL
, *output_filename
=NULL
;
71 int inputfd
=-1, outputfd
=-1, dry
=1, print_dds
=0, do_direct_io
= 0, verbose
= 0, err
=-1, err2
=-1, flags
=0, go
=1, relaxed
=0;
72 unsigned int i
=0, retries
=0;
74 uint64_t input_length
=0, output_length
=0;
76 struct disk_exception de
;
78 uint64_t total_count
=0;
81 /* taken from libtomcrypt */
82 void burn_stack(unsigned long len
)
84 unsigned char buf
[32];
85 memset(buf
, 0, sizeof(buf
));
86 if (len
> (unsigned long)sizeof(buf
))
87 burn_stack(len
- sizeof(buf
));
94 if(input_filename
) free(input_filename
);
95 if(output_filename
) free(output_filename
);
96 if(buf
) memset(buf
, 0, chunk_size
);
97 if(buf_orig
) free(buf_orig
);
98 if(chunk
) memset(chunk
, 0, chunk_size
);
99 if(chunk_orig
) free(chunk_orig
);
100 if(header
) memset(header
, 0, 512);
101 if(header_orig
) free(header_orig
);
102 /* obfuscate it a little */
107 /* called for each exception */
108 inline void read_write_chunk()
112 if(print_dds
) fprintf(stdout
, "dd of=\"%s\" seek=%llu if='%s' iflag=direct skip=%llu count=1 bs=%db\n", output_filename
? output_filename
: "${origin}", de
.old_chunk
, input_filename
, de
.new_chunk
, chunk_size
/512);
119 fprintf(stderr
, "Warning: retrying pread() on exception chunk at %llu\n", de
.new_chunk
*chunk_size
);
121 err
= pread(inputfd
, chunk
, chunk_size
, de
.new_chunk
*chunk_size
);
126 perror("pread(inputfd)");
131 fputs("pread(inputfd): early EOF!\n", stderr
);
134 else if(err
!= chunk_size
)
138 fputs("pread(inputfd): incomplete read!\n", stderr
);
150 fprintf(stderr
, "Warning: retrying pwrite() at %llu\n", de
.old_chunk
*chunk_size
);
152 err
= pwrite(outputfd
, chunk
, chunk_size
, de
.old_chunk
*chunk_size
);
157 perror("pwrite(outputfd)");
160 else if(err
!= chunk_size
)
164 fputs("pwrite(outputfd): incomplete write!\n", stderr
);
171 } /* read_write_chunk() */
177 fputs("Usage: dm-merge [options] -i <snapshot_device> [ -o <output_device> ]\n\n", stderr
);
178 fputs("Options:\n", stderr
);
179 fputs("-f\t\tREALLY do it (no dry run)\n", stderr
);
180 fputs("-x\t\tRelaxed mode; allow some things (namely ioctl) to fail\n", stderr
);
181 fputs("-d\t\tPrint dd lines as list_exception_chunks.pl would\n", stderr
);
182 fputs("-D\t\tTry to use O_DIRECT\n", stderr
);
183 fputs("-v\t\tBe verbose (more '-v's increase verbosity)\n", stderr
);
184 fputs("-i <file>\tInput (snapshot) COW (copy-on-write) filename\n", stderr
);
185 fputs("-o <file>\tOutput (the device being snapshoted) filename\n\n", stderr
);
186 fputs("This program is still experimental. USE WITH CARE! Read the README!\n\n", stderr
);
192 int main(int argc
, char ** argv
)
198 while( (c
= getopt(argc
, argv
, "fdi:o:Dvhx")) != -1 )
202 case 'x': relaxed
= 1; break;
203 case 'f': dry
= 0; break;
204 case 'd': print_dds
= 1; break;
205 case 'D': do_direct_io
= 1; break;
206 case 'v': verbose
++; break;
207 case 'i': input_filename
= strdup(optarg
); break;
208 case 'o': output_filename
= strdup(optarg
); break;
209 case 'h': help(); exit(0);
210 default: help(); exit(1);
211 } /* switch argument */
212 } /* while getopt() */
217 fputs("Error: input filename not specified\n\n", stderr
);
222 if(!dry
&& !output_filename
)
224 fputs("Error: no dry run and no output filename\n\n", stderr
);
230 /* better safe than sorry */
233 /* check and open snapshot */
237 err
= stat(input_filename
, &st
);
240 perror("stat(snapshot)");
244 /* block device; will set O_DIRECT */
245 if(S_ISBLK(st
.st_mode
) && do_direct_io
)
248 inputfd
= open(input_filename
, flags
);
251 perror("open(snapshot)");
255 /* determine size & flush buffers */
256 if(S_ISBLK(st
.st_mode
))
258 err
= ioctl(inputfd
, BLKGETSIZE64
, &input_length
);
261 perror("ioctl(snapshot, BLKGETSIZE64)");
265 err
= ioctl(inputfd
, BLKFLSBUF
, 0);
268 perror("ioctl(snapshot, BLKFLSBUF)");
269 if(!relaxed
) exit(1);
270 else fputs("relaxed mode, will continue ...\n", stderr
);
274 input_length
= st
.st_size
;
276 /* now the same for the output */
281 err
= stat(output_filename
, &st
);
284 perror("stat(output)");
288 /* block device; will set O_DIRECT */
289 if(S_ISBLK(st
.st_mode
) && do_direct_io
)
292 outputfd
= open(output_filename
, flags
);
295 perror("open(output)");
299 /* determine size & flush buffers */
300 if(S_ISBLK(st
.st_mode
))
302 err
= ioctl(outputfd
, BLKGETSIZE64
, &output_length
);
305 perror("ioctl(output, BLKGETSIZE64)");
309 err
= ioctl(outputfd
, BLKFLSBUF
, 0);
312 perror("ioctl(output, BLKFLSBUF)");
313 if(!relaxed
) exit(1);
314 else fputs("relaxed mode, will continue ...\n", stderr
);
318 output_length
= st
.st_size
;
322 /* FIXME perhaps add an override option? */
323 if(input_length
< 4096 || (!dry
&& output_length
< (4 * 1024 * 1024)))
325 fputs("Error: suspicious file/device sizes\n", stderr
);
330 /* the allocations; special care for O_DIRECT */
333 header_orig
= malloc(512 + PAGE_SIZE
);
339 header
= (unsigned char *) PAGE_ALIGN((ptrdiff_t) header_orig
);
341 fprintf(stdout
, "header_orig = %p (%lu), header = %p (%lu)\n", header_orig
, (unsigned long) header_orig
% PAGE_SIZE
, header
, (unsigned long) header
% PAGE_SIZE
);
345 header_orig
= header
= malloc(512);
353 /* Not sure if BLKFLSBUF waits for the flushing to finish; better safe than sorry */
354 fputs("Artificial sleep (1 second)\n", stdout
);
358 /* FIXME: do retries here as well? */
359 err
= pread(inputfd
, header
, 512, 0);
362 perror("read(snapshot, header)");
366 magic
= (uint32_t *) header
;
367 *magic
= __le32_to_cpu(*magic
);
368 if(SNAP_MAGIC
!= *magic
)
370 fputs("Invalid header MAGIC\n", stderr
);
371 fprintf(stderr
, "%#x != %#x\n", *magic
, SNAP_MAGIC
);
374 fprintf(stdout
, "Found a proper MAGIC header: %#x\n", *magic
);
376 valid
= (uint32_t *) (header
+4);
377 *valid
= __le32_to_cpu(*valid
);
380 fputs("valid == 0\n", stderr
);
383 fprintf(stdout
, "valid = %u\n", *valid
);
385 version
= (uint32_t *) (header
+8);
386 *version
= __le32_to_cpu(*version
);
387 if(*version
!= SNAPSHOT_DISK_VERSION
)
389 fputs("version != 1\n", stderr
);
392 fprintf(stdout
, "version = %u\n", *version
);
394 cs
= (uint32_t *) (header
+12);
395 *cs
= __le32_to_cpu(*cs
);
397 if(chunk_size
< 1 || chunk_size
> 1024 || (0 != (chunk_size
& (chunk_size
-1))))
399 fputs("chunk size has to be >=1 and <=1024 and a power of 2\n", stderr
);
402 fprintf(stdout
, "chunk_size = %u (%u bytes)\n", chunk_size
, chunk_size
*512);
406 /* the allocations; special care for O_DIRECT */
409 buf_orig
= buf
= malloc(chunk_size
+ PAGE_SIZE
);
410 chunk_orig
= chunk
= malloc(chunk_size
+ PAGE_SIZE
);
411 if(!buf_orig
|| !chunk_orig
)
416 buf
= (unsigned char *) PAGE_ALIGN((ptrdiff_t) buf_orig
);
418 fprintf(stdout
, "buf_orig = %p (%lu), buf = %p (%lu)\n", buf_orig
, (unsigned long) buf_orig
% PAGE_SIZE
, buf
, (unsigned long) buf
% PAGE_SIZE
);
419 chunk
= (unsigned char *) PAGE_ALIGN((ptrdiff_t) chunk_orig
);
421 fprintf(stdout
, "chunk_orig = %p (%lu), chunk = %p (%lu)\n", chunk_orig
, (unsigned long) chunk_orig
% PAGE_SIZE
, chunk
, (unsigned long) chunk
% PAGE_SIZE
);
425 buf_orig
= buf
= malloc(chunk_size
);
426 chunk_orig
= chunk
= malloc(chunk_size
);
427 if(!buf_orig
|| !chunk_orig
)
444 fprintf(stderr
, "Warning: retrying pread() on exception area %llu at %llu\n", chunk_now
, chunk_now
*chunk_size
);
446 err
= pread(inputfd
, buf
, chunk_size
, chunk_now
*chunk_size
);
451 perror("pread(inputfd)");
456 fputs("pread(inputfd): early EOF!\n", stderr
);
459 else if(err
!= chunk_size
)
463 fputs("pread(inputfd): incomplete read!\n", stderr
);
470 /* process the exception area */
471 for(i
=0; i
< chunk_size
/16; i
++)
473 temp_u64
= (uint64_t *)(buf
+(i
*16));
474 de
.old_chunk
= __le64_to_cpu(*temp_u64
);
475 temp_u64
= (uint64_t *)(buf
+(i
*16)+8);
476 de
.new_chunk
= __le64_to_cpu(*temp_u64
);
479 fprintf(stdout
, "... chunk_now = %llu, i = %u, de.old_chunk = %llu, de.new_chunk = %llu, old %p, new %p\n", chunk_now
, i
, de
.old_chunk
, de
.new_chunk
, buf
+(i
*16), buf
+(i
*16)+8);
481 /* 0 as a new chunk means "we've reached the end" */
482 if(0 == de
.new_chunk
)
487 else if((1 == chunk_now
&& 0 == i
&& de
.new_chunk
!= 2) || (de
.new_chunk
< 2))
489 fputs("(1 == chunk_now && 0 == i && de.new_chunk != 2) || (de.new_chunk < 2), perhaps not a snapshot?\n", stderr
);
495 /* the data transfer */
500 chunk_now
+= chunk_size
/16 + 1;
502 fprintf(stdout
, "Seeking into exception area in chunk %llu\n", chunk_now
);
506 /* flush buffers again (no error handling this time as there's nothing to do anyway) */
507 if(S_ISBLK(st
.st_mode
)) err
= ioctl(outputfd
, BLKFLSBUF
, 0);
509 /* better safe than sorry */
512 fprintf(stdout
, "Found %llu exceptions of chunksize %u, total size %llu bytes (%llu KiB, %.3Lf MiB, %.3Lf GiB).\n", total_count
, chunk_size
, total_count
*chunk_size
, (total_count
*chunk_size
)/1024, ((long double)(total_count
*chunk_size
))/(1024*1024), ((long double)(total_count
*chunk_size
))/(1024*1024*1024));
515 if(-1 != outputfd
) close(outputfd
);
517 memset(buf
, 0, chunk_size
);
518 memset(chunk
, 0, chunk_size
);
520 /* cleanup() will do the rest */