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

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

Fix type aliasing that's strictly speaking incompatible and may actually be skipped by the compiler
(according to http://blog.worldofcoding.com/2010/02/solving-gcc-44-strict-aliasing-problems.html).

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