source: box/trunk/lib/backupstore/BackupStoreFileCombine.cpp @ 2963

Revision 2963, 12.2 KB checked in by chris, 12 months ago (diff)

Move remaining parts of BackupStoreFile? into lib/backupstore, and fix module
dependencies to fail if anything else required by bbstored is still in
lib/backupclient instead of lib/backupstore.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    BackupStoreFileCombine.cpp
5//              Purpose: File combining for BackupStoreFile
6//              Created: 16/1/04
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <new>
13
14#include "BackupStoreFile.h"
15#include "BackupStoreFileWire.h"
16#include "BackupStoreObjectMagic.h"
17#include "BackupStoreException.h"
18#include "BackupStoreConstants.h"
19#include "BackupStoreFilename.h"
20#include "FileStream.h"
21
22#include "MemLeakFindOn.h"
23
24typedef struct
25{
26        int64_t mFilePosition;
27} FromIndexEntry;
28
29static void LoadFromIndex(IOStream &rFrom, FromIndexEntry *pIndex, int64_t NumEntries);
30static void CopyData(IOStream &rDiffData, IOStream &rDiffIndex, int64_t DiffNumBlocks, IOStream &rFrom, FromIndexEntry *pFromIndex, int64_t FromNumBlocks, IOStream &rOut);
31static void WriteNewIndex(IOStream &rDiff, int64_t DiffNumBlocks, FromIndexEntry *pFromIndex, int64_t FromNumBlocks, IOStream &rOut);
32
33// --------------------------------------------------------------------------
34//
35// Function
36//              Name:    BackupStoreFile::CombineFile(IOStream &, IOStream &, IOStream &)
37//              Purpose: Where rDiff is a store file which is incomplete as a result of a
38//                               diffing operation, rFrom is the file it is diffed from, and
39//                               rOut is the stream in which to place the result, the old file
40//                               and new file are combined into a file containing all the data.
41//                               rDiff2 is the same file as rDiff, opened again to get two
42//                               independent streams to the same file.
43//              Created: 16/1/04
44//
45// --------------------------------------------------------------------------
46void BackupStoreFile::CombineFile(IOStream &rDiff, IOStream &rDiff2, IOStream &rFrom, IOStream &rOut)
47{
48        // Read and copy the header.
49        file_StreamFormat hdr;
50        if(!rDiff.ReadFullBuffer(&hdr, sizeof(hdr), 0))
51        {
52                THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
53        }
54        if(ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1)
55        {
56                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
57        }
58        // Copy
59        rOut.Write(&hdr, sizeof(hdr));
60        // Copy over filename and attributes
61        // BLOCK
62        {
63                BackupStoreFilename filename;
64                filename.ReadFromStream(rDiff, IOStream::TimeOutInfinite);
65                filename.WriteToStream(rOut);
66                StreamableMemBlock attr;
67                attr.ReadFromStream(rDiff, IOStream::TimeOutInfinite);
68                attr.WriteToStream(rOut);
69        }
70       
71        // Read the header for the From file
72        file_StreamFormat fromHdr;
73        if(!rFrom.ReadFullBuffer(&fromHdr, sizeof(fromHdr), 0))
74        {
75                THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
76        }
77        if(ntohl(fromHdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1)
78        {
79                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
80        }
81        // Skip over the filename and attributes of the From file
82        // BLOCK
83        {
84                BackupStoreFilename filename2;
85                filename2.ReadFromStream(rFrom, IOStream::TimeOutInfinite);
86                int32_t size_s;
87                if(!rFrom.ReadFullBuffer(&size_s, sizeof(size_s), 0 /* not interested in bytes read if this fails */))
88                {
89                        THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead)
90                }
91                int size = ntohl(size_s);
92                // Skip forward the size
93                rFrom.Seek(size, IOStream::SeekType_Relative);         
94        }
95       
96        // Allocate memory for the block index of the From file
97        int64_t fromNumBlocks = box_ntoh64(fromHdr.mNumBlocks);
98        // NOTE: An extra entry is required so that the length of the last block can be calculated
99        FromIndexEntry *pFromIndex = (FromIndexEntry*)::malloc((fromNumBlocks+1) * sizeof(FromIndexEntry));
100        if(pFromIndex == 0)
101        {
102                throw std::bad_alloc();
103        }
104       
105        try
106        {
107                // Load the index from the From file, calculating the offsets in the
108                // file as we go along, and enforce that everything should be present.
109                LoadFromIndex(rFrom, pFromIndex, fromNumBlocks);
110               
111                // Read in the block index of the Diff file in small chunks, and output data
112                // for each block, either from this file, or the other file.
113                int64_t diffNumBlocks = box_ntoh64(hdr.mNumBlocks);
114                CopyData(rDiff /* positioned at start of data */, rDiff2, diffNumBlocks, rFrom, pFromIndex, fromNumBlocks, rOut);
115               
116                // Read in the block index again, and output the new block index, simply
117                // filling in the sizes of blocks from the old file.
118                WriteNewIndex(rDiff, diffNumBlocks, pFromIndex, fromNumBlocks, rOut);
119               
120                // Free buffers
121                ::free(pFromIndex);
122                pFromIndex = 0;
123        }
124        catch(...)
125        {
126                // Clean up
127                if(pFromIndex != 0)
128                {
129                        ::free(pFromIndex);
130                        pFromIndex = 0;
131                }       
132                throw;
133        }
134}
135
136
137// --------------------------------------------------------------------------
138//
139// Function
140//              Name:    static LoadFromIndex(IOStream &, FromIndexEntry *, int64_t)
141//              Purpose: Static. Load the index from the From file
142//              Created: 16/1/04
143//
144// --------------------------------------------------------------------------
145static void LoadFromIndex(IOStream &rFrom, FromIndexEntry *pIndex, int64_t NumEntries)
146{
147        ASSERT(pIndex != 0);
148        ASSERT(NumEntries >= 0);
149
150        // Get the starting point in the file
151        int64_t filePos = rFrom.GetPosition();
152       
153        // Jump to the end of the file to read the index
154        rFrom.Seek(0 - ((NumEntries * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader)), IOStream::SeekType_End);
155       
156        // Read block index header
157        file_BlockIndexHeader blkhdr;
158        if(!rFrom.ReadFullBuffer(&blkhdr, sizeof(blkhdr), 0))
159        {
160                THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
161        }
162        if(ntohl(blkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1
163                || (int64_t)box_ntoh64(blkhdr.mNumBlocks) != NumEntries)
164        {
165                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
166        }
167       
168        // And then the block entries
169        for(int64_t b = 0; b < NumEntries; ++b)
170        {
171                // Read
172                file_BlockIndexEntry en;
173                if(!rFrom.ReadFullBuffer(&en, sizeof(en), 0))
174                {
175                        THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
176                }
177               
178                // Add to list
179                pIndex[b].mFilePosition = filePos;
180
181                // Encoded size?
182                int64_t encodedSize = box_ntoh64(en.mEncodedSize);
183                // Check that the block is actually there
184                if(encodedSize <= 0)
185                {
186                        THROW_EXCEPTION(BackupStoreException, OnCombineFromFileIsIncomplete)
187                }
188
189                // Move file pointer on
190                filePos += encodedSize;
191        }
192       
193        // Store the position in the very last entry, so the size of the last entry can be calculated
194        pIndex[NumEntries].mFilePosition = filePos;
195}
196
197
198// --------------------------------------------------------------------------
199//
200// Function
201//              Name:    static CopyData(IOStream &, IOStream &, int64_t, IOStream &, FromIndexEntry *, int64_t, IOStream &)
202//              Purpose: Static. Copy data from the Diff and From file to the out file.
203//                               rDiffData is at beginning of data.
204//                               rDiffIndex at any position.
205//                               rFrom is at any position.
206//                               rOut is after the header, ready for data
207//              Created: 16/1/04
208//
209// --------------------------------------------------------------------------
210static void CopyData(IOStream &rDiffData, IOStream &rDiffIndex, int64_t DiffNumBlocks,
211        IOStream &rFrom, FromIndexEntry *pFromIndex, int64_t FromNumBlocks, IOStream &rOut)
212{
213        // Jump to the end of the diff file to read the index
214        rDiffIndex.Seek(0 - ((DiffNumBlocks * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader)), IOStream::SeekType_End);
215       
216        // Read block index header
217        file_BlockIndexHeader diffBlkhdr;
218        if(!rDiffIndex.ReadFullBuffer(&diffBlkhdr, sizeof(diffBlkhdr), 0))
219        {
220                THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
221        }
222        if(ntohl(diffBlkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1
223                || (int64_t)box_ntoh64(diffBlkhdr.mNumBlocks) != DiffNumBlocks)
224        {
225                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
226        }
227       
228        // Record where the From file is
229        int64_t fromPos = rFrom.GetPosition();
230       
231        // Buffer data
232        void *buffer = 0;
233        int bufferSize = 0;
234       
235        try
236        {
237                // Read the blocks in!
238                for(int64_t b = 0; b < DiffNumBlocks; ++b)
239                {
240                        // Read
241                        file_BlockIndexEntry en;
242                        if(!rDiffIndex.ReadFullBuffer(&en, sizeof(en), 0))
243                        {
244                                THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
245                        }
246                       
247                        // What's the size value stored in the entry
248                        int64_t encodedSize = box_ntoh64(en.mEncodedSize);
249                       
250                        // How much data will be read?
251                        int32_t blockSize = 0;
252                        if(encodedSize > 0)
253                        {
254                                // The block is actually in the diff file
255                                blockSize = encodedSize;
256                        }
257                        else
258                        {
259                                // It's in the from file. First, check to see if it's valid
260                                int64_t blockIdx = (0 - encodedSize);
261                                if(blockIdx > FromNumBlocks)
262                                {
263                                        // References a block which doesn't actually exist
264                                        THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
265                                }
266                                // Calculate size. This operation is safe because of the extra entry at the end
267                                blockSize = pFromIndex[blockIdx + 1].mFilePosition - pFromIndex[blockIdx].mFilePosition;
268                        }
269                        ASSERT(blockSize > 0);
270                       
271                        // Make sure there's memory available to copy this
272                        if(bufferSize < blockSize || buffer == 0)
273                        {
274                                // Free old block
275                                if(buffer != 0)
276                                {
277                                        ::free(buffer);
278                                        buffer = 0;
279                                        bufferSize = 0;
280                                }
281                                // Allocate new block
282                                buffer = ::malloc(blockSize);
283                                if(buffer == 0)
284                                {
285                                        throw std::bad_alloc();
286                                }
287                                bufferSize = blockSize;
288                        }
289                        ASSERT(bufferSize >= blockSize);
290                       
291                        // Load in data from one of the files
292                        if(encodedSize > 0)
293                        {
294                                // Load from diff file
295                                if(!rDiffData.ReadFullBuffer(buffer, blockSize, 0))
296                                {
297                                        THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
298                                }                               
299                        }
300                        else
301                        {
302                                // Locate and read the data from the from file
303                                int64_t blockIdx = (0 - encodedSize);
304                                // Seek if necessary
305                                if(fromPos != pFromIndex[blockIdx].mFilePosition)
306                                {
307                                        rFrom.Seek(pFromIndex[blockIdx].mFilePosition, IOStream::SeekType_Absolute);
308                                        fromPos = pFromIndex[blockIdx].mFilePosition;
309                                }
310                                // Read
311                                if(!rFrom.ReadFullBuffer(buffer, blockSize, 0))
312                                {
313                                        THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
314                                }
315                               
316                                // Update fromPos to current position
317                                fromPos += blockSize;
318                        }
319                       
320                        // Write data to out file
321                        rOut.Write(buffer, blockSize);
322                }
323               
324                // Free buffer, if allocated
325                if(buffer != 0)
326                {
327                        ::free(buffer);
328                        buffer = 0;
329                }
330        }
331        catch(...)
332        {
333                if(buffer != 0)
334                {
335                        ::free(buffer);
336                        buffer = 0;
337                }
338                throw;
339        }
340}
341
342
343
344// --------------------------------------------------------------------------
345//
346// Function
347//              Name:    static WriteNewIndex(IOStream &, int64_t, FromIndexEntry *, int64_t, IOStream &)
348//              Purpose: Write the index to the out file, just copying from the diff file and
349//                               adjusting the entries.
350//              Created: 16/1/04
351//
352// --------------------------------------------------------------------------
353static void WriteNewIndex(IOStream &rDiff, int64_t DiffNumBlocks, FromIndexEntry *pFromIndex, int64_t FromNumBlocks, IOStream &rOut)
354{
355        // Jump to the end of the diff file to read the index
356        rDiff.Seek(0 - ((DiffNumBlocks * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader)), IOStream::SeekType_End);
357       
358        // Read block index header
359        file_BlockIndexHeader diffBlkhdr;
360        if(!rDiff.ReadFullBuffer(&diffBlkhdr, sizeof(diffBlkhdr), 0))
361        {
362                THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
363        }
364        if(ntohl(diffBlkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1
365                || (int64_t)box_ntoh64(diffBlkhdr.mNumBlocks) != DiffNumBlocks)
366        {
367                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
368        }
369       
370        // Write it out with a blanked out other file ID
371        diffBlkhdr.mOtherFileID = box_hton64(0);
372        rOut.Write(&diffBlkhdr, sizeof(diffBlkhdr));
373       
374        // Rewrite the index
375        for(int64_t b = 0; b < DiffNumBlocks; ++b)
376        {
377                file_BlockIndexEntry en;
378                if(!rDiff.ReadFullBuffer(&en, sizeof(en), 0))
379                {
380                        THROW_EXCEPTION(BackupStoreException, FailedToReadBlockOnCombine)
381                }
382               
383                // What's the size value stored in the entry
384                int64_t encodedSize = box_ntoh64(en.mEncodedSize);
385               
386                // Need to adjust it?
387                if(encodedSize <= 0)
388                {
389                        // This actually refers to a block in the from file. So rewrite this.
390                        int64_t blockIdx = (0 - encodedSize);
391                        if(blockIdx > FromNumBlocks)
392                        {
393                                // References a block which doesn't actually exist
394                                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
395                        }
396                        // Calculate size. This operation is safe because of the extra entry at the end
397                        int32_t blockSize = pFromIndex[blockIdx + 1].mFilePosition - pFromIndex[blockIdx].mFilePosition;
398                        // Then replace entry
399                        en.mEncodedSize = box_hton64(((uint64_t)blockSize));
400                }
401               
402                // Write entry
403                rOut.Write(&en, sizeof(en));
404        }
405}
406
407
408
409
410
Note: See TracBrowser for help on using the repository browser.