| 1 | // -------------------------------------------------------------------------- |
|---|
| 2 | // |
|---|
| 3 | // File |
|---|
| 4 | // Name: BackupStoreFileCmbDiff.cpp |
|---|
| 5 | // Purpose: Combine two diffs together |
|---|
| 6 | // Created: 12/7/04 |
|---|
| 7 | // |
|---|
| 8 | // -------------------------------------------------------------------------- |
|---|
| 9 | |
|---|
| 10 | #include "Box.h" |
|---|
| 11 | |
|---|
| 12 | #include <new> |
|---|
| 13 | #include <stdlib.h> |
|---|
| 14 | |
|---|
| 15 | #include "BackupStoreFile.h" |
|---|
| 16 | #include "BackupStoreFileWire.h" |
|---|
| 17 | #include "BackupStoreObjectMagic.h" |
|---|
| 18 | #include "BackupStoreException.h" |
|---|
| 19 | #include "BackupStoreConstants.h" |
|---|
| 20 | #include "BackupStoreFilename.h" |
|---|
| 21 | |
|---|
| 22 | #include "MemLeakFindOn.h" |
|---|
| 23 | |
|---|
| 24 | // -------------------------------------------------------------------------- |
|---|
| 25 | // |
|---|
| 26 | // Function |
|---|
| 27 | // Name: BackupStoreFile::CombineDiffs(IOStream &, IOStream &, IOStream &rOut) |
|---|
| 28 | // Purpose: Given two diffs, combine them into a single diff, to produce a diff |
|---|
| 29 | // which, combined with the original file, creates the result of applying |
|---|
| 30 | // rDiff, then rDiff2. Two opens of rDiff2 are required |
|---|
| 31 | // Created: 12/7/04 |
|---|
| 32 | // |
|---|
| 33 | // -------------------------------------------------------------------------- |
|---|
| 34 | void BackupStoreFile::CombineDiffs(IOStream &rDiff1, IOStream &rDiff2, IOStream &rDiff2b, IOStream &rOut) |
|---|
| 35 | { |
|---|
| 36 | // Skip header of first diff, record where the data starts, and skip to the index |
|---|
| 37 | int64_t diff1DataStarts = 0; |
|---|
| 38 | { |
|---|
| 39 | // Read the header for the From file |
|---|
| 40 | file_StreamFormat diff1Hdr; |
|---|
| 41 | if(!rDiff1.ReadFullBuffer(&diff1Hdr, sizeof(diff1Hdr), 0)) |
|---|
| 42 | { |
|---|
| 43 | THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine) |
|---|
| 44 | } |
|---|
| 45 | if(ntohl(diff1Hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1) |
|---|
| 46 | { |
|---|
| 47 | THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile) |
|---|
| 48 | } |
|---|
| 49 | // Skip over the filename and attributes of the From file |
|---|
| 50 | // BLOCK |
|---|
| 51 | { |
|---|
| 52 | BackupStoreFilename filename2; |
|---|
| 53 | filename2.ReadFromStream(rDiff1, IOStream::TimeOutInfinite); |
|---|
| 54 | int32_t size_s; |
|---|
| 55 | if(!rDiff1.ReadFullBuffer(&size_s, sizeof(size_s), 0 /* not interested in bytes read if this fails */)) |
|---|
| 56 | { |
|---|
| 57 | THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) |
|---|
| 58 | } |
|---|
| 59 | int size = ntohl(size_s); |
|---|
| 60 | // Skip forward the size |
|---|
| 61 | rDiff1.Seek(size, IOStream::SeekType_Relative); |
|---|
| 62 | } |
|---|
| 63 | // Record position |
|---|
| 64 | diff1DataStarts = rDiff1.GetPosition(); |
|---|
| 65 | // Skip to index |
|---|
| 66 | rDiff1.Seek(0 - (((box_ntoh64(diff1Hdr.mNumBlocks)) * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader)), IOStream::SeekType_End); |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | // Read the index of the first diff |
|---|
| 70 | // Header first |
|---|
| 71 | file_BlockIndexHeader diff1IdxHdr; |
|---|
| 72 | if(!rDiff1.ReadFullBuffer(&diff1IdxHdr, sizeof(diff1IdxHdr), 0)) |
|---|
| 73 | { |
|---|
| 74 | THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream) |
|---|
| 75 | } |
|---|
| 76 | if(ntohl(diff1IdxHdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1) |
|---|
| 77 | { |
|---|
| 78 | THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile) |
|---|
| 79 | } |
|---|
| 80 | int64_t diff1NumBlocks = box_ntoh64(diff1IdxHdr.mNumBlocks); |
|---|
| 81 | // Allocate some memory |
|---|
| 82 | int64_t *diff1BlockStartPositions = (int64_t*)::malloc((diff1NumBlocks + 1) * sizeof(int64_t)); |
|---|
| 83 | if(diff1BlockStartPositions == 0) |
|---|
| 84 | { |
|---|
| 85 | throw std::bad_alloc(); |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | // Buffer data |
|---|
| 89 | void *buffer = 0; |
|---|
| 90 | int bufferSize = 0; |
|---|
| 91 | |
|---|
| 92 | try |
|---|
| 93 | { |
|---|
| 94 | // Then the entries: |
|---|
| 95 | // For each entry, want to know if it's in the file, and if so, how big it is. |
|---|
| 96 | // We'll store this as an array of file positions in the file, with an additioal |
|---|
| 97 | // entry on the end so that we can work out the length of the last block. |
|---|
| 98 | // If an entry isn't in the file, then store 0 - (position in other file). |
|---|
| 99 | int64_t diff1Position = diff1DataStarts; |
|---|
| 100 | for(int64_t b = 0; b < diff1NumBlocks; ++b) |
|---|
| 101 | { |
|---|
| 102 | file_BlockIndexEntry e; |
|---|
| 103 | if(!rDiff1.ReadFullBuffer(&e, sizeof(e), 0)) |
|---|
| 104 | { |
|---|
| 105 | THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream) |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | // Where's the block? |
|---|
| 109 | int64_t blockEn = box_ntoh64(e.mEncodedSize); |
|---|
| 110 | if(blockEn <= 0) |
|---|
| 111 | { |
|---|
| 112 | // Just store the negated block number |
|---|
| 113 | diff1BlockStartPositions[b] = blockEn; |
|---|
| 114 | } |
|---|
| 115 | else |
|---|
| 116 | { |
|---|
| 117 | // Block is present in this file |
|---|
| 118 | diff1BlockStartPositions[b] = diff1Position; |
|---|
| 119 | diff1Position += blockEn; |
|---|
| 120 | } |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | // Finish off the list, so the last entry can have it's size calcuated. |
|---|
| 124 | diff1BlockStartPositions[diff1NumBlocks] = diff1Position; |
|---|
| 125 | |
|---|
| 126 | // Now read the second diff's header, copying it to the out file |
|---|
| 127 | file_StreamFormat diff2Hdr; |
|---|
| 128 | if(!rDiff2.ReadFullBuffer(&diff2Hdr, sizeof(diff2Hdr), 0)) |
|---|
| 129 | { |
|---|
| 130 | THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine) |
|---|
| 131 | } |
|---|
| 132 | if(ntohl(diff2Hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1) |
|---|
| 133 | { |
|---|
| 134 | THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile) |
|---|
| 135 | } |
|---|
| 136 | // Copy |
|---|
| 137 | rOut.Write(&diff2Hdr, sizeof(diff2Hdr)); |
|---|
| 138 | // Copy over filename and attributes |
|---|
| 139 | // BLOCK |
|---|
| 140 | { |
|---|
| 141 | BackupStoreFilename filename; |
|---|
| 142 | filename.ReadFromStream(rDiff2, IOStream::TimeOutInfinite); |
|---|
| 143 | filename.WriteToStream(rOut); |
|---|
| 144 | StreamableMemBlock attr; |
|---|
| 145 | attr.ReadFromStream(rDiff2, IOStream::TimeOutInfinite); |
|---|
| 146 | attr.WriteToStream(rOut); |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | // Get to the index of rDiff2b, and read the header |
|---|
| 150 | MoveStreamPositionToBlockIndex(rDiff2b); |
|---|
| 151 | file_BlockIndexHeader diff2IdxHdr; |
|---|
| 152 | if(!rDiff2b.ReadFullBuffer(&diff2IdxHdr, sizeof(diff2IdxHdr), 0)) |
|---|
| 153 | { |
|---|
| 154 | THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream) |
|---|
| 155 | } |
|---|
| 156 | if(ntohl(diff2IdxHdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1) |
|---|
| 157 | { |
|---|
| 158 | THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile) |
|---|
| 159 | } |
|---|
| 160 | int64_t diff2NumBlocks = box_ntoh64(diff2IdxHdr.mNumBlocks); |
|---|
| 161 | int64_t diff2IndexEntriesStart = rDiff2b.GetPosition(); |
|---|
| 162 | |
|---|
| 163 | // Then read all the entries |
|---|
| 164 | int64_t diff2FilePosition = rDiff2.GetPosition(); |
|---|
| 165 | for(int64_t b = 0; b < diff2NumBlocks; ++b) |
|---|
| 166 | { |
|---|
| 167 | file_BlockIndexEntry e; |
|---|
| 168 | if(!rDiff2b.ReadFullBuffer(&e, sizeof(e), 0)) |
|---|
| 169 | { |
|---|
| 170 | THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream) |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | // What do to next about copying data |
|---|
| 174 | bool copyBlock = false; |
|---|
| 175 | int copySize = 0; |
|---|
| 176 | int64_t copyFrom = 0; |
|---|
| 177 | bool fromFileDiff1 = false; |
|---|
| 178 | |
|---|
| 179 | // Where's the block? |
|---|
| 180 | int64_t blockEn = box_ntoh64(e.mEncodedSize); |
|---|
| 181 | if(blockEn > 0) |
|---|
| 182 | { |
|---|
| 183 | // Block is present in this file -- copy to out |
|---|
| 184 | copyBlock = true; |
|---|
| 185 | copyFrom = diff2FilePosition; |
|---|
| 186 | copySize = (int)blockEn; |
|---|
| 187 | |
|---|
| 188 | // Move pointer onwards |
|---|
| 189 | diff2FilePosition += blockEn; |
|---|
| 190 | } |
|---|
| 191 | else |
|---|
| 192 | { |
|---|
| 193 | // Block isn't present here -- is it present in the old one? |
|---|
| 194 | int64_t blockIndex = 0 - blockEn; |
|---|
| 195 | if(blockIndex < 0 || blockIndex > diff1NumBlocks) |
|---|
| 196 | { |
|---|
| 197 | THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile) |
|---|
| 198 | } |
|---|
| 199 | if(diff1BlockStartPositions[blockIndex] > 0) |
|---|
| 200 | { |
|---|
| 201 | // Block is in the old diff file, copy it across |
|---|
| 202 | copyBlock = true; |
|---|
| 203 | copyFrom = diff1BlockStartPositions[blockIndex]; |
|---|
| 204 | int nb = blockIndex + 1; |
|---|
| 205 | while(diff1BlockStartPositions[nb] <= 0) |
|---|
| 206 | { |
|---|
| 207 | // This is safe, because the last entry will terminate it properly! |
|---|
| 208 | ++nb; |
|---|
| 209 | ASSERT(nb <= diff1NumBlocks); |
|---|
| 210 | } |
|---|
| 211 | copySize = diff1BlockStartPositions[nb] - copyFrom; |
|---|
| 212 | fromFileDiff1 = true; |
|---|
| 213 | } |
|---|
| 214 | } |
|---|
| 215 | //TRACE4("%d %d %lld %d\n", copyBlock, copySize, copyFrom, fromFileDiff1); |
|---|
| 216 | |
|---|
| 217 | // Copy data to the output file? |
|---|
| 218 | if(copyBlock) |
|---|
| 219 | { |
|---|
| 220 | // Allocate enough space |
|---|
| 221 | if(bufferSize < copySize || buffer == 0) |
|---|
| 222 | { |
|---|
| 223 | // Free old block |
|---|
| 224 | if(buffer != 0) |
|---|
| 225 | { |
|---|
| 226 | ::free(buffer); |
|---|
| 227 | buffer = 0; |
|---|
| 228 | bufferSize = 0; |
|---|
| 229 | } |
|---|
| 230 | // Allocate new block |
|---|
| 231 | buffer = ::malloc(copySize); |
|---|
| 232 | if(buffer == 0) |
|---|
| 233 | { |
|---|
| 234 | throw std::bad_alloc(); |
|---|
| 235 | } |
|---|
| 236 | bufferSize = copySize; |
|---|
| 237 | } |
|---|
| 238 | ASSERT(bufferSize >= copySize); |
|---|
| 239 | |
|---|
| 240 | // Load in the data |
|---|
| 241 | if(fromFileDiff1) |
|---|
| 242 | { |
|---|
| 243 | rDiff1.Seek(copyFrom, IOStream::SeekType_Absolute); |
|---|
| 244 | if(!rDiff1.ReadFullBuffer(buffer, copySize, 0)) |
|---|
| 245 | { |
|---|
| 246 | THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine) |
|---|
| 247 | } |
|---|
| 248 | } |
|---|
| 249 | else |
|---|
| 250 | { |
|---|
| 251 | rDiff2.Seek(copyFrom, IOStream::SeekType_Absolute); |
|---|
| 252 | if(!rDiff2.ReadFullBuffer(buffer, copySize, 0)) |
|---|
| 253 | { |
|---|
| 254 | THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine) |
|---|
| 255 | } |
|---|
| 256 | } |
|---|
| 257 | // Write out data |
|---|
| 258 | rOut.Write(buffer, copySize); |
|---|
| 259 | } |
|---|
| 260 | } |
|---|
| 261 | |
|---|
| 262 | // Write the modified header |
|---|
| 263 | diff2IdxHdr.mOtherFileID = diff1IdxHdr.mOtherFileID; |
|---|
| 264 | rOut.Write(&diff2IdxHdr, sizeof(diff2IdxHdr)); |
|---|
| 265 | |
|---|
| 266 | // Then we'll write out the index, reading the data again |
|---|
| 267 | rDiff2b.Seek(diff2IndexEntriesStart, IOStream::SeekType_Absolute); |
|---|
| 268 | for(int64_t b = 0; b < diff2NumBlocks; ++b) |
|---|
| 269 | { |
|---|
| 270 | file_BlockIndexEntry e; |
|---|
| 271 | if(!rDiff2b.ReadFullBuffer(&e, sizeof(e), 0)) |
|---|
| 272 | { |
|---|
| 273 | THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream) |
|---|
| 274 | } |
|---|
| 275 | |
|---|
| 276 | // Where's the block? |
|---|
| 277 | int64_t blockEn = box_ntoh64(e.mEncodedSize); |
|---|
| 278 | |
|---|
| 279 | // If it's not in this file, it needs modification... |
|---|
| 280 | if(blockEn <= 0) |
|---|
| 281 | { |
|---|
| 282 | int64_t blockIndex = 0 - blockEn; |
|---|
| 283 | // In another file. Need to translate this against the other diff |
|---|
| 284 | if(diff1BlockStartPositions[blockIndex] > 0) |
|---|
| 285 | { |
|---|
| 286 | // Block is in the first diff file, stick in size |
|---|
| 287 | int nb = blockIndex + 1; |
|---|
| 288 | while(diff1BlockStartPositions[nb] <= 0) |
|---|
| 289 | { |
|---|
| 290 | // This is safe, because the last entry will terminate it properly! |
|---|
| 291 | ++nb; |
|---|
| 292 | ASSERT(nb <= diff1NumBlocks); |
|---|
| 293 | } |
|---|
| 294 | int64_t size = diff1BlockStartPositions[nb] - diff1BlockStartPositions[blockIndex]; |
|---|
| 295 | e.mEncodedSize = box_hton64(size); |
|---|
| 296 | } |
|---|
| 297 | else |
|---|
| 298 | { |
|---|
| 299 | // Block in the original file, use translated value |
|---|
| 300 | e.mEncodedSize = box_hton64(diff1BlockStartPositions[blockIndex]); |
|---|
| 301 | } |
|---|
| 302 | } |
|---|
| 303 | |
|---|
| 304 | // Write entry |
|---|
| 305 | rOut.Write(&e, sizeof(e)); |
|---|
| 306 | } |
|---|
| 307 | } |
|---|
| 308 | catch(...) |
|---|
| 309 | { |
|---|
| 310 | // clean up |
|---|
| 311 | ::free(diff1BlockStartPositions); |
|---|
| 312 | if(buffer != 0) |
|---|
| 313 | { |
|---|
| 314 | ::free(buffer); |
|---|
| 315 | } |
|---|
| 316 | throw; |
|---|
| 317 | } |
|---|
| 318 | |
|---|
| 319 | // Clean up allocated memory |
|---|
| 320 | ::free(diff1BlockStartPositions); |
|---|
| 321 | if(buffer != 0) |
|---|
| 322 | { |
|---|
| 323 | ::free(buffer); |
|---|
| 324 | } |
|---|
| 325 | } |
|---|
| 326 | |
|---|