source: box/trunk/lib/backupstore/BackupStoreFile.cpp @ 2945

Revision 2945, 45.4 KB checked in by chris, 13 months ago (diff)

Major refactoring to make lib/backupclient depend on lib/backupstore rather
than the other way around. This is needed to allow clients to have all the
code that they'd need to implement local backups (using the Local protocol)
in subsequent commits.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    BackupStoreFile.cpp
5//              Purpose: Utils for manipulating files
6//              Created: 2003/08/28
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#ifdef HAVE_UNISTD_H
13        #include <unistd.h>
14#endif
15
16#include <sys/stat.h>
17#include <string.h>
18#include <new>
19#include <string.h>
20
21#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
22        #include <stdio.h>
23#endif
24
25#include "BackupStoreFile.h"
26#include "BackupStoreFileWire.h"
27#include "BackupStoreFileCryptVar.h"
28#include "BackupStoreFilename.h"
29#include "BackupStoreException.h"
30#include "IOStream.h"
31#include "Guards.h"
32#include "FileModificationTime.h"
33#include "FileStream.h"
34#include "BackupClientFileAttributes.h"
35#include "BackupStoreObjectMagic.h"
36#include "Compress.h"
37#include "CipherContext.h"
38#include "CipherBlowfish.h"
39#include "CipherAES.h"
40#include "BackupStoreConstants.h"
41#include "CollectInBufferStream.h"
42#include "RollingChecksum.h"
43#include "MD5Digest.h"
44#include "ReadGatherStream.h"
45#include "Random.h"
46#include "BackupStoreFileEncodeStream.h"
47#include "Logging.h"
48
49#include "MemLeakFindOn.h"
50
51using namespace BackupStoreFileCryptVar;
52
53// How big a buffer to use for copying files
54#define COPY_BUFFER_SIZE        (8*1024)
55
56// Statistics
57BackupStoreFileStats BackupStoreFile::msStats = {0,0,0};
58
59#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
60        bool sWarnedAboutBackwardsCompatiblity = false;
61#endif
62
63// --------------------------------------------------------------------------
64//
65// Function
66//              Name:    BackupStoreFile::EncodeFile(IOStream &, IOStream &)
67//              Purpose: Encode a file into something for storing on file server.
68//                       Requires a real filename so full info can be stored.
69//
70//                       Returns a stream. Most of the work is done by the stream
71//                       when data is actually requested -- the file will be held
72//                       open until the stream is deleted or the file finished.
73//              Created: 2003/08/28
74//
75// --------------------------------------------------------------------------
76std::auto_ptr<IOStream> BackupStoreFile::EncodeFile(
77        const char *Filename, int64_t ContainerID,
78        const BackupStoreFilename &rStoreFilename,
79        int64_t *pModificationTime,
80        ReadLoggingStream::Logger* pLogger,
81        RunStatusProvider* pRunStatusProvider)
82{
83        // Create the stream
84        std::auto_ptr<IOStream> stream(new BackupStoreFileEncodeStream);
85
86        // Do the initial setup
87        ((BackupStoreFileEncodeStream*)stream.get())->Setup(Filename,
88                0 /* no recipe, just encode */,
89                ContainerID, rStoreFilename, pModificationTime, pLogger,
90                pRunStatusProvider);
91       
92        // Return the stream for the caller
93        return stream;
94}
95
96// --------------------------------------------------------------------------
97//
98// Function
99//              Name:    BackupStoreFile::VerifyEncodedFileFormat(IOStream &)
100//              Purpose: Verify that an encoded file meets the format
101//                       requirements. Doesn't verify that the data is intact
102//                       and can be decoded. Optionally returns the ID of the
103//                       file which it is diffed from, and the (original)
104//                       container ID.
105//              Created: 2003/08/28
106//
107// --------------------------------------------------------------------------
108bool BackupStoreFile::VerifyEncodedFileFormat(IOStream &rFile, int64_t *pDiffFromObjectIDOut, int64_t *pContainerIDOut)
109{
110        // Get the size of the file
111        int64_t fileSize = rFile.BytesLeftToRead();
112        if(fileSize == IOStream::SizeOfStreamUnknown)
113        {
114                THROW_EXCEPTION(BackupStoreException, StreamDoesntHaveRequiredFeatures)
115        }
116
117        // Get the header...
118        file_StreamFormat hdr;
119        if(!rFile.ReadFullBuffer(&hdr, sizeof(hdr), 0 /* not interested in bytes read if this fails */))
120        {
121                // Couldn't read header
122                return false;
123        }
124       
125        // Check magic number
126        if(ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1
127#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
128                && ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V0
129#endif
130                )
131        {
132                return false;
133        }
134       
135        // Get a filename, see if it loads OK
136        try
137        {
138                BackupStoreFilename fn;
139                fn.ReadFromStream(rFile, IOStream::TimeOutInfinite);
140        }
141        catch(...)
142        {
143                // an error occured while reading it, so that's not good
144                return false;
145        }
146       
147        // Skip the attributes -- because they're encrypted, the server can't tell whether they're OK or not
148        try
149        {
150                int32_t size_s;
151                if(!rFile.ReadFullBuffer(&size_s, sizeof(size_s), 0 /* not interested in bytes read if this fails */))
152                {
153                        THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead)
154                }
155                int size = ntohl(size_s);
156                // Skip forward the size
157                rFile.Seek(size, IOStream::SeekType_Relative);
158        }
159        catch(...)
160        {
161                // an error occured while reading it, so that's not good
162                return false;
163        }
164
165        // Get current position in file -- the end of the header
166        int64_t headerEnd = rFile.GetPosition();
167       
168        // Get number of blocks
169        int64_t numBlocks = box_ntoh64(hdr.mNumBlocks);
170       
171        // Calculate where the block index will be, check it's reasonable
172        int64_t blockIndexLoc = fileSize - ((numBlocks * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader));
173        if(blockIndexLoc < headerEnd)
174        {
175                // Not enough space left for the block index, let alone the blocks themselves
176                return false;
177        }
178
179        // Load the block index header
180        rFile.Seek(blockIndexLoc, IOStream::SeekType_Absolute);
181        file_BlockIndexHeader blkhdr;
182        if(!rFile.ReadFullBuffer(&blkhdr, sizeof(blkhdr), 0 /* not interested in bytes read if this fails */))
183        {
184                // Couldn't read block index header -- assume bad file
185                return false;
186        }
187       
188        // Check header
189        if((ntohl(blkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1
190#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
191                && ntohl(blkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0
192#endif
193                )
194                || (int64_t)box_ntoh64(blkhdr.mNumBlocks) != numBlocks)
195        {
196                // Bad header -- either magic value or number of blocks is wrong
197                return false;
198        }
199       
200        // Flag for recording whether a block is referenced from another file
201        bool blockFromOtherFileReferenced = false;
202       
203        // Read the index, checking that the length values all make sense
204        int64_t currentBlockStart = headerEnd;
205        for(int64_t b = 0; b < numBlocks; ++b)
206        {
207                // Read block entry
208                file_BlockIndexEntry blk;
209                if(!rFile.ReadFullBuffer(&blk, sizeof(blk), 0 /* not interested in bytes read if this fails */))
210                {
211                        // Couldn't read block index entry -- assume bad file
212                        return false;
213                }
214               
215                // Check size and location
216                int64_t blkSize = box_ntoh64(blk.mEncodedSize);
217                if(blkSize <= 0)
218                {
219                        // Mark that this file references another file
220                        blockFromOtherFileReferenced = true;
221                }
222                else
223                {
224                        // This block is actually in this file
225                        if((currentBlockStart + blkSize) > blockIndexLoc)
226                        {
227                                // Encoded size makes the block run over the index
228                                return false;
229                        }
230                       
231                        // Move the current block start ot the end of this block
232                        currentBlockStart += blkSize;
233                }
234        }
235       
236        // Check that there's no empty space
237        if(currentBlockStart != blockIndexLoc)
238        {
239                return false;
240        }
241       
242        // Check that if another block is references, then the ID is there, and if one isn't there is no ID.
243        int64_t otherID = box_ntoh64(blkhdr.mOtherFileID);
244        if((otherID != 0 && blockFromOtherFileReferenced == false)
245                || (otherID == 0 && blockFromOtherFileReferenced == true))
246        {
247                // Doesn't look good!
248                return false;
249        }
250       
251        // Does the caller want the other ID?
252        if(pDiffFromObjectIDOut)
253        {
254                *pDiffFromObjectIDOut = otherID;
255        }
256       
257        // Does the caller want the container ID?
258        if(pContainerIDOut)
259        {
260                *pContainerIDOut = box_ntoh64(hdr.mContainerID);
261        }
262
263        // Passes all tests
264        return true;
265}
266
267// --------------------------------------------------------------------------
268//
269// Function
270//              Name:    BackupStoreFile::DecodeFile(IOStream &, const char *)
271//              Purpose: Decode a file. Will set file attributes. File must not exist.
272//              Created: 2003/08/28
273//
274// --------------------------------------------------------------------------
275void BackupStoreFile::DecodeFile(IOStream &rEncodedFile, const char *DecodedFilename, int Timeout, const BackupClientFileAttributes *pAlterativeAttr)
276{
277        // Does file exist?
278        EMU_STRUCT_STAT st;
279        if(EMU_STAT(DecodedFilename, &st) == 0)
280        {
281                THROW_EXCEPTION(BackupStoreException, OutputFileAlreadyExists)
282        }
283       
284        // Try, delete output file if error
285        try
286        {
287                // Make a stream for outputting this file
288                FileStream out(DecodedFilename, O_WRONLY | O_CREAT | O_EXCL);
289
290                // Get the decoding stream
291                std::auto_ptr<DecodedStream> stream(DecodeFileStream(rEncodedFile, Timeout, pAlterativeAttr));
292               
293                // Is it a symlink?
294                if(!stream->IsSymLink())
295                {
296                        // Copy it out to the file
297                        stream->CopyStreamTo(out);
298                }
299
300                out.Close();
301
302                // The stream might have uncertain size, in which case
303                // we need to drain it to get the
304                // Protocol::ProtocolStreamHeader_EndOfStream byte
305                // out of our connection stream.
306                char buffer[1];
307                int drained = rEncodedFile.Read(buffer, 1);
308
309                // The Read will return 0 if we are actually at the end
310                // of the stream, but some tests decode files directly,
311                // in which case we are actually positioned at the start
312                // of the block index. I hope that reading an extra byte
313                // doesn't hurt!
314                // ASSERT(drained == 0);
315               
316                // Write the attributes
317                try
318                {
319                        stream->GetAttributes().WriteAttributes(DecodedFilename);
320                }
321                catch (std::exception& e)
322                {
323                        BOX_WARNING("Failed to restore attributes on " <<
324                                DecodedFilename << ": " << e.what());
325                }
326        }
327        catch(...)
328        {
329                ::unlink(DecodedFilename);
330                throw;
331        }
332}
333
334
335// --------------------------------------------------------------------------
336//
337// Function
338//              Name:    BackupStoreFile::DecodeFileStream(IOStream &, int, const BackupClientFileAttributes *)
339//              Purpose: Return a stream which will decode the encrypted file data on the fly.
340//                               Accepts streams in block index first, or main header first, order. In the latter case,
341//                               the stream must be Seek()able.
342//
343//                               Before you use the returned stream, call IsSymLink() -- symlink streams won't allow
344//                               you to read any data to enforce correct logic. See BackupStoreFile::DecodeFile() implementation.
345//              Created: 9/12/03
346//
347// --------------------------------------------------------------------------
348std::auto_ptr<BackupStoreFile::DecodedStream> BackupStoreFile::DecodeFileStream(IOStream &rEncodedFile, int Timeout, const BackupClientFileAttributes *pAlterativeAttr)
349{
350        // Create stream
351        std::auto_ptr<DecodedStream> stream(new DecodedStream(rEncodedFile, Timeout));
352       
353        // Get it ready
354        stream->Setup(pAlterativeAttr);
355       
356        // Return to caller
357        return stream;
358}
359
360
361// --------------------------------------------------------------------------
362//
363// Function
364//              Name:    BackupStoreFile::DecodedStream::DecodedStream(IOStream &, int)
365//              Purpose: Constructor
366//              Created: 9/12/03
367//
368// --------------------------------------------------------------------------
369BackupStoreFile::DecodedStream::DecodedStream(IOStream &rEncodedFile, int Timeout)
370        : mrEncodedFile(rEncodedFile),
371          mTimeout(Timeout),
372          mNumBlocks(0),
373          mpBlockIndex(0),
374          mpEncodedData(0),
375          mpClearData(0),
376          mClearDataSize(0),
377          mCurrentBlock(-1),
378          mCurrentBlockClearSize(0),
379          mPositionInCurrentBlock(0),
380          mEntryIVBase(42)      // different to default value in the encoded stream!
381#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
382          , mIsOldVersion(false)
383#endif
384{
385}
386
387
388// --------------------------------------------------------------------------
389//
390// Function
391//              Name:    BackupStoreFile::DecodedStream::~DecodedStream()
392//              Purpose: Desctructor
393//              Created: 9/12/03
394//
395// --------------------------------------------------------------------------
396BackupStoreFile::DecodedStream::~DecodedStream()
397{
398        // Free any allocated memory
399        if(mpBlockIndex)
400        {
401                ::free(mpBlockIndex);
402        }
403        if(mpEncodedData)
404        {
405                BackupStoreFile::CodingChunkFree(mpEncodedData);
406        }
407        if(mpClearData)
408        {
409                ::free(mpClearData);
410        }
411}
412
413
414// --------------------------------------------------------------------------
415//
416// Function
417//              Name:    BackupStoreFile::DecodedStream::Setup(const BackupClientFileAttributes *)
418//              Purpose: Get the stream ready to decode -- reads in headers
419//              Created: 9/12/03
420//
421// --------------------------------------------------------------------------
422void BackupStoreFile::DecodedStream::Setup(const BackupClientFileAttributes *pAlterativeAttr)
423{
424        // Get the size of the file
425        int64_t fileSize = mrEncodedFile.BytesLeftToRead();
426
427        // Get the magic number to work out which order the stream is in
428        int32_t magic;
429        if(!mrEncodedFile.ReadFullBuffer(&magic, sizeof(magic), 0 /* not interested in bytes read if this fails */, mTimeout))
430        {
431                // Couldn't read magic value
432                THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
433        }
434
435        bool inFileOrder = true;       
436        switch(ntohl(magic))
437        {
438#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
439        case OBJECTMAGIC_FILE_MAGIC_VALUE_V0:
440                mIsOldVersion = true;
441                // control flows on
442#endif
443        case OBJECTMAGIC_FILE_MAGIC_VALUE_V1:
444                inFileOrder = true;
445                break;
446
447#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
448        case OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0:
449                mIsOldVersion = true;
450                // control flows on
451#endif
452        case OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1:
453                inFileOrder = false;
454                break;
455
456        default:
457                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
458        }
459       
460        // If not in file order, then the index list must be read now
461        if(!inFileOrder)
462        {
463                ReadBlockIndex(true /* have already read and verified the magic number */);
464        }
465
466        // Get header
467        file_StreamFormat hdr;
468        if(inFileOrder)
469        {
470                // Read the header, without the magic number
471                if(!mrEncodedFile.ReadFullBuffer(((uint8_t*)&hdr) + sizeof(magic), sizeof(hdr) - sizeof(magic),
472                        0 /* not interested in bytes read if this fails */, mTimeout))
473                {
474                        // Couldn't read header
475                        THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
476                }
477                // Put in magic number
478                hdr.mMagicValue = magic;
479        }
480        else
481        {
482                // Not in file order, so need to read the full header
483                if(!mrEncodedFile.ReadFullBuffer(&hdr, sizeof(hdr), 0 /* not interested in bytes read if this fails */, mTimeout))
484                {
485                        // Couldn't read header
486                        THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
487                }
488        }       
489
490        // Check magic number
491        if(ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1
492#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
493                && ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V0
494#endif
495                )
496        {
497                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
498        }
499
500        // Get the filename
501        mFilename.ReadFromStream(mrEncodedFile, mTimeout);
502       
503        // Get the attributes (either from stream, or supplied attributes)
504        if(pAlterativeAttr != 0)
505        {
506                // Read dummy attributes
507                BackupClientFileAttributes attr;
508                attr.ReadFromStream(mrEncodedFile, mTimeout);
509
510                // Set to supplied attributes
511                mAttributes = *pAlterativeAttr;
512        }
513        else
514        {
515                // Read the attributes from the stream
516                mAttributes.ReadFromStream(mrEncodedFile, mTimeout);
517        }
518       
519        // If it is in file order, go and read the file attributes
520        // Requires that the stream can seek
521        if(inFileOrder)
522        {
523                // Make sure the file size is known
524                if(fileSize == IOStream::SizeOfStreamUnknown)
525                {
526                        THROW_EXCEPTION(BackupStoreException, StreamDoesntHaveRequiredFeatures)
527                }
528       
529                // Store current location (beginning of encoded blocks)
530                int64_t endOfHeaderPos = mrEncodedFile.GetPosition();
531               
532                // Work out where the index is
533                int64_t numBlocks = box_ntoh64(hdr.mNumBlocks);
534                int64_t blockHeaderPos = fileSize - ((numBlocks * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader));
535               
536                // Seek to that position
537                mrEncodedFile.Seek(blockHeaderPos, IOStream::SeekType_Absolute);
538               
539                // Read the block index
540                ReadBlockIndex(false /* magic number still to be read */);             
541               
542                // Seek back to the end of header position, ready for reading the chunks
543                mrEncodedFile.Seek(endOfHeaderPos, IOStream::SeekType_Absolute);
544        }
545       
546        // Check view of blocks from block header and file header match
547        if(mNumBlocks != (int64_t)box_ntoh64(hdr.mNumBlocks))
548        {
549                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
550        }
551       
552        // Need to allocate some memory for the two blocks for reading encoded data, and clear data
553        if(mNumBlocks > 0)
554        {
555                // Find the maximum encoded data size
556                int32_t maxEncodedDataSize = 0;
557                const file_BlockIndexEntry *entry = (file_BlockIndexEntry *)mpBlockIndex;
558                ASSERT(entry != 0);
559                for(int64_t e = 0; e < mNumBlocks; e++)
560                {
561                        // Get the clear and encoded size
562                        int32_t encodedSize = box_ntoh64(entry[e].mEncodedSize);
563                        ASSERT(encodedSize > 0);
564                       
565                        // Larger?
566                        if(encodedSize > maxEncodedDataSize) maxEncodedDataSize = encodedSize;
567                }
568               
569                // Allocate those blocks!
570                mpEncodedData = (uint8_t*)BackupStoreFile::CodingChunkAlloc(maxEncodedDataSize + 32);
571
572                // Allocate the block for the clear data, using the hint from the header.
573                // If this is wrong, things will exception neatly later on, so it can't be used
574                // to do anything more than cause an error on downloading.
575                mClearDataSize = OutputBufferSizeForKnownOutputSize(ntohl(hdr.mMaxBlockClearSize)) + 32;
576                mpClearData = (uint8_t*)::malloc(mClearDataSize);
577        }
578}
579
580
581// --------------------------------------------------------------------------
582//
583// Function
584//              Name:    BackupStoreFile::DecodedStream::ReadBlockIndex(bool)
585//              Purpose: Read the block index from the stream, and store in internal buffer (minus header)
586//              Created: 9/12/03
587//
588// --------------------------------------------------------------------------
589void BackupStoreFile::DecodedStream::ReadBlockIndex(bool MagicAlreadyRead)
590{
591        // Header
592        file_BlockIndexHeader blkhdr;
593       
594        // Read it in -- way depends on how whether the magic number has already been read
595        if(MagicAlreadyRead)
596        {
597                // Read the header, without the magic number
598                if(!mrEncodedFile.ReadFullBuffer(((uint8_t*)&blkhdr) + sizeof(blkhdr.mMagicValue), sizeof(blkhdr) - sizeof(blkhdr.mMagicValue),
599                        0 /* not interested in bytes read if this fails */, mTimeout))
600                {
601                        // Couldn't read header
602                        THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
603                }
604        }
605        else
606        {
607                // Magic not already read, so need to read the full header
608                if(!mrEncodedFile.ReadFullBuffer(&blkhdr, sizeof(blkhdr), 0 /* not interested in bytes read if this fails */, mTimeout))
609                {
610                        // Couldn't read header
611                        THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
612                }
613               
614                // Check magic value
615                if(ntohl(blkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1
616#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
617                        && ntohl(blkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0
618#endif
619                        )
620                {
621                        THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
622                }
623        }
624       
625        // Get the number of blocks out of the header
626        mNumBlocks = box_ntoh64(blkhdr.mNumBlocks);
627       
628        // Read the IV base
629        mEntryIVBase = box_ntoh64(blkhdr.mEntryIVBase);
630       
631        // Load the block entries in?
632        if(mNumBlocks > 0)
633        {
634                // How big is the index?
635                int64_t indexSize = sizeof(file_BlockIndexEntry) * mNumBlocks;
636               
637                // Allocate some memory
638                mpBlockIndex = ::malloc(indexSize);
639                if(mpBlockIndex == 0)
640                {
641                        throw std::bad_alloc();
642                }
643               
644                // Read it in
645                if(!mrEncodedFile.ReadFullBuffer(mpBlockIndex, indexSize, 0 /* not interested in bytes read if this fails */, mTimeout))
646                {
647                        // Couldn't read header
648                        THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
649                }
650        }
651}
652
653
654// --------------------------------------------------------------------------
655//
656// Function
657//              Name:    BackupStoreFile::DecodedStream::Read(void *, int, int)
658//              Purpose: As interface. Reads decrpyted data.
659//              Created: 9/12/03
660//
661// --------------------------------------------------------------------------
662int BackupStoreFile::DecodedStream::Read(void *pBuffer, int NBytes, int Timeout)
663{
664        // Symlinks don't have data. So can't read it. Not even zero bytes.
665        if(IsSymLink())
666        {
667                // Don't allow reading in this case
668                THROW_EXCEPTION(BackupStoreException, ThereIsNoDataInASymLink);
669        }
670
671        // Already finished?
672        if(mCurrentBlock >= mNumBlocks)
673        {
674                // At end of stream, nothing to do
675                return 0;
676        }
677
678        int bytesToRead = NBytes;
679        uint8_t *output = (uint8_t*)pBuffer;
680       
681        while(bytesToRead > 0 && mCurrentBlock < mNumBlocks)
682        {
683                // Anything left in the current block?
684                if(mPositionInCurrentBlock < mCurrentBlockClearSize)
685                {
686                        // Copy data out of this buffer
687                        int s = mCurrentBlockClearSize - mPositionInCurrentBlock;
688                        if(s > bytesToRead) s = bytesToRead;    // limit to requested data
689                       
690                        // Copy
691                        ::memcpy(output, mpClearData + mPositionInCurrentBlock, s);
692                       
693                        // Update positions
694                        output += s;
695                        mPositionInCurrentBlock += s;
696                        bytesToRead -= s;
697                }
698               
699                // Need to get some more data?
700                if(bytesToRead > 0 && mPositionInCurrentBlock >= mCurrentBlockClearSize)
701                {
702                        // Number of next block
703                        ++mCurrentBlock;
704                        if(mCurrentBlock >= mNumBlocks)
705                        {
706                                // Stop now!
707                                break;
708                        }
709               
710                        // Get the size from the block index
711                        const file_BlockIndexEntry *entry = (file_BlockIndexEntry *)mpBlockIndex;
712                        int32_t encodedSize = box_ntoh64(entry[mCurrentBlock].mEncodedSize);
713                        if(encodedSize <= 0)
714                        {
715                                // The caller is attempting to decode a file which is the direct result of a diff
716                                // operation, and so does not contain all the data.
717                                // It needs to be combined with the previous version first.
718                                THROW_EXCEPTION(BackupStoreException, CannotDecodeDiffedFilesWithoutCombining)
719                        }
720                       
721                        // Load in next block
722                        if(!mrEncodedFile.ReadFullBuffer(mpEncodedData, encodedSize, 0 /* not interested in bytes read if this fails */, mTimeout))
723                        {
724                                // Couldn't read header
725                                THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
726                        }
727                       
728                        // Decode the data
729                        mCurrentBlockClearSize = BackupStoreFile::DecodeChunk(mpEncodedData, encodedSize, mpClearData, mClearDataSize);
730
731                        // Calculate IV for this entry
732                        uint64_t iv = mEntryIVBase;
733                        iv += mCurrentBlock;
734                        // Convert to network byte order before encrypting with it, so that restores work on
735                        // platforms with different endiannesses.
736                        iv = box_hton64(iv);
737                        sBlowfishDecryptBlockEntry.SetIV(&iv);
738                       
739                        // Decrypt the encrypted section
740                        file_BlockIndexEntryEnc entryEnc;
741                        int sectionSize = sBlowfishDecryptBlockEntry.TransformBlock(&entryEnc, sizeof(entryEnc),
742                                        entry[mCurrentBlock].mEnEnc, sizeof(entry[mCurrentBlock].mEnEnc));
743                        if(sectionSize != sizeof(entryEnc))
744                        {
745                                THROW_EXCEPTION(BackupStoreException, BlockEntryEncodingDidntGiveExpectedLength)
746                        }
747
748                        // Make sure this is the right size
749                        if(mCurrentBlockClearSize != (int32_t)ntohl(entryEnc.mSize))
750                        {
751#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
752                                if(!mIsOldVersion)
753                                {
754                                        THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
755                                }
756                                // Versions 0.05 and previous of Box Backup didn't properly handle endianess of the
757                                // IV for the encrypted section. Try again, with the thing the other way round
758                                iv = box_swap64(iv);
759                                sBlowfishDecryptBlockEntry.SetIV(&iv);
760                                int sectionSize = sBlowfishDecryptBlockEntry.TransformBlock(&entryEnc, sizeof(entryEnc),
761                                                entry[mCurrentBlock].mEnEnc, sizeof(entry[mCurrentBlock].mEnEnc));
762                                if(sectionSize != sizeof(entryEnc))
763                                {
764                                        THROW_EXCEPTION(BackupStoreException, BlockEntryEncodingDidntGiveExpectedLength)
765                                }
766                                if(mCurrentBlockClearSize != (int32_t)ntohl(entryEnc.mSize))
767                                {
768                                        THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
769                                }
770                                else
771                                {
772                                        // Warn and log this issue
773                                        if(!sWarnedAboutBackwardsCompatiblity)
774                                        {
775                                                BOX_WARNING("WARNING: Decoded one or more files using backwards compatibility mode for block index.");
776                                                sWarnedAboutBackwardsCompatiblity = true;
777                                        }
778                                }
779#else
780                                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
781#endif
782                        }
783                       
784                        // Check the digest
785                        MD5Digest md5;
786                        md5.Add(mpClearData, mCurrentBlockClearSize);
787                        md5.Finish();
788                        if(!md5.DigestMatches((uint8_t*)entryEnc.mStrongChecksum))
789                        {
790                                THROW_EXCEPTION(BackupStoreException, BackupStoreFileFailedIntegrityCheck)
791                        }
792                       
793                        // Set vars to say what's happening
794                        mPositionInCurrentBlock = 0;
795                }
796        }
797       
798        ASSERT(bytesToRead >= 0);
799        ASSERT(bytesToRead <= NBytes);
800
801        return NBytes - bytesToRead;
802}
803
804
805// --------------------------------------------------------------------------
806//
807// Function
808//              Name:    BackupStoreFile::DecodedStream::IsSymLink()
809//              Purpose: Is the unencoded file actually a symlink?
810//              Created: 10/12/03
811//
812// --------------------------------------------------------------------------
813bool BackupStoreFile::DecodedStream::IsSymLink()
814{
815        // First, check in with the attributes
816        if(!mAttributes.IsSymLink())
817        {
818                return false;
819        }
820       
821        // So the attributes think it is a symlink.
822        // Consistency check...
823        if(mNumBlocks != 0)
824        {
825                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
826        }
827       
828        return true;
829}
830
831
832// --------------------------------------------------------------------------
833//
834// Function
835//              Name:    BackupStoreFile::DecodedStream::Write(const void *, int)
836//              Purpose: As interface. Throws exception, as you can't write to this stream.
837//              Created: 9/12/03
838//
839// --------------------------------------------------------------------------
840void BackupStoreFile::DecodedStream::Write(const void *pBuffer, int NBytes)
841{
842        THROW_EXCEPTION(BackupStoreException, CantWriteToDecodedFileStream)
843}
844
845
846// --------------------------------------------------------------------------
847//
848// Function
849//              Name:    BackupStoreFile::DecodedStream::StreamDataLeft()
850//              Purpose: As interface. Any data left?
851//              Created: 9/12/03
852//
853// --------------------------------------------------------------------------
854bool BackupStoreFile::DecodedStream::StreamDataLeft()
855{
856        return mCurrentBlock < mNumBlocks;
857}
858
859
860// --------------------------------------------------------------------------
861//
862// Function
863//              Name:    BackupStoreFile::DecodedStream::StreamClosed()
864//              Purpose: As interface. Always returns true, no writing allowed.
865//              Created: 9/12/03
866//
867// --------------------------------------------------------------------------
868bool BackupStoreFile::DecodedStream::StreamClosed()
869{
870        // Can't write to this stream!
871        return true;
872}
873
874
875
876
877
878// --------------------------------------------------------------------------
879//
880// Function
881//              Name:    BackupStoreFile::SetBlowfishKey(const void *, int)
882//              Purpose: Static. Sets the key to use for encryption and decryption.
883//              Created: 7/12/03
884//
885// --------------------------------------------------------------------------
886void BackupStoreFile::SetBlowfishKeys(const void *pKey, int KeyLength, const void *pBlockEntryKey, int BlockEntryKeyLength)
887{
888        // IVs set later
889        sBlowfishEncrypt.Reset();
890        sBlowfishEncrypt.Init(CipherContext::Encrypt, CipherBlowfish(CipherDescription::Mode_CBC, pKey, KeyLength));
891        sBlowfishDecrypt.Reset();
892        sBlowfishDecrypt.Init(CipherContext::Decrypt, CipherBlowfish(CipherDescription::Mode_CBC, pKey, KeyLength));
893
894        sBlowfishEncryptBlockEntry.Reset();
895        sBlowfishEncryptBlockEntry.Init(CipherContext::Encrypt, CipherBlowfish(CipherDescription::Mode_CBC, pBlockEntryKey, BlockEntryKeyLength));
896        sBlowfishEncryptBlockEntry.UsePadding(false);
897        sBlowfishDecryptBlockEntry.Reset();
898        sBlowfishDecryptBlockEntry.Init(CipherContext::Decrypt, CipherBlowfish(CipherDescription::Mode_CBC, pBlockEntryKey, BlockEntryKeyLength));
899        sBlowfishDecryptBlockEntry.UsePadding(false);
900}
901
902
903#ifndef HAVE_OLD_SSL
904// --------------------------------------------------------------------------
905//
906// Function
907//              Name:    BackupStoreFile::SetAESKey(const void *, int)
908//              Purpose: Sets the AES key to use for file data encryption. Will select AES as
909//                               the cipher to use when encrypting.
910//              Created: 27/4/04
911//
912// --------------------------------------------------------------------------
913void BackupStoreFile::SetAESKey(const void *pKey, int KeyLength)
914{
915        // Setup context
916        sAESEncrypt.Reset();
917        sAESEncrypt.Init(CipherContext::Encrypt, CipherAES(CipherDescription::Mode_CBC, pKey, KeyLength));
918        sAESDecrypt.Reset();
919        sAESDecrypt.Init(CipherContext::Decrypt, CipherAES(CipherDescription::Mode_CBC, pKey, KeyLength));
920       
921        // Set encryption to use this key, instead of the "default" blowfish key
922        spEncrypt = &sAESEncrypt;
923        sEncryptCipherType = HEADER_AES_ENCODING;
924}
925#endif
926
927
928// --------------------------------------------------------------------------
929//
930// Function
931//              Name:    BackupStoreFile::MaxBlockSizeForChunkSize(int)
932//              Purpose: The maximum output size of a block, given the chunk size
933//              Created: 7/12/03
934//
935// --------------------------------------------------------------------------
936int BackupStoreFile::MaxBlockSizeForChunkSize(int ChunkSize)
937{
938        // Calculate... the maximum size of output by first the largest it could be after compression,
939        // which is encrypted, and has a 1 bytes header and the IV added, plus 1 byte for luck
940        // And then on top, add 128 bytes just to make sure. (Belts and braces approach to fixing
941        // an problem where a rather non-compressable file didn't fit in a block buffer.)
942        return sBlowfishEncrypt.MaxOutSizeForInBufferSize(Compress_MaxSizeForCompressedData(ChunkSize)) + 1 + 1
943                + sBlowfishEncrypt.GetIVLength() + 128;
944}
945
946
947
948// --------------------------------------------------------------------------
949//
950// Function
951//              Name:    BackupStoreFile::EncodeChunk(const void *, int, BackupStoreFile::EncodingBuffer &)
952//              Purpose: Encodes a chunk (encryption, possible compressed beforehand)
953//              Created: 8/12/03
954//
955// --------------------------------------------------------------------------
956int BackupStoreFile::EncodeChunk(const void *Chunk, int ChunkSize, BackupStoreFile::EncodingBuffer &rOutput)
957{
958        ASSERT(spEncrypt != 0);
959
960        // Check there's some space in the output block
961        if(rOutput.mBufferSize < 256)
962        {
963                rOutput.Reallocate(256);
964        }
965       
966        // Check alignment of the block
967        ASSERT((((uint32_t)(long)rOutput.mpBuffer) % BACKUPSTOREFILE_CODING_BLOCKSIZE) == BACKUPSTOREFILE_CODING_OFFSET);
968
969        // Want to compress it?
970        bool compressChunk = (ChunkSize >= BACKUP_FILE_MIN_COMPRESSED_CHUNK_SIZE);
971
972        // Build header
973        uint8_t header = sEncryptCipherType << HEADER_ENCODING_SHIFT;
974        if(compressChunk) header |= HEADER_CHUNK_IS_COMPRESSED;
975
976        // Store header
977        rOutput.mpBuffer[0] = header;
978        int outOffset = 1;
979
980        // Setup cipher, and store the IV
981        int ivLen = 0;
982        const void *iv = spEncrypt->SetRandomIV(ivLen);
983        ::memcpy(rOutput.mpBuffer + outOffset, iv, ivLen);
984        outOffset += ivLen;
985       
986        // Start encryption process
987        spEncrypt->Begin();
988       
989        #define ENCODECHUNK_CHECK_SPACE(ToEncryptSize)                                                                  \
990                {                                                                                                                                                       \
991                        if((rOutput.mBufferSize - outOffset) < ((ToEncryptSize) + 128))                 \
992                        {                                                                                                                                               \
993                                rOutput.Reallocate(rOutput.mBufferSize + (ToEncryptSize) + 128);        \
994                        }                                                                                                                                               \
995                }
996       
997        // Encode the chunk
998        if(compressChunk)
999        {
1000                // buffer to compress into
1001                uint8_t buffer[2048];
1002               
1003                // Set compressor with all the chunk as an input
1004                Compress<true> compress;
1005                compress.Input(Chunk, ChunkSize);
1006                compress.FinishInput();
1007
1008                // Get and encrypt output
1009                while(!compress.OutputHasFinished())
1010                {
1011                        int s = compress.Output(buffer, sizeof(buffer));
1012                        if(s > 0)
1013                        {
1014                                ENCODECHUNK_CHECK_SPACE(s)
1015                                outOffset += spEncrypt->Transform(rOutput.mpBuffer + outOffset, rOutput.mBufferSize - outOffset, buffer, s);                           
1016                        }
1017                        else
1018                        {
1019                                // Should never happen, as we put all the input in in one go.
1020                                // So if this happens, it means there's a logical problem somewhere
1021                                THROW_EXCEPTION(BackupStoreException, Internal)
1022                        }
1023                }
1024                ENCODECHUNK_CHECK_SPACE(16)
1025                outOffset += spEncrypt->Final(rOutput.mpBuffer + outOffset, rOutput.mBufferSize - outOffset);
1026        }
1027        else
1028        {
1029                // Straight encryption
1030                ENCODECHUNK_CHECK_SPACE(ChunkSize)
1031                outOffset += spEncrypt->Transform(rOutput.mpBuffer + outOffset, rOutput.mBufferSize - outOffset, Chunk, ChunkSize);
1032                ENCODECHUNK_CHECK_SPACE(16)
1033                outOffset += spEncrypt->Final(rOutput.mpBuffer + outOffset, rOutput.mBufferSize - outOffset);
1034        }
1035       
1036        ASSERT(outOffset < rOutput.mBufferSize);                // first check should have sorted this -- merely logic check
1037
1038        return outOffset;
1039}
1040
1041// --------------------------------------------------------------------------
1042//
1043// Function
1044//              Name:    BackupStoreFile::DecodeChunk(const void *, int, void *, int)
1045//              Purpose: Decode an encoded chunk -- use OutputBufferSizeForKnownOutputSize() to find
1046//                               the extra output buffer size needed before calling.
1047//                               See notes in EncodeChunk() for notes re alignment of the
1048//                               encoded data.
1049//              Created: 8/12/03
1050//
1051// --------------------------------------------------------------------------
1052int BackupStoreFile::DecodeChunk(const void *Encoded, int EncodedSize, void *Output, int OutputSize)
1053{
1054        // Check alignment of the encoded block
1055        ASSERT((((uint32_t)(long)Encoded) % BACKUPSTOREFILE_CODING_BLOCKSIZE) == BACKUPSTOREFILE_CODING_OFFSET);
1056
1057        // First check
1058        if(EncodedSize < 1)
1059        {
1060                THROW_EXCEPTION(BackupStoreException, BadEncodedChunk)
1061        }
1062
1063        const uint8_t *input = (uint8_t*)Encoded;
1064       
1065        // Get header, make checks, etc
1066        uint8_t header = input[0];
1067        bool chunkCompressed = (header & HEADER_CHUNK_IS_COMPRESSED) == HEADER_CHUNK_IS_COMPRESSED;
1068        uint8_t encodingType = (header >> HEADER_ENCODING_SHIFT);
1069        if(encodingType != HEADER_BLOWFISH_ENCODING && encodingType != HEADER_AES_ENCODING)
1070        {
1071                THROW_EXCEPTION(BackupStoreException, ChunkHasUnknownEncoding)
1072        }
1073       
1074#ifndef HAVE_OLD_SSL
1075        // Choose cipher
1076        CipherContext &cipher((encodingType == HEADER_AES_ENCODING)?sAESDecrypt:sBlowfishDecrypt);
1077#else
1078        // AES not supported with this version of OpenSSL
1079        if(encodingType == HEADER_AES_ENCODING)
1080        {
1081                THROW_EXCEPTION(BackupStoreException, AEScipherNotSupportedByInstalledOpenSSL)
1082        }
1083        CipherContext &cipher(sBlowfishDecrypt);
1084#endif
1085       
1086        // Check enough space for header, an IV and one byte of input
1087        int ivLen = cipher.GetIVLength();
1088        if(EncodedSize < (1 + ivLen + 1))
1089        {
1090                THROW_EXCEPTION(BackupStoreException, BadEncodedChunk)
1091        }
1092
1093        // Set IV in decrypt context, and start
1094        cipher.SetIV(input + 1);
1095        cipher.Begin();
1096       
1097        // Setup vars for code
1098        int inOffset = 1 + ivLen;
1099        uint8_t *output = (uint8_t*)Output;
1100        int outOffset = 0;
1101
1102        // Do action
1103        if(chunkCompressed)
1104        {
1105                // Do things in chunks
1106                uint8_t buffer[2048];
1107                int inputBlockLen = cipher.InSizeForOutBufferSize(sizeof(buffer));
1108               
1109                // Decompressor
1110                Compress<false> decompress;
1111               
1112                while(inOffset < EncodedSize)
1113                {
1114                        // Decrypt a block
1115                        int bl = inputBlockLen;
1116                        if(bl > (EncodedSize - inOffset)) bl = EncodedSize - inOffset;  // not too long
1117                        int s = cipher.Transform(buffer, sizeof(buffer), input + inOffset, bl);
1118                        inOffset += bl;
1119                       
1120                        // Decompress the decrypted data
1121                        if(s > 0)
1122                        {
1123                                decompress.Input(buffer, s);
1124                                int os = 0;
1125                                do
1126                                {
1127                                        os = decompress.Output(output + outOffset, OutputSize - outOffset);
1128                                        outOffset += os;
1129                                } while(os > 0);
1130                               
1131                                // Check that there's space left in the output buffer -- there always should be
1132                                if(outOffset >= OutputSize)
1133                                {
1134                                        THROW_EXCEPTION(BackupStoreException, NotEnoughSpaceToDecodeChunk)
1135                                }
1136                        }
1137                }
1138               
1139                // Get any compressed data remaining in the cipher context and compression
1140                int s = cipher.Final(buffer, sizeof(buffer));
1141                decompress.Input(buffer, s);
1142                decompress.FinishInput();
1143                while(!decompress.OutputHasFinished())
1144                {
1145                        int os = decompress.Output(output + outOffset, OutputSize - outOffset);
1146                        outOffset += os;
1147
1148                        // Check that there's space left in the output buffer -- there always should be
1149                        if(outOffset >= OutputSize)
1150                        {
1151                                THROW_EXCEPTION(BackupStoreException, NotEnoughSpaceToDecodeChunk)
1152                        }
1153                }
1154        }
1155        else
1156        {
1157                // Easy decryption
1158                outOffset += cipher.Transform(output + outOffset, OutputSize - outOffset, input + inOffset, EncodedSize - inOffset);
1159                outOffset += cipher.Final(output + outOffset, OutputSize - outOffset);
1160        }
1161       
1162        return outOffset;
1163}
1164
1165
1166
1167// --------------------------------------------------------------------------
1168//
1169// Function
1170//              Name:    BackupStoreFile::ReorderFileToStreamOrder(IOStream *, bool)
1171//              Purpose: Returns a stream which gives a Stream order version of the encoded file.
1172//                               If TakeOwnership == true, then the input stream will be deleted when the
1173//                               returned stream is deleted.
1174//                               The input stream must be seekable.
1175//              Created: 10/12/03
1176//
1177// --------------------------------------------------------------------------
1178std::auto_ptr<IOStream> BackupStoreFile::ReorderFileToStreamOrder(IOStream *pStream, bool TakeOwnership)
1179{
1180        ASSERT(pStream != 0);
1181
1182        // Get the size of the file
1183        int64_t fileSize = pStream->BytesLeftToRead();
1184        if(fileSize == IOStream::SizeOfStreamUnknown)
1185        {
1186                THROW_EXCEPTION(BackupStoreException, StreamDoesntHaveRequiredFeatures)
1187        }
1188
1189        // Read the header
1190        int bytesRead = 0;
1191        file_StreamFormat hdr;
1192        bool readBlock = pStream->ReadFullBuffer(&hdr, sizeof(hdr), &bytesRead);
1193
1194        // Seek backwards to put the file pointer back where it was before we started this
1195        pStream->Seek(0 - bytesRead, IOStream::SeekType_Relative);
1196
1197        // Check we got a block
1198        if(!readBlock)
1199        {
1200                // Couldn't read header -- assume file bad
1201                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
1202        }
1203
1204        // Check magic number
1205        if(ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1
1206#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
1207                && ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V0
1208#endif
1209                )
1210        {
1211                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
1212        }
1213       
1214        // Get number of blocks
1215        int64_t numBlocks = box_ntoh64(hdr.mNumBlocks);
1216       
1217        // Calculate where the block index will be, check it's reasonable
1218        int64_t blockIndexSize = ((numBlocks * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader));
1219        int64_t blockIndexLoc = fileSize - blockIndexSize;
1220        if(blockIndexLoc < 0)
1221        {
1222                // Doesn't look good!
1223                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
1224        }
1225       
1226        // Build a reordered stream
1227        std::auto_ptr<IOStream> reordered(new ReadGatherStream(TakeOwnership));
1228       
1229        // Set it up...
1230        ReadGatherStream &rreordered(*((ReadGatherStream*)reordered.get()));
1231        int component = rreordered.AddComponent(pStream);
1232        // Send out the block index
1233        rreordered.AddBlock(component, blockIndexSize, true, blockIndexLoc);
1234        // And then the rest of the file
1235        rreordered.AddBlock(component, blockIndexLoc, true, 0);
1236               
1237        return reordered;
1238}
1239
1240
1241
1242// --------------------------------------------------------------------------
1243//
1244// Function
1245//              Name:    BackupStoreFile::ResetStats()
1246//              Purpose: Reset the gathered statistics
1247//              Created: 20/1/04
1248//
1249// --------------------------------------------------------------------------
1250void BackupStoreFile::ResetStats()
1251{
1252        msStats.mBytesInEncodedFiles = 0;
1253        msStats.mBytesAlreadyOnServer = 0;
1254        msStats.mTotalFileStreamSize = 0;
1255}
1256
1257
1258
1259// --------------------------------------------------------------------------
1260//
1261// Function
1262//              Name:    BackupStoreFile::CompareFileContentsAgainstBlockIndex(const char *, IOStream &)
1263//              Purpose: Compares the contents of a file against the checksums contained in the
1264//                               block index. Returns true if the checksums match, meaning the file is
1265//                               extremely likely to match the original. Will always consume the entire index.
1266//              Created: 21/1/04
1267//
1268// --------------------------------------------------------------------------
1269bool BackupStoreFile::CompareFileContentsAgainstBlockIndex(const char *Filename, IOStream &rBlockIndex, int Timeout)
1270{
1271        // is it a symlink?
1272        bool sourceIsSymlink = false;
1273        {
1274                EMU_STRUCT_STAT st;
1275                if(EMU_LSTAT(Filename, &st) == -1)
1276                {
1277                        THROW_EXCEPTION(CommonException, OSFileError)
1278                }
1279                if((st.st_mode & S_IFMT) == S_IFLNK)
1280                {
1281                        sourceIsSymlink = true;
1282                }
1283        }
1284
1285        // Open file, if it's not a symlink
1286        std::auto_ptr<FileStream> in;
1287        if(!sourceIsSymlink)
1288        {
1289                in.reset(new FileStream(Filename));
1290        }
1291       
1292        // Read header
1293        file_BlockIndexHeader hdr;
1294        if(!rBlockIndex.ReadFullBuffer(&hdr, sizeof(hdr), 0 /* not interested in bytes read if this fails */, Timeout))
1295        {
1296                // Couldn't read header
1297                THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream)
1298        }
1299
1300        // Check magic
1301        if(hdr.mMagicValue != (int32_t)htonl(OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1)
1302#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
1303                && hdr.mMagicValue != (int32_t)htonl(OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0)
1304#endif
1305                )
1306        {
1307                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
1308        }
1309
1310#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
1311        bool isOldVersion = hdr.mMagicValue == (int32_t)htonl(OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0);
1312#endif
1313
1314        // Get basic information
1315        int64_t numBlocks = box_ntoh64(hdr.mNumBlocks);
1316        uint64_t entryIVBase = box_ntoh64(hdr.mEntryIVBase);
1317       
1318        //TODO: Verify that these sizes look reasonable
1319       
1320        // setup
1321        void *data = 0;
1322        int32_t dataSize = -1;
1323        bool matches = true;
1324        int64_t totalSizeInBlockIndex = 0;
1325       
1326        try
1327        {       
1328                for(int64_t b = 0; b < numBlocks; ++b)
1329                {
1330                        // Read an entry from the stream
1331                        file_BlockIndexEntry entry;
1332                        if(!rBlockIndex.ReadFullBuffer(&entry, sizeof(entry), 0 /* not interested in bytes read if this fails */, Timeout))
1333                        {
1334                                // Couldn't read entry
1335                                THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream)
1336                        }       
1337               
1338                        // Calculate IV for this entry
1339                        uint64_t iv = entryIVBase;
1340                        iv += b;
1341                        iv = box_hton64(iv);
1342#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
1343                        if(isOldVersion)
1344                        {
1345                                // Reverse the IV for compatibility
1346                                iv = box_swap64(iv);
1347                        }
1348#endif
1349                        sBlowfishDecryptBlockEntry.SetIV(&iv);                 
1350                       
1351                        // Decrypt the encrypted section
1352                        file_BlockIndexEntryEnc entryEnc;
1353                        int sectionSize = sBlowfishDecryptBlockEntry.TransformBlock(&entryEnc, sizeof(entryEnc),
1354                                        entry.mEnEnc, sizeof(entry.mEnEnc));
1355                        if(sectionSize != sizeof(entryEnc))
1356                        {
1357                                THROW_EXCEPTION(BackupStoreException, BlockEntryEncodingDidntGiveExpectedLength)
1358                        }
1359
1360                        // Size of block
1361                        int32_t blockClearSize = ntohl(entryEnc.mSize);
1362                        if(blockClearSize < 0 || blockClearSize > (BACKUP_FILE_MAX_BLOCK_SIZE + 1024))
1363                        {
1364                                THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
1365                        }
1366                        totalSizeInBlockIndex += blockClearSize;
1367
1368                        // Make sure there's enough memory allocated to load the block in
1369                        if(dataSize < blockClearSize)
1370                        {
1371                                // Too small, free the block if it's already allocated
1372                                if(data != 0)
1373                                {
1374                                        ::free(data);
1375                                        data = 0;
1376                                }
1377                                // Allocate a block
1378                                data = ::malloc(blockClearSize + 128);
1379                                if(data == 0)
1380                                {
1381                                        throw std::bad_alloc();
1382                                }
1383                                dataSize = blockClearSize + 128;
1384                        }
1385                       
1386                        // Load in the block from the file, if it's not a symlink
1387                        if(!sourceIsSymlink)
1388                        {
1389                                if(in->Read(data, blockClearSize) != blockClearSize)
1390                                {
1391                                        // Not enough data left in the file, can't possibly match
1392                                        matches = false;
1393                                }
1394                                else
1395                                {
1396                                        // Check the checksum
1397                                        MD5Digest md5;
1398                                        md5.Add(data, blockClearSize);
1399                                        md5.Finish();
1400                                        if(!md5.DigestMatches(entryEnc.mStrongChecksum))
1401                                        {
1402                                                // Checksum didn't match
1403                                                matches = false;
1404                                        }
1405                                }
1406                        }
1407                       
1408                        // Keep on going regardless, to make sure the entire block index stream is read
1409                        // -- must always be consistent about what happens with the stream.
1410                }
1411        }
1412        catch(...)
1413        {
1414                // clean up in case of errors
1415                if(data != 0)
1416                {
1417                        ::free(data);
1418                        data = 0;
1419                }
1420                throw;
1421        }
1422       
1423        // free block
1424        if(data != 0)
1425        {
1426                ::free(data);
1427                data = 0;
1428        }
1429       
1430        // Check for data left over if it's not a symlink
1431        if(!sourceIsSymlink)
1432        {
1433                // Anything left to read in the file?
1434                if(in->BytesLeftToRead() != 0)
1435                {
1436                        // File has extra data at the end
1437                        matches = false;
1438                }
1439        }
1440       
1441        // Symlinks must have zero size on server
1442        if(sourceIsSymlink)
1443        {
1444                matches = (totalSizeInBlockIndex == 0);
1445        }
1446       
1447        return matches;
1448}
1449
1450
1451// --------------------------------------------------------------------------
1452//
1453// Function
1454//              Name:    BackupStoreFile::EncodingBuffer::EncodingBuffer()
1455//              Purpose: Constructor
1456//              Created: 25/11/04
1457//
1458// --------------------------------------------------------------------------
1459BackupStoreFile::EncodingBuffer::EncodingBuffer()
1460        : mpBuffer(0),
1461          mBufferSize(0)
1462{
1463}
1464
1465
1466// --------------------------------------------------------------------------
1467//
1468// Function
1469//              Name:    BackupStoreFile::EncodingBuffer::~EncodingBuffer()
1470//              Purpose: Destructor
1471//              Created: 25/11/04
1472//
1473// --------------------------------------------------------------------------
1474BackupStoreFile::EncodingBuffer::~EncodingBuffer()
1475{
1476        if(mpBuffer != 0)
1477        {
1478                BackupStoreFile::CodingChunkFree(mpBuffer);
1479                mpBuffer = 0;
1480        }
1481}
1482
1483
1484// --------------------------------------------------------------------------
1485//
1486// Function
1487//              Name:    BackupStoreFile::EncodingBuffer::Allocate(int)
1488//              Purpose: Do initial allocation of block
1489//              Created: 25/11/04
1490//
1491// --------------------------------------------------------------------------
1492void BackupStoreFile::EncodingBuffer::Allocate(int Size)
1493{
1494        ASSERT(mpBuffer == 0);
1495        uint8_t *buffer = (uint8_t*)BackupStoreFile::CodingChunkAlloc(Size);
1496        if(buffer == 0)
1497        {
1498                throw std::bad_alloc();
1499        }
1500        mpBuffer = buffer;
1501        mBufferSize = Size;
1502}
1503
1504
1505// --------------------------------------------------------------------------
1506//
1507// Function
1508//              Name:    BackupStoreFile::EncodingBuffer::Reallocate(int)
1509//              Purpose: Reallocate the block. Try not to call this, it has to copy
1510//                               the entire contents as the block can't be reallocated straight.
1511//              Created: 25/11/04
1512//
1513// --------------------------------------------------------------------------
1514void BackupStoreFile::EncodingBuffer::Reallocate(int NewSize)
1515{
1516        BOX_TRACE("Reallocating EncodingBuffer from " << mBufferSize <<
1517                " to " << NewSize);
1518        ASSERT(mpBuffer != 0);
1519        uint8_t *buffer = (uint8_t*)BackupStoreFile::CodingChunkAlloc(NewSize);
1520        if(buffer == 0)
1521        {
1522                throw std::bad_alloc();
1523        }
1524        // Copy data
1525        ::memcpy(buffer, mpBuffer, (NewSize > mBufferSize)?mBufferSize:NewSize);
1526       
1527        // Free old
1528        BackupStoreFile::CodingChunkFree(mpBuffer);
1529       
1530        // Store new buffer
1531        mpBuffer = buffer;
1532        mBufferSize = NewSize;
1533}
1534
1535
1536// --------------------------------------------------------------------------
1537//
1538// Function
1539//              Name:    DiffTimer::DiffTimer();
1540//              Purpose: Constructor
1541//              Created: 2005/02/01
1542//
1543// --------------------------------------------------------------------------
1544DiffTimer::DiffTimer()
1545{
1546}
1547
1548
1549// --------------------------------------------------------------------------
1550//
1551// Function
1552//              Name:    DiffTimer::DiffTimer();
1553//              Purpose: Destructor
1554//              Created: 2005/02/01
1555//
1556// --------------------------------------------------------------------------
1557DiffTimer::~DiffTimer()
1558{       
1559}
Note: See TracBrowser for help on using the repository browser.