source: box/trunk/lib/backupstore/BackupClientFileAttributes.cpp @ 3067

Last change on this file since 3067 was 3067, checked in by chris, 3 years ago

Allow constructing a BackupClientFileAttributes? from a struct EMU_STAT.

  • Property svn:eol-style set to native
File size: 35.8 KB
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    BackupClientFileAttributes.cpp
5//              Purpose: Storage of file attributes
6//              Created: 2003/10/07
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#ifdef HAVE_UNISTD_H
13        #include <unistd.h>
14#endif
15
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <errno.h>
19#include <limits.h>
20
21#include <algorithm>
22#include <cstring>
23#include <new>
24#include <vector>
25
26#ifdef HAVE_SYS_XATTR_H
27#include <cerrno>
28#include <sys/xattr.h>
29#endif
30
31#include <cstring>
32
33#include "BackupClientFileAttributes.h"
34#include "CommonException.h"
35#include "FileModificationTime.h"
36#include "BoxTimeToUnix.h"
37#include "BackupStoreException.h"
38#include "CipherContext.h"
39#include "CipherBlowfish.h"
40#include "MD5Digest.h"
41
42#include "MemLeakFindOn.h"
43
44// set packing to one byte
45#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS
46#include "BeginStructPackForWire.h"
47#else
48BEGIN_STRUCTURE_PACKING_FOR_WIRE
49#endif
50
51#define ATTRIBUTETYPE_GENERIC_UNIX      1
52
53#define ATTRIBUTE_ENCODING_BLOWFISH     2
54
55typedef struct 
56{
57        int32_t         AttributeType;
58        u_int32_t       UID;
59        u_int32_t       GID;
60        u_int64_t       ModificationTime;
61        u_int64_t       AttrModificationTime;
62        u_int32_t       UserDefinedFlags;
63        u_int32_t       FileGenerationNumber;
64        u_int16_t       Mode;
65        // Symbolic link filename may follow
66        // Extended attribute (xattr) information may follow, format is:
67        //   u_int32_t     Size of extended attribute block (excluding this word)
68        // For each of NumberOfAttributes (sorted by AttributeName):
69        //   u_int16_t     AttributeNameLength
70        //   char          AttributeName[AttributeNameLength]
71        //   u_int32_t     AttributeValueLength
72        //   unsigned char AttributeValue[AttributeValueLength]
73        // AttributeName is 0 terminated, AttributeValue is not (and may be binary data)
74} attr_StreamFormat;
75
76// This has wire packing so it's compatible across platforms
77// Use wider than necessary sizes, just to be careful.
78typedef struct
79{
80        int32_t uid, gid, mode;
81        #ifdef WIN32
82        int64_t fileCreationTime;
83        #endif
84} attributeHashData;
85
86// Use default packing
87#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS
88#include "EndStructPackForWire.h"
89#else
90END_STRUCTURE_PACKING_FOR_WIRE
91#endif
92
93
94#define MAX_ATTRIBUTE_HASH_SECRET_LENGTH        256
95
96// Hide private static variables from the rest of the world
97// -- don't put them as static class variables to avoid openssl/evp.h being
98// included all over the project.
99namespace
100{
101        CipherContext sBlowfishEncrypt;
102        CipherContext sBlowfishDecrypt;
103        uint8_t sAttributeHashSecret[MAX_ATTRIBUTE_HASH_SECRET_LENGTH];
104        int sAttributeHashSecretLength = 0;
105}
106
107
108
109// --------------------------------------------------------------------------
110//
111// Function
112//              Name:    BackupClientFileAttributes::BackupClientFileAttributes()
113//              Purpose: Default constructor
114//              Created: 2003/10/07
115//
116// --------------------------------------------------------------------------
117BackupClientFileAttributes::BackupClientFileAttributes()
118        : mpClearAttributes(0)
119{
120        ASSERT(sizeof(u_int64_t) == sizeof(box_time_t));
121}
122
123// --------------------------------------------------------------------------
124//
125// Function
126//              Name:    BackupClientFileAttributes::BackupClientFileAttributes()
127//              Purpose: Artifical constructor
128//              Created: 2011/12/06
129//
130// --------------------------------------------------------------------------
131BackupClientFileAttributes::BackupClientFileAttributes(const EMU_STRUCT_STAT &st)
132: mpClearAttributes(0)
133{
134        ASSERT(sizeof(u_int64_t) == sizeof(box_time_t));
135        StreamableMemBlock *pnewAttr = new StreamableMemBlock;
136        FillAttributes(*pnewAttr, (const char *)NULL, st, true);
137
138        // Attributes ready. Encrypt into this block
139        EncryptAttr(*pnewAttr);
140       
141        // Store the new attributes
142        RemoveClear();
143        mpClearAttributes = pnewAttr;
144        pnewAttr = 0;
145}
146
147// --------------------------------------------------------------------------
148//
149// Function
150//              Name:    BackupClientFileAttributes::BackupClientFileAttributes(const BackupClientFileAttributes &)
151//              Purpose: Copy constructor
152//              Created: 2003/10/07
153//
154// --------------------------------------------------------------------------
155BackupClientFileAttributes::BackupClientFileAttributes(const BackupClientFileAttributes &rToCopy)
156        : StreamableMemBlock(rToCopy), // base class does the hard work
157          mpClearAttributes(0)
158{
159}
160BackupClientFileAttributes::BackupClientFileAttributes(const StreamableMemBlock &rToCopy)
161        : StreamableMemBlock(rToCopy), // base class does the hard work
162          mpClearAttributes(0)
163{
164}
165
166// --------------------------------------------------------------------------
167//
168// Function
169//              Name:    BackupClientFileAttributes::~BackupClientFileAttributes()
170//              Purpose: Destructor
171//              Created: 2003/10/07
172//
173// --------------------------------------------------------------------------
174BackupClientFileAttributes::~BackupClientFileAttributes()
175{
176        if(mpClearAttributes)
177        {
178                delete mpClearAttributes;
179                mpClearAttributes = 0;
180        }
181}
182
183// --------------------------------------------------------------------------
184//
185// Function
186//              Name:    BackupClientFileAttributes &operator=(const BackupClientFileAttributes &)
187//              Purpose: Assignment operator
188//              Created: 2003/10/07
189//
190// --------------------------------------------------------------------------
191BackupClientFileAttributes &BackupClientFileAttributes::operator=(const BackupClientFileAttributes &rAttr)
192{
193        StreamableMemBlock::Set(rAttr);
194        RemoveClear();  // make sure no decrypted version held
195        return *this;
196}
197// Assume users play nice
198BackupClientFileAttributes &BackupClientFileAttributes::operator=(const StreamableMemBlock &rAttr)
199{
200        StreamableMemBlock::Set(rAttr);
201        RemoveClear();  // make sure no decrypted version held
202        return *this;
203}
204
205
206// --------------------------------------------------------------------------
207//
208// Function
209//              Name:    BackupClientFileAttributes::operator==(const BackupClientFileAttributes &)
210//              Purpose: Comparison operator
211//              Created: 2003/10/09
212//
213// --------------------------------------------------------------------------
214bool BackupClientFileAttributes::operator==(const BackupClientFileAttributes &rAttr) const
215{
216        EnsureClearAvailable();
217        rAttr.EnsureClearAvailable();
218
219        return mpClearAttributes->operator==(*rAttr.mpClearAttributes);
220}
221// Too dangerous to allow -- put the two names the wrong way round, and it compares encrypted data.
222/*bool BackupClientFileAttributes::operator==(const StreamableMemBlock &rAttr) const
223{
224        StreamableMemBlock *pDecoded = 0;
225
226        try
227        {
228                EnsureClearAvailable();
229                StreamableMemBlock *pDecoded = MakeClear(rAttr);
230
231                // Compare using clear version
232                bool compared = mpClearAttributes->operator==(rAttr);
233               
234                // Delete temporary
235                delete pDecoded;
236       
237                return compared;
238        }
239        catch(...)
240        {
241                delete pDecoded;
242                throw;
243        }
244}*/
245
246
247// --------------------------------------------------------------------------
248//
249// Function
250//              Name:    BackupClientFileAttributes::Compare(const BackupClientFileAttributes &, bool)
251//              Purpose: Compare, optionally ignoring the attribute
252//                       modification time and/or modification time, and some
253//                       data which is irrelevant in practise (eg file
254//                       generation number)
255//              Created: 10/12/03
256//
257// --------------------------------------------------------------------------
258bool BackupClientFileAttributes::Compare(const BackupClientFileAttributes &rAttr,
259        bool IgnoreAttrModTime, bool IgnoreModTime) const
260{
261        EnsureClearAvailable();
262        rAttr.EnsureClearAvailable();
263
264        // Check sizes are the same, as a first check
265        if(mpClearAttributes->GetSize() != rAttr.mpClearAttributes->GetSize())
266        {
267                BOX_TRACE("Attribute Compare: Attributes objects are "
268                        "different sizes, cannot compare them: local " <<
269                        mpClearAttributes->GetSize() << " bytes, remote " <<
270                        rAttr.mpClearAttributes->GetSize() << " bytes");
271                return false;
272        }
273       
274        // Then check the elements of the two things
275        // Bytes are checked in network order, but this doesn't matter as we're only checking for equality.
276        attr_StreamFormat *a1 = (attr_StreamFormat*)mpClearAttributes->GetBuffer();
277        attr_StreamFormat *a2 = (attr_StreamFormat*)rAttr.mpClearAttributes->GetBuffer();
278
279        #define COMPARE(attribute, message) \
280        if (a1->attribute != a2->attribute) \
281        { \
282                BOX_TRACE("Attribute Compare: " << message << " differ: " \
283                        "local "  << ntoh(a1->attribute) << ", " \
284                        "remote " << ntoh(a2->attribute)); \
285                return false; \
286        }
287        COMPARE(AttributeType, "Attribute types");
288        COMPARE(UID, "UIDs");
289        COMPARE(GID, "GIDs");
290        COMPARE(UserDefinedFlags, "User-defined flags");
291        COMPARE(Mode, "Modes");
292
293        if(!IgnoreModTime)
294        {
295                uint64_t t1 = box_ntoh64(a1->ModificationTime);
296                uint64_t t2 = box_ntoh64(a2->ModificationTime);
297                time_t s1 = BoxTimeToSeconds(t1);
298                time_t s2 = BoxTimeToSeconds(t2);
299                if(s1 != s2)
300                {
301                        BOX_TRACE("Attribute Compare: File modification "
302                                "times differ: local " <<
303                                FormatTime(t1, true) << " (" << s1 << "), "
304                                "remote " <<
305                                FormatTime(t2, true) << " (" << s2 << ")");
306                        return false;
307                }
308        }
309       
310        if(!IgnoreAttrModTime)
311        {
312                uint64_t t1 = box_ntoh64(a1->AttrModificationTime);
313                uint64_t t2 = box_ntoh64(a2->AttrModificationTime);
314                time_t s1 = BoxTimeToSeconds(t1);
315                time_t s2 = BoxTimeToSeconds(t2);
316                if(s1 != s2)
317                {
318                        BOX_TRACE("Attribute Compare: Attribute modification "
319                                "times differ: local " <<
320                                FormatTime(t1, true) << " (" << s1 << "), "
321                                "remote " <<
322                                FormatTime(t2, true) << " (" << s2 << ")");
323                        return false;
324                }
325        }
326       
327        // Check symlink string?
328        unsigned int size = mpClearAttributes->GetSize();
329        if(size > sizeof(attr_StreamFormat))
330        {
331                // Symlink strings don't match. This also compares xattrs
332                int datalen = size - sizeof(attr_StreamFormat);
333
334                if(::memcmp(a1 + 1, a2 + 1, datalen) != 0)
335                {
336                        std::string s1((char *)(a1 + 1), datalen);
337                        std::string s2((char *)(a2 + 1), datalen);
338                        BOX_TRACE("Attribute Compare: Symbolic link target "
339                                "or extended attributes differ: "
340                                "local "  << PrintEscapedBinaryData(s1) << ", "
341                                "remote " << PrintEscapedBinaryData(s2));
342                        return false;
343                }
344        }
345       
346        // Passes all test, must be OK
347        return true;
348}
349
350
351
352
353// --------------------------------------------------------------------------
354//
355// Function
356//              Name:    BackupClientFileAttributes::ReadAttributes(
357//                       const char *Filename, bool ZeroModificationTimes,
358//                       box_time_t *pModTime, box_time_t *pAttrModTime,
359//                       int64_t *pFileSize, InodeRefType *pInodeNumber,
360//                       bool *pHasMultipleLinks)
361//              Purpose: Read the attributes of the file, and store them
362//                       ready for streaming. Optionally retrieve the
363//                       modification time and attribute modification time.
364//              Created: 2003/10/07
365//
366// --------------------------------------------------------------------------
367void BackupClientFileAttributes::ReadAttributes(const char *Filename,
368        bool ZeroModificationTimes, box_time_t *pModTime,
369        box_time_t *pAttrModTime, int64_t *pFileSize,
370        InodeRefType *pInodeNumber, bool *pHasMultipleLinks)
371{
372        StreamableMemBlock *pnewAttr = 0;
373        try
374        {
375                EMU_STRUCT_STAT st;
376                if(EMU_LSTAT(Filename, &st) != 0)
377                {
378                        BOX_LOG_SYS_ERROR("Failed to stat file: '" <<
379                                Filename << "'");
380                        THROW_EXCEPTION(CommonException, OSFileError)
381                }
382               
383                // Modification times etc
384                if(pModTime) {*pModTime = FileModificationTime(st);}
385                if(pAttrModTime) {*pAttrModTime = FileAttrModificationTime(st);}
386                if(pFileSize) {*pFileSize = st.st_size;}
387                if(pInodeNumber) {*pInodeNumber = st.st_ino;}
388                if(pHasMultipleLinks) {*pHasMultipleLinks = (st.st_nlink > 1);}
389
390                pnewAttr = new StreamableMemBlock;
391
392                FillAttributes(*pnewAttr, Filename, st, ZeroModificationTimes);
393
394#ifndef WIN32
395                // Is it a link?
396                if((st.st_mode & S_IFMT) == S_IFLNK)
397                {
398                        FillAttributesLink(*pnewAttr, Filename, st);
399                }
400#endif
401
402                FillExtendedAttr(*pnewAttr, Filename);
403
404#ifdef WIN32
405                //this is to catch those problems with invalid time stamps stored...
406                //need to find out the reason why - but also a catch as well.
407
408                attr_StreamFormat *pattr = 
409                        (attr_StreamFormat*)pnewAttr->GetBuffer();
410                ASSERT(pattr != 0);
411               
412                // __time64_t winTime = BoxTimeToSeconds(
413                // pnewAttr->ModificationTime);
414
415                u_int64_t  modTime = box_ntoh64(pattr->ModificationTime);
416                box_time_t modSecs = BoxTimeToSeconds(modTime);
417                __time64_t winTime = modSecs;
418
419                // _MAX__TIME64_T doesn't seem to be defined, but the code below
420                // will throw an assertion failure if we exceed it :-)
421                // Microsoft says dates up to the year 3000 are valid, which
422                // is a bit more than 15 * 2^32. Even that doesn't seem
423                // to be true (still aborts), but it can at least hold 2^32.
424                if (winTime >= 0x100000000LL || _gmtime64(&winTime) == 0)
425                {
426                        BOX_ERROR("Invalid Modification Time caught for "
427                                "file: '" << Filename << "'");
428                        pattr->ModificationTime = 0;
429                }
430
431                modTime = box_ntoh64(pattr->AttrModificationTime);
432                modSecs = BoxTimeToSeconds(modTime);
433                winTime = modSecs;
434
435                if (winTime > 0x100000000LL || _gmtime64(&winTime) == 0)
436                {
437                        BOX_ERROR("Invalid Attribute Modification Time " 
438                                "caught for file: '" << Filename << "'");
439                        pattr->AttrModificationTime = 0;
440                }
441#endif
442
443                // Attributes ready. Encrypt into this block
444                EncryptAttr(*pnewAttr);
445               
446                // Store the new attributes
447                RemoveClear();
448                mpClearAttributes = pnewAttr;
449                pnewAttr = 0;
450        }
451        catch(...)
452        {
453                // clean up
454                delete pnewAttr;
455                pnewAttr = 0;
456                throw;
457        }
458}
459
460// --------------------------------------------------------------------------
461//
462// Function
463//              Name:    BackupClientFileAttributes::FillAttributes()
464//              Purpose: Private function, handles standard attributes for all objects
465//              Created: 2003/10/07
466//
467// --------------------------------------------------------------------------
468void BackupClientFileAttributes::FillAttributes(
469        StreamableMemBlock &outputBlock, const char *Filename,
470        const EMU_STRUCT_STAT &st, bool ZeroModificationTimes
471)
472{
473        outputBlock.ResizeBlock(sizeof(attr_StreamFormat));
474        attr_StreamFormat *pattr = (attr_StreamFormat*)outputBlock.GetBuffer();
475        ASSERT(pattr != 0);
476
477        // Fill in the entries
478        pattr->AttributeType = htonl(ATTRIBUTETYPE_GENERIC_UNIX);
479        pattr->UID = htonl(st.st_uid);
480        pattr->GID = htonl(st.st_gid);
481        if(ZeroModificationTimes)
482        {
483                pattr->ModificationTime = 0;
484                pattr->AttrModificationTime = 0;
485        }
486        else
487        {
488                pattr->ModificationTime = box_hton64(FileModificationTime(st));
489                pattr->AttrModificationTime = box_hton64(FileAttrModificationTime(st));
490        }
491        pattr->Mode = htons(st.st_mode);
492
493#ifndef HAVE_STRUCT_STAT_ST_FLAGS
494        pattr->UserDefinedFlags = 0;
495        pattr->FileGenerationNumber = 0;
496#else
497        pattr->UserDefinedFlags = htonl(st.st_flags);
498        pattr->FileGenerationNumber = htonl(st.st_gen);
499#endif
500}
501#ifndef WIN32
502// --------------------------------------------------------------------------
503//
504// Function
505//              Name:    BackupClientFileAttributes::FillAttributesLink(
506//                       StreamableMemBlock &outputBlock,
507//                       const char *Filename, struct stat &st)
508//              Purpose: Private function, handles the case where a symbolic link is needed
509//              Created: 2003/10/07
510//
511// --------------------------------------------------------------------------
512void BackupClientFileAttributes::FillAttributesLink(StreamableMemBlock &outputBlock, const char *Filename, struct stat &st)
513{
514        // Make sure we're only called for symbolic links
515        ASSERT((st.st_mode & S_IFMT) == S_IFLNK);
516
517        // Get the filename the link is linked to
518        char linkedTo[PATH_MAX+4];
519        int linkedToSize = ::readlink(Filename, linkedTo, PATH_MAX);
520        if(linkedToSize == -1)
521        {
522                BOX_LOG_SYS_ERROR("Failed to readlink '" << Filename << "'");
523                THROW_EXCEPTION(CommonException, OSFileError);
524        }
525
526        int oldSize = outputBlock.GetSize();
527        outputBlock.ResizeBlock(oldSize+linkedToSize+1);
528        char* buffer = static_cast<char*>(outputBlock.GetBuffer());
529
530        // Add the path name for the symbolic link, and add 0 termination
531        std::memcpy(buffer+oldSize, linkedTo, linkedToSize);
532        buffer[oldSize+linkedToSize] = '\0';
533}
534#endif
535
536// --------------------------------------------------------------------------
537//
538// Function
539//              Name:    BackupClientFileAttributes::FillExtendedAttr(const char *, unsigned char**)
540//              Purpose: Private function, read the extended attributes of the file into the block
541//              Created: 2005/06/12
542//
543// --------------------------------------------------------------------------
544void BackupClientFileAttributes::FillExtendedAttr(StreamableMemBlock &outputBlock, const char *Filename)
545{
546#ifdef HAVE_SYS_XATTR_H
547        int listBufferSize = 10000;
548        char* list = new char[listBufferSize];
549
550        try
551        {
552                // This returns an unordered list of attribute names, each 0 terminated,
553                // concatenated together
554                int listSize = ::llistxattr(Filename, list, listBufferSize);
555
556                if(listSize>listBufferSize)
557                {
558                        delete[] list, list = NULL;
559                        list = new char[listSize];
560                        listSize = ::llistxattr(Filename, list, listSize);
561                }
562
563                if(listSize>0)
564                {
565                        // Extract list of attribute names so we can sort them
566                        std::vector<std::string> attrKeys;
567                        for(int i = 0; i<listSize; ++i)
568                        {
569                                std::string attrKey(list+i);
570                                i += attrKey.size();
571                                attrKeys.push_back(attrKey);
572                        }
573                        sort(attrKeys.begin(), attrKeys.end());
574
575                        // Make initial space in block
576                        int xattrSize = outputBlock.GetSize();
577                        int xattrBufferSize = (xattrSize+listSize)>500 ? (xattrSize+listSize)*2 : 1000;
578                        outputBlock.ResizeBlock(xattrBufferSize);
579                        unsigned char* buffer = static_cast<unsigned char*>(outputBlock.GetBuffer());
580
581                        // Leave space for attr block size later
582                        int xattrBlockSizeOffset = xattrSize;
583                        xattrSize += sizeof(u_int32_t);
584
585                        // Loop for each attribute
586                        for(std::vector<std::string>::const_iterator attrKeyI = attrKeys.begin(); attrKeyI!=attrKeys.end(); ++attrKeyI)
587                        {
588                                std::string attrKey(*attrKeyI);
589
590                                if(xattrSize+sizeof(u_int16_t)+attrKey.size()+1+sizeof(u_int32_t)>static_cast<unsigned int>(xattrBufferSize))
591                                {
592                                        xattrBufferSize = (xattrBufferSize+sizeof(u_int16_t)+attrKey.size()+1+sizeof(u_int32_t))*2;
593                                        outputBlock.ResizeBlock(xattrBufferSize);
594                                        buffer = static_cast<unsigned char*>(outputBlock.GetBuffer());
595                                }
596
597                                // Store length and text for attibute name
598                                u_int16_t keyLength = htons(attrKey.size()+1);
599                                std::memcpy(buffer+xattrSize, &keyLength, sizeof(u_int16_t));
600                                xattrSize += sizeof(u_int16_t);
601                                std::memcpy(buffer+xattrSize, attrKey.c_str(), attrKey.size()+1);
602                                xattrSize += attrKey.size()+1;
603
604                                // Leave space for value size
605                                int valueSizeOffset = xattrSize;
606                                xattrSize += sizeof(u_int32_t);
607
608                                // Find size of attribute (must call with buffer and length 0 on some platforms,
609                                // as -1 is returned if the data doesn't fit.)
610                                int valueSize = ::lgetxattr(Filename, attrKey.c_str(), 0, 0);
611                                if(valueSize<0)
612                                {
613                                        BOX_LOG_SYS_ERROR("Failed to get "
614                                                "extended attribute size of "
615                                                "'" << Filename << "': " <<
616                                                attrKey);
617                                        THROW_EXCEPTION(CommonException, OSFileError);
618                                }
619
620                                // Resize block, if needed
621                                if(xattrSize+valueSize>xattrBufferSize)
622                                {
623                                        xattrBufferSize = (xattrBufferSize+valueSize)*2;
624                                        outputBlock.ResizeBlock(xattrBufferSize);
625                                        buffer = static_cast<unsigned char*>(outputBlock.GetBuffer());
626                                }
627
628                                // This gets the attribute value (may be text or binary), no termination
629                                valueSize = ::lgetxattr(Filename, attrKey.c_str(), buffer+xattrSize, xattrBufferSize-xattrSize);
630                                if(valueSize<0)
631                                {
632                                        BOX_LOG_SYS_ERROR("Failed to get "
633                                                "extended attribute of " 
634                                                "'" << Filename << "': " <<
635                                                attrKey);
636                                        THROW_EXCEPTION(CommonException, OSFileError);
637                                }
638                                xattrSize += valueSize;
639
640                                // Fill in value size
641                                u_int32_t valueLength = htonl(valueSize);
642                                std::memcpy(buffer+valueSizeOffset, &valueLength, sizeof(u_int32_t));
643                        }
644
645                        // Fill in attribute block size
646                        u_int32_t xattrBlockLength = htonl(xattrSize-xattrBlockSizeOffset-sizeof(u_int32_t));
647                        std::memcpy(buffer+xattrBlockSizeOffset, &xattrBlockLength, sizeof(u_int32_t));
648
649                        outputBlock.ResizeBlock(xattrSize);
650                }
651                else if(listSize<0)
652                {
653                        if(errno == EOPNOTSUPP || errno == EACCES)
654                        {
655                                // fail silently
656                        }
657                        else if(errno == ERANGE)
658                        {
659                                BOX_ERROR("Failed to list extended "
660                                        "attributes of '" << Filename << "': "
661                                        "buffer too small, not backed up");
662                        }
663                        else
664                        {
665                                BOX_LOG_SYS_ERROR("Failed to list extended "
666                                        "attributes of '" << Filename << "', "
667                                        "not backed up");
668                                THROW_EXCEPTION(CommonException, OSFileError);
669                        }
670                }
671        }
672        catch(...)
673        {
674                delete[] list;
675                throw;
676        }
677        delete[] list;
678#endif
679}
680
681// --------------------------------------------------------------------------
682//
683// Function
684//              Name:    BackupClientFileAttributes::GetModificationTimes()
685//              Purpose: Returns the modification time embedded in the
686//                       attributes.
687//              Created: 2010/02/24
688//
689// --------------------------------------------------------------------------
690void BackupClientFileAttributes::GetModificationTimes(
691        box_time_t *pModificationTime,
692        box_time_t *pAttrModificationTime) const
693{
694        // Got something loaded
695        if(GetSize() <= 0)
696        {
697                THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
698        }
699       
700        // Make sure there are clear attributes to use
701        EnsureClearAvailable();
702        ASSERT(mpClearAttributes != 0);
703
704        // Check if the decrypted attributes are small enough, and the type of attributes stored
705        if(mpClearAttributes->GetSize() < (int)sizeof(int32_t))
706        {
707                THROW_EXCEPTION(BackupStoreException, AttributesNotUnderstood);
708        }
709        int32_t *type = (int32_t*)mpClearAttributes->GetBuffer();
710        ASSERT(type != 0);
711        if(ntohl(*type) != ATTRIBUTETYPE_GENERIC_UNIX)
712        {
713                // Don't know what to do with these
714                THROW_EXCEPTION(BackupStoreException, AttributesNotUnderstood);
715        }
716       
717        // Check there is enough space for an attributes block
718        if(mpClearAttributes->GetSize() < (int)sizeof(attr_StreamFormat))
719        {
720                // Too small
721                THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
722        }
723
724        // Get pointer to structure
725        attr_StreamFormat *pattr = (attr_StreamFormat*)mpClearAttributes->GetBuffer();
726
727        if(pModificationTime)
728        {
729                *pModificationTime = box_ntoh64(pattr->ModificationTime);
730        }
731       
732        if(pAttrModificationTime)
733        {
734                *pAttrModificationTime = box_ntoh64(pattr->AttrModificationTime);
735        }
736}
737
738// --------------------------------------------------------------------------
739//
740// Function
741//              Name:    BackupClientFileAttributes::WriteAttributes(const char *)
742//              Purpose: Apply the stored attributes to the file
743//              Created: 2003/10/07
744//
745// --------------------------------------------------------------------------
746void BackupClientFileAttributes::WriteAttributes(const char *Filename,
747        bool MakeUserWritable) const
748{
749        // Got something loaded
750        if(GetSize() <= 0)
751        {
752                THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
753        }
754       
755        // Make sure there are clear attributes to use
756        EnsureClearAvailable();
757        ASSERT(mpClearAttributes != 0);
758
759        // Check if the decrypted attributes are small enough, and the type of attributes stored
760        if(mpClearAttributes->GetSize() < (int)sizeof(int32_t))
761        {
762                THROW_EXCEPTION(BackupStoreException, AttributesNotUnderstood);
763        }
764        int32_t *type = (int32_t*)mpClearAttributes->GetBuffer();
765        ASSERT(type != 0);
766        if(ntohl(*type) != ATTRIBUTETYPE_GENERIC_UNIX)
767        {
768                // Don't know what to do with these
769                THROW_EXCEPTION(BackupStoreException, AttributesNotUnderstood);
770        }
771       
772        // Check there is enough space for an attributes block
773        if(mpClearAttributes->GetSize() < (int)sizeof(attr_StreamFormat))
774        {
775                // Too small
776                THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
777        }
778
779        // Get pointer to structure
780        attr_StreamFormat *pattr = (attr_StreamFormat*)mpClearAttributes->GetBuffer();
781        int xattrOffset = sizeof(attr_StreamFormat);
782
783        // is it a symlink?
784        int16_t mode = ntohs(pattr->Mode);
785        if((mode & S_IFMT) == S_IFLNK)
786        {
787                // Check things are sensible
788                if(mpClearAttributes->GetSize() < (int)sizeof(attr_StreamFormat) + 1)
789                {
790                        // Too small
791                        THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
792                }
793       
794#ifdef WIN32
795                BOX_WARNING("Cannot create symbolic links on Windows: '" <<
796                        Filename << "'");
797#else
798                // Make a symlink, first deleting anything in the way
799                ::unlink(Filename);
800                if(::symlink((char*)(pattr + 1), Filename) != 0)
801                {
802                        BOX_LOG_SYS_ERROR("Failed to symlink '" << Filename <<
803                                "' to '" << (char*)(pattr + 1) << "'");
804                        THROW_EXCEPTION(CommonException, OSFileError)
805                }
806#endif
807
808                xattrOffset += std::strlen(reinterpret_cast<char*>(pattr+1))+1;
809        }
810       
811        // If working as root, set user IDs
812        if(::geteuid() == 0)
813        {
814                #ifndef HAVE_LCHOWN
815                        // only if not a link, can't set their owner on this platform
816                        if((mode & S_IFMT) != S_IFLNK)
817                        {
818                                // Not a link, use normal chown
819                                if(::chown(Filename, ntohl(pattr->UID), ntohl(pattr->GID)) != 0)
820                                {
821                                        BOX_LOG_SYS_ERROR("Failed to change "
822                                                "owner of file "
823                                                "'" << Filename << "'");
824                                        THROW_EXCEPTION(CommonException, OSFileError)
825                                }
826                        }
827                #else
828                        // use the version which sets things on symlinks
829                        if(::lchown(Filename, ntohl(pattr->UID), ntohl(pattr->GID)) != 0)
830                        {
831                                BOX_LOG_SYS_ERROR("Failed to change owner of "
832                                        "symbolic link '" << Filename << "'");
833                                THROW_EXCEPTION(CommonException, OSFileError)
834                        }
835                #endif
836        }
837
838        if(static_cast<int>(xattrOffset+sizeof(u_int32_t))<=mpClearAttributes->GetSize())
839        {
840                WriteExtendedAttr(Filename, xattrOffset);
841        }
842
843        // Stop now if symlink, because otherwise it'll just be applied to the target
844        if((mode & S_IFMT) == S_IFLNK)
845        {
846                return;
847        }
848
849        // Set modification time?
850        box_time_t modtime = box_ntoh64(pattr->ModificationTime);
851        if(modtime != 0)
852        {
853                // Work out times as timevals
854                struct timeval times[2];
855
856                #ifdef WIN32
857                BoxTimeToTimeval(box_ntoh64(pattr->ModificationTime), 
858                        times[1]);
859                BoxTimeToTimeval(box_ntoh64(pattr->AttrModificationTime), 
860                        times[0]);
861                // Because stat() returns the creation time in the ctime
862                // field under Windows, and this gets saved in the
863                // AttrModificationTime field of the serialised attributes,
864                // we subvert the first parameter of emu_utimes() to allow
865                // it to be reset to the right value on the restored file.
866                #else
867                BoxTimeToTimeval(modtime, times[1]);
868                // Copy access time as well, why not, got to set it to something
869                times[0] = times[1];
870                // Attr modification time will be changed anyway,
871                // nothing that can be done about it
872                #endif
873               
874                // Try to apply
875                if(::utimes(Filename, times) != 0)
876                {
877                        BOX_LOG_SYS_WARNING("Failed to change times of "
878                                "file '" << Filename << "' to ctime=" <<
879                                BOX_FORMAT_TIMESPEC(times[0]) << ", mtime=" << 
880                                BOX_FORMAT_TIMESPEC(times[1]));
881                }
882        }
883
884        if (MakeUserWritable)
885        {
886                mode |= S_IRWXU;
887        }
888
889        // Apply everything else... (allowable mode flags only)
890        // Mode must be done last (think setuid)
891        if(::chmod(Filename, mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID
892                | S_ISGID | S_ISVTX)) != 0)
893        {
894                BOX_LOG_SYS_ERROR("Failed to change permissions of file "
895                        "'" << Filename << "'");
896                THROW_EXCEPTION(CommonException, OSFileError)
897        }
898}
899
900
901// --------------------------------------------------------------------------
902//
903// Function
904//              Name:    BackupClientFileAttributes::IsSymLink()
905//              Purpose: Do these attributes represent a symbolic link?
906//              Created: 2003/10/07
907//
908// --------------------------------------------------------------------------
909bool BackupClientFileAttributes::IsSymLink() const
910{
911        EnsureClearAvailable();
912
913        // Got the right kind of thing?
914        if(mpClearAttributes->GetSize() < (int)sizeof(int32_t))
915        {
916                THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
917        }
918       
919        // Get the type of attributes stored
920        int32_t *type = (int32_t*)mpClearAttributes->GetBuffer();
921        ASSERT(type != 0);
922        if(ntohl(*type) == ATTRIBUTETYPE_GENERIC_UNIX && mpClearAttributes->GetSize() > (int)sizeof(attr_StreamFormat))
923        {
924                // Check link
925                attr_StreamFormat *pattr = (attr_StreamFormat*)mpClearAttributes->GetBuffer();
926                return ((ntohs(pattr->Mode)) & S_IFMT) == S_IFLNK;
927        }
928       
929        return false;
930}
931
932
933// --------------------------------------------------------------------------
934//
935// Function
936//              Name:    BackupClientFileAttributes::RemoveClear()
937//              Purpose: Private. Deletes any clear version of the attributes that may be held
938//              Created: 3/12/03
939//
940// --------------------------------------------------------------------------
941void BackupClientFileAttributes::RemoveClear() const
942{
943        if(mpClearAttributes)
944        {
945                delete mpClearAttributes;
946        }
947        mpClearAttributes = 0;
948}
949
950
951// --------------------------------------------------------------------------
952//
953// Function
954//              Name:    BackupClientFileAttributes::EnsureClearAvailable()
955//              Purpose: Private. Makes sure the clear version is available
956//              Created: 3/12/03
957//
958// --------------------------------------------------------------------------
959void BackupClientFileAttributes::EnsureClearAvailable() const
960{
961        if(mpClearAttributes == 0)
962        {
963                mpClearAttributes = MakeClear(*this);
964        }
965}
966
967// --------------------------------------------------------------------------
968//
969// Function
970//              Name:    BackupClientFileAttributes::WriteExtendedAttr(const char *Filename, int xattrOffset)
971//              Purpose: Private function, apply the stored extended attributes to the file
972//              Created: 2005/06/13
973//
974// --------------------------------------------------------------------------
975void BackupClientFileAttributes::WriteExtendedAttr(const char *Filename, int xattrOffset) const
976{
977#ifdef HAVE_SYS_XATTR_H
978        const char* buffer = static_cast<char*>(mpClearAttributes->GetBuffer());
979
980        u_int32_t xattrBlockLength = 0;
981        std::memcpy(&xattrBlockLength, buffer+xattrOffset, sizeof(u_int32_t));
982        int xattrBlockSize = ntohl(xattrBlockLength);
983        xattrOffset += sizeof(u_int32_t);
984
985        int xattrEnd = xattrOffset+xattrBlockSize;
986        if(xattrEnd>mpClearAttributes->GetSize())
987        {
988                // Too small
989                THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
990        }
991
992        while(xattrOffset<xattrEnd)
993        {
994                u_int16_t keyLength = 0;
995                std::memcpy(&keyLength, buffer+xattrOffset, sizeof(u_int16_t));
996                int keySize = ntohs(keyLength);
997                xattrOffset += sizeof(u_int16_t);
998
999                const char* key = buffer+xattrOffset;
1000                xattrOffset += keySize;
1001
1002                u_int32_t valueLength = 0;
1003                std::memcpy(&valueLength, buffer+xattrOffset, sizeof(u_int32_t));
1004                int valueSize = ntohl(valueLength);
1005                xattrOffset += sizeof(u_int32_t);
1006
1007                // FIXME: Warn on EOPNOTSUPP
1008                if(::lsetxattr(Filename, key, buffer+xattrOffset, valueSize, 0)!=0 && errno!=EOPNOTSUPP)
1009                {
1010                        BOX_LOG_SYS_ERROR("Failed to set extended attributes "
1011                                "on file '" << Filename << "'");
1012                        THROW_EXCEPTION(CommonException, OSFileError);
1013                }
1014
1015                xattrOffset += valueSize;
1016        }
1017
1018        ASSERT(xattrOffset==xattrEnd);
1019#endif
1020}
1021
1022
1023// --------------------------------------------------------------------------
1024//
1025// Function
1026//              Name:    BackupClientFileAttributes::MakeClear(const StreamableMemBlock &)
1027//              Purpose: Static. Decrypts stored attributes.
1028//              Created: 3/12/03
1029//
1030// --------------------------------------------------------------------------
1031StreamableMemBlock *BackupClientFileAttributes::MakeClear(const StreamableMemBlock &rEncrypted)
1032{
1033        // New block
1034        StreamableMemBlock *pdecrypted = 0;
1035
1036        try
1037        {
1038                // Check the block is big enough for IV and header
1039                int ivSize = sBlowfishEncrypt.GetIVLength();
1040                if(rEncrypted.GetSize() <= (ivSize + 1))
1041                {
1042                        THROW_EXCEPTION(BackupStoreException, BadEncryptedAttributes);
1043                }
1044               
1045                // How much space is needed for the output?
1046                int maxDecryptedSize = sBlowfishDecrypt.MaxOutSizeForInBufferSize(rEncrypted.GetSize() - ivSize);
1047               
1048                // Allocate it
1049                pdecrypted = new StreamableMemBlock(maxDecryptedSize);
1050       
1051                // ptr to block
1052                uint8_t *encBlock = (uint8_t*)rEncrypted.GetBuffer();
1053
1054                // Check that the header has right type
1055                if(encBlock[0] != ATTRIBUTE_ENCODING_BLOWFISH)
1056                {
1057                        THROW_EXCEPTION(BackupStoreException, EncryptedAttributesHaveUnknownEncoding);
1058                }
1059
1060                // Set IV
1061                sBlowfishDecrypt.SetIV(encBlock + 1);
1062               
1063                // Decrypt
1064                int decryptedSize = sBlowfishDecrypt.TransformBlock(pdecrypted->GetBuffer(), maxDecryptedSize, encBlock + 1 + ivSize, rEncrypted.GetSize() - (ivSize + 1));
1065
1066                // Resize block to fit
1067                pdecrypted->ResizeBlock(decryptedSize);
1068        }
1069        catch(...)
1070        {
1071                delete pdecrypted;
1072                pdecrypted = 0;
1073        }
1074
1075        return pdecrypted;
1076}
1077
1078
1079// --------------------------------------------------------------------------
1080//
1081// Function
1082//              Name:    BackupClientFileAttributes::SetBlowfishKey(const void *, int)
1083//              Purpose: Static. Sets the key to use for encryption and decryption.
1084//              Created: 3/12/03
1085//
1086// --------------------------------------------------------------------------
1087void BackupClientFileAttributes::SetBlowfishKey(const void *pKey, int KeyLength)
1088{
1089        // IVs set later
1090        sBlowfishEncrypt.Reset();
1091        sBlowfishEncrypt.Init(CipherContext::Encrypt, CipherBlowfish(CipherDescription::Mode_CBC, pKey, KeyLength));
1092        sBlowfishDecrypt.Reset();
1093        sBlowfishDecrypt.Init(CipherContext::Decrypt, CipherBlowfish(CipherDescription::Mode_CBC, pKey, KeyLength));
1094}
1095
1096
1097
1098// --------------------------------------------------------------------------
1099//
1100// Function
1101//              Name:    BackupClientFileAttributes::EncryptAttr(const StreamableMemBlock &)
1102//              Purpose: Private. Encrypt the given attributes into this block.
1103//              Created: 3/12/03
1104//
1105// --------------------------------------------------------------------------
1106void BackupClientFileAttributes::EncryptAttr(const StreamableMemBlock &rToEncrypt)
1107{
1108        // Free any existing block
1109        FreeBlock();
1110       
1111        // Work out the maximum amount of space we need
1112        int maxEncryptedSize = sBlowfishEncrypt.MaxOutSizeForInBufferSize(rToEncrypt.GetSize());
1113        // And the size of the IV
1114        int ivSize = sBlowfishEncrypt.GetIVLength();
1115       
1116        // Allocate this space
1117        AllocateBlock(maxEncryptedSize + ivSize + 1);
1118       
1119        // Store the encoding byte
1120        uint8_t *block = (uint8_t*)GetBuffer();
1121        block[0] = ATTRIBUTE_ENCODING_BLOWFISH;
1122       
1123        // Generate and store an IV for this attribute block
1124        int ivSize2 = 0;
1125        const void *iv = sBlowfishEncrypt.SetRandomIV(ivSize2);
1126        ASSERT(ivSize == ivSize2);
1127       
1128        // Copy into the encrypted block
1129        ::memcpy(block + 1, iv, ivSize);
1130       
1131        // Do the transform
1132        int encrytedSize = sBlowfishEncrypt.TransformBlock(block + 1 + ivSize, maxEncryptedSize, rToEncrypt.GetBuffer(), rToEncrypt.GetSize());
1133
1134        // Resize this block
1135        ResizeBlock(encrytedSize + ivSize + 1);
1136}
1137
1138
1139// --------------------------------------------------------------------------
1140//
1141// Function
1142//              Name:    BackupClientFileAttributes::SetAttributeHashSecret(const void *, int)
1143//              Purpose: Set the secret for the filename attribute hash
1144//              Created: 25/4/04
1145//
1146// --------------------------------------------------------------------------
1147void BackupClientFileAttributes::SetAttributeHashSecret(const void *pSecret, int SecretLength)
1148{
1149        if(SecretLength > (int)sizeof(sAttributeHashSecret))
1150        {
1151                SecretLength = sizeof(sAttributeHashSecret);
1152        }
1153        if(SecretLength < 0)
1154        {
1155                THROW_EXCEPTION(BackupStoreException, Internal)
1156        }
1157       
1158        // Copy
1159        ::memcpy(sAttributeHashSecret, pSecret, SecretLength);
1160        sAttributeHashSecretLength = SecretLength;
1161}
1162
1163
1164// --------------------------------------------------------------------------
1165//
1166// Function
1167//              Name:    BackupClientFileAttributes::GenerateAttributeHash(
1168//                       struct stat &, const std::string &,
1169//                       const std::string &)
1170//              Purpose: Generate a 64 bit hash from the attributes, used to
1171//                       detect changes. Include filename in the hash, so
1172//                       that it changes from one file to another, so don't
1173//                       reveal identical attributes.
1174//              Created: 25/4/04
1175//
1176// --------------------------------------------------------------------------
1177uint64_t BackupClientFileAttributes::GenerateAttributeHash(EMU_STRUCT_STAT &st,
1178        const std::string &filename, const std::string &leafname)
1179{
1180        if(sAttributeHashSecretLength == 0)
1181        {
1182                THROW_EXCEPTION(BackupStoreException, AttributeHashSecretNotSet)
1183        }
1184       
1185        // Assemble stuff we're interested in
1186        attributeHashData hashData;
1187        memset(&hashData, 0, sizeof(hashData));
1188        // Use network byte order and large sizes to be cross platform
1189        hashData.uid = htonl(st.st_uid);
1190        hashData.gid = htonl(st.st_gid);
1191        hashData.mode = htonl(st.st_mode);
1192
1193        #ifdef WIN32
1194        // On Windows, the "file attribute modification time" is the
1195        // file creation time, and we want to back this up, restore
1196        // it and compare it.
1197        //
1198        // On other platforms, it's not very important and can't
1199        // reliably be set to anything other than the current time.
1200        hashData.fileCreationTime = box_hton64(st.st_ctime);
1201        #endif
1202
1203        StreamableMemBlock xattr;
1204        FillExtendedAttr(xattr, filename.c_str());
1205
1206        // Create a MD5 hash of the data, filename, and secret
1207        MD5Digest digest;
1208        digest.Add(&hashData, sizeof(hashData));
1209        digest.Add(xattr.GetBuffer(), xattr.GetSize());
1210        digest.Add(leafname.c_str(), leafname.size());
1211        digest.Add(sAttributeHashSecret, sAttributeHashSecretLength);   
1212        digest.Finish();
1213       
1214        // Return the first 64 bits of the hash
1215        uint64_t result;
1216        memcpy(&result, digest.DigestAsData(), sizeof(result));
1217        return result;
1218}
Note: See TracBrowser for help on using the repository browser.