source: box/trunk/lib/raidfile/RaidFileRead.cpp @ 2889

Revision 2889, 47.9 KB checked in by chris, 14 months ago (diff)

Work around lack of unistd.h on MSVC.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    RaidFileRead.cpp
5//              Purpose: Read Raid like Files
6//              Created: 2003/07/13
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <errno.h>
13#include <fcntl.h>
14#include <stdarg.h>
15
16#ifdef HAVE_UNISTD_H
17#       include <unistd.h>
18#endif
19
20#include <sys/stat.h>
21#include <sys/types.h>
22
23#ifdef HAVE_SYS_UIO_H
24        #include <sys/uio.h>
25#endif
26
27#ifdef HAVE_DIRENT_H
28        #include <dirent.h>
29#endif
30
31#include <cstdio>
32#include <cstdlib>
33#include <cstring>
34#include <map>
35#include <memory>
36
37#include "RaidFileRead.h"
38#include "RaidFileException.h"
39#include "RaidFileController.h"
40#include "RaidFileUtil.h"
41
42#include "MemLeakFindOn.h"
43
44#define READ_NUMBER_DISCS_REQUIRED      3
45#define READV_MAX_BLOCKS                64
46
47// We want to use POSIX fstat() for now, not the emulated one
48#undef fstat
49
50// --------------------------------------------------------------------------
51//
52// Class
53//              Name:    RaidFileRead_NonRaid
54//              Purpose: Internal class for reading RaidFiles which haven't been transformed
55//                               into the RAID like form yet.
56//              Created: 2003/07/13
57//
58// --------------------------------------------------------------------------
59class RaidFileRead_NonRaid : public RaidFileRead
60{
61public:
62        RaidFileRead_NonRaid(int SetNumber, const std::string &Filename, int OSFileHandle);
63        virtual ~RaidFileRead_NonRaid();
64private:
65        RaidFileRead_NonRaid(const RaidFileRead_NonRaid &rToCopy);
66
67public:
68        virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite);
69        virtual pos_type GetPosition() const;
70        virtual void Seek(IOStream::pos_type Offset, int SeekType);
71        virtual void Close();
72        virtual pos_type GetFileSize() const;
73        virtual bool StreamDataLeft();
74
75private:
76        int mOSFileHandle;
77        bool mEOF;
78};
79
80// --------------------------------------------------------------------------
81//
82// Function
83//              Name:    RaidFileRead_NonRaid(int, const std::string &, const std::string &)
84//              Purpose: Constructor
85//              Created: 2003/07/13
86//
87// --------------------------------------------------------------------------
88RaidFileRead_NonRaid::RaidFileRead_NonRaid(int SetNumber, const std::string &Filename, int OSFileHandle)
89        : RaidFileRead(SetNumber, Filename),
90          mOSFileHandle(OSFileHandle),
91          mEOF(false)
92{
93}
94
95// --------------------------------------------------------------------------
96//
97// Function
98//              Name:    RaidFileRead_NonRaid::~RaidFileRead_NonRaid()
99//              Purpose: Destructor
100//              Created: 2003/07/13
101//
102// --------------------------------------------------------------------------
103RaidFileRead_NonRaid::~RaidFileRead_NonRaid()
104{
105        if(mOSFileHandle != -1)
106        {
107                Close();
108        }
109}
110
111// --------------------------------------------------------------------------
112//
113// Function
114//              Name:    RaidFileRead_NonRaid::Read(const void *, int)
115//              Purpose: Reads bytes from the file
116//              Created: 2003/07/13
117//
118// --------------------------------------------------------------------------
119int RaidFileRead_NonRaid::Read(void *pBuffer, int NBytes, int Timeout)
120{
121        // open?
122        if(mOSFileHandle == -1)
123        {
124                THROW_EXCEPTION(RaidFileException, NotOpen)
125        }
126       
127        // Read data
128        int bytesRead = ::read(mOSFileHandle, pBuffer, NBytes);
129        if(bytesRead == -1)
130        {
131                THROW_EXCEPTION(RaidFileException, OSError)
132        }
133        // Check for EOF
134        if(bytesRead == 0)
135        {
136                mEOF = true;
137        }
138
139        return bytesRead;
140}
141
142// --------------------------------------------------------------------------
143//
144// Function
145//              Name:    RaidFileRead_NonRaid::GetPosition()
146//              Purpose: Returns current position
147//              Created: 2003/07/13
148//
149// --------------------------------------------------------------------------
150RaidFileRead::pos_type RaidFileRead_NonRaid::GetPosition() const
151{
152        // open?
153        if(mOSFileHandle == -1)
154        {
155                THROW_EXCEPTION(RaidFileException, NotOpen)
156        }
157       
158        // Use lseek to find the current file position
159        off_t p = ::lseek(mOSFileHandle, 0, SEEK_CUR);
160        if(p == -1)
161        {
162                THROW_EXCEPTION(RaidFileException, OSError)
163        }
164
165        return p;
166}
167
168// --------------------------------------------------------------------------
169//
170// Function
171//              Name:    RaidFileRead_NonRaid::Seek(pos_type, int)
172//              Purpose: Seek within the file
173//              Created: 2003/07/13
174//
175// --------------------------------------------------------------------------
176void RaidFileRead_NonRaid::Seek(IOStream::pos_type Offset, int SeekType)
177{
178        // open?
179        if(mOSFileHandle == -1)
180        {
181                THROW_EXCEPTION(RaidFileException, NotOpen)
182        }
183       
184        // Seek...
185        if(::lseek(mOSFileHandle, Offset, ConvertSeekTypeToOSWhence(SeekType)) == -1)
186        {
187                THROW_EXCEPTION(RaidFileException, OSError)
188        }
189
190        // Not EOF any more
191        mEOF = false;
192}
193
194// --------------------------------------------------------------------------
195//
196// Function
197//              Name:    RaidFileRead_NonRaid::Close()
198//              Purpose: Close the file (automatically done by destructor)
199//              Created: 2003/07/13
200//
201// --------------------------------------------------------------------------
202void RaidFileRead_NonRaid::Close()
203{
204        // open?
205        if(mOSFileHandle == -1)
206        {
207                THROW_EXCEPTION(RaidFileException, NotOpen)
208        }
209       
210        // Close file...
211        if(::close(mOSFileHandle) != 0)
212        {
213                THROW_EXCEPTION(RaidFileException, OSError)
214        }
215        mOSFileHandle = -1;
216        mEOF = true;
217}
218
219// --------------------------------------------------------------------------
220//
221// Function
222//              Name:    RaidFileRead_NonRaid::GetFileSize()
223//              Purpose: Returns file size.
224//              Created: 2003/07/14
225//
226// --------------------------------------------------------------------------
227RaidFileRead::pos_type RaidFileRead_NonRaid::GetFileSize() const
228{
229        // open?
230        if(mOSFileHandle == -1)
231        {
232                THROW_EXCEPTION(RaidFileException, NotOpen)
233        }
234       
235        // stat the file
236        struct stat st;
237        if(::fstat(mOSFileHandle, &st) != 0)
238        {
239                THROW_EXCEPTION(RaidFileException, OSError)
240        }
241
242        return st.st_size;
243}
244
245// --------------------------------------------------------------------------
246//
247// Function
248//              Name:    RaidFileRead_NonRaid::StreamDataLeft()
249//              Purpose: Any data left?
250//              Created: 2003/08/21
251//
252// --------------------------------------------------------------------------
253bool RaidFileRead_NonRaid::StreamDataLeft()
254{
255        return !mEOF;
256}
257
258// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
259
260
261// --------------------------------------------------------------------------
262//
263// Class
264//              Name:    RaidFileRead_Raid
265//              Purpose: Internal class for reading RaidFiles have been transformed.
266//              Created: 2003/07/13
267//
268// --------------------------------------------------------------------------
269class RaidFileRead_Raid : public RaidFileRead
270{
271public:
272        friend class RaidFileRead;
273        RaidFileRead_Raid(int SetNumber, const std::string &Filename, int Stripe1Handle,
274                int Stripe2Handle, int ParityHandle, pos_type FileSize, unsigned int BlockSize,
275                bool LastBlockHasSize);
276        virtual ~RaidFileRead_Raid();
277private:
278        RaidFileRead_Raid(const RaidFileRead_Raid &rToCopy);
279
280public:
281        virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite);
282        virtual pos_type GetPosition() const;
283        virtual void Seek(IOStream::pos_type Offset, int SeekType);
284        virtual void Close();
285        virtual pos_type GetFileSize() const;
286        virtual bool StreamDataLeft();
287
288private:
289        int ReadRecovered(void *pBuffer, int NBytes);
290        void AttemptToRecoverFromIOError(bool Stripe1);
291        void SetPosition(pos_type FilePosition);
292        static void MoveDamagedFileAlertDaemon(int SetNumber, const std::string &Filename, bool Stripe1);
293
294private:
295        int mStripe1Handle;
296        int mStripe2Handle;
297        int mParityHandle;
298        pos_type mFileSize;
299        unsigned int mBlockSize;
300        pos_type mCurrentPosition;
301        char *mRecoveryBuffer;
302        pos_type mRecoveryBufferStart;
303        bool mLastBlockHasSize;
304        bool mEOF;
305};
306
307// --------------------------------------------------------------------------
308//
309// Function
310//              Name:    RaidFileRead_Raid(int, const std::string &, const std::string &)
311//              Purpose: Constructor
312//              Created: 2003/07/13
313//
314// --------------------------------------------------------------------------
315RaidFileRead_Raid::RaidFileRead_Raid(int SetNumber, const std::string &Filename, int Stripe1Handle, int Stripe2Handle, int ParityHandle, pos_type FileSize, unsigned int BlockSize, bool LastBlockHasSize)
316        : RaidFileRead(SetNumber, Filename),
317          mStripe1Handle(Stripe1Handle),
318          mStripe2Handle(Stripe2Handle),
319          mParityHandle(ParityHandle),
320          mFileSize(FileSize),
321          mBlockSize(BlockSize),
322          mCurrentPosition(0),
323          mRecoveryBuffer(0),
324          mRecoveryBufferStart(-1),
325          mLastBlockHasSize(LastBlockHasSize),
326          mEOF(false)
327{
328        // Make sure size of the IOStream::pos_type matches the pos_type used
329        ASSERT(sizeof(pos_type) >= sizeof(off_t));
330       
331        // Sanity check handles
332        if(mStripe1Handle != -1 && mStripe2Handle != -1)
333        {
334                // Everything is lovely, got two perfect files
335        }
336        else
337        {
338                // Check we have at least one stripe and a parity file
339                if((mStripe1Handle == -1 && mStripe2Handle == -1) || mParityHandle == -1)
340                {
341                        // Should never have got this far
342                        THROW_EXCEPTION(RaidFileException, Internal)
343                }
344        }
345}
346
347// --------------------------------------------------------------------------
348//
349// Function
350//              Name:    RaidFileRead_Raid::~RaidFileRead_Raid()
351//              Purpose: Destructor
352//              Created: 2003/07/13
353//
354// --------------------------------------------------------------------------
355RaidFileRead_Raid::~RaidFileRead_Raid()
356{
357        Close();
358        if(mRecoveryBuffer != 0)
359        {
360                ::free(mRecoveryBuffer);
361        }
362}
363
364// --------------------------------------------------------------------------
365//
366// Function
367//              Name:    RaidFileRead_Raid::Read(const void *, int)
368//              Purpose: Reads bytes from the file
369//              Created: 2003/07/13
370//
371// --------------------------------------------------------------------------
372int RaidFileRead_Raid::Read(void *pBuffer, int NBytes, int Timeout)
373{
374        // How many more bytes could we read?
375        unsigned int maxRead = mFileSize - mCurrentPosition;
376        if((unsigned int)NBytes > maxRead)
377        {
378                NBytes = maxRead;
379        }
380       
381        // Return immediately if there's nothing to read, and set EOF
382        if(NBytes == 0)
383        {
384                mEOF = true;
385                return 0;
386        }
387       
388        // Can we use the normal file reading routine?
389        if(mStripe1Handle == -1 || mStripe2Handle == -1)
390        {
391                // File is damaged, try a the recovery read function
392                return ReadRecovered(pBuffer, NBytes);
393        }
394       
395        // Vectors for reading stuff from the files
396        struct iovec stripe1Reads[READV_MAX_BLOCKS];
397        struct iovec stripe2Reads[READV_MAX_BLOCKS];
398        struct iovec *stripeReads[2] = {stripe1Reads, stripe2Reads};
399        unsigned int stripeReadsDataSize[2] = {0, 0};
400        unsigned int stripeReadsSize[2] = {0, 0};
401        int stripeHandles[2] = {mStripe1Handle, mStripe2Handle};
402       
403        // Which block are we doing?
404        unsigned int currentBlock = mCurrentPosition / mBlockSize;
405        unsigned int bytesLeftInCurrentBlock = mBlockSize - (mCurrentPosition % mBlockSize);
406        ASSERT(bytesLeftInCurrentBlock > 0)
407        unsigned int leftToRead = NBytes;
408        char *bufferPtr = (char*)pBuffer;
409       
410        // Now... add some whole block entries in...
411        try
412        {
413                while(leftToRead > 0)
414                {
415                        int whichStripe = (currentBlock & 1);
416                        size_t rlen = mBlockSize;
417                        // Adjust if it's the first block
418                        if(bytesLeftInCurrentBlock != 0)
419                        {
420                                rlen = bytesLeftInCurrentBlock;
421                                bytesLeftInCurrentBlock = 0;
422                        }
423                        // Adjust if we're out of bytes
424                        if(rlen > leftToRead)
425                        {
426                                rlen = leftToRead;
427                        }
428                        stripeReads[whichStripe][stripeReadsSize[whichStripe]].iov_base = bufferPtr;
429                        stripeReads[whichStripe][stripeReadsSize[whichStripe]].iov_len = rlen;
430                        stripeReadsSize[whichStripe]++;
431                        stripeReadsDataSize[whichStripe] += rlen;
432                        leftToRead -= rlen;
433                        bufferPtr += rlen;
434                        currentBlock++;
435
436                        // Read data?
437                        for(int s = 0; s < 2; ++s)
438                        {
439                                if((leftToRead == 0 || stripeReadsSize[s] >= READV_MAX_BLOCKS) && stripeReadsSize[s] > 0)
440                                {
441                                        int r = ::readv(stripeHandles[s], stripeReads[s], stripeReadsSize[s]);
442                                        if(r == -1)
443                                        {
444                                                // Bad news... IO error?
445                                                if(errno == EIO)
446                                                {
447                                                        // Attempt to recover from this failure
448                                                        AttemptToRecoverFromIOError((s == 0) /* is stripe 1 */);
449                                                        // Retry
450                                                        return Read(pBuffer, NBytes, Timeout);
451                                                }
452                                                else
453                                                {
454                                                        // Can't do anything, throw
455                                                        THROW_EXCEPTION(RaidFileException, OSError)
456                                                }
457                                        }
458                                        else if(r != (int)stripeReadsDataSize[s])
459                                        {
460                                                // Got the file sizes wrong/logic error!
461                                                THROW_EXCEPTION(RaidFileException, Internal)
462                                        }
463                                        stripeReadsSize[s] = 0;
464                                        stripeReadsDataSize[s] = 0;
465                                }
466                        }
467                }
468        }
469        catch(...)
470        {
471                // Get file pointers to right place (to meet exception safe stuff)
472                SetPosition(mCurrentPosition);
473               
474                throw;
475        }
476       
477        // adjust current position
478        mCurrentPosition += NBytes;
479
480        return NBytes;
481}
482
483
484// --------------------------------------------------------------------------
485//
486// Function
487//              Name:    RaidFileRead_Raid::MoveDamagedFileAlertDaemon(bool)
488//              Purpose: Moves a file into the damaged directory, and alerts the Daemon to recover it properly later.
489//              Created: 2003/07/22
490//
491// --------------------------------------------------------------------------
492void RaidFileRead_Raid::MoveDamagedFileAlertDaemon(int SetNumber, const std::string &Filename, bool Stripe1)
493{
494        // Move the dodgy file away
495        // Get the controller and the disc set we're on
496        RaidFileController &rcontroller(RaidFileController::GetController());
497        RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(SetNumber));
498        if(READ_NUMBER_DISCS_REQUIRED != rdiscSet.size())
499        {
500                THROW_EXCEPTION(RaidFileException, WrongNumberOfDiscsInSet)
501        }
502        // Start disc
503        int startDisc = rdiscSet.GetSetNumForWriteFiles(Filename);
504        int errOnDisc = (startDisc + (Stripe1?0:1)) % READ_NUMBER_DISCS_REQUIRED;
505       
506        // Make a munged filename for renaming
507        std::string mungeFn(Filename + RAIDFILE_EXTENSION);
508        std::string awayName;
509        for(std::string::const_iterator i = mungeFn.begin(); i != mungeFn.end(); ++i)
510        {
511                char c = (*i);
512                if(c == DIRECTORY_SEPARATOR_ASCHAR)
513                {
514                        awayName += '_';
515                }
516                else if(c == '_')
517                {
518                        awayName += "__";
519                }
520                else
521                {
522                        awayName += c;
523                }
524        }
525        // Make sure the error files directory exists
526        std::string dirname(rdiscSet[errOnDisc] + DIRECTORY_SEPARATOR ".raidfile-unreadable");
527        int mdr = ::mkdir(dirname.c_str(), 0750);
528        if(mdr != 0 && errno != EEXIST)
529        {
530                THROW_EXCEPTION(RaidFileException, OSError)
531        }
532        // Attempt to rename the file there -- ignore any return code here, as it's dubious anyway
533        std::string errorFile(RaidFileUtil::MakeRaidComponentName(rdiscSet, Filename, errOnDisc));
534        ::rename(errorFile.c_str(), (dirname + DIRECTORY_SEPARATOR_ASCHAR + awayName).c_str());
535
536        // TODO: Inform the recovery daemon
537}
538
539
540// --------------------------------------------------------------------------
541//
542// Function
543//              Name:    RaidFileRead_Raid::AttemptToRecoverFromIOError(bool)
544//              Purpose: Attempt to recover from an IO error, setting up to read from parity instead.
545//                               Will exception if this isn't possible.
546//              Created: 2003/07/14
547//
548// --------------------------------------------------------------------------
549void RaidFileRead_Raid::AttemptToRecoverFromIOError(bool Stripe1)
550{
551        BOX_WARNING("Attempting to recover from I/O error: " << mSetNumber <<
552                " " << mFilename << ", on stripe " << (Stripe1?1:2));
553
554        // Close offending file
555        if(Stripe1)
556        {
557                if(mStripe1Handle != -1)
558                {
559                        ::close(mStripe1Handle);
560                        mStripe1Handle = -1;
561                }
562        }
563        else
564        {
565                if(mStripe2Handle != -1)
566                {
567                        ::close(mStripe2Handle);
568                        mStripe2Handle = -1;
569                }
570        }
571
572        // Check...
573        ASSERT((Stripe1?mStripe2Handle:mStripe1Handle) != -1);
574
575        // Get rid of the damaged file
576        MoveDamagedFileAlertDaemon(mSetNumber, mFilename, Stripe1);
577
578        // Get the controller and the disc set we're on
579        RaidFileController &rcontroller(RaidFileController::GetController());
580        RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(mSetNumber));
581        if(READ_NUMBER_DISCS_REQUIRED != rdiscSet.size())
582        {
583                THROW_EXCEPTION(RaidFileException, WrongNumberOfDiscsInSet)
584        }
585        // Start disc
586        int startDisc = rdiscSet.GetSetNumForWriteFiles(mFilename);
587
588        // Mark as nothing in recovery buffer
589        mRecoveryBufferStart = -1;
590       
591        // Seek to zero on the remaining file -- get to nice state
592        if(::lseek(Stripe1?mStripe2Handle:mStripe1Handle, 0, SEEK_SET) == -1)
593        {
594                THROW_EXCEPTION(RaidFileException, OSError)
595        }
596
597        // Open the parity file
598        std::string parityFilename(RaidFileUtil::MakeRaidComponentName(rdiscSet, mFilename, (2 + startDisc) % READ_NUMBER_DISCS_REQUIRED));
599        mParityHandle = ::open(parityFilename.c_str(), 
600                O_RDONLY | O_BINARY, 0555);
601        if(mParityHandle == -1)
602        {
603                THROW_EXCEPTION(RaidFileException, OSError)
604        }
605       
606        // Work out whether or not there's a size XORed into the last block
607        unsigned int bytesInLastTwoBlocks = mFileSize % (mBlockSize * 2);
608        if(bytesInLastTwoBlocks > mBlockSize && bytesInLastTwoBlocks < ((mBlockSize * 2) - sizeof(FileSizeType)))
609        {
610                // Yes, there's something to XOR in the last block
611                mLastBlockHasSize = true;
612        }
613}
614
615// --------------------------------------------------------------------------
616//
617// Function
618//              Name:    RaidFileRead_Raid::ReadRecovered(const void *, int)
619//              Purpose: Reads data recreating from the parity stripe
620//              Created: 2003/07/14
621//
622// --------------------------------------------------------------------------
623int RaidFileRead_Raid::ReadRecovered(void *pBuffer, int NBytes)
624{
625        // Note: NBytes has been adjusted to definately be a range
626        // inside the given file length.
627
628        // Make sure a buffer is allocated
629        if(mRecoveryBuffer == 0)
630        {
631                mRecoveryBuffer = (char*)::malloc(mBlockSize * 2);
632                if(mRecoveryBuffer == 0)
633                {
634                        throw std::bad_alloc();
635                }
636        }
637       
638        // Which stripe?
639        int stripe = (mStripe1Handle != -1)?mStripe1Handle:mStripe2Handle;
640        if(stripe == -1)
641        {
642                // Not enough file handles around
643                THROW_EXCEPTION(RaidFileException, FileIsDamagedNotRecoverable)
644        }
645       
646        char *outptr = (char*)pBuffer;
647        int bytesToGo = NBytes;
648       
649        pos_type preservedCurrentPosition = mCurrentPosition;
650       
651        try
652        {
653                // Start offset within buffer
654                int offset = (mCurrentPosition - mRecoveryBufferStart);
655                // Let's go!
656                while(bytesToGo > 0)
657                {
658                        int bytesLeftInBuffer = 0;
659                        if(mRecoveryBufferStart != -1)
660                        {
661                                bytesLeftInBuffer = (mRecoveryBufferStart + (mBlockSize*2)) - mCurrentPosition;
662                                ASSERT(bytesLeftInBuffer >= 0);
663                        }
664                       
665                        // How many bytes can be copied out?
666                        int toCopy = bytesLeftInBuffer;
667                        if(toCopy > bytesToGo) toCopy = bytesToGo;
668                        //printf("offset = %d, tocopy = %d, bytestogo = %d, leftinbuffer = %d\n", (int)offset, toCopy, bytesToGo, bytesLeftInBuffer);
669                        if(toCopy > 0)
670                        {
671                                for(int l = 0; l < toCopy; ++l)
672                                {
673                                        *(outptr++) = mRecoveryBuffer[offset++];
674                                }
675                                bytesToGo -= toCopy;
676                                mCurrentPosition += toCopy;
677                        }
678                       
679                        // Load in the next buffer?
680                        if(bytesToGo > 0)
681                        {
682                                // Calculate the blocks within the file that are needed to be loaded.
683                                pos_type fileBlock = mCurrentPosition / (mBlockSize * 2);
684                                // Is this the last block
685                                bool isLastBlock = (fileBlock == (mFileSize / (mBlockSize * 2)));
686                               
687                                // Need to reposition file pointers?
688                                if(mRecoveryBufferStart == -1)
689                                {
690                                        // Yes!
691                                        // And the offset from which to read it
692                                        pos_type filePos = fileBlock * mBlockSize;
693                                        // Then seek
694                                        if(::lseek(stripe, filePos, SEEK_SET) == -1
695                                                || ::lseek(mParityHandle, filePos, SEEK_SET) == -1)
696                                        {
697                                                THROW_EXCEPTION(RaidFileException, OSError)
698                                        }
699                                }
700                               
701                                // Load a block from each file, getting the ordering the right way round
702                                int r1 = ::read((mStripe1Handle != -1)?stripe:mParityHandle, mRecoveryBuffer, mBlockSize);
703                                int r2 = ::read((mStripe1Handle != -1)?mParityHandle:stripe, mRecoveryBuffer + mBlockSize, mBlockSize);                         
704                                if(r1 == -1 || r2 == -1)
705                                {
706                                        THROW_EXCEPTION(RaidFileException, OSError)
707                                }
708
709                                // error checking and manipulation
710                                if(isLastBlock)
711                                {
712                                        // Allow not full reads, and append zeros if necessary to fill the space.
713                                        int r1zeros = mBlockSize - r1;
714                                        if(r1zeros > 0)
715                                        {
716                                                ::memset(mRecoveryBuffer + r1, 0, r1zeros);
717                                        }
718                                        int r2zeros = mBlockSize - r2;
719                                        if(r2zeros > 0)
720                                        {
721                                                ::memset(mRecoveryBuffer + mBlockSize + r2, 0, r2zeros);
722                                        }
723                                       
724                                        // if it's got the file size in it, XOR it off
725                                        if(mLastBlockHasSize)
726                                        {
727                                                int sizeXorOffset = (mBlockSize - sizeof(FileSizeType)) + ((mStripe1Handle != -1)?mBlockSize:0);
728                                                *((FileSizeType*)(mRecoveryBuffer + sizeXorOffset)) ^= box_ntoh64(mFileSize);
729                                        }
730                                }
731                                else
732                                {
733                                        // Must have got a full block, otherwise things are a bit bad here.
734                                        if(r1 != (int)mBlockSize || r2 != (int)mBlockSize)
735                                        {
736                                                THROW_EXCEPTION(RaidFileException, InvalidRaidFile)
737                                        }
738                                }
739                               
740                                // Go XORing!
741                                unsigned int *b1 = (unsigned int*)mRecoveryBuffer;
742                                unsigned int *b2 = (unsigned int *)(mRecoveryBuffer + mBlockSize);
743                                if((mStripe1Handle == -1))
744                                {
745                                        b1 = b2;
746                                        b2 = (unsigned int*)mRecoveryBuffer;
747                                }
748                                for(int x = ((mBlockSize/sizeof(unsigned int)) - 1); x >= 0; --x)
749                                {
750                                        *b2 = (*b1) ^ (*b2);
751                                        ++b1;
752                                        ++b2;
753                                }
754                               
755                                // New block location
756                                mRecoveryBufferStart = fileBlock * (mBlockSize * 2);
757                               
758                                // New offset withing block
759                                offset = (mCurrentPosition - mRecoveryBufferStart);
760                                ASSERT(offset >= 0);
761                        }
762                }
763        }
764        catch(...)
765        {
766                // Change variables so 1) buffer is invalidated and 2) the file will be seeked properly the next time round
767                mRecoveryBufferStart = -1;
768                mCurrentPosition = preservedCurrentPosition;
769                throw;
770        }
771       
772        return NBytes;
773}
774
775
776// --------------------------------------------------------------------------
777//
778// Function
779//              Name:    RaidFileRead_Raid::GetPosition()
780//              Purpose: Returns current position
781//              Created: 2003/07/13
782//
783// --------------------------------------------------------------------------
784IOStream::pos_type RaidFileRead_Raid::GetPosition() const
785{
786        return mCurrentPosition;
787}
788
789// --------------------------------------------------------------------------
790//
791// Function
792//              Name:    RaidFileRead_Raid::Seek(RaidFileRead::pos_type, bool)
793//              Purpose: Seek within the file
794//              Created: 2003/07/13
795//
796// --------------------------------------------------------------------------
797void RaidFileRead_Raid::Seek(IOStream::pos_type Offset, int SeekType)
798{
799        pos_type newpos = mCurrentPosition;
800        switch(SeekType)
801        {
802        case IOStream::SeekType_Absolute:
803                newpos = Offset;
804                break;
805               
806        case IOStream::SeekType_Relative:
807                newpos += Offset;
808                break;
809               
810        case IOStream::SeekType_End:
811                newpos = mFileSize + Offset;
812                break;
813               
814        default:
815                THROW_EXCEPTION(CommonException, IOStreamBadSeekType)
816        }
817       
818        if(newpos != mCurrentPosition)
819        {
820                SetPosition(newpos);
821        }
822}
823
824// --------------------------------------------------------------------------
825//
826// Function
827//              Name:    RaidFileRead_Raid::SetPosition(pos_type)
828//              Purpose: Move the file pointers
829//              Created: 2003/07/14
830//
831// --------------------------------------------------------------------------
832void RaidFileRead_Raid::SetPosition(pos_type FilePosition)
833{
834        if(FilePosition > mFileSize)
835        {
836                FilePosition = mFileSize;
837        }
838
839        if(mStripe1Handle != -1 && mStripe2Handle != -1)
840        {
841                // right then... which block is it in?
842                pos_type block = FilePosition / mBlockSize;
843                pos_type offset = FilePosition % mBlockSize;
844               
845                // Calculate offsets for each file
846                pos_type basepos = (block / 2) * mBlockSize;
847                pos_type s1p, s2p;
848                if((block & 1) == 0)
849                {
850                        s1p = basepos + offset;
851                        s2p = basepos;
852                }
853                else
854                {
855                        s1p = basepos + mBlockSize;
856                        s2p = basepos + offset;
857                }
858                // Note: lseek isn't in the man pages to return EIO, but assuming that it can return this,
859                // as it calls various OS bits and returns their error codes, and those fns look like they might.
860                if(::lseek(mStripe1Handle, s1p, SEEK_SET) == -1)
861                {
862                        if(errno == EIO)
863                        {
864                                BOX_ERROR("I/O error when seeking in " <<
865                                        mSetNumber << " " << mFilename <<
866                                        " (to " << FilePosition << "), " <<
867                                        "stripe 1");
868                                // Attempt to recover
869                                AttemptToRecoverFromIOError(true /* is stripe 1 */);
870                                ASSERT(mStripe1Handle == -1);
871                                // Retry
872                                SetPosition(FilePosition);
873                                return;
874                        }
875                        else
876                        {
877                                THROW_EXCEPTION(RaidFileException, OSError)
878                        }
879                }
880                if(::lseek(mStripe2Handle, s2p, SEEK_SET) == -1)
881                {
882                        if(errno == EIO)
883                        {
884                                BOX_ERROR("I/O error when seeking in " <<
885                                        mSetNumber << " " << mFilename <<
886                                        " (to " << FilePosition << "), " <<
887                                        "stripe 2");
888                                // Attempt to recover
889                                AttemptToRecoverFromIOError(false /* is stripe 2 */);
890                                ASSERT(mStripe2Handle == -1);
891                                // Retry
892                                SetPosition(FilePosition);
893                                return;
894                        }
895                        else
896                        {
897                                THROW_EXCEPTION(RaidFileException, OSError)
898                        }
899                }
900               
901                // Store position
902                mCurrentPosition = FilePosition;
903        }
904        else
905        {
906                // Simply store, and mark the recovery buffer invalid
907                mCurrentPosition = FilePosition;
908                mRecoveryBufferStart = -1;
909        }
910
911        // not EOF any more
912        mEOF = false;
913}
914
915
916// --------------------------------------------------------------------------
917//
918// Function
919//              Name:    RaidFileRead_Raid::Close()
920//              Purpose: Close the file (automatically done by destructor)
921//              Created: 2003/07/13
922//
923// --------------------------------------------------------------------------
924void RaidFileRead_Raid::Close()
925{
926        if(mStripe1Handle != -1)
927        {
928                ::close(mStripe1Handle);
929                mStripe1Handle = -1;
930        }
931        if(mStripe2Handle != -1)
932        {
933                ::close(mStripe2Handle);
934                mStripe2Handle = -1;
935        }
936        if(mParityHandle != -1)
937        {
938                ::close(mParityHandle);
939                mParityHandle = -1;
940        }
941       
942        mEOF = true;
943}
944
945// --------------------------------------------------------------------------
946//
947// Function
948//              Name:    RaidFileRead_NonRaid::StreamDataLeft()
949//              Purpose: Any data left?
950//              Created: 2003/08/21
951//
952// --------------------------------------------------------------------------
953bool RaidFileRead_Raid::StreamDataLeft()
954{
955        return !mEOF;
956}
957
958
959// --------------------------------------------------------------------------
960//
961// Function
962//              Name:    RaidFileRead_Raid::GetFileSize()
963//              Purpose: Returns file size.
964//              Created: 2003/07/14
965//
966// --------------------------------------------------------------------------
967RaidFileRead::pos_type RaidFileRead_Raid::GetFileSize() const
968{
969        return mFileSize;
970}
971
972
973// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
974
975
976
977// --------------------------------------------------------------------------
978//
979// Function
980//              Name:    RaidFileRead::RaidFileRead(int, const std::string &)
981//              Purpose: Constructor
982//              Created: 2003/07/13
983//
984// --------------------------------------------------------------------------
985RaidFileRead::RaidFileRead(int SetNumber, const std::string &Filename)
986        : mSetNumber(SetNumber),
987          mFilename(Filename)
988{
989}
990
991
992// --------------------------------------------------------------------------
993//
994// Function
995//              Name:    RaidFileRead::~RaidFileRead()
996//              Purpose: Destructor
997//              Created: 2003/07/13
998//
999// --------------------------------------------------------------------------
1000RaidFileRead::~RaidFileRead()
1001{
1002}
1003
1004// --------------------------------------------------------------------------
1005//
1006// Function
1007//              Name:    RaidFileRead::Open(int, const std::string &, int)
1008//              Purpose: Opens a RaidFile for reading.
1009//              Created: 2003/07/13
1010//
1011// --------------------------------------------------------------------------
1012std::auto_ptr<RaidFileRead> RaidFileRead::Open(int SetNumber, const std::string &Filename, int64_t *pRevisionID, int BufferSizeHint)
1013{
1014        // See what's available...
1015        // Get disc set
1016        RaidFileController &rcontroller(RaidFileController::GetController());
1017        RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(SetNumber));
1018        if(READ_NUMBER_DISCS_REQUIRED != rdiscSet.size() && 1 != rdiscSet.size()) // allow non-RAID configurations
1019        {
1020                THROW_EXCEPTION(RaidFileException, WrongNumberOfDiscsInSet)
1021        }
1022
1023        // See if the file exists
1024        int startDisc = 0, existingFiles = 0;
1025        RaidFileUtil::ExistType existance = RaidFileUtil::RaidFileExists(rdiscSet, Filename, &startDisc, &existingFiles, pRevisionID);
1026        if(existance == RaidFileUtil::NoFile)
1027        {
1028                BOX_ERROR("Expected raidfile " << Filename << " does not exist");
1029                THROW_EXCEPTION(RaidFileException, RaidFileDoesntExist)
1030        }
1031        else if(existance == RaidFileUtil::NonRaid)
1032        {
1033                // Simple non-RAID file so far...
1034       
1035                // Get the filename for the write file
1036                std::string writeFilename(RaidFileUtil::MakeWriteFileName(rdiscSet, Filename));
1037
1038                // Attempt to open
1039                int osFileHandle = ::open(writeFilename.c_str(), 
1040                        O_RDONLY | O_BINARY, 0);
1041                if(osFileHandle == -1)
1042                {
1043                        THROW_EXCEPTION(RaidFileException, ErrorOpeningFileForRead)
1044                }
1045               
1046                // Return a read object for this file
1047                try
1048                {
1049                        return std::auto_ptr<RaidFileRead>(new RaidFileRead_NonRaid(SetNumber, Filename, osFileHandle));
1050                }
1051                catch(...)
1052                {
1053                        ::close(osFileHandle);
1054                        throw;
1055                }
1056        }
1057        else if(existance == RaidFileUtil::AsRaid
1058                || ((existingFiles & RaidFileUtil::Stripe1Exists) && (existingFiles & RaidFileUtil::Stripe2Exists)))
1059        {
1060                if(existance != RaidFileUtil::AsRaid)
1061                {
1062                        BOX_ERROR("Opening " << SetNumber << " " <<
1063                                Filename << " in normal mode, but "
1064                                "parity file doesn't exist");
1065                        // TODO: Alert recovery daemon
1066                }
1067       
1068                // Open the two stripe files
1069                std::string stripe1Filename(RaidFileUtil::MakeRaidComponentName(rdiscSet, Filename, (0 + startDisc) % READ_NUMBER_DISCS_REQUIRED));
1070                std::string stripe2Filename(RaidFileUtil::MakeRaidComponentName(rdiscSet, Filename, (1 + startDisc) % READ_NUMBER_DISCS_REQUIRED));
1071                int stripe1 = -1;
1072                int stripe1errno = 0;
1073                int stripe2 = -1;
1074                int stripe2errno = 0;
1075               
1076                try
1077                {
1078                        // Open stripe1
1079                        stripe1 = ::open(stripe1Filename.c_str(), 
1080                                O_RDONLY | O_BINARY, 0555);
1081                        if(stripe1 == -1)
1082                        {
1083                                stripe1errno = errno;
1084                        }
1085                        // Open stripe2
1086                        stripe2 = ::open(stripe2Filename.c_str(), 
1087                                O_RDONLY | O_BINARY, 0555);
1088                        if(stripe2 == -1)
1089                        {
1090                                stripe2errno = errno;
1091                        }
1092
1093                        if(stripe1errno != 0)
1094                        {
1095                                THROW_SYS_FILE_ERRNO("Failed to open RaidFile",
1096                                        stripe1Filename, stripe1errno,
1097                                        RaidFileException, ErrorOpeningFileForRead);
1098                        }
1099
1100                        if(stripe2errno != 0)
1101                        {
1102                                THROW_SYS_FILE_ERRNO("Failed to open RaidFile",
1103                                        stripe2Filename, stripe2errno,
1104                                        RaidFileException, ErrorOpeningFileForRead);
1105                        }
1106                       
1107                        // stat stripe 1 to find ('half' of) length...
1108                        struct stat st;
1109                        if(::fstat(stripe1, &st) != 0)
1110                        {
1111                                stripe1errno = errno;
1112                        }
1113                        pos_type length = st.st_size;
1114                       
1115                        // stat stripe2 to find (other 'half' of) length...
1116                        if(::fstat(stripe2, &st) != 0)
1117                        {
1118                                stripe2errno = errno;
1119                        }
1120                        length += st.st_size;
1121                       
1122                        // Handle errors
1123                        if(stripe1errno != 0)
1124                        {
1125                                THROW_SYS_FILE_ERRNO("Failed to stat RaidFile",
1126                                        stripe1Filename, stripe1errno,
1127                                        RaidFileException, OSError);
1128                        }
1129
1130                        if(stripe2errno != 0)
1131                        {
1132                                THROW_SYS_FILE_ERRNO("Failed to stat RaidFile",
1133                                        stripe2Filename, stripe2errno,
1134                                        RaidFileException, OSError);
1135                        }
1136       
1137                        // Make a nice object to represent this file
1138                        return std::auto_ptr<RaidFileRead>(new RaidFileRead_Raid(SetNumber, Filename, stripe1, stripe2, -1, length, rdiscSet.GetBlockSize(), false /* actually we don't know */));
1139                }
1140                catch(...)
1141                {
1142                        // Close open files
1143                        if(stripe1 != -1)
1144                        {
1145                                ::close(stripe1);
1146                                stripe1 = -1;
1147                        }
1148                        if(stripe2 != -1)
1149                        {
1150                                ::close(stripe2);
1151                                stripe2 = -1;
1152                        }
1153                       
1154                        // Now... maybe we can try again with one less file?
1155                        bool oktotryagain = true;
1156                        if(stripe1errno == EIO)
1157                        {
1158                                BOX_ERROR("I/O error on opening " <<
1159                                        SetNumber << " " << Filename <<
1160                                        " stripe 1, trying recovery mode");
1161                                RaidFileRead_Raid::MoveDamagedFileAlertDaemon(SetNumber, Filename, true /* is stripe 1 */);
1162
1163                                existingFiles = existingFiles & ~RaidFileUtil::Stripe1Exists;
1164                                existance = (existance == RaidFileUtil::AsRaidWithMissingReadable)
1165                                        ?RaidFileUtil::AsRaidWithMissingNotRecoverable
1166                                        :RaidFileUtil::AsRaidWithMissingReadable;
1167                        }
1168                        else if(stripe1errno != 0)
1169                        {
1170                                oktotryagain = false;
1171                        }
1172                       
1173                        if(stripe2errno == EIO)
1174                        {
1175                                BOX_ERROR("I/O error on opening " <<
1176                                        SetNumber << " " << Filename <<
1177                                        " stripe 2, trying recovery mode");
1178                                RaidFileRead_Raid::MoveDamagedFileAlertDaemon(SetNumber, Filename, false /* is stripe 2 */);
1179
1180                                existingFiles = existingFiles & ~RaidFileUtil::Stripe2Exists;
1181                                existance = (existance == RaidFileUtil::AsRaidWithMissingReadable)
1182                                        ?RaidFileUtil::AsRaidWithMissingNotRecoverable
1183                                        :RaidFileUtil::AsRaidWithMissingReadable;
1184                        }
1185                        else if(stripe2errno != 0)
1186                        {
1187                                oktotryagain = false;
1188                        }
1189                       
1190                        if(!oktotryagain)
1191                        {
1192                                throw;
1193                        }
1194                }
1195        }
1196
1197        if(existance == RaidFileUtil::AsRaidWithMissingReadable)
1198        {
1199                BOX_ERROR("Attempting to open RAID file " << SetNumber <<
1200                        " " << Filename << " in recovery mode (stripe " <<
1201                        ((existingFiles & RaidFileUtil::Stripe1Exists)?1:2) <<
1202                        " present)");
1203       
1204                // Generate the filenames of all the lovely files
1205                std::string stripe1Filename(RaidFileUtil::MakeRaidComponentName(rdiscSet, Filename, (0 + startDisc) % READ_NUMBER_DISCS_REQUIRED));
1206                std::string stripe2Filename(RaidFileUtil::MakeRaidComponentName(rdiscSet, Filename, (1 + startDisc) % READ_NUMBER_DISCS_REQUIRED));
1207                std::string parityFilename(RaidFileUtil::MakeRaidComponentName(rdiscSet, Filename, (2 + startDisc) % READ_NUMBER_DISCS_REQUIRED));
1208
1209                int stripe1 = -1;
1210                int stripe2 = -1;
1211                int parity = -1;
1212
1213                try
1214                {
1215                        // Open stripe1?
1216                        if(existingFiles & RaidFileUtil::Stripe1Exists)
1217                        {
1218                                stripe1 = ::open(stripe1Filename.c_str(), 
1219                                        O_RDONLY | O_BINARY, 0555);
1220                                if(stripe1 == -1)
1221                                {
1222                                        THROW_EXCEPTION(RaidFileException, OSError)
1223                                }
1224                        }
1225                        // Open stripe2?
1226                        if(existingFiles & RaidFileUtil::Stripe2Exists)
1227                        {
1228                                stripe2 = ::open(stripe2Filename.c_str(), 
1229                                        O_RDONLY | O_BINARY, 0555);
1230                                if(stripe2 == -1)
1231                                {
1232                                        THROW_EXCEPTION(RaidFileException, OSError)
1233                                }
1234                        }
1235                        // Open parity
1236                        parity = ::open(parityFilename.c_str(), 
1237                                O_RDONLY | O_BINARY, 0555);
1238                        if(parity == -1)
1239                        {
1240                                THROW_EXCEPTION(RaidFileException, OSError)
1241                        }
1242                       
1243                        // Find the length. This is slightly complex.
1244                        unsigned int blockSize = rdiscSet.GetBlockSize();
1245                        pos_type length = 0;
1246                       
1247                        // The easy one... if the parity file is of an integral block size + sizeof(FileSizeType)
1248                        // then it's stored at the end of the parity file
1249                        struct stat st;
1250                        if(::fstat(parity, &st) != 0)
1251                        {
1252                                THROW_EXCEPTION(RaidFileException, OSError)
1253                        }
1254                        pos_type paritySize = st.st_size;
1255                        FileSizeType parityLastData = 0;
1256                        bool parityIntegralPlusOffT = ((paritySize % blockSize) == sizeof(FileSizeType));
1257                        if(paritySize >= static_cast<pos_type>(sizeof(parityLastData)) && (parityIntegralPlusOffT || stripe1 != -1))
1258                        {
1259                                // Seek to near the end
1260                                ASSERT(sizeof(FileSizeType) == 8); // compiler bug (I think) prevents from using 0 - sizeof(FileSizeType)...
1261                                if(::lseek(parity, -8 /*(0 - sizeof(FileSizeType))*/, SEEK_END) == -1)
1262                                {
1263                                        THROW_SYS_FILE_ERROR("Failed to seek "
1264                                                "in parity RaidFile",
1265                                                parityFilename, 
1266                                                RaidFileException, OSError);
1267                                }
1268                                // Read it in
1269                                if(::read(parity, &parityLastData, sizeof(parityLastData)) != sizeof(parityLastData))
1270                                {
1271                                        THROW_SYS_FILE_ERROR("Failed to read "
1272                                                "parity RaidFile",
1273                                                parityFilename, 
1274                                                RaidFileException, OSError);
1275                                }
1276
1277                                // Set back to beginning of file
1278                                if(::lseek(parity, 0, SEEK_SET) == -1)
1279                                {
1280                                        THROW_SYS_FILE_ERROR("Failed to seek "
1281                                                "in parity RaidFile",
1282                                                parityFilename, 
1283                                                RaidFileException, OSError);
1284                                }
1285                        }
1286                       
1287                        bool lastBlockHasSize = false;
1288                        if(parityIntegralPlusOffT)
1289                        {
1290                                // Wonderful! Have the value
1291                                length = box_ntoh64(parityLastData);
1292                        }
1293                        else
1294                        {
1295                                // Have to resort to more devious means.
1296                                if(existingFiles & RaidFileUtil::Stripe1Exists)
1297                                {
1298                                        // Procedure for stripe 1 existence...
1299                                        //      Get size of stripe1.
1300                                        //  If this is not an integral block size, then size can use this
1301                                        //  to work out the size of the file.
1302                                        //  Otherwise, read in the end of the last block, and use a bit of XORing
1303                                        //  to get the size from the FileSizeType value at end of the file.
1304                                        if(::fstat(stripe1, &st) != 0)
1305                                        {
1306                                                THROW_SYS_FILE_ERROR("Failed to "
1307                                                        "stat RaidFile stripe 1",
1308                                                        stripe1Filename,
1309                                                        RaidFileException, OSError);
1310                                        }
1311                                        pos_type stripe1Size = st.st_size;
1312                                        // Is size integral?
1313                                        if((stripe1Size % ((pos_type)blockSize)) != 0)
1314                                        {
1315                                                // No, so know the size.
1316                                                length = stripe1Size + ((stripe1Size / blockSize) * blockSize);
1317                                        }
1318                                        else
1319                                        {
1320                                                // Must read the last bit of data from the block and XOR.
1321                                                FileSizeType stripe1LastData = 0;       // initialise to zero, as we may not read everything from it
1322                                               
1323                                                // Work out how many bytes to read
1324                                                int btr = 0;                    // bytes to read off end
1325                                                unsigned int lbs = stripe1Size % blockSize;
1326                                                if(lbs == 0 && stripe1Size > 0)
1327                                                {
1328                                                        // integral size, need the entire bit
1329                                                        btr = sizeof(FileSizeType);
1330                                                }
1331                                                else if(lbs > (blockSize - sizeof(FileSizeType)))
1332                                                {
1333                                                        btr = lbs - (blockSize - sizeof(FileSizeType));
1334                                                }
1335                                               
1336                                                // Seek to near the end
1337                                                if(btr > 0)
1338                                                {
1339                                                        ASSERT(sizeof(FileSizeType) == 8); // compiler bug (I think) prevents from using 0 - sizeof(FileSizeType)...
1340                                                        ASSERT(btr <= (int)sizeof(FileSizeType));
1341                                                        if(::lseek(stripe1, 0 - btr, SEEK_END) == -1)
1342                                                        {
1343                                                                THROW_SYS_FILE_ERROR("Failed to "
1344                                                                        "seek in RaidFile stripe 1",
1345                                                                        stripe1Filename,
1346                                                                        RaidFileException, OSError);
1347                                                        }
1348                                                        // Read it in
1349                                                        if(::read(stripe1, &stripe1LastData, btr) != btr)
1350                                                        {
1351                                                                THROW_SYS_FILE_ERROR("Failed to "
1352                                                                        "read RaidFile stripe 1",
1353                                                                        stripe1Filename,
1354                                                                        RaidFileException, OSError);
1355                                                        }
1356                                                        // Set back to beginning of file
1357                                                        if(::lseek(stripe1, 0, SEEK_SET) == -1)
1358                                                        {
1359                                                                THROW_SYS_FILE_ERROR("Failed to "
1360                                                                        "seek in RaidFile stripe 1",
1361                                                                        stripe1Filename,
1362                                                                        RaidFileException, OSError);
1363                                                        }
1364                                                }
1365                                                // Lovely!
1366                                                length = stripe1LastData ^ parityLastData;
1367                                                // Convert to host byte order
1368                                                length = box_ntoh64(length);
1369                                                ASSERT(length <= (paritySize + stripe1Size));
1370                                                // Mark is as having this to aid code later
1371                                                lastBlockHasSize = true;
1372                                        }
1373                                }
1374                                else
1375                                {
1376                                        ASSERT(existingFiles & RaidFileUtil::Stripe2Exists);
1377                                }
1378
1379                                if(existingFiles & RaidFileUtil::Stripe2Exists)
1380                                {
1381                                        // Get size of stripe2 file
1382                                        if(::fstat(stripe2, &st) != 0)
1383                                        {
1384                                                THROW_SYS_FILE_ERROR("Failed to "
1385                                                        "stat RaidFile stripe 2",
1386                                                        stripe2Filename,
1387                                                        RaidFileException, OSError);
1388                                        }
1389                                        pos_type stripe2Size = st.st_size;
1390                                       
1391                                        // Is it an integral size?
1392                                        if(stripe2Size % blockSize != 0)
1393                                        {
1394                                                // No. Working out the size is easy.
1395                                                length = stripe2Size + (((stripe2Size / blockSize)+1) * blockSize);
1396                                                // Got last block size in there?
1397                                                if((stripe2Size % blockSize) <= static_cast<pos_type>((blockSize - sizeof(pos_type))))
1398                                                {
1399                                                        // Yes...
1400                                                        lastBlockHasSize = true;
1401                                                }
1402                                        }
1403                                        else
1404                                        {
1405                                                // Yes. So we need to compare with the parity file to get a clue...
1406                                                pos_type stripe2Blocks = stripe2Size / blockSize;
1407                                                pos_type parityBlocks = paritySize / blockSize;
1408                                                if(stripe2Blocks == parityBlocks)
1409                                                {
1410                                                        // Same size, so stripe1 must be the same size
1411                                                        length = (stripe2Blocks * 2) * blockSize;
1412                                                }
1413                                                else
1414                                                {
1415                                                        // Different size, so stripe1 must be one block bigger
1416                                                        ASSERT(stripe2Blocks < parityBlocks);
1417                                                        length = ((stripe2Blocks * 2)+1) * blockSize;
1418                                                }
1419                                               
1420                                                // Then... add in the extra bit of the parity length
1421                                                unsigned int lastBlockSize = paritySize % blockSize;
1422                                                length += lastBlockSize;
1423                                        }
1424                                }
1425                                else
1426                                {
1427                                        ASSERT(existingFiles & RaidFileUtil::Stripe1Exists);
1428                                }
1429                        }
1430
1431                        // Create a lovely object to return
1432                        return std::auto_ptr<RaidFileRead>(new RaidFileRead_Raid(SetNumber, Filename, stripe1, stripe2, parity, length, blockSize, lastBlockHasSize));
1433                }
1434                catch(...)
1435                {
1436                        // Close open files
1437                        if(stripe1 != -1)
1438                        {
1439                                ::close(stripe1);
1440                                stripe1 = -1;
1441                        }
1442                        if(stripe2 != -1)
1443                        {
1444                                ::close(stripe2);
1445                                stripe2 = -1;
1446                        }
1447                        if(parity != -1)
1448                        {
1449                                ::close(parity);
1450                                parity = -1;
1451                        }
1452                        throw;
1453                }
1454        }
1455       
1456        THROW_FILE_ERROR("Failed to recover RaidFile", Filename,
1457                RaidFileException, FileIsDamagedNotRecoverable);
1458       
1459        // Avoid compiler warning -- it'll never get here...
1460        return std::auto_ptr<RaidFileRead>();
1461}
1462
1463
1464
1465
1466// --------------------------------------------------------------------------
1467//
1468// Function
1469//              Name:    RaidFileRead::DirectoryExists(int, const std::string &)
1470//              Purpose: Returns true if the directory exists. Throws exception if it's partially in existence.
1471//              Created: 2003/08/20
1472//
1473// --------------------------------------------------------------------------
1474bool RaidFileRead::DirectoryExists(int SetNumber, const std::string &rDirName)
1475{
1476        // Get disc set
1477        RaidFileController &rcontroller(RaidFileController::GetController());
1478        RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(SetNumber));
1479
1480        return DirectoryExists(rdiscSet, rDirName);
1481}
1482
1483// --------------------------------------------------------------------------
1484//
1485// Function
1486//              Name:    RaidFileRead::DirectoryExists(const RaidFileDiscSet &, const std::string &)
1487//              Purpose: Returns true if the directory exists. Throws exception if it's partially in existence.
1488//              Created: 2003/08/20
1489//
1490// --------------------------------------------------------------------------
1491bool RaidFileRead::DirectoryExists(const RaidFileDiscSet &rSet, const std::string &rDirName)
1492{
1493        // For each directory, test to see if it exists
1494        unsigned int nexist = 0;
1495        for(unsigned int l = 0; l < rSet.size(); ++l)
1496        {
1497                // build name
1498                std::string dn(rSet[l] + DIRECTORY_SEPARATOR + rDirName);
1499               
1500                // check for existence
1501                struct stat st;
1502                if(::stat(dn.c_str(), &st) == 0)
1503                {
1504                        // Directory?
1505                        if(st.st_mode & S_IFDIR)
1506                        {
1507                                // yes
1508                                nexist++;
1509                        }
1510                        else
1511                        {
1512                                // No. It's a file. Bad!
1513                                THROW_EXCEPTION(RaidFileException, UnexpectedFileInDirPlace)
1514                        }
1515                }
1516                else
1517                {
1518                        // Was it a non-exist error?
1519                        if(errno != ENOENT)
1520                        {
1521                                // No. Bad things.
1522                                THROW_EXCEPTION(RaidFileException, OSError)
1523                        }
1524                }
1525        }
1526       
1527        // Were all of them found?
1528        if(nexist == 0)
1529        {
1530                // None.
1531                return false;
1532        }
1533        else if(nexist == rSet.size())
1534        {
1535                // All
1536                return true;
1537        }
1538
1539        // Some exist. We don't like this -- it shows something bad happened before
1540        // TODO: notify recovery daemon
1541        THROW_EXCEPTION(RaidFileException, DirectoryIncomplete)
1542        return false;   // avoid compiler warning
1543}
1544
1545// --------------------------------------------------------------------------
1546//
1547// Function
1548//              Name:    RaidFileRead::FileExists(int, const std::string &, int64_t *)
1549//              Purpose: Does a Raid file exist? Optionally return a revision number, which is
1550//                               unique to this saving of the file. (revision number may change
1551//                               after transformation to RAID -- so only use for cache control,
1552//                               not detecting changes to content).
1553//              Created: 2003/09/02
1554//
1555// --------------------------------------------------------------------------
1556bool RaidFileRead::FileExists(int SetNumber, const std::string &rFilename, int64_t *pRevisionID)
1557{
1558        // Get disc set
1559        RaidFileController &rcontroller(RaidFileController::GetController());
1560        RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(SetNumber));
1561
1562        return RaidFileUtil::RaidFileExists(rdiscSet, rFilename, 0, 0, pRevisionID) != RaidFileUtil::NoFile;
1563}
1564
1565// --------------------------------------------------------------------------
1566//
1567// Function
1568//              Name:    ReadDirectoryContents(int, const std::string &, int, std::vector<std::string> &)
1569//              Purpose: Read directory contents, returning whether or not all entries are likely to be readable or not
1570//              Created: 2003/08/20
1571//
1572// --------------------------------------------------------------------------
1573bool RaidFileRead::ReadDirectoryContents(int SetNumber, const std::string &rDirName, int DirReadType, std::vector<std::string> &rOutput)
1574{
1575        // Remove anything in the vector to begin with.
1576        rOutput.clear();
1577       
1578        // Controller and set
1579        RaidFileController &rcontroller(RaidFileController::GetController());
1580        RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(SetNumber));
1581
1582        // Collect the directory listings
1583        std::map<std::string, unsigned int> counts;
1584       
1585        unsigned int numDiscs = rdiscSet.size();
1586       
1587        for(unsigned int l = 0; l < numDiscs; ++l)
1588        {
1589                // build name
1590                std::string dn(rdiscSet[l] + DIRECTORY_SEPARATOR + rDirName);
1591               
1592                // read the contents...
1593                DIR *dirHandle = 0;
1594                try
1595                {
1596                        dirHandle = ::opendir(dn.c_str());
1597                        if(dirHandle == 0)
1598                        {
1599                                THROW_EXCEPTION(RaidFileException, OSError)
1600                        }
1601                       
1602                        struct dirent *en = 0;
1603                        while((en = ::readdir(dirHandle)) != 0)
1604                        {
1605                                if(en->d_name[0] == '.' && 
1606                                        (en->d_name[1] == '\0' || (en->d_name[1] == '.' && en->d_name[2] == '\0')))
1607                                {
1608                                        // ignore, it's . or ..
1609                                        continue;
1610                                }
1611                               
1612                                // Entry...
1613                                std::string name;
1614                                unsigned int countToAdd = 1;
1615
1616                                // stat the file to find out what type it is
1617#ifdef HAVE_VALID_DIRENT_D_TYPE
1618                                if(DirReadType == DirReadType_FilesOnly && en->d_type == DT_REG)
1619#else
1620                                struct stat st;
1621                                std::string fullName(dn + DIRECTORY_SEPARATOR + en->d_name);
1622                                if(::lstat(fullName.c_str(), &st) != 0)
1623                                {
1624                                        THROW_EXCEPTION(RaidFileException, OSError)
1625                                }
1626                                if(DirReadType == DirReadType_FilesOnly && (st.st_mode & S_IFDIR) == 0)
1627#endif
1628                                {
1629                                        // File. Complex, need to check the extension
1630                                        int dot = -1;
1631                                        int p = 0;
1632                                        while(en->d_name[p] != '\0')
1633                                        {
1634                                                if(en->d_name[p] == '.')
1635                                                {
1636                                                        // store location of dot
1637                                                        dot = p;
1638                                                }
1639                                                ++p;
1640                                        }
1641                                        // p is length of string
1642                                        if(dot != -1 && ((p - dot) == 3 || (p - dot) == 4)
1643                                                && en->d_name[dot+1] == 'r' && en->d_name[dot+2] == 'f'
1644                                                && (en->d_name[dot+3] == 'w' || en->d_name[dot+3] == '\0'))
1645                                        {
1646                                                // so has right extension
1647                                                name.assign(en->d_name, dot);   /* get name up to last . */
1648                                                // Was it a write file (which counts as everything)
1649                                                if(en->d_name[dot+3] == 'w')
1650                                                {
1651                                                        countToAdd = numDiscs;
1652                                                }
1653                                        }
1654                                }
1655#ifdef HAVE_VALID_DIRENT_D_TYPE
1656                                if(DirReadType == DirReadType_DirsOnly && en->d_type == DT_DIR)
1657#else
1658                                if(DirReadType == DirReadType_DirsOnly && (st.st_mode & S_IFDIR))
1659#endif
1660                                {
1661                                        // Directory, and we want directories
1662                                        name = en->d_name;
1663                                }
1664                                // Eligable for entry?
1665                                if(!name.empty())
1666                                {
1667                                        // add to map...
1668                                        std::map<std::string, unsigned int>::iterator i = counts.find(name);
1669                                        if(i != counts.end())
1670                                        {
1671                                                // add to count
1672                                                i->second += countToAdd;
1673                                        }
1674                                        else
1675                                        {
1676                                                // insert into map
1677                                                counts[name] = countToAdd;
1678                                        }
1679                                }
1680                        }
1681                       
1682                        if(::closedir(dirHandle) != 0)
1683                        {
1684                                THROW_EXCEPTION(RaidFileException, OSError)
1685                        }
1686                        dirHandle = 0;
1687                }
1688                catch(...)
1689                {
1690                        if(dirHandle != 0)
1691                        {
1692                                ::closedir(dirHandle);
1693                        }
1694                        throw;
1695                }
1696        }
1697       
1698        // Now go through the map, adding in entries
1699        bool everythingReadable = true;
1700       
1701        for(std::map<std::string, unsigned int>::const_iterator i = counts.begin(); i != counts.end(); ++i)
1702        {
1703                if(i->second < (numDiscs - 1))
1704                {
1705                        // Too few discs to be confident of reading everything
1706                        everythingReadable = false;
1707                }
1708               
1709                // Add name to vector
1710                rOutput.push_back(i->first);
1711        }
1712       
1713        return everythingReadable;
1714}
1715
1716// --------------------------------------------------------------------------
1717//
1718// Function
1719//              Name:    RaidFileRead::Write(const void *, int)
1720//              Purpose: Not support, throws exception
1721//              Created: 2003/08/21
1722//
1723// --------------------------------------------------------------------------
1724void RaidFileRead::Write(const void *pBuffer, int NBytes)
1725{
1726        THROW_EXCEPTION(RaidFileException, UnsupportedReadWriteOrClose)
1727}
1728
1729// --------------------------------------------------------------------------
1730//
1731// Function
1732//              Name:    RaidFileRead::StreamClosed()
1733//              Purpose: Never any data to write
1734//              Created: 2003/08/21
1735//
1736// --------------------------------------------------------------------------
1737bool RaidFileRead::StreamClosed()
1738{
1739        return true;
1740}
1741
1742// --------------------------------------------------------------------------
1743//
1744// Function
1745//              Name:    RaidFileRead::BytesLeftToRead()
1746//              Purpose: Can tell how many bytes there are to go
1747//              Created: 2003/08/26
1748//
1749// --------------------------------------------------------------------------
1750IOStream::pos_type RaidFileRead::BytesLeftToRead()
1751{
1752        return GetFileSize() - GetPosition();
1753}
1754
1755// --------------------------------------------------------------------------
1756//
1757// Function
1758//              Name:    RaidFileRead::GetDiscUsageInBlocks()
1759//              Purpose: Return how many blocks are used.
1760//              Created: 2003/09/03
1761//
1762// --------------------------------------------------------------------------
1763IOStream::pos_type RaidFileRead::GetDiscUsageInBlocks()
1764{
1765        RaidFileController &rcontroller(RaidFileController::GetController());
1766        RaidFileDiscSet rdiscSet(rcontroller.GetDiscSet(mSetNumber));
1767        return RaidFileUtil::DiscUsageInBlocks(GetFileSize(), rdiscSet);
1768}
1769
1770
1771
1772
Note: See TracBrowser for help on using the repository browser.