summaryrefslogtreecommitdiff
path: root/lib/backupstore/BackupStoreFileCmbDiff.cpp
blob: 1a88fa3f08ebf702e56b65045f173cbd9e9060fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
// --------------------------------------------------------------------------
//
// File
//		Name:    BackupStoreFileCmbDiff.cpp
//		Purpose: Combine two diffs together
//		Created: 12/7/04
//
// --------------------------------------------------------------------------

#include "Box.h"

#include <new>
#include <stdlib.h>

#include "BackupStoreFile.h"
#include "BackupStoreFileWire.h"
#include "BackupStoreObjectMagic.h"
#include "BackupStoreException.h"
#include "BackupStoreConstants.h"
#include "BackupStoreFilename.h"

#include "MemLeakFindOn.h"

// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreFile::CombineDiffs(IOStream &, IOStream &, IOStream &rOut)
//		Purpose: Given two diffs, combine them into a single diff, to produce a diff
//				 which, combined with the original file, creates the result of applying
//				 rDiff, then rDiff2. Two opens of rDiff2 are required
//		Created: 12/7/04
//
// --------------------------------------------------------------------------
void BackupStoreFile::CombineDiffs(IOStream &rDiff1, IOStream &rDiff2, IOStream &rDiff2b, IOStream &rOut)
{
	// Skip header of first diff, record where the data starts, and skip to the index
	int64_t diff1DataStarts = 0;
	{
		// Read the header for the From file
		file_StreamFormat diff1Hdr;
		if(!rDiff1.ReadFullBuffer(&diff1Hdr, sizeof(diff1Hdr), 0))
		{
			THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
		}
		if(ntohl(diff1Hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1)
		{
			THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
		}
		// Skip over the filename and attributes of the From file
		// BLOCK
		{
			BackupStoreFilename filename2;
			filename2.ReadFromStream(rDiff1, IOStream::TimeOutInfinite);
			int32_t size_s;
			if(!rDiff1.ReadFullBuffer(&size_s, sizeof(size_s), 0 /* not interested in bytes read if this fails */))
			{
				THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead)
			}
			int size = ntohl(size_s);
			// Skip forward the size
			rDiff1.Seek(size, IOStream::SeekType_Relative);		
		}
		// Record position
		diff1DataStarts = rDiff1.GetPosition();
		// Skip to index
		rDiff1.Seek(0 - (((box_ntoh64(diff1Hdr.mNumBlocks)) * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader)), IOStream::SeekType_End);
	}

	// Read the index of the first diff
	// Header first
	file_BlockIndexHeader diff1IdxHdr;
	if(!rDiff1.ReadFullBuffer(&diff1IdxHdr, sizeof(diff1IdxHdr), 0))
	{
		THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream)
	}
	if(ntohl(diff1IdxHdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1)
	{
		THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
	}
	int64_t diff1NumBlocks = box_ntoh64(diff1IdxHdr.mNumBlocks);
	// Allocate some memory
	int64_t *diff1BlockStartPositions = (int64_t*)::malloc((diff1NumBlocks + 1) * sizeof(int64_t));
	if(diff1BlockStartPositions == 0)
	{
		throw std::bad_alloc();
	}

	// Buffer data
	void *buffer = 0;
	int bufferSize = 0;	
	
	try
	{
		// Then the entries:
		// For each entry, want to know if it's in the file, and if so, how big it is.
		// We'll store this as an array of file positions in the file, with an additioal
		// entry on the end so that we can work out the length of the last block.
		// If an entry isn't in the file, then store 0 - (position in other file).
		int64_t diff1Position = diff1DataStarts;
		for(int64_t b = 0; b < diff1NumBlocks; ++b)
		{
			file_BlockIndexEntry e;
			if(!rDiff1.ReadFullBuffer(&e, sizeof(e), 0))
			{
				THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream)
			}
	
			// Where's the block?
			int64_t blockEn = box_ntoh64(e.mEncodedSize);
			if(blockEn <= 0)
			{
				// Just store the negated block number
				diff1BlockStartPositions[b] = blockEn;
			}
			else
			{
				// Block is present in this file
				diff1BlockStartPositions[b] = diff1Position;
				diff1Position += blockEn;
			}
		}
		
		// Finish off the list, so the last entry can have it's size calcuated.
		diff1BlockStartPositions[diff1NumBlocks] = diff1Position;

		// Now read the second diff's header, copying it to the out file
		file_StreamFormat diff2Hdr;
		if(!rDiff2.ReadFullBuffer(&diff2Hdr, sizeof(diff2Hdr), 0))
		{
			THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
		}
		if(ntohl(diff2Hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1)
		{
			THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
		}
		// Copy
		rOut.Write(&diff2Hdr, sizeof(diff2Hdr));
		// Copy over filename and attributes
		// BLOCK
		{
			BackupStoreFilename filename;
			filename.ReadFromStream(rDiff2, IOStream::TimeOutInfinite);
			filename.WriteToStream(rOut);
			StreamableMemBlock attr;
			attr.ReadFromStream(rDiff2, IOStream::TimeOutInfinite);
			attr.WriteToStream(rOut);
		}
		
		// Get to the index of rDiff2b, and read the header
		MoveStreamPositionToBlockIndex(rDiff2b);
		file_BlockIndexHeader diff2IdxHdr;
		if(!rDiff2b.ReadFullBuffer(&diff2IdxHdr, sizeof(diff2IdxHdr), 0))
		{
			THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream)
		}
		if(ntohl(diff2IdxHdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1)
		{
			THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
		}
		int64_t diff2NumBlocks = box_ntoh64(diff2IdxHdr.mNumBlocks);
		int64_t diff2IndexEntriesStart = rDiff2b.GetPosition();
		
		// Then read all the entries
		int64_t diff2FilePosition = rDiff2.GetPosition();
		for(int64_t b = 0; b < diff2NumBlocks; ++b)
		{
			file_BlockIndexEntry e;
			if(!rDiff2b.ReadFullBuffer(&e, sizeof(e), 0))
			{
				THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream)
			}

			// What do to next about copying data
			bool copyBlock = false;
			int copySize = 0;
			int64_t copyFrom = 0;
			bool fromFileDiff1 = false;
	
			// Where's the block?
			int64_t blockEn = box_ntoh64(e.mEncodedSize);
			if(blockEn > 0)
			{
				// Block is present in this file -- copy to out
				copyBlock = true;
				copyFrom = diff2FilePosition;
				copySize = (int)blockEn;
				
				// Move pointer onwards
				diff2FilePosition += blockEn;
			}
			else
			{
				// Block isn't present here -- is it present in the old one?
				int64_t blockIndex = 0 - blockEn;
				if(blockIndex < 0 || blockIndex > diff1NumBlocks)
				{
					THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
				}
				if(diff1BlockStartPositions[blockIndex] > 0)
				{
					// Block is in the old diff file, copy it across
					copyBlock = true;
					copyFrom = diff1BlockStartPositions[blockIndex];
					int nb = blockIndex + 1;
					while(diff1BlockStartPositions[nb] <= 0)
					{
						// This is safe, because the last entry will terminate it properly!
						++nb;
						ASSERT(nb <= diff1NumBlocks);
					}
					copySize = diff1BlockStartPositions[nb] - copyFrom;
					fromFileDiff1 = true;
				}
			}
			//TRACE4("%d %d %lld %d\n", copyBlock, copySize, copyFrom, fromFileDiff1);
						
			// Copy data to the output file?
			if(copyBlock)
			{
				// Allocate enough space
				if(bufferSize < copySize || buffer == 0)
				{
					// Free old block
					if(buffer != 0)
					{
						::free(buffer);
						buffer = 0;
						bufferSize = 0;
					}
					// Allocate new block
					buffer = ::malloc(copySize);
					if(buffer == 0)
					{
						throw std::bad_alloc();
					}
					bufferSize = copySize;
				}
				ASSERT(bufferSize >= copySize);
				
				// Load in the data
				if(fromFileDiff1)
				{
					rDiff1.Seek(copyFrom, IOStream::SeekType_Absolute);
					if(!rDiff1.ReadFullBuffer(buffer, copySize, 0))
					{
						THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
					}
				}
				else
				{
					rDiff2.Seek(copyFrom, IOStream::SeekType_Absolute);
					if(!rDiff2.ReadFullBuffer(buffer, copySize, 0))
					{
						THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
					}
				}
				// Write out data
				rOut.Write(buffer, copySize);
			}
		}
		
		// Write the modified header
		diff2IdxHdr.mOtherFileID = diff1IdxHdr.mOtherFileID;
		rOut.Write(&diff2IdxHdr, sizeof(diff2IdxHdr));
		
		// Then we'll write out the index, reading the data again
		rDiff2b.Seek(diff2IndexEntriesStart, IOStream::SeekType_Absolute);
		for(int64_t b = 0; b < diff2NumBlocks; ++b)
		{
			file_BlockIndexEntry e;
			if(!rDiff2b.ReadFullBuffer(&e, sizeof(e), 0))
			{
				THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream)
			}
	
			// Where's the block?
			int64_t blockEn = box_ntoh64(e.mEncodedSize);
		
			// If it's not in this file, it needs modification...
			if(blockEn <= 0)
			{
				int64_t blockIndex = 0 - blockEn;
				// In another file. Need to translate this against the other diff
				if(diff1BlockStartPositions[blockIndex] > 0)
				{
					// Block is in the first diff file, stick in size
					int nb = blockIndex + 1;
					while(diff1BlockStartPositions[nb] <= 0)
					{
						// This is safe, because the last entry will terminate it properly!
						++nb;
						ASSERT(nb <= diff1NumBlocks);
					}
					int64_t size = diff1BlockStartPositions[nb] - diff1BlockStartPositions[blockIndex];
					e.mEncodedSize = box_hton64(size);
				}
				else
				{
					// Block in the original file, use translated value
					e.mEncodedSize = box_hton64(diff1BlockStartPositions[blockIndex]);
				}
			}
			
			// Write entry
			rOut.Write(&e, sizeof(e));
		}
	}
	catch(...)
	{
		// clean up
		::free(diff1BlockStartPositions);
		if(buffer != 0)
		{
			::free(buffer);
		}
		throw;
	}
	
	// Clean up allocated memory
	::free(diff1BlockStartPositions);
	if(buffer != 0)
	{
		::free(buffer);
	}
}