// distribution boxbackup-0.10 (svn version: 494)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must
// display the following acknowledgment:
// This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// [Where legally impermissible the Authors do not disclaim liability for
// direct physical injury or death caused solely by defects in the software
// unless it is modified by a third party.]
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//
//
// --------------------------------------------------------------------------
//
// File
// Name: BackupClientFileAttributes.cpp
// Purpose: Storage of file attributes
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
#include "Box.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <limits.h>
#include <algorithm>
#include <new>
#include <vector>
#ifdef HAVE_SYS_XATTR_H
#include <cerrno>
#include <sys/xattr.h>
#endif
#include "BackupClientFileAttributes.h"
#include "CommonException.h"
#include "FileModificationTime.h"
#include "BoxTimeToUnix.h"
#include "BackupStoreException.h"
#include "CipherContext.h"
#include "CipherBlowfish.h"
#include "MD5Digest.h"
#include "MemLeakFindOn.h"
// set packing to one byte
#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS
#include "BeginStructPackForWire.h"
#else
BEGIN_STRUCTURE_PACKING_FOR_WIRE
#endif
#define ATTRIBUTETYPE_GENERIC_UNIX 1
#define ATTRIBUTE_ENCODING_BLOWFISH 2
typedef struct
{
int32_t AttributeType;
u_int32_t UID;
u_int32_t GID;
u_int64_t ModificationTime;
u_int64_t AttrModificationTime;
u_int32_t UserDefinedFlags;
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
// Use wider than necessary sizes, just to be careful.
typedef struct
{
int32_t uid, gid, mode;
} attributeHashData;
// Use default packing
#ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS
#include "EndStructPackForWire.h"
#else
END_STRUCTURE_PACKING_FOR_WIRE
#endif
#define MAX_ATTRIBUTE_HASH_SECRET_LENGTH 256
// Hide private static variables from the rest of the world
// -- don't put them as static class variables to avoid openssl/evp.h being
// included all over the project.
namespace
{
CipherContext sBlowfishEncrypt;
CipherContext sBlowfishDecrypt;
uint8_t sAttributeHashSecret[MAX_ATTRIBUTE_HASH_SECRET_LENGTH];
int sAttributeHashSecretLength = 0;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::BackupClientFileAttributes()
// Purpose: Default constructor
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
BackupClientFileAttributes::BackupClientFileAttributes()
: mpClearAttributes(0)
{
ASSERT(sizeof(u_int64_t) == sizeof(box_time_t));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::BackupClientFileAttributes(const BackupClientFileAttributes &)
// Purpose: Copy constructor
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
BackupClientFileAttributes::BackupClientFileAttributes(const BackupClientFileAttributes &rToCopy)
: StreamableMemBlock(rToCopy), // base class does the hard work
mpClearAttributes(0)
{
}
BackupClientFileAttributes::BackupClientFileAttributes(const StreamableMemBlock &rToCopy)
: StreamableMemBlock(rToCopy), // base class does the hard work
mpClearAttributes(0)
{
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::~BackupClientFileAttributes()
// Purpose: Destructor
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
BackupClientFileAttributes::~BackupClientFileAttributes()
{
if(mpClearAttributes)
{
delete mpClearAttributes;
mpClearAttributes = 0;
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes &operator=(const BackupClientFileAttributes &)
// Purpose: Assignment operator
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
BackupClientFileAttributes &BackupClientFileAttributes::operator=(const BackupClientFileAttributes &rAttr)
{
StreamableMemBlock::Set(rAttr);
RemoveClear(); // make sure no decrypted version held
return *this;
}
// Assume users play nice
BackupClientFileAttributes &BackupClientFileAttributes::operator=(const StreamableMemBlock &rAttr)
{
StreamableMemBlock::Set(rAttr);
RemoveClear(); // make sure no decrypted version held
return *this;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::operator==(const BackupClientFileAttributes &)
// Purpose: Comparison operator
// Created: 2003/10/09
//
// --------------------------------------------------------------------------
bool BackupClientFileAttributes::operator==(const BackupClientFileAttributes &rAttr) const
{
EnsureClearAvailable();
rAttr.EnsureClearAvailable();
return mpClearAttributes->operator==(*rAttr.mpClearAttributes);
}
// Too dangerous to allow -- put the two names the wrong way round, and it compares encrypted data.
/*bool BackupClientFileAttributes::operator==(const StreamableMemBlock &rAttr) const
{
StreamableMemBlock *pDecoded = 0;
try
{
EnsureClearAvailable();
StreamableMemBlock *pDecoded = MakeClear(rAttr);
// Compare using clear version
bool compared = mpClearAttributes->operator==(rAttr);
// Delete temporary
delete pDecoded;
return compared;
}
catch(...)
{
delete pDecoded;
throw;
}
}*/
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::Compare(const BackupClientFileAttributes &, bool)
// Purpose: Compare, optionally ignoring the attribute modification time and/or modification time, and some data which is
// irrelevant in practise (eg file generation number)
// Created: 10/12/03
//
// --------------------------------------------------------------------------
bool BackupClientFileAttributes::Compare(const BackupClientFileAttributes &rAttr, bool IgnoreAttrModTime, bool IgnoreModTime) const
{
EnsureClearAvailable();
rAttr.EnsureClearAvailable();
// Check sizes are the same, as a first check
if(mpClearAttributes->GetSize() != rAttr.mpClearAttributes->GetSize())
{
return false;
}
// Then check the elements of the two things
// Bytes are checked in network order, but this doesn't matter as we're only checking for equality.
attr_StreamFormat *a1 = (attr_StreamFormat*)mpClearAttributes->GetBuffer();
attr_StreamFormat *a2 = (attr_StreamFormat*)rAttr.mpClearAttributes->GetBuffer();
if(a1->AttributeType != a2->AttributeType
|| a1->UID != a2->UID
|| a1->GID != a2->GID
|| a1->UserDefinedFlags != a2->UserDefinedFlags
|| a1->Mode != a2->Mode)
{
return false;
}
if(!IgnoreModTime)
{
if(a1->ModificationTime != a2->ModificationTime)
{
return false;
}
}
if(!IgnoreAttrModTime)
{
if(a1->AttrModificationTime != a2->AttrModificationTime)
{
return false;
}
}
// Check symlink string?
unsigned int size = mpClearAttributes->GetSize();
if(size > sizeof(attr_StreamFormat))
{
// Symlink strings don't match. This also compares xattrs
if(::memcmp(a1 + 1, a2 + 1, size - sizeof(attr_StreamFormat)) != 0)
{
return false;
}
}
// Passes all test, must be OK
return true;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::ReadAttributes(const char *)
// Purpose: Read the attributes of the file, and store them ready for streaming.
// Optionally retrieve the modification time and attribute modification time.
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
void BackupClientFileAttributes::ReadAttributes(const char *Filename, bool ZeroModificationTimes, box_time_t *pModTime,
box_time_t *pAttrModTime, int64_t *pFileSize, InodeRefType *pInodeNumber, bool *pHasMultipleLinks)
{
StreamableMemBlock *pnewAttr = 0;
try
{
struct stat st;
if(::lstat(Filename, &st) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
// Modification times etc
if(pModTime) {*pModTime = FileModificationTime(st);}
if(pAttrModTime) {*pAttrModTime = FileAttrModificationTime(st);}
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);
#ifndef WIN32
// Is it a link?
if((st.st_mode & S_IFMT) == S_IFLNK)
{
FillAttributesLink(*pnewAttr, Filename, st);
}
#endif
FillExtendedAttr(*pnewAttr, Filename);
#ifdef WIN32
//this is to catch those problems with invalid time stamps stored...
//need to find out the reason why - but also a catch as well.
attr_StreamFormat *pattr =
(attr_StreamFormat*)pnewAttr->GetBuffer();
ASSERT(pattr != 0);
// __time64_t winTime = BoxTimeToSeconds(
// pnewAttr->ModificationTime);
u_int64_t modTime = box_ntoh64(pattr->ModificationTime);
box_time_t modSecs = BoxTimeToSeconds(modTime);
__time64_t winTime = modSecs;
// _MAX__TIME64_T doesn't seem to be defined, but the code below
// will throw an assertion failure if we exceed it :-)
// Microsoft says dates up to the year 3000 are valid, which
// is a bit more than 15 * 2^32. Even that doesn't seem
// to be true (still aborts), but it can at least hold 2^32.
if (winTime >= 0x100000000LL || _gmtime64(&winTime) == 0)
{
::syslog(LOG_ERR, "Invalid Modification Time "
"caught for file: %s", Filename);
pattr->ModificationTime = 0;
}
modTime = box_ntoh64(pattr->AttrModificationTime);
modSecs = BoxTimeToSeconds(modTime);
winTime = modSecs;
if (winTime > 0x100000000LL || _gmtime64(&winTime) == 0)
{
::syslog(LOG_ERR, "Invalid Attribute Modification "
"Time caught for file: %s", Filename);
pattr->AttrModificationTime = 0;
}
#endif
// Attributes ready. Encrypt into this block
EncryptAttr(*pnewAttr);
// Store the new attributes
RemoveClear();
mpClearAttributes = pnewAttr;
pnewAttr = 0;
}
catch(...)
{
// clean up
delete pnewAttr;
pnewAttr = 0;
throw;
}
}
// --------------------------------------------------------------------------
//
// 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 = box_hton64(FileModificationTime(st));
pattr->AttrModificationTime = box_hton64(FileAttrModificationTime(st));
}
pattr->Mode = htons(st.st_mode);
#ifndef HAVE_STRUCT_STAT_ST_FLAGS
pattr->UserDefinedFlags = 0;
pattr->FileGenerationNumber = 0;
#else
pattr->UserDefinedFlags = htonl(st.st_flags);
pattr->FileGenerationNumber = htonl(st.st_gen);
#endif
}
#ifndef WIN32
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::ReadAttributesLink()
// Purpose: Private function, handles the case where a symbolic link is needed
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
void BackupClientFileAttributes::FillAttributesLink(StreamableMemBlock &outputBlock, const char *Filename, struct stat &st)
{
// 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';
}
#endif
// --------------------------------------------------------------------------
//
// 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 HAVE_SYS_XATTR_H
int listBufferSize = 1000;
char* list = new char[listBufferSize];
try
{
// This returns an unordered list of attribute names, each 0 terminated,
// concatenated together
int listSize = ::llistxattr(Filename, list, listBufferSize);
if(listSize>listBufferSize)
{
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);
// Find size of attribute (must call with buffer and length 0 on some platforms,
// as -1 is returned if the data doesn't fit.)
int valueSize = ::lgetxattr(Filename, attrKey.c_str(), 0, 0);
if(valueSize<0)
{
THROW_EXCEPTION(CommonException, OSFileError);
}
// Resize block, if needed
if(xattrSize+valueSize>xattrBufferSize)
{
xattrBufferSize = (xattrBufferSize+valueSize)*2;
outputBlock.ResizeBlock(xattrBufferSize);
buffer = static_cast<unsigned char*>(outputBlock.GetBuffer());
}
// This gets the attribute value (may be text or binary), no termination
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 && errno!=EACCES)
{
THROW_EXCEPTION(CommonException, OSFileError);
}
}
catch(...)
{
delete[] list;
throw;
}
delete[] list;
#endif
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::WriteAttributes(const char *)
// Purpose: Apply the stored attributes to the file
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
void BackupClientFileAttributes::WriteAttributes(const char *Filename) const
{
// Got something loaded
if(GetSize() <= 0)
{
THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
}
// Make sure there are clear attributes to use
EnsureClearAvailable();
ASSERT(mpClearAttributes != 0);
// Check if the decrypted attributes are small enough, and the type of attributes stored
if(mpClearAttributes->GetSize() < (int)sizeof(int32_t))
{
THROW_EXCEPTION(BackupStoreException, AttributesNotUnderstood);
}
int32_t *type = (int32_t*)mpClearAttributes->GetBuffer();
ASSERT(type != 0);
if(ntohl(*type) != ATTRIBUTETYPE_GENERIC_UNIX)
{
// Don't know what to do with these
THROW_EXCEPTION(BackupStoreException, AttributesNotUnderstood);
}
// Check there is enough space for an attributes block
if(mpClearAttributes->GetSize() < (int)sizeof(attr_StreamFormat))
{
// Too small
THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
}
// 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)
{
// Check things are sensible
if(mpClearAttributes->GetSize() < (int)sizeof(attr_StreamFormat) + 1)
{
// Too small
THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
}
#ifdef WIN32
::syslog(LOG_WARNING,
"Cannot create symbolic links on Windows: %s",
Filename);
#else
// Make a symlink, first deleting anything in the way
::unlink(Filename);
if(::symlink((char*)(pattr + 1), Filename) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
#endif
xattrOffset += std::strlen(reinterpret_cast<char*>(pattr+1))+1;
}
// If working as root, set user IDs
if(::geteuid() == 0)
{
#ifndef HAVE_LCHOWN
// only if not a link, can't set their owner on this platform
if((mode & S_IFMT) != S_IFLNK)
{
// Not a link, use normal chown
if(::chown(Filename, ntohl(pattr->UID), ntohl(pattr->GID)) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
}
#else
if(::lchown(Filename, ntohl(pattr->UID), ntohl(pattr->GID)) != 0) // use the version which sets things on symlinks
{
THROW_EXCEPTION(CommonException, OSFileError)
}
#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)
{
return;
}
// Set modification time?
box_time_t modtime = box_ntoh64(pattr->ModificationTime);
if(modtime != 0)
{
// Work out times as timevals
struct timeval times[2];
BoxTimeToTimeval(modtime, times[1]);
// Copy access time as well, why not, got to set it to something
times[0] = times[1];
// Attr modification time will be changed anyway, nothing that can be done about it
// Try to apply
if(::utimes(Filename, times) != 0)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
}
// Apply everything else... (allowable mode flags only)
if(::chmod(Filename, mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX)) != 0) // mode must be done last (think setuid)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::IsSymLink()
// Purpose: Do these attributes represent a symbolic link?
// Created: 2003/10/07
//
// --------------------------------------------------------------------------
bool BackupClientFileAttributes::IsSymLink() const
{
EnsureClearAvailable();
// Got the right kind of thing?
if(mpClearAttributes->GetSize() < (int)sizeof(int32_t))
{
THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
}
// Get the type of attributes stored
int32_t *type = (int32_t*)mpClearAttributes->GetBuffer();
ASSERT(type != 0);
if(ntohl(*type) == ATTRIBUTETYPE_GENERIC_UNIX && mpClearAttributes->GetSize() > (int)sizeof(attr_StreamFormat))
{
// Check link
attr_StreamFormat *pattr = (attr_StreamFormat*)mpClearAttributes->GetBuffer();
return ((ntohs(pattr->Mode)) & S_IFMT) == S_IFLNK;
}
return false;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::RemoveClear()
// Purpose: Private. Deletes any clear version of the attributes that may be held
// Created: 3/12/03
//
// --------------------------------------------------------------------------
void BackupClientFileAttributes::RemoveClear() const
{
if(mpClearAttributes)
{
delete mpClearAttributes;
}
mpClearAttributes = 0;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::EnsureClearAvailable()
// Purpose: Private. Makes sure the clear version is available
// Created: 3/12/03
//
// --------------------------------------------------------------------------
void BackupClientFileAttributes::EnsureClearAvailable() const
{
if(mpClearAttributes == 0)
{
mpClearAttributes = MakeClear(*this);
}
}
// --------------------------------------------------------------------------
//
// 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 HAVE_SYS_XATTR_H
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
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::MakeClear(const StreamableMemBlock &)
// Purpose: Static. Decrypts stored attributes.
// Created: 3/12/03
//
// --------------------------------------------------------------------------
StreamableMemBlock *BackupClientFileAttributes::MakeClear(const StreamableMemBlock &rEncrypted)
{
// New block
StreamableMemBlock *pdecrypted = 0;
try
{
// Check the block is big enough for IV and header
int ivSize = sBlowfishEncrypt.GetIVLength();
if(rEncrypted.GetSize() <= (ivSize + 1))
{
THROW_EXCEPTION(BackupStoreException, BadEncryptedAttributes);
}
// How much space is needed for the output?
int maxDecryptedSize = sBlowfishDecrypt.MaxOutSizeForInBufferSize(rEncrypted.GetSize() - ivSize);
// Allocate it
pdecrypted = new StreamableMemBlock(maxDecryptedSize);
// ptr to block
uint8_t *encBlock = (uint8_t*)rEncrypted.GetBuffer();
// Check that the header has right type
if(encBlock[0] != ATTRIBUTE_ENCODING_BLOWFISH)
{
THROW_EXCEPTION(BackupStoreException, EncryptedAttributesHaveUnknownEncoding);
}
// Set IV
sBlowfishDecrypt.SetIV(encBlock + 1);
// Decrypt
int decryptedSize = sBlowfishDecrypt.TransformBlock(pdecrypted->GetBuffer(), maxDecryptedSize, encBlock + 1 + ivSize, rEncrypted.GetSize() - (ivSize + 1));
// Resize block to fit
pdecrypted->ResizeBlock(decryptedSize);
}
catch(...)
{
delete pdecrypted;
pdecrypted = 0;
}
return pdecrypted;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::SetBlowfishKey(const void *, int)
// Purpose: Static. Sets the key to use for encryption and decryption.
// Created: 3/12/03
//
// --------------------------------------------------------------------------
void BackupClientFileAttributes::SetBlowfishKey(const void *pKey, int KeyLength)
{
// IVs set later
sBlowfishEncrypt.Reset();
sBlowfishEncrypt.Init(CipherContext::Encrypt, CipherBlowfish(CipherDescription::Mode_CBC, pKey, KeyLength));
sBlowfishDecrypt.Reset();
sBlowfishDecrypt.Init(CipherContext::Decrypt, CipherBlowfish(CipherDescription::Mode_CBC, pKey, KeyLength));
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::EncryptAttr(const StreamableMemBlock &)
// Purpose: Private. Encrypt the given attributes into this block.
// Created: 3/12/03
//
// --------------------------------------------------------------------------
void BackupClientFileAttributes::EncryptAttr(const StreamableMemBlock &rToEncrypt)
{
// Free any existing block
FreeBlock();
// Work out the maximum amount of space we need
int maxEncryptedSize = sBlowfishEncrypt.MaxOutSizeForInBufferSize(rToEncrypt.GetSize());
// And the size of the IV
int ivSize = sBlowfishEncrypt.GetIVLength();
// Allocate this space
AllocateBlock(maxEncryptedSize + ivSize + 1);
// Store the encoding byte
uint8_t *block = (uint8_t*)GetBuffer();
block[0] = ATTRIBUTE_ENCODING_BLOWFISH;
// Generate and store an IV for this attribute block
int ivSize2 = 0;
const void *iv = sBlowfishEncrypt.SetRandomIV(ivSize2);
ASSERT(ivSize == ivSize2);
// Copy into the encrypted block
::memcpy(block + 1, iv, ivSize);
// Do the transform
int encrytedSize = sBlowfishEncrypt.TransformBlock(block + 1 + ivSize, maxEncryptedSize, rToEncrypt.GetBuffer(), rToEncrypt.GetSize());
// Resize this block
ResizeBlock(encrytedSize + ivSize + 1);
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupClientFileAttributes::SetAttributeHashSecret(const void *, int)
// Purpose: Set the secret for the filename attribute hash
// Created: 25/4/04
//
// --------------------------------------------------------------------------
void BackupClientFileAttributes::SetAttributeHashSecret(const void *pSecret, int SecretLength)
{
if(SecretLength > (int)sizeof(sAttributeHashSecret))
{
SecretLength = sizeof(sAttributeHashSecret);
}
if(SecretLength < 0)
{
THROW_EXCEPTION(BackupStoreException, Internal)
}
// Copy
::memcpy(sAttributeHashSecret, pSecret, SecretLength);
sAttributeHashSecretLength = SecretLength;
}
// --------------------------------------------------------------------------
//
// Function
// 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 &filename, const std::string &leafname)
{
if(sAttributeHashSecretLength == 0)
{
THROW_EXCEPTION(BackupStoreException, AttributeHashSecretNotSet)
}
// Assemble stuff we're interested in
attributeHashData hashData;
memset(&hashData, 0, sizeof(hashData));
// Use network byte order and large sizes to be cross platform
hashData.uid = htonl(st.st_uid);
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(xattr.GetBuffer(), xattr.GetSize());
digest.Add(leafname.c_str(), leafname.size());
digest.Add(sAttributeHashSecret, sAttributeHashSecretLength);
digest.Finish();
// Return the first 64 bits of the hash
uint64_t result = *((uint64_t *)(digest.DigestAsData()));
return result;
}
syntax highlighted by Code2HTML, v. 0.9.1