From 15fe4e977048085cb929a50e0561b223cf95c9a9 Mon Sep 17 00:00:00 2001 From: Martin Ebourne Date: Wed, 30 Nov 2005 23:18:04 +0000 Subject: Merged martin/xattr at r5 to trunk --- lib/backupclient/BackupClientFileAttributes.cpp | 343 ++++++++++++++++++------ lib/backupclient/BackupClientFileAttributes.h | 7 +- 2 files changed, 264 insertions(+), 86 deletions(-) (limited to 'lib/backupclient') 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 #include #include +#include +#include +#include +#ifdef PLATFORM_HAVE_XATTR +#include +#include +#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); @@ -351,6 +334,45 @@ 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 @@ -359,53 +381,141 @@ void BackupClientFileAttributes::ReadAttributes(const char *Filename, bool ZeroM // 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(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 attrKeys; + for(int i = 0; i500 ? (xattrSize+listSize)*2 : 1000; + outputBlock.ResizeBlock(xattrBufferSize); + unsigned char* buffer = static_cast(outputBlock.GetBuffer()); + + // Leave space for attr block size later + int xattrBlockSizeOffset = xattrSize; + xattrSize += sizeof(u_int32_t); + + // Loop for each attribute + for(std::vector::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(xattrBufferSize)) + { + xattrBufferSize = (xattrBufferSize+sizeof(u_int16_t)+attrKey.size()+1+sizeof(u_int32_t))*2; + outputBlock.ResizeBlock(xattrBufferSize); + buffer = static_cast(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(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(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(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(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