diff options
-rw-r--r-- | lib/backupclient/BackupClientFileAttributes.cpp | 24 | ||||
-rw-r--r-- | lib/common/BoxPlatform.h | 13 | ||||
-rw-r--r-- | test/bbackupd/testbbackupd.cpp | 224 |
3 files changed, 244 insertions, 17 deletions
diff --git a/lib/backupclient/BackupClientFileAttributes.cpp b/lib/backupclient/BackupClientFileAttributes.cpp index 4fbdd90a..70c0d09d 100644 --- a/lib/backupclient/BackupClientFileAttributes.cpp +++ b/lib/backupclient/BackupClientFileAttributes.cpp @@ -33,19 +33,6 @@ #include "MemLeakFindOn.h" -// Handle differing xattr APIs -#ifdef HAVE_SYS_XATTR_H - #if !defined(HAVE_LLISTXATTR) && defined(HAVE_LISTXATTR) && HAVE_DECL_XATTR_NOFOLLOW - #define llistxattr(a,b,c) listxattr(a,b,c,XATTR_NOFOLLOW) - #endif - #if !defined(HAVE_LGETXATTR) && defined(HAVE_GETXATTR) && HAVE_DECL_XATTR_NOFOLLOW - #define lgetxattr(a,b,c,d) getxattr(a,b,c,d,0,XATTR_NOFOLLOW) - #endif - #if !defined(HAVE_LSETXATTR) && defined(HAVE_SETXATTR) && HAVE_DECL_XATTR_NOFOLLOW - #define lsetxattr(a,b,c,d,e) setxattr(a,b,c,d,0,(e)|XATTR_NOFOLLOW) - #endif -#endif - // set packing to one byte #ifdef STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS #include "BeginStructPackForWire.h" @@ -521,18 +508,21 @@ void BackupClientFileAttributes::FillExtendedAttr(StreamableMemBlock &outputBloc 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); + // 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); + // Resize block, if needed 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); } + // 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); diff --git a/lib/common/BoxPlatform.h b/lib/common/BoxPlatform.h index 9906b33c..42eb13e8 100644 --- a/lib/common/BoxPlatform.h +++ b/lib/common/BoxPlatform.h @@ -62,6 +62,19 @@ #define STRUCTURE_PACKING_FOR_WIRE_USE_HEADERS #endif +// Handle differing xattr APIs +#ifdef HAVE_SYS_XATTR_H + #if !defined(HAVE_LLISTXATTR) && defined(HAVE_LISTXATTR) && HAVE_DECL_XATTR_NOFOLLOW + #define llistxattr(a,b,c) listxattr(a,b,c,XATTR_NOFOLLOW) + #endif + #if !defined(HAVE_LGETXATTR) && defined(HAVE_GETXATTR) && HAVE_DECL_XATTR_NOFOLLOW + #define lgetxattr(a,b,c,d) getxattr(a,b,c,d,0,XATTR_NOFOLLOW) + #endif + #if !defined(HAVE_LSETXATTR) && defined(HAVE_SETXATTR) && HAVE_DECL_XATTR_NOFOLLOW + #define lsetxattr(a,b,c,d,e) setxattr(a,b,c,d,0,(e)|XATTR_NOFOLLOW) + #endif +#endif + #if defined WIN32 && !defined __MINGW32__ typedef __int8 int8_t; typedef __int16 int16_t; diff --git a/test/bbackupd/testbbackupd.cpp b/test/bbackupd/testbbackupd.cpp index 560cf16f..72917978 100644 --- a/test/bbackupd/testbbackupd.cpp +++ b/test/bbackupd/testbbackupd.cpp @@ -16,6 +16,11 @@ #include <string.h> #include <sys/wait.h> #include <unistd.h> +#ifdef HAVE_SYS_XATTR_H +#include <cerrno> +#include <sys/xattr.h> +#endif +#include <map> #include "Test.h" #include "BackupClientFileAttributes.h" @@ -57,12 +62,188 @@ void wait_for_backup_operation(int seconds = TIME_TO_WAIT_FOR_BACKUP_OPERATION) int bbstored_pid = 0; +#ifdef HAVE_SYS_XATTR_H +bool readxattr_into_map(const char *filename, std::map<std::string,std::string> &rOutput) +{ + rOutput.clear(); + + ssize_t xattrNamesBufferSize = llistxattr(filename, NULL, 0); + if(xattrNamesBufferSize < 0) + { + return false; + } + else if(xattrNamesBufferSize > 0) + { + // There is some data there to look at + char *xattrNamesBuffer = (char*)malloc(xattrNamesBufferSize + 4); + if(xattrNamesBuffer == NULL) return false; + char *xattrDataBuffer = 0; + int xattrDataBufferSize = 0; + // note: will leak these buffers if a read error occurs. (test code, so doesn't matter) + + ssize_t ns = llistxattr(filename, xattrNamesBuffer, xattrNamesBufferSize); + if(ns < 0) + { + return false; + } + else if(ns > 0) + { + // Read all the attribute values + const char *xattrName = xattrNamesBuffer; + while(xattrName < (xattrNamesBuffer + ns)) + { + // Store size of name + int xattrNameSize = strlen(xattrName); + + bool ok = true; + + ssize_t dataSize = lgetxattr(filename, xattrName, NULL, 0); + if(dataSize < 0) + { + if(errno == ENOATTR) + { + // Deleted from under us + ok = false; + } + else + { + return false; + } + } + else if(dataSize == 0) + { + // something must have removed all the data from under us + ok = false; + } + else + { + // Make sure there's enough space in the buffer to get the attribute + if(xattrDataBuffer == 0) + { + xattrDataBuffer = (char*)malloc(dataSize + 4); + xattrDataBufferSize = dataSize + 4; + } + else if(xattrDataBufferSize < (dataSize + 4)) + { + char *resized = (char*)realloc(xattrDataBuffer, dataSize + 4); + if(resized == NULL) return false; + xattrDataBuffer = resized; + xattrDataBufferSize = dataSize + 4; + } + } + + // Read the data! + dataSize = 0; + if(ok) + { + dataSize = lgetxattr(filename, xattrName, xattrDataBuffer, + xattrDataBufferSize - 1 /*for terminator*/); + if(dataSize < 0) + { + if(errno == ENOATTR) + { + // Deleted from under us + ok = false; + } + else + { + return false; + } + } + else if(dataSize == 0) + { + // something must have deleted this from under us + ok = false; + } + else + { + // Terminate the data + xattrDataBuffer[dataSize] = '\0'; + } + // Got the data in the buffer + } + + // Store in map + if(ok) + { + rOutput[std::string(xattrName)] = std::string(xattrDataBuffer, dataSize); + } + + // Next attribute + xattrName += xattrNameSize + 1; + } + } + + if(xattrNamesBuffer != 0) ::free(xattrNamesBuffer); + if(xattrDataBuffer != 0) ::free(xattrDataBuffer); + } + + return true; +} + +static FILE *xattrTestDataHandle = 0; +bool write_xattr_test(const char *filename, const char *attrName, unsigned int length, bool *pNotSupported = 0) +{ + if(xattrTestDataHandle == 0) + { + xattrTestDataHandle = ::fopen("testfiles/test3.tgz", "rb"); // largest test file + } + if(xattrTestDataHandle == 0) + { + return false; + } + else + { + char data[4096]; + if(length > sizeof(data)) length = sizeof(data); + + if(::fread(data, length, 1, xattrTestDataHandle) != 1) + { + return false; + } + + if(::lsetxattr(filename, attrName, data, length, 0) != 0) + { + if(pNotSupported != 0) + { + *pNotSupported = (errno == ENOTSUP); + } + return false; + } + } + + return true; +} +void finish_with_write_xattr_test() +{ + if(xattrTestDataHandle != 0) + { + ::fclose(xattrTestDataHandle); + } +} +#endif // HAVE_SYS_XATTR_H + bool attrmatch(const char *f1, const char *f2) { struct stat s1, s2; TEST_THAT(::lstat(f1, &s1) == 0); TEST_THAT(::lstat(f2, &s2) == 0); +#ifdef HAVE_SYS_XATTR_H + { + std::map<std::string,std::string> xattr1, xattr2; + if(!readxattr_into_map(f1, xattr1) + || !readxattr_into_map(f2, xattr2)) + { + return false; + } + if(!(xattr1 == xattr2)) + { + return false; + } + } +#endif // HAVE_SYS_XATTR_H + // if link, just make sure other file is a link too, and that the link to names match if((s1.st_mode & S_IFMT) == S_IFLNK) { @@ -143,6 +324,49 @@ int test_basics() TEST_THAT(t1_r == t1b); TEST_THAT(t1b_r == t1); +#ifdef HAVE_SYS_XATTR_H + // Write some attributes to the file, checking for ENOTSUP + bool xattrNotSupported = false; + if(!write_xattr_test("testfiles/test1", "attr_1", 1276, &xattrNotSupported) && xattrNotSupported) + { + ::printf("***********\nYour platform supports xattr, but your filesystem does not.\nSkipping tests.\n***********\n"); + } + else + { + BackupClientFileAttributes x1, x2, x3, x4; + + // Write more attributes + TEST_THAT(write_xattr_test("testfiles/test1", "attr_2", 3427)); + TEST_THAT(write_xattr_test("testfiles/test1", "sadfohij39998.3hj", 123)); + + // Read file attributes + x1.ReadAttributes("testfiles/test1"); + + // Write file attributes + FILE *f = fopen("testfiles/test1_nx", "w"); + fclose(f); + x1.WriteAttributes("testfiles/test1_nx"); + + // Compare to see if xattr copied + TEST_THAT(attrmatch("testfiles/test1", "testfiles/test1_nx")); + + // Add more attributes to a file + x2.ReadAttributes("testfiles/test1"); + TEST_THAT(write_xattr_test("testfiles/test1", "328989sj..sdf", 23)); + + // Read them again, and check that the Compare() function detects that they're different + x3.ReadAttributes("testfiles/test1"); + TEST_THAT(x1.Compare(x2, true, true)); + TEST_THAT(!x1.Compare(x3, true, true)); + + // Change the value of one of them, leaving the size the same. + TEST_THAT(write_xattr_test("testfiles/test1", "328989sj..sdf", 23)); + x4.ReadAttributes("testfiles/test1"); + TEST_THAT(!x1.Compare(x4, true, true)); + } + finish_with_write_xattr_test(); +#endif // HAVE_SYS_XATTR_H + return 0; } |