diff options
Diffstat (limited to 'lib')
-rwxr-xr-x | lib/backupclient/BackupClientFileAttributes.cpp | 343 | ||||
-rwxr-xr-x | lib/backupclient/BackupClientFileAttributes.h | 7 | ||||
-rwxr-xr-x | lib/common/DebugMemLeakFinder.cpp | 40 | ||||
-rwxr-xr-x | lib/common/StreamableMemBlock.cpp | 1 |
4 files changed, 286 insertions, 105 deletions
diff --git a/lib/backupclient/BackupClientFileAttributes.cpp b/lib/backupclient/BackupClientFileAttributes.cpp index 4aa4edba..24e90697 100755 --- a/lib/backupclient/BackupClientFileAttributes.cpp +++ b/lib/backupclient/BackupClientFileAttributes.cpp @@ -14,6 +14,13 @@ #include <string.h> #include <unistd.h> #include <limits.h> +#include <algorithm> +#include <new> +#include <vector> +#ifdef PLATFORM_HAVE_XATTR +#include <cerrno> +#include <sys/xattr.h> +#endif #include "BackupClientFileAttributes.h" #include "CommonException.h" @@ -48,6 +55,14 @@ typedef struct u_int32_t FileGenerationNumber; u_int16_t Mode; // Symbolic link filename may follow + // Extended attribute (xattr) information may follow, format is: + // u_int32_t Size of extended attribute block (excluding this word) + // For each of NumberOfAttributes (sorted by AttributeName): + // u_int16_t AttributeNameLength + // char AttributeName[AttributeNameLength] + // u_int32_t AttributeValueLength + // unsigned char AttributeValue[AttributeValueLength] + // AttributeName is 0 terminated, AttributeValue is not (and may be binary data) } attr_StreamFormat; // This has wire packing so it's compatible across platforms @@ -248,7 +263,7 @@ bool BackupClientFileAttributes::Compare(const BackupClientFileAttributes &rAttr unsigned int size = mpClearAttributes->GetSize(); if(size > sizeof(attr_StreamFormat)) { - // Symlink strings don't match + // Symlink strings don't match. This also compares xattrs if(::memcmp(a1 + 1, a2 + 1, size - sizeof(attr_StreamFormat)) != 0) { return false; @@ -289,51 +304,19 @@ void BackupClientFileAttributes::ReadAttributes(const char *Filename, bool ZeroM if(pFileSize) {*pFileSize = st.st_size;} if(pInodeNumber) {*pInodeNumber = st.st_ino;} if(pHasMultipleLinks) {*pHasMultipleLinks = (st.st_nlink > 1);} - + + pnewAttr = new StreamableMemBlock; + + FillAttributes(*pnewAttr, Filename, st, ZeroModificationTimes); + // Is it a link? if((st.st_mode & S_IFMT) == S_IFLNK) { - ReadAttributesLink(Filename, &st, ZeroModificationTimes); - return; + FillAttributesLink(*pnewAttr, Filename, st); } - ASSERT((st.st_mode & S_IFMT) != S_IFLNK); - - // Now, can allocate the block - pnewAttr = new StreamableMemBlock(sizeof(attr_StreamFormat)); - - // Fill in the entries - attr_StreamFormat *pattr = (attr_StreamFormat*)pnewAttr->GetBuffer(); - -#define FILL_IN_ATTRIBUTES \ - ::memset(pattr, 0, sizeof(attr_StreamFormat)); \ - ASSERT(pattr != 0); \ - pattr->AttributeType = htonl(ATTRIBUTETYPE_GENERIC_UNIX); \ - pattr->UID = htonl(st.st_uid); \ - pattr->GID = htonl(st.st_gid); \ - if(ZeroModificationTimes) \ - { \ - pattr->ModificationTime = 0; \ - pattr->AttrModificationTime = 0; \ - } \ - else \ - { \ - pattr->ModificationTime = hton64(FileModificationTime(st)); \ - pattr->AttrModificationTime = hton64(FileAttrModificationTime(st)); \ - } \ - pattr->Mode = htons(st.st_mode); -#ifdef PLATFORM_stat_NO_st_flags - #define FILL_IN_ATTRIBUTES_2 \ - pattr->UserDefinedFlags = 0; \ - pattr->FileGenerationNumber = 0; -#else - #define FILL_IN_ATTRIBUTES_2 \ - pattr->UserDefinedFlags = htonl(st.st_flags); \ - pattr->FileGenerationNumber = htonl(st.st_gen); -#endif - - FILL_IN_ATTRIBUTES - FILL_IN_ATTRIBUTES_2 - + + FillExtendedAttr(*pnewAttr, Filename); + // Attributes ready. Encrypt into this block EncryptAttr(*pnewAttr); @@ -355,57 +338,184 @@ void BackupClientFileAttributes::ReadAttributes(const char *Filename, bool ZeroM // // Function // Name: BackupClientFileAttributes::ReadAttributesLink() +// Purpose: Private function, handles standard attributes for all objects +// Created: 2003/10/07 +// +// -------------------------------------------------------------------------- +void BackupClientFileAttributes::FillAttributes(StreamableMemBlock &outputBlock, const char *Filename, struct stat &st, bool ZeroModificationTimes) +{ + outputBlock.ResizeBlock(sizeof(attr_StreamFormat)); + attr_StreamFormat *pattr = (attr_StreamFormat*)outputBlock.GetBuffer(); + ASSERT(pattr != 0); + + // Fill in the entries + pattr->AttributeType = htonl(ATTRIBUTETYPE_GENERIC_UNIX); + pattr->UID = htonl(st.st_uid); + pattr->GID = htonl(st.st_gid); + if(ZeroModificationTimes) + { + pattr->ModificationTime = 0; + pattr->AttrModificationTime = 0; + } + else + { + pattr->ModificationTime = hton64(FileModificationTime(st)); + pattr->AttrModificationTime = hton64(FileAttrModificationTime(st)); + } + pattr->Mode = htons(st.st_mode); + +#ifdef PLATFORM_stat_NO_st_flags + pattr->UserDefinedFlags = 0; + pattr->FileGenerationNumber = 0; +#else + pattr->UserDefinedFlags = htonl(st.st_flags); + pattr->FileGenerationNumber = htonl(st.st_gen); +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientFileAttributes::ReadAttributesLink() // Purpose: Private function, handles the case where a symbolic link is needed // Created: 2003/10/07 // // -------------------------------------------------------------------------- -void BackupClientFileAttributes::ReadAttributesLink(const char *Filename, void *pst, bool ZeroModificationTimes) +void BackupClientFileAttributes::FillAttributesLink(StreamableMemBlock &outputBlock, const char *Filename, struct stat &st) { - StreamableMemBlock *pnewAttr = 0; + // Make sure we're only called for symbolic links + ASSERT((st.st_mode & S_IFMT) == S_IFLNK); + + // Get the filename the link is linked to + char linkedTo[PATH_MAX+4]; + int linkedToSize = ::readlink(Filename, linkedTo, PATH_MAX); + if(linkedToSize == -1) + { + THROW_EXCEPTION(CommonException, OSFileError); + } + + int oldSize = outputBlock.GetSize(); + outputBlock.ResizeBlock(oldSize+linkedToSize+1); + char* buffer = static_cast<char*>(outputBlock.GetBuffer()); + + // Add the path name for the symbolic link, and add 0 termination + std::memcpy(buffer+oldSize, linkedTo, linkedToSize); + buffer[oldSize+linkedToSize] = '\0'; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientFileAttributes::ReadExtendedAttr(const char *, unsigned char**) +// Purpose: Private function, read the extended attributes of the file into the block +// Created: 2005/06/12 +// +// -------------------------------------------------------------------------- +void BackupClientFileAttributes::FillExtendedAttr(StreamableMemBlock &outputBlock, const char *Filename) +{ +#ifdef PLATFORM_HAVE_XATTR + int listBufferSize = 1000; + char* list = new char[listBufferSize]; + try { - // Avoid needing to have struct stat available when including header file - struct stat &st = *((struct stat *)pst); - // Make sure we're only called for symbolic links - ASSERT((st.st_mode & S_IFMT) == S_IFLNK); - - // Get the filename the link is linked to - char linkedTo[PATH_MAX+4]; - int linkedToSize = ::readlink(Filename, linkedTo, PATH_MAX); - if(linkedToSize == -1) + // This returns an unordered list of attribute names, each 0 terminated, + // concatenated together + int listSize = ::llistxattr(Filename, list, listBufferSize); + + if(listSize>listBufferSize) { - THROW_EXCEPTION(CommonException, OSFileError) + delete[] list, list = NULL; + list = new char[listSize]; + listSize = ::llistxattr(Filename, list, listSize); + } + + if(listSize>0) + { + // Extract list of attribute names so we can sort them + std::vector<std::string> attrKeys; + for(int i = 0; i<listSize; ++i) + { + std::string attrKey(list+i); + i += attrKey.size(); + attrKeys.push_back(attrKey); + } + sort(attrKeys.begin(), attrKeys.end()); + + // Make initial space in block + int xattrSize = outputBlock.GetSize(); + int xattrBufferSize = (xattrSize+listSize)>500 ? (xattrSize+listSize)*2 : 1000; + outputBlock.ResizeBlock(xattrBufferSize); + unsigned char* buffer = static_cast<unsigned char*>(outputBlock.GetBuffer()); + + // Leave space for attr block size later + int xattrBlockSizeOffset = xattrSize; + xattrSize += sizeof(u_int32_t); + + // Loop for each attribute + for(std::vector<std::string>::const_iterator attrKeyI = attrKeys.begin(); attrKeyI!=attrKeys.end(); ++attrKeyI) + { + std::string attrKey(*attrKeyI); + + if(xattrSize+sizeof(u_int16_t)+attrKey.size()+1+sizeof(u_int32_t)>static_cast<unsigned int>(xattrBufferSize)) + { + xattrBufferSize = (xattrBufferSize+sizeof(u_int16_t)+attrKey.size()+1+sizeof(u_int32_t))*2; + outputBlock.ResizeBlock(xattrBufferSize); + buffer = static_cast<unsigned char*>(outputBlock.GetBuffer()); + } + + // Store length and text for attibute name + u_int16_t keyLength = htons(attrKey.size()+1); + std::memcpy(buffer+xattrSize, &keyLength, sizeof(u_int16_t)); + xattrSize += sizeof(u_int16_t); + std::memcpy(buffer+xattrSize, attrKey.c_str(), attrKey.size()+1); + xattrSize += attrKey.size()+1; + + // Leave space for value size + int valueSizeOffset = xattrSize; + xattrSize += sizeof(u_int32_t); + + // This gets the attribute value (may be text or binary), no termination + int valueSize = ::lgetxattr(Filename, attrKey.c_str(), buffer+xattrSize, xattrBufferSize-xattrSize); + + if(xattrSize+valueSize>xattrBufferSize) + { + xattrBufferSize = (xattrBufferSize+valueSize)*2; + outputBlock.ResizeBlock(xattrBufferSize); + buffer = static_cast<unsigned char*>(outputBlock.GetBuffer()); + + valueSize = ::lgetxattr(Filename, attrKey.c_str(), buffer+xattrSize, xattrBufferSize-xattrSize); + } + + if(valueSize<0) + { + THROW_EXCEPTION(CommonException, OSFileError); + } + xattrSize += valueSize; + + // Fill in value size + u_int32_t valueLength = htonl(valueSize); + std::memcpy(buffer+valueSizeOffset, &valueLength, sizeof(u_int32_t)); + } + + // Fill in attribute block size + u_int32_t xattrBlockLength = htonl(xattrSize-xattrBlockSizeOffset-sizeof(u_int32_t)); + std::memcpy(buffer+xattrBlockSizeOffset, &xattrBlockLength, sizeof(u_int32_t)); + + outputBlock.ResizeBlock(xattrSize); + } + else if(listSize<0 && errno!=EOPNOTSUPP) + { + THROW_EXCEPTION(CommonException, OSFileError); } - - // Now, can allocate the block - pnewAttr = new StreamableMemBlock(sizeof(attr_StreamFormat) + linkedToSize + 1); - - // Fill in the entries - attr_StreamFormat *pattr = (attr_StreamFormat*)pnewAttr->GetBuffer(); - - FILL_IN_ATTRIBUTES - FILL_IN_ATTRIBUTES_2 - - // Add the path name for the symbolic link - ::memcpy(pattr + 1, linkedTo, linkedToSize); - // Make sure it's neatly terminated - ((char*)(pattr + 1))[linkedToSize] = '\0'; - - // Attributes ready. Encrypt into this block - EncryptAttr(*pnewAttr); - - // Store the new attributes - RemoveClear(); - mpClearAttributes = pnewAttr; - pnewAttr = 0; } catch(...) { - // clean up - delete pnewAttr; - pnewAttr = 0; + delete[] list; throw; } + delete[] list; +#endif } @@ -451,7 +561,8 @@ void BackupClientFileAttributes::WriteAttributes(const char *Filename) const // Get pointer to structure attr_StreamFormat *pattr = (attr_StreamFormat*)mpClearAttributes->GetBuffer(); - + int xattrOffset = sizeof(attr_StreamFormat); + // is it a symlink? int16_t mode = ntohs(pattr->Mode); if((mode & S_IFMT) == S_IFLNK) @@ -469,6 +580,8 @@ void BackupClientFileAttributes::WriteAttributes(const char *Filename) const { THROW_EXCEPTION(CommonException, OSFileError) } + + xattrOffset += std::strlen(reinterpret_cast<char*>(pattr+1))+1; } // If working as root, set user IDs @@ -491,7 +604,12 @@ void BackupClientFileAttributes::WriteAttributes(const char *Filename) const } #endif } - + + if(static_cast<int>(xattrOffset+sizeof(u_int32_t))<=mpClearAttributes->GetSize()) + { + WriteExtendedAttr(Filename, xattrOffset); + } + // Stop now if symlink, because otherwise it'll just be applied to the target if((mode & S_IFMT) == S_IFLNK) { @@ -590,6 +708,59 @@ void BackupClientFileAttributes::EnsureClearAvailable() const } } +// -------------------------------------------------------------------------- +// +// Function +// Name: BackupClientFileAttributes::WriteExtendedAttr(const char *Filename, int xattrOffset) +// Purpose: Private function, apply the stored extended attributes to the file +// Created: 2005/06/13 +// +// -------------------------------------------------------------------------- +void BackupClientFileAttributes::WriteExtendedAttr(const char *Filename, int xattrOffset) const +{ +#ifdef PLATFORM_HAVE_XATTR + const char* buffer = static_cast<char*>(mpClearAttributes->GetBuffer()); + + u_int32_t xattrBlockLength = 0; + std::memcpy(&xattrBlockLength, buffer+xattrOffset, sizeof(u_int32_t)); + int xattrBlockSize = ntohl(xattrBlockLength); + xattrOffset += sizeof(u_int32_t); + + int xattrEnd = xattrOffset+xattrBlockSize; + if(xattrEnd>mpClearAttributes->GetSize()) + { + // Too small + THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded); + } + + while(xattrOffset<xattrEnd) + { + u_int16_t keyLength = 0; + std::memcpy(&keyLength, buffer+xattrOffset, sizeof(u_int16_t)); + int keySize = ntohs(keyLength); + xattrOffset += sizeof(u_int16_t); + + const char* key = buffer+xattrOffset; + xattrOffset += keySize; + + u_int32_t valueLength = 0; + std::memcpy(&valueLength, buffer+xattrOffset, sizeof(u_int32_t)); + int valueSize = ntohl(valueLength); + xattrOffset += sizeof(u_int32_t); + + // FIXME: Warn on EOPNOTSUPP + if(::lsetxattr(Filename, key, buffer+xattrOffset, valueSize, 0)!=0 && errno!=EOPNOTSUPP) + { + THROW_EXCEPTION(CommonException, OSFileError); + } + + xattrOffset += valueSize; + } + + ASSERT(xattrOffset==xattrEnd); +#endif +} + // -------------------------------------------------------------------------- // @@ -735,14 +906,14 @@ void BackupClientFileAttributes::SetAttributeHashSecret(const void *pSecret, int // -------------------------------------------------------------------------- // // Function -// Name: BackupClientFileAttributes::GenerateAttributeHash(struct stat &, const std::string &) +// Name: BackupClientFileAttributes::GenerateAttributeHash(struct stat &, const std::string &, const std::string &) // Purpose: Generate a 64 bit hash from the attributes, used to detect changes. // Include filename in the hash, so that it changes from one file to another, // so don't reveal identical attributes. // Created: 25/4/04 // // -------------------------------------------------------------------------- -uint64_t BackupClientFileAttributes::GenerateAttributeHash(struct stat &st, const std::string &rFilename) +uint64_t BackupClientFileAttributes::GenerateAttributeHash(struct stat &st, const std::string &filename, const std::string &leafname) { if(sAttributeHashSecretLength == 0) { @@ -757,10 +928,14 @@ uint64_t BackupClientFileAttributes::GenerateAttributeHash(struct stat &st, cons hashData.gid = htonl(st.st_gid); hashData.mode = htonl(st.st_mode); + StreamableMemBlock xattr; + FillExtendedAttr(xattr, filename.c_str()); + // Create a MD5 hash of the data, filename, and secret MD5Digest digest; digest.Add(&hashData, sizeof(hashData)); - digest.Add(rFilename.c_str(), rFilename.size()); + digest.Add(xattr.GetBuffer(), xattr.GetSize()); + digest.Add(leafname.c_str(), leafname.size()); digest.Add(sAttributeHashSecret, sAttributeHashSecretLength); digest.Finish(); diff --git a/lib/backupclient/BackupClientFileAttributes.h b/lib/backupclient/BackupClientFileAttributes.h index dd930684..17124039 100755 --- a/lib/backupclient/BackupClientFileAttributes.h +++ b/lib/backupclient/BackupClientFileAttributes.h @@ -52,10 +52,13 @@ public: static void SetBlowfishKey(const void *pKey, int KeyLength); static void SetAttributeHashSecret(const void *pSecret, int SecretLength); - static uint64_t GenerateAttributeHash(struct stat &st, const std::string &rFilename); + static uint64_t GenerateAttributeHash(struct stat &st, const std::string &filename, const std::string &leafname); + static void FillExtendedAttr(StreamableMemBlock &outputBlock, const char *Filename); private: - void ReadAttributesLink(const char *Filename, void *pst, bool ZeroModificationTimes); + static void FillAttributes(StreamableMemBlock &outputBlock, const char *Filename, struct stat &st, bool ZeroModificationTimes); + static void FillAttributesLink(StreamableMemBlock &outputBlock, const char *Filename, struct stat &st); + void WriteExtendedAttr(const char *Filename, int xattrOffset) const; void RemoveClear() const; void EnsureClearAvailable() const; diff --git a/lib/common/DebugMemLeakFinder.cpp b/lib/common/DebugMemLeakFinder.cpp index e2a4a140..6c7fd3eb 100755 --- a/lib/common/DebugMemLeakFinder.cpp +++ b/lib/common/DebugMemLeakFinder.cpp @@ -93,33 +93,37 @@ void *memleakfinder_realloc(void *ptr, size_t size) // Check it's been allocated std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr)); - if(i == sMallocBlocks.end()) + if(ptr && i == sMallocBlocks.end()) { TRACE1("Block %x realloc(), but not in list. Error? Or allocated in startup static objects?\n", ptr); - void *b = ::realloc(ptr, size); - memleakfinder_malloc_add_block(b, size, "FOUND-IN-REALLOC", 0); - return b; } void *b = ::realloc(ptr, size); - - // Worked? - if(b != 0) + + if(ptr && i!=sMallocBlocks.end()) { - // Update map - MallocBlockInfo inf = i->second; - inf.size = size; - sMallocBlocks.erase(i); - sMallocBlocks[b] = inf; - - if(sTrackMallocInSection) + // Worked? + if(b != 0) { - std::set<void *>::iterator si(sSectionMallocBlocks.find(ptr)); - if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); - sSectionMallocBlocks.insert(b); + // Update map + MallocBlockInfo inf = i->second; + inf.size = size; + sMallocBlocks.erase(i); + sMallocBlocks[b] = inf; + + if(sTrackMallocInSection) + { + std::set<void *>::iterator si(sSectionMallocBlocks.find(ptr)); + if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); + sSectionMallocBlocks.insert(b); + } } } - + else + { + memleakfinder_malloc_add_block(b, size, "FOUND-IN-REALLOC", 0); + } + //TRACE3("realloc(), %d, %08x->%08x\n", size, ptr, b); return b; } diff --git a/lib/common/StreamableMemBlock.cpp b/lib/common/StreamableMemBlock.cpp index 4ea9e398..7700152d 100755 --- a/lib/common/StreamableMemBlock.cpp +++ b/lib/common/StreamableMemBlock.cpp @@ -225,7 +225,6 @@ void StreamableMemBlock::AllocateBlock(int Size) // -------------------------------------------------------------------------- void StreamableMemBlock::ResizeBlock(int Size) { - ASSERT(mpBuffer != 0); ASSERT(Size > 0); if(Size > 0) { |