[Box Backup-dev] COMMIT r806 - in box/chris/acl: bin/bbackupd bin/bbackupquery infrastructure/msvc/2005 lib/backupclient lib/common lib/win32 test/win32

boxbackup-dev@fluffy.co.uk boxbackup-dev@fluffy.co.uk
Sun, 20 Aug 2006 23:52:09 +0100


Author: chris
Date: 2006-08-20 23:52:09 +0100 (Sun, 20 Aug 2006)
New Revision: 806

Added:
   box/chris/acl/lib/common/DiscardStream.cpp
   box/chris/acl/lib/common/DiscardStream.h
Modified:
   box/chris/acl/bin/bbackupd/BackupClientDirectoryRecord.cpp
   box/chris/acl/bin/bbackupd/BackupDaemon.cpp
   box/chris/acl/bin/bbackupquery/BackupQueries.cpp
   box/chris/acl/bin/bbackupquery/documentation.txt
   box/chris/acl/infrastructure/msvc/2005/common.vcproj
   box/chris/acl/lib/backupclient/BackupClientFileAttributes.cpp
   box/chris/acl/lib/backupclient/BackupClientFileAttributes.h
   box/chris/acl/lib/backupclient/BackupClientRestore.cpp
   box/chris/acl/lib/backupclient/BackupStoreException.txt
   box/chris/acl/lib/win32/emu.cpp
   box/chris/acl/lib/win32/emu.h
   box/chris/acl/test/win32/testlibwin32.cpp
Log:
* test/win32/testlibwin32.cpp
* infrastructure/msvc/2005/common.vcproj
* lib/win32/emu.cpp
* lib/win32/emu.h
* lib/backupclient/BackupStoreException.txt
* lib/backupclient/BackupClientRestore.cpp
* lib/backupclient/BackupClientFileAttributes.cpp
* lib/backupclient/BackupClientFileAttributes.h
* lib/common/DiscardStream.h
* lib/common/DiscardStream.cpp
* bin/bbackupd/BackupClientDirectoryRecord.cpp
* bin/bbackupd/BackupDaemon.cpp
* bin/bbackupquery/BackupQueries.cpp
* bin/bbackupquery/documentation.txt
- Checking in work in progress


Modified: box/chris/acl/bin/bbackupd/BackupClientDirectoryRecord.cpp
===================================================================
--- box/chris/acl/bin/bbackupd/BackupClientDirectoryRecord.cpp	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/bin/bbackupd/BackupClientDirectoryRecord.cpp	2006-08-20 22:52:09 UTC (rev 806)
@@ -156,6 +156,12 @@
 	// Build the current state checksum to compare against while getting info from dirs
 	// Note checksum is used locally only, so byte order isn't considered.
 	MD5Digest currentStateChecksum;
+
+	bool logChanges = false;
+	if (rLocalPath == "C:\\Cygwin\\Home\\Administrator\\pcre-6.3")
+	{
+		logChanges = true;
+	}
 	
 	// Stat the directory, to get attribute info
 	{
@@ -186,6 +192,14 @@
 		StreamableMemBlock xattr;
 		BackupClientFileAttributes::FillExtendedAttr(xattr, rLocalPath.c_str());
 		currentStateChecksum.Add(xattr.GetBuffer(), xattr.GetSize());
+
+		if (logChanges)
+		{
+			::syslog(LOG_INFO, "%s: mode %.3o, uid %d, gid %d, "
+				"ino %d, xattr %d\n", rLocalPath.c_str(), 
+				st.st_mode, st.st_uid, st.st_gid, st.st_ino,
+				xattr.GetSize());
+		}
 	}
 	
 	// Read directory entries, building arrays of names
@@ -236,6 +250,11 @@
 				// Stat file to get info
 				filename = MakeFullPath(rLocalPath, en->d_name);
 
+				if (logChanges && strcmp(en->d_name, ".libs") == 0)
+				{
+					printf("foo");
+				}
+
 				#ifdef WIN32
 				int type = en->d_type;
 				#else
@@ -312,6 +331,15 @@
 				checksum_info.mSize = st.st_size;
 				currentStateChecksum.Add(&checksum_info, sizeof(checksum_info));
 				currentStateChecksum.Add(en->d_name, strlen(en->d_name));
+				if (logChanges && strcmp(en->d_name, ".libs") == 0)
+				{
+					::syslog(LOG_INFO, "%s: mod %lld, "
+						"attr %lld, size %lld\n",
+						filename.c_str(),
+						(long long)checksum_info.mModificationTime,
+						(long long)checksum_info.mAttributeModificationTime,
+						(long long)checksum_info.mSize);
+				}
 				
 				// If the file has been modified madly into the future, download the 
 				// directory record anyway to ensure that it doesn't get uploaded
@@ -353,6 +381,12 @@
 		// The checksum is the same, and there was one to compare with
 		checksumDifferent = false;
 	}
+	else
+	{
+		std::string digest(currentStateChecksum.DigestAsString());
+		::syslog(LOG_INFO, "Checksum changed on %s: %s\n", 
+			rLocalPath.c_str(), digest.c_str());
+	}
 
 	// Pointer to potentially downloaded store directory info
 	BackupStoreDirectory *pdirOnStore = 0;

Modified: box/chris/acl/bin/bbackupd/BackupDaemon.cpp
===================================================================
--- box/chris/acl/bin/bbackupd/BackupDaemon.cpp	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/bin/bbackupd/BackupDaemon.cpp	2006-08-20 22:52:09 UTC (rev 806)
@@ -75,6 +75,7 @@
 #include "IOStreamGetLine.h"
 #include "Conversion.h"
 #include "Archive.h"
+#include "BoxTimeToText.h"
 
 #include "MemLeakFindOn.h"
 
@@ -852,6 +853,12 @@
 					SerializeStoreObjectInfo(
 						clientStoreMarker, 
 						lastSyncTime, nextSyncTime);
+				std::string last(BoxTimeToISO8601String(
+					lastSyncTime, true));
+				std::string next(BoxTimeToISO8601String(
+					nextSyncTime, true));
+				printf("Last sync was at %s, next will be %s\n",
+					last.c_str(), next.c_str());
 
 				// --------------------------------------------------------------------------------------------
 			}

Modified: box/chris/acl/bin/bbackupquery/BackupQueries.cpp
===================================================================
--- box/chris/acl/bin/bbackupquery/BackupQueries.cpp	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/bin/bbackupquery/BackupQueries.cpp	2006-08-20 22:52:09 UTC (rev 806)
@@ -7,6 +7,11 @@
 //
 // --------------------------------------------------------------------------
 
+#ifdef WIN32
+// we need features from Windows 2000 and above for ACL support
+#define WINVER 0x0500
+#endif
+
 #include "Box.h"
 
 #ifdef HAVE_UNISTD_H
@@ -28,6 +33,12 @@
 #include <set>
 #include <limits>
 
+#ifdef WIN32
+#include <AccCtrl.h>
+#include <Aclapi.h>
+#include <Sddl.h>
+#endif
+
 #include "BackupQueries.h"
 #include "Utils.h"
 #include "Configuration.h"
@@ -46,6 +57,9 @@
 #include "BackupStoreException.h"
 #include "ExcludeList.h"
 #include "BackupClientMakeExcludeList.h"
+#include "DiscardStream.h"
+#include "Archive.h"
+#include "CollectInBufferStream.h"
 
 #include "MemLeakFindOn.h"
 
@@ -180,7 +194,7 @@
 	{
 		{ "quit", "" },
 		{ "exit", "" },
-		{ "list", "rodIFtTsh", },
+		{ "list", "rodIFtTshw", },
 		{ "pwd",  "" },
 		{ "cd",   "od" },
 		{ "lcd",  "" },
@@ -355,6 +369,7 @@
 	#define LIST_OPTION_TIMES_UTC		'T'
 	#define LIST_OPTION_SIZEINBLOCKS	's'
 	#define LIST_OPTION_DISPLAY_HASH	'h'
+	#define LIST_OPTION_WINDOWS_ATTR        'w'
 
 	// default to using the current directory
 	int64_t rootDir = GetCurrentDirectoryID();
@@ -391,6 +406,264 @@
 }
 
 
+#ifdef WIN32
+bool PrintSid(Archive& rArchive)
+{
+	int nametype;
+	rArchive.Read(nametype);
+
+	std::string sid;
+	rArchive.Read(sid);
+
+	std::string domain;
+	rArchive.Read(domain);
+
+	std::string name;
+	rArchive.Read(name);
+
+	printf("%s\\%s (%s)", domain.c_str(), name.c_str(), sid.c_str());
+	return true;
+}
+
+void PrintWindowsAttributes(const StreamableMemBlock& rBlock)
+{
+	attr_StreamFormat_Generic* attr = 
+		(attr_StreamFormat_Generic*)
+		rBlock.GetBuffer();
+
+	if (attr->AttributeType != htonl(ATTRIBUTETYPE_GENERIC_WINDOWS))
+	{
+		return;
+	}
+
+	attr_StreamFormat_Windows* wattr = 
+		(attr_StreamFormat_Windows*)
+		rBlock.GetBuffer();
+
+	DWORD attribs = wattr->Attributes;
+	printf("\tFile attributes: ");
+
+	#define PRINT_ATTR(x) \
+		if (attribs & FILE_ATTRIBUTE_ ## x) \
+		{ \
+			printf(#x " "); \
+			attribs &= ~FILE_ATTRIBUTE_ ## x; \
+		}
+
+	PRINT_ATTR(ARCHIVE);
+	PRINT_ATTR(COMPRESSED);
+	PRINT_ATTR(DIRECTORY);
+	PRINT_ATTR(ENCRYPTED);
+	PRINT_ATTR(HIDDEN);
+	PRINT_ATTR(OFFLINE);
+	PRINT_ATTR(READONLY);
+	PRINT_ATTR(REPARSE_POINT);
+	PRINT_ATTR(SPARSE_FILE);
+	PRINT_ATTR(SYSTEM);
+	PRINT_ATTR(TEMPORARY);
+
+	#undef PRINT_ATTR
+
+	attribs &= ~FILE_ATTRIBUTE_NORMAL;
+
+	if (wattr->Attributes)
+	{
+		if (attribs)
+		{
+			printf("Other (%08x)", 
+				(unsigned int)attribs);
+		}
+	}
+	else
+	{
+		printf("None");
+	}
+
+	printf("\n");
+
+	CollectInBufferStream buffer;
+	buffer.Write(wattr + 1, 
+		rBlock.GetSize() - sizeof(attr_StreamFormat_Windows));
+	buffer.SetForReading();
+
+	Archive archive(buffer, 0);
+	int data;
+
+	#define ERR_MSG "\tWindows attributes error: "
+
+	archive.Read(data);
+	if (data != ACL_FORMAT_MAGIC)
+	{
+		printf(ERR_MSG "wrong magic\n");
+		return;
+	}
+
+	archive.Read(data);
+	if (data != ACL_FORMAT_VERSION)
+	{
+		printf(ERR_MSG "wrong version\n");
+		return;
+	}
+
+	EXPLICIT_ACCESS* pEntries = NULL;
+
+	try
+	{
+		printf("\tOwner: ");
+		if (!PrintSid(archive))
+		{
+			printf(ERR_MSG "failed to read owner\n");
+			throw 1;
+		}
+
+		printf("\n\tGroup: ");
+		if (!PrintSid(archive))
+		{
+			printf(ERR_MSG "failed to read group\n");
+			throw 1;
+		}
+		printf("\n");
+
+		int numEntries;
+		archive.Read(numEntries);
+
+		pEntries = (EXPLICIT_ACCESS*)calloc(numEntries,
+			sizeof(EXPLICIT_ACCESS));
+
+		if (!pEntries)
+		{
+			printf(ERR_MSG "failed to allocate enough memory\n");
+			throw 1;
+		}
+
+		for (int i = 0; i < numEntries; i++)
+		{
+			PEXPLICIT_ACCESS pEntry = &(pEntries[i]);
+			
+			archive.Read(data); pEntry->grfAccessPermissions = 
+				data;
+			archive.Read(data); pEntry->grfAccessMode =
+				(ACCESS_MODE)data;
+			archive.Read(data); pEntry->Trustee.TrusteeForm =
+				(TRUSTEE_FORM)data;
+			archive.Read(data); pEntry->Trustee.TrusteeType =
+				(TRUSTEE_TYPE)data;
+
+			if (pEntry->Trustee.TrusteeForm != TRUSTEE_IS_SID)
+			{
+				printf(ERR_MSG "trustee is not a SID\n");
+				throw 1;
+			}
+
+			printf("\t");
+			if (!PrintSid(archive))
+			{
+				printf(ERR_MSG "failed to read trustee SID\n");
+				throw 1;
+			}
+
+			printf(" (");
+			switch(pEntry->Trustee.TrusteeType)
+			{
+			case TRUSTEE_IS_UNKNOWN:
+				printf("unknown type"); break;
+			case TRUSTEE_IS_USER:
+				printf("user"); break;
+			case TRUSTEE_IS_GROUP:
+				printf("group"); break;
+			case TRUSTEE_IS_DOMAIN:
+				printf("domain"); break;
+			case TRUSTEE_IS_ALIAS:
+				printf("alias"); break;
+			case TRUSTEE_IS_WELL_KNOWN_GROUP:
+				printf("well-known group"); break;
+			case TRUSTEE_IS_DELETED:
+				printf("deleted account"); break;
+			case TRUSTEE_IS_INVALID:
+				printf("invalid trustee type"); break;
+			case TRUSTEE_IS_COMPUTER:
+				printf("computer\n"); break;
+			default:
+				printf("unknown type %d\n", 
+					pEntry->Trustee.TrusteeType); 
+			}
+
+			printf("): ");
+
+			switch(pEntry->grfAccessMode)
+			{
+			case NOT_USED_ACCESS:
+				printf("NOT_USED_ACCESS "); break;
+			case GRANT_ACCESS:
+				printf("Grant "); break;
+			case DENY_ACCESS:
+				printf("Deny "); break;
+			case REVOKE_ACCESS:
+				printf("Revoke "); break;
+			case SET_AUDIT_SUCCESS:
+				printf("Audit Success "); break;
+			case SET_AUDIT_FAILURE:
+				printf("Audit Failure "); break;
+			default:
+				printf("Unknown (%08x)\n", 
+					pEntry->grfAccessMode);
+			}
+
+			data = pEntry->grfAccessPermissions;
+
+			#define PRINT_PERM(name) \
+			if (data & name == name) \
+			{ \
+				printf(#name " "); \
+				data &= ~name; \
+			}
+
+			PRINT_PERM(FILE_ADD_FILE);
+			PRINT_PERM(FILE_ADD_SUBDIRECTORY);
+			PRINT_PERM(FILE_ALL_ACCESS);
+			PRINT_PERM(FILE_APPEND_DATA);
+			PRINT_PERM(FILE_CREATE_PIPE_INSTANCE);
+			PRINT_PERM(FILE_DELETE_CHILD);
+			PRINT_PERM(FILE_EXECUTE);
+			PRINT_PERM(FILE_LIST_DIRECTORY);
+			PRINT_PERM(FILE_READ_ATTRIBUTES);
+			PRINT_PERM(FILE_READ_DATA);
+			PRINT_PERM(FILE_READ_EA);
+			PRINT_PERM(FILE_TRAVERSE);
+			PRINT_PERM(FILE_WRITE_ATTRIBUTES);
+			PRINT_PERM(FILE_WRITE_DATA);
+			PRINT_PERM(FILE_WRITE_EA);
+			PRINT_PERM(STANDARD_RIGHTS_READ);
+			PRINT_PERM(STANDARD_RIGHTS_WRITE);
+			PRINT_PERM(SYNCHRONIZE);
+			PRINT_PERM(DELETE);
+			PRINT_PERM(READ_CONTROL);
+			PRINT_PERM(WRITE_DAC);
+			PRINT_PERM(WRITE_OWNER);
+			PRINT_PERM(MAXIMUM_ALLOWED);
+			PRINT_PERM(GENERIC_ALL);
+			PRINT_PERM(GENERIC_EXECUTE);
+			PRINT_PERM(GENERIC_WRITE);
+			PRINT_PERM(GENERIC_READ);
+
+			if (data)
+			{
+				printf(" and others (%08x)", data);
+			}
+
+			printf("\n");
+		}
+
+		free(pEntries);
+	}
+	catch(...)
+	{
+		if (pEntries) free(pEntries);
+	}
+}
+#endif
+
+
 // --------------------------------------------------------------------------
 //
 // Function
@@ -534,6 +807,75 @@
 		}
 
 		printf("\n");
+
+		BackupClientFileAttributes storeAttr;
+
+#ifdef WIN32
+		if (opts[LIST_OPTION_WINDOWS_ATTR])
+		{
+			if (en->HasAttributes())
+			{
+				const StreamableMemBlock &storeAttrEnc(
+					en->GetAttributes());
+				storeAttr = BackupClientFileAttributes(storeAttrEnc);
+			}
+			else if (en->GetFlags() & 
+				BackupStoreDirectory::Entry::Flags_Dir)
+			{
+				// TODO: find a way to read the attributes of a directory
+				// without reading and discarding its contents :-(
+
+				mrConnection.QueryListDirectory(
+					en->GetObjectID(),
+					BackupProtocolClientListDirectory::Flags_INCLUDE_EVERYTHING,	
+					// both files and directories
+					excludeFlags,
+					true /* want attributes */);
+
+				// Retrieve the directory from the stream following
+				BackupStoreDirectory dummy;
+				std::auto_ptr<IOStream> dummydirstream(
+					mrConnection.ReceiveStream());
+				dummy.ReadFromStream(*dummydirstream, 
+					mrConnection.GetTimeout());
+
+				const StreamableMemBlock &storeAttrEnc(
+					dummy.GetAttributes());
+				storeAttr = BackupClientFileAttributes(
+					storeAttrEnc);
+			}
+			else
+			{
+				// TODO: find a way to read the attributes of a file
+				// without reading and discarding its contents :-(
+
+				mrConnection.QueryGetFile(DirID, en->GetObjectID());
+			
+				// Stream containing encoded file
+				std::auto_ptr<IOStream> objectStream(
+					mrConnection.ReceiveStream());
+
+				DiscardStream out;
+
+				// Get the decoding stream
+				std::auto_ptr<BackupStoreFile::DecodedStream> stream(
+					BackupStoreFile::DecodeFileStream(
+						*objectStream, mrConnection.GetTimeout(), NULL));
+			
+				// Is it a symlink?
+				if(!stream->IsSymLink())
+				{
+					// Copy it out to the file
+					stream->CopyStreamTo(out);
+				}
+			
+				storeAttr = stream->GetAttributes();
+			}
+
+			StreamableMemBlock attrBlock(storeAttr.GetAttributes());
+			PrintWindowsAttributes(attrBlock);
+		}
+#endif // WIN32
 		
 		// Directory?
 		if((en->GetFlags() & BackupStoreDirectory::Entry::Flags_Dir) != 0)

Modified: box/chris/acl/bin/bbackupquery/documentation.txt
===================================================================
--- box/chris/acl/bin/bbackupquery/documentation.txt	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/bin/bbackupquery/documentation.txt	2006-08-20 22:52:09 UTC (rev 806)
@@ -39,9 +39,10 @@
 	-I -- don't display object ID
 	-F -- don't display flags
 	-t -- show file modification time
-			(and attr mod time if has the object has attributes, ~ separated)
+	      (and attr mod time if has the object has attributes, ~ separated)
 	-s -- show file size in blocks used on server
-			(only very approximate indication of size locally)
+	      (only very approximate indication of size locally)
+	-w -- show Windows attributes and access control list
 
 ls can be used as an alias.
 <

Modified: box/chris/acl/infrastructure/msvc/2005/common.vcproj
===================================================================
--- box/chris/acl/infrastructure/msvc/2005/common.vcproj	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/infrastructure/msvc/2005/common.vcproj	2006-08-20 22:52:09 UTC (rev 806)
@@ -220,6 +220,14 @@
 						>
 					</File>
 					<File
+						RelativePath="..\..\..\lib\common\DiscardStream.cpp"
+						>
+					</File>
+					<File
+						RelativePath="..\..\..\lib\common\DiscardStream.h"
+						>
+					</File>
+					<File
 						RelativePath="..\..\..\lib\common\EventWatchFilesystemObject.cpp"
 						>
 					</File>

Modified: box/chris/acl/lib/backupclient/BackupClientFileAttributes.cpp
===================================================================
--- box/chris/acl/lib/backupclient/BackupClientFileAttributes.cpp	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/lib/backupclient/BackupClientFileAttributes.cpp	2006-08-20 22:52:09 UTC (rev 806)
@@ -7,6 +7,12 @@
 //
 // --------------------------------------------------------------------------
 
+#ifdef WIN32
+// we need features from Windows 2000 and above for ACL support
+#define WINVER 0x0500
+#define _WIN32_WINNT 0x0500
+#endif
+
 #include "Box.h"
 
 #ifdef HAVE_UNISTD_H
@@ -25,6 +31,13 @@
 #include <sys/xattr.h>
 #endif
 
+#ifdef WIN32
+#include <windows.h>
+#include <AccCtrl.h>
+#include <Aclapi.h>
+#include <Sddl.h>
+#endif
+
 #include "BackupClientFileAttributes.h"
 #include "CommonException.h"
 #include "FileModificationTime.h"
@@ -33,56 +46,13 @@
 #include "CipherContext.h"
 #include "CipherBlowfish.h"
 #include "MD5Digest.h"
+#include "CollectInBufferStream.h"
+#include "Archive.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
@@ -234,45 +204,106 @@
 	
 	// 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();
+	attr_StreamFormat_Generic *a1 = (attr_StreamFormat_Generic*)
+		mpClearAttributes->GetBuffer();
+	attr_StreamFormat_Generic *a2 = (attr_StreamFormat_Generic*)
+		rAttr.mpClearAttributes->GetBuffer();
 	
-	if(a1->AttributeType != a2->AttributeType
-		|| a1->UID != a2->UID
-		|| a1->GID != a2->GID
-		|| a1->UserDefinedFlags != a2->UserDefinedFlags
-		|| a1->Mode != a2->Mode)
+	if(a1->AttributeType != a2->AttributeType)
 	{
 		return false;
 	}
-	
-	if(!IgnoreModTime)
+
+	if (ntohl(a1->AttributeType) == ATTRIBUTETYPE_GENERIC_UNIX)
 	{
-		if(a1->ModificationTime != a2->ModificationTime)
+		attr_StreamFormat_Unix* ua1 = (attr_StreamFormat_Unix*)a1;
+		attr_StreamFormat_Unix* ua2 = (attr_StreamFormat_Unix*)a2;
+		if (ua1->UID != ua2->UID
+			|| ua1->GID != ua2->GID
+			|| ua1->UserDefinedFlags != ua2->UserDefinedFlags
+			|| ua1->Mode != ua2->Mode)
 		{
 			return false;
 		}
-	}
+		
+		if(!IgnoreModTime)
+		{
+			if(ua1->ModificationTime != ua2->ModificationTime)
+			{
+				return false;
+			}
+		}
 
-	if(!IgnoreAttrModTime)
-	{
-		if(a1->AttrModificationTime != a2->AttrModificationTime)
+		if(!IgnoreAttrModTime)
 		{
+			if(ua1->AttrModificationTime != ua2->AttrModificationTime)
+			{
+				return false;
+			}
+		}
+		
+		// Check symlink string?
+		unsigned int size = mpClearAttributes->GetSize();
+		if(size == sizeof(attr_StreamFormat_Unix))
+		{
+			return true;
+		}
+
+		// Check whether symlink strings and xattrs match
+		if(::memcmp(ua1 + 1, ua2 + 1, 
+			size - sizeof(attr_StreamFormat_Unix)) != 0)
+		{
 			return false;
 		}
+
+		return true;
 	}
-	
-	// Check symlink string?
-	unsigned int size = mpClearAttributes->GetSize();
-	if(size > sizeof(attr_StreamFormat))
+	else if (ntohl(a1->AttributeType) == ATTRIBUTETYPE_GENERIC_WINDOWS)
 	{
-		// Symlink strings don't match. This also compares xattrs
-		if(::memcmp(a1 + 1, a2 + 1, size - sizeof(attr_StreamFormat)) != 0)
+		attr_StreamFormat_Windows* wa1 = (attr_StreamFormat_Windows*)a1;
+		attr_StreamFormat_Windows* wa2 = (attr_StreamFormat_Windows*)a2;
+
+		if(wa1->Attributes != wa2->Attributes)
 		{
 			return false;
 		}
+
+		if(wa1->CreationTime != wa2->CreationTime)
+		{
+			return false;
+		}
+		
+		if(!IgnoreModTime)
+		{
+			if(wa1->LastWriteTime != wa2->LastWriteTime)
+			{
+				return false;
+			}
+		}
+
+		unsigned int size = mpClearAttributes->GetSize();
+		if(size == sizeof(attr_StreamFormat_Windows))
+		{
+			return true;
+		}
+
+		// The rest of the record is the Windows ACL data, 
+		// which we can compare byte-for-byte for now.
+
+		if(::memcmp(wa1 + 1, wa2 + 1, 
+			size - sizeof(attr_StreamFormat_Windows)) != 0)
+		{
+			return false;
+		}
+
+		return true;
 	}
-	
+	else
+	{
+		// unknown attribute type
+		return false;
+	}
+
 	// Passes all test, must be OK
 	return true;
 }
@@ -292,7 +323,7 @@
 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;
+	StreamableMemBlock *pAttrBlock = 0;
 	try
 	{
 		struct stat st;
@@ -308,72 +339,40 @@
 		if(pInodeNumber) {*pInodeNumber = st.st_ino;}
 		if(pHasMultipleLinks) {*pHasMultipleLinks = (st.st_nlink > 1);}
 
-		pnewAttr = new StreamableMemBlock;
+		pAttrBlock = new StreamableMemBlock;
 
-		FillAttributes(*pnewAttr, Filename, st, ZeroModificationTimes);
+#ifdef WIN32
+		FillAttributesWindows(*pAttrBlock, Filename, st, 
+			ZeroModificationTimes);
+#else // !WIN32
+		FillAttributes(*pAttrBlock, Filename, st, 
+			ZeroModificationTimes);
 
-#ifndef WIN32
 		// Is it a link?
 		if((st.st_mode & S_IFMT) == S_IFLNK)
 		{
-			FillAttributesLink(*pnewAttr, Filename, st);
+			FillAttributesLink(*pAttrBlock, Filename, st);
 		}
-#endif
 
-		FillExtendedAttr(*pnewAttr, Filename);
+		FillExtendedAttr(*pAttrBlock, Filename);
+#endif // WIN32
 
-#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);
+		EncryptAttr(*pAttrBlock);
 		
 		// Store the new attributes
 		RemoveClear();
-		mpClearAttributes = pnewAttr;
-		pnewAttr = 0;
+		mpClearAttributes = pAttrBlock;
+		pAttrBlock = 0;
 	}
 	catch(...)
 	{
 		// clean up
-		delete pnewAttr;
-		pnewAttr = 0;
+		if (pAttrBlock)
+		{
+			delete pAttrBlock;
+			pAttrBlock = 0;
+		}
 		throw;
 	}
 }
@@ -388,8 +387,9 @@
 // --------------------------------------------------------------------------
 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();
+	outputBlock.ResizeBlock(sizeof(attr_StreamFormat_Unix));
+	attr_StreamFormat_Unix *pattr = (attr_StreamFormat_Unix*)
+		outputBlock.GetBuffer();
 	ASSERT(pattr != 0);
 
 	// Fill in the entries
@@ -416,6 +416,242 @@
 	pattr->FileGenerationNumber = htonl(st.st_gen);
 #endif
 }
+
+
+#ifdef WIN32
+void WriteSid(Archive& rArchiver, PSID pSID)
+{
+	CHAR* pSidString;
+	if (!ConvertSidToStringSid(pSID, &pSidString))
+	{
+		::syslog(LOG_WARNING, "Failed to convert Security Identifier "
+			"to a string: error %d", GetLastError());
+		THROW_EXCEPTION(BackupStoreException, LookupAccountFailed)
+	}
+
+	std::string sid(pSidString);
+	LocalFree((HLOCAL)pSidString);
+
+	char namebuf[1024];
+	char domainbuf[1024];
+	SID_NAME_USE nametype;
+	DWORD namelen = sizeof(namebuf);
+	DWORD domainlen = sizeof(domainbuf);
+
+	if(!LookupAccountSid(NULL, pSID, namebuf, &namelen,
+		domainbuf, &domainlen, &nametype))
+	{
+		::syslog(LOG_WARNING, "Failed to find account details for "
+			"'%s': error %d", sid.c_str(), GetLastError());
+		THROW_EXCEPTION(BackupStoreException, LookupAccountFailed)
+	}
+
+	std::string domain(domainbuf);
+	std::string name(namebuf);
+
+	rArchiver.Write((int)nametype);
+	rArchiver.Write(sid);
+	rArchiver.Write(domain);
+	rArchiver.Write(name);
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    BackupClientFileAttributes::FillAttributesWindows()
+//		Purpose: Private function, handles Windows attributes (ACL)
+//		Created: 2006/02/27
+//
+// --------------------------------------------------------------------------
+void BackupClientFileAttributes::FillAttributesWindows
+(
+	StreamableMemBlock &rOutputBlock, const char *pFilename, 
+	struct stat &rStat, bool ZeroLastWriteTime
+)
+{
+	rOutputBlock.ResizeBlock(sizeof(attr_StreamFormat_Windows));
+	attr_StreamFormat_Windows *pAttr = (attr_StreamFormat_Windows*)
+		rOutputBlock.GetBuffer();
+	ASSERT(pAttr != 0);
+
+	// Fill in the entries
+	pAttr->AttributeType = htonl(ATTRIBUTETYPE_GENERIC_WINDOWS);
+	pAttr->CreationTime = box_hton64(rStat.st_ctime * MICRO_SEC_IN_SEC_LL);
+
+	if(ZeroLastWriteTime)
+	{
+		pAttr->LastWriteTime = 0;
+	}
+	else
+	{
+		pAttr->LastWriteTime = box_hton64(FileModificationTime(rStat));
+	}
+
+	// not very efficient to stat the file again, but emu_fstat
+	// doesn't return the information that we need.
+
+	HANDLE handle = OpenFileByNameUtf8(pFilename);
+
+	if (handle == INVALID_HANDLE_VALUE)
+	{
+		::syslog(LOG_WARNING, "Failed to open file to read "
+			"Windows attributes: '%s': error %d", 
+			pFilename, GetLastError());
+		THROW_EXCEPTION(CommonException, OSFileError)
+	}
+
+	BY_HANDLE_FILE_INFORMATION fi;
+	bool result = GetFileInformationByHandle(handle, &fi);
+
+	if (!result)
+	{
+		CloseHandle(handle);
+		::syslog(LOG_WARNING, "Failed to read Windows "
+			"file attributes for '%s': error %d", 
+			pFilename, GetLastError());
+		THROW_EXCEPTION(CommonException, OSFileError)
+	}
+
+	if (INVALID_FILE_ATTRIBUTES == fi.dwFileAttributes)
+	{
+		CloseHandle(handle);
+		::syslog(LOG_WARNING, "Failed to read valid Windows "
+			"file attributes for '%s': error %d", 
+			pFilename, GetLastError());
+		THROW_EXCEPTION(CommonException, OSFileError)
+	}
+
+	pAttr->Attributes = fi.dwFileAttributes;
+
+	// Now start writing our custom data, with the magic and version
+	CollectInBufferStream buffer;
+	Archive archiver(buffer, 0);
+
+	archiver.Write((int) ACL_FORMAT_MAGIC);
+	archiver.Write((int) ACL_FORMAT_VERSION);
+
+	PSID psidOwner = NULL;
+	PSID psidGroup = NULL;
+	PACL pDacl = NULL;
+	PSECURITY_DESCRIPTOR pSecurityDesc = NULL;
+	PEXPLICIT_ACCESS pEntries = NULL;
+
+	try
+	{
+		DWORD result = GetSecurityInfo(
+			handle, // object handle
+			SE_FILE_OBJECT, // ObjectType
+			DACL_SECURITY_INFORMATION  | // SecurityInfo
+			GROUP_SECURITY_INFORMATION |
+			OWNER_SECURITY_INFORMATION,
+			&psidOwner, // ppsidOwner,
+			&psidGroup, // ppsidGroup,
+			&pDacl,     // ppDacl,
+			NULL,       // ppSacl,
+			&pSecurityDesc // ppSecurityDescriptor
+		);
+
+		CloseHandle(handle);
+
+		if (result != ERROR_SUCCESS)
+		{
+			::syslog(LOG_WARNING, "Failed to get Windows "
+				"security info for '%s': error %d",
+				pFilename, result);
+			THROW_EXCEPTION(CommonException, OSFileError)
+		}
+
+		WriteSid(archiver, psidOwner);
+		WriteSid(archiver, psidGroup);
+
+		ULONG numEntries;
+		result = GetExplicitEntriesFromAcl
+		(
+			pDacl,       // pAcl
+			&numEntries, // pcCountOfExplicitEntries,
+			&pEntries    // pListOfExplicitEntries
+		);
+
+		if(result != ERROR_SUCCESS)
+		{
+			::syslog(LOG_WARNING, "Failed to get Windows "
+				"access control list entries for '%s': "
+				"error %d", pFilename, result);
+			THROW_EXCEPTION(CommonException, OSFileError)
+		}
+
+		archiver.Write((int)numEntries);
+
+		for (ULONG i = 0; i < numEntries; i++)
+		{
+			EXPLICIT_ACCESS* pEntry = &(pEntries[i]);
+
+			archiver.Write((int)pEntry->grfAccessPermissions);
+			archiver.Write((int)pEntry->grfAccessMode);
+			archiver.Write((int)pEntry->grfInheritance);
+			archiver.Write((int)pEntry->Trustee.TrusteeForm);
+			archiver.Write((int)pEntry->Trustee.TrusteeType);
+
+			if(pEntry->Trustee.pMultipleTrustee != NULL)
+			{
+				::syslog(LOG_WARNING, "Failed to process "
+					"access control entry for '%s': "
+					"multiple trustees not supported",
+					pFilename);
+				THROW_EXCEPTION(CommonException, OSFileError);
+			}
+
+			if(pEntry->Trustee.MultipleTrusteeOperation != 
+				NO_MULTIPLE_TRUSTEE)
+			{
+				::syslog(LOG_WARNING, "Failed to process "
+					"access control entry for '%s': "
+					"multiple trustees not supported (2)",
+					pFilename);
+				THROW_EXCEPTION(CommonException, OSFileError);
+			}
+
+			switch(pEntry->Trustee.TrusteeForm)
+			{
+			case TRUSTEE_IS_SID:
+				{
+					PSID trusteeSid = (PSID)(
+						pEntry->Trustee.ptstrName);
+					WriteSid(archiver, trusteeSid);
+				}
+				break;
+			default:
+				::syslog(LOG_WARNING, "Failed to process "
+					"access control entry for '%s': "
+					"trustee form %d not supported",
+					pFilename, 
+					pEntry->Trustee.TrusteeForm);
+				THROW_EXCEPTION(CommonException, OSFileError);
+			}
+
+		}
+	}
+	catch(...)
+	{
+		if (pEntries)      LocalFree((HLOCAL)pEntries);
+		if (pSecurityDesc) LocalFree((HLOCAL)pSecurityDesc);
+		throw;
+	}
+
+	LocalFree((HLOCAL)pEntries);
+	LocalFree((HLOCAL)pSecurityDesc);
+
+	// Copy the serialised data into the output block
+	buffer.SetForReading();
+	rOutputBlock.ResizeBlock(rOutputBlock.GetSize() + buffer.GetSize());
+
+	// block might have moved after resize
+	pAttr = (attr_StreamFormat_Windows *)(rOutputBlock.GetBuffer());
+	memcpy(pAttr + 1, buffer.GetBuffer(), buffer.GetSize());
+}
+#endif // WIN32
+
+
 #ifndef WIN32
 // --------------------------------------------------------------------------
 //
@@ -597,6 +833,15 @@
 	}
 	int32_t *type = (int32_t*)mpClearAttributes->GetBuffer();
 	ASSERT(type != 0);
+
+#ifdef WIN32
+	if(ntohl(*type) == ATTRIBUTETYPE_GENERIC_WINDOWS)
+	{
+		WriteAttributesWindows(Filename);
+		return;
+	}
+#endif
+
 	if(ntohl(*type) != ATTRIBUTETYPE_GENERIC_UNIX)
 	{
 		// Don't know what to do with these
@@ -604,22 +849,24 @@
 	}
 	
 	// Check there is enough space for an attributes block
-	if(mpClearAttributes->GetSize() < (int)sizeof(attr_StreamFormat))
+	if(mpClearAttributes->GetSize() < (int)sizeof(attr_StreamFormat_Unix))
 	{
 		// Too small
 		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
 	}
 
 	// Get pointer to structure
-	attr_StreamFormat *pattr = (attr_StreamFormat*)mpClearAttributes->GetBuffer();
-	int xattrOffset = sizeof(attr_StreamFormat);
+	attr_StreamFormat_Unix *pattr = (attr_StreamFormat_Unix*)
+		mpClearAttributes->GetBuffer();
+	int xattrOffset = sizeof(attr_StreamFormat_Unix);
 
 	// 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)
+		if(mpClearAttributes->GetSize() < 
+			(int)sizeof(attr_StreamFormat_Unix) + 1)
 		{
 			// Too small
 			THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
@@ -715,9 +962,248 @@
 }
 
 
+#ifdef WIN32
+bool ReadSid(Archive& rArchive, PSID* pPSID)
+{
+	int nametype;
+	rArchive.Read(nametype);
+
+	std::string sid;
+	rArchive.Read(sid);
+
+	std::string domain;
+	rArchive.Read(domain);
+
+	std::string name;
+	rArchive.Read(name);
+
+	name = domain + '\\' + name;
+
+	DWORD sidBufSize = 0;
+	DWORD domainBufSize = 0;
+	SID_NAME_USE use;
+
+	// TODO: if we don't find the account name, 
+	// use the stored SID instead
+
+	if (!LookupAccountName(NULL, name.c_str(), NULL, &sidBufSize,
+		NULL, &domainBufSize, &use) && 
+		GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+	{
+		::syslog(LOG_ERR, "Failed to lookup account details for "
+			"'%s': error %d", name.c_str(), GetLastError());
+		return false;
+	}
+
+	*pPSID = malloc(sidBufSize);
+	TCHAR* domainstr = (TCHAR *)malloc(domainBufSize);
+
+	BOOL result = LookupAccountName(NULL, name.c_str(),
+		*pPSID, &sidBufSize, domainstr, &domainBufSize, &use);
+	free(domainstr);
+
+	if (!result)
+	{
+		::syslog(LOG_ERR, "Failed to lookup account details for "
+			"'%s': error %d", name.c_str(), GetLastError());
+		free(*pPSID);
+		*pPSID = NULL;
+		return false;
+	}
+
+	return true;
+}
+
+
 // --------------------------------------------------------------------------
 //
 // Function
+//		Name:    BackupClientFileAttributes::WriteAttributesWindows(
+//			 const char *)
+//		Purpose: Apply the stored Windows attributes to the file
+//		Created: 2006/03/12
+//
+// --------------------------------------------------------------------------
+void BackupClientFileAttributes::WriteAttributesWindows(const char *Filename) 
+const
+{
+	// Check there is enough space for an attributes block
+	if(mpClearAttributes->GetSize() < 
+		(int)sizeof(attr_StreamFormat_Windows))
+	{
+		// Too small
+		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
+	}
+
+	// Get pointer to structure
+	attr_StreamFormat_Windows *pAttr = (attr_StreamFormat_Windows*)
+		mpClearAttributes->GetBuffer();
+
+	std::wstring wideName;
+	if (!GetFileNameWide(Filename, wideName))
+	{
+		THROW_EXCEPTION(CommonException, OSFileError);
+	}
+
+	DWORD attributes = pAttr->Attributes;
+	attributes &= ~FILE_ATTRIBUTE_OFFLINE;
+	attributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
+
+	if (!SetFileAttributesW(wideName.c_str(), attributes))
+	{
+		::syslog(LOG_ERR, "Failed to restore attributes of '%s': "
+			"error %d", Filename, GetLastError());
+		return;
+	}
+
+	CollectInBufferStream buffer;
+	buffer.Write(pAttr + 1, mpClearAttributes->GetSize() -
+		sizeof(attr_StreamFormat_Windows));
+	buffer.SetForReading();
+
+	Archive archive(buffer, 0);
+	int data;
+
+	archive.Read(data);
+	if (data != ACL_FORMAT_MAGIC)
+	{
+		::syslog(LOG_ERR, "Failed to restore ACL of '%s': "
+			"wrong magic in attribute block", Filename);
+		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
+	}
+
+	archive.Read(data);
+	if (data != ACL_FORMAT_VERSION)
+	{
+		::syslog(LOG_ERR, "Failed to restore ACL of '%s': "
+			"wrong version in attribute block", Filename);
+		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
+	}
+
+	PACL pNewAcl = NULL;
+	PSID pSidOwner = NULL, pSidGroup = NULL;
+
+	if (!ReadSid(archive, &pSidOwner))
+	{
+		::syslog(LOG_ERR, "Failed to restore ACL of '%s': "
+			"failed to decode file owner", Filename);
+		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
+	}
+
+	if (!ReadSid(archive, &pSidGroup))
+	{
+		::syslog(LOG_ERR, "Failed to restore ACL of '%s': "
+			"failed to decode file group", Filename);
+		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
+	}
+
+	int numEntries;
+	archive.Read(numEntries);
+
+	EXPLICIT_ACCESS* pEntries = (EXPLICIT_ACCESS*)calloc(numEntries, 
+		sizeof(EXPLICIT_ACCESS));
+
+	if (!pEntries)
+	{
+		::syslog(LOG_ERR, "Failed to restore ACL of '%s': "
+			"out of memory", Filename);
+		if (pSidOwner) free(pSidOwner);
+		if (pSidGroup) free(pSidGroup);
+		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
+	}
+	
+	for (int i = 0; i < numEntries; i++)
+	{
+		PEXPLICIT_ACCESS pEntry = &(pEntries[i]);
+
+		int data;
+		archive.Read(data); pEntry->grfAccessPermissions = data;
+		archive.Read(data); pEntry->grfAccessMode = (ACCESS_MODE)data;
+		archive.Read(data); pEntry->grfInheritance = data;
+		archive.Read(data); pEntry->Trustee.TrusteeForm = 
+			(TRUSTEE_FORM)data;
+		archive.Read(data); pEntry->Trustee.TrusteeType = 
+			(TRUSTEE_TYPE)data;
+
+		if (pEntry->Trustee.TrusteeForm != TRUSTEE_IS_SID)
+		{
+			::syslog(LOG_ERR, "Failed to restore ACL of '%s': "
+				"only SID trustees are supported", Filename);
+			if (pSidOwner) free(pSidOwner);
+			if (pSidGroup) free(pSidGroup);
+			THROW_EXCEPTION(BackupStoreException, 
+				AttributesNotLoaded);
+		}
+
+		LPSTR* pPSIDstr = &pEntry->Trustee.ptstrName;
+		if (!ReadSid(archive, (PSID*)pPSIDstr))
+		{
+			::syslog(LOG_ERR, "Failed to restore ACL of '%s': "
+				"failed to read ACL entry %d SID", Filename, i);
+			if (pSidOwner) free(pSidOwner);
+			if (pSidGroup) free(pSidGroup);
+			THROW_EXCEPTION(BackupStoreException, 
+				AttributesNotLoaded);
+		}
+	}
+		
+	DWORD result = SetEntriesInAcl(numEntries, pEntries, NULL, &pNewAcl);
+
+	for (int i = 0; i < numEntries; i++)
+	{
+		PEXPLICIT_ACCESS pEntry = &(pEntries[i]);
+		PSID pSid = (PSID)(pEntry->Trustee.ptstrName);
+		free(pSid);
+	}
+
+	free(pEntries);
+
+	if (result != ERROR_SUCCESS)
+	{
+		::syslog(LOG_ERR, "Failed to create access control list "
+			"for '%s': error %d", Filename, result);
+		if (pSidOwner) free(pSidOwner);
+		if (pSidGroup) free(pSidGroup);
+		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
+	}
+
+	WCHAR* pNameBuffer = new WCHAR [wideName.size() + 1];
+	if (!pNameBuffer)
+	{
+		::syslog(LOG_ERR, "Failed to create access control list "
+			"for '%s': out of memory", Filename);
+		if (pSidOwner) free(pSidOwner);
+		if (pSidGroup) free(pSidGroup);
+		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
+	}
+
+	memcpy(pNameBuffer, wideName.c_str(), wideName.size() * sizeof(WCHAR));
+
+	result = SetNamedSecurityInfoW(pNameBuffer, SE_FILE_OBJECT,
+		DACL_SECURITY_INFORMATION | 
+		OWNER_SECURITY_INFORMATION |
+		GROUP_SECURITY_INFORMATION
+		/* | UNPROTECTED_DACL_SECURITY_INFORMATION */,
+		pSidOwner, pSidGroup, pNewAcl, NULL);
+
+	delete [] pNameBuffer;
+	LocalFree((HLOCAL)pNewAcl);
+
+	if (result != ERROR_SUCCESS)
+	{
+		::syslog(LOG_ERR, "Failed to apply access control list "
+			"to '%s': error %d", Filename, result);
+		if (pSidOwner) free(pSidOwner);
+		if (pSidGroup) free(pSidGroup);
+		THROW_EXCEPTION(BackupStoreException, AttributesNotLoaded);
+	}
+}
+#endif
+
+
+// --------------------------------------------------------------------------
+//
+// Function
 //		Name:    BackupClientFileAttributes::IsSymLink()
 //		Purpose: Do these attributes represent a symbolic link?
 //		Created: 2003/10/07
@@ -736,10 +1222,12 @@
 	// 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))
+	if(ntohl(*type) == ATTRIBUTETYPE_GENERIC_UNIX && 
+		mpClearAttributes->GetSize() > (int)sizeof(attr_StreamFormat_Unix))
 	{
 		// Check link
-		attr_StreamFormat *pattr = (attr_StreamFormat*)mpClearAttributes->GetBuffer();
+		attr_StreamFormat_Unix *pattr = (attr_StreamFormat_Unix*)
+			mpClearAttributes->GetBuffer();
 		return ((ntohs(pattr->Mode)) & S_IFMT) == S_IFLNK;
 	}
 	

Modified: box/chris/acl/lib/backupclient/BackupClientFileAttributes.h
===================================================================
--- box/chris/acl/lib/backupclient/BackupClientFileAttributes.h	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/lib/backupclient/BackupClientFileAttributes.h	2006-08-20 22:52:09 UTC (rev 806)
@@ -17,6 +17,69 @@
 
 struct stat;
 
+// 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 ATTRIBUTETYPE_GENERIC_WINDOWS	2
+
+#ifdef WIN32
+#define ACL_FORMAT_MAGIC   0x31415927
+#define ACL_FORMAT_VERSION 0x1
+#endif
+
+typedef struct 
+{
+	int32_t		AttributeType;
+} attr_StreamFormat_Generic;
+
+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_Unix;
+
+typedef struct 
+{
+	int32_t		AttributeType;
+	u_int32_t	Attributes;
+	u_int64_t	CreationTime;
+	u_int64_t	LastWriteTime;
+} attr_StreamFormat_Windows;
+	
+// 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
+
 // --------------------------------------------------------------------------
 //
 // Class
@@ -58,6 +121,14 @@
 private:
 	static void FillAttributes(StreamableMemBlock &outputBlock, const char *Filename, struct stat &st, bool ZeroModificationTimes);
 	static void FillAttributesLink(StreamableMemBlock &outputBlock, const char *Filename, struct stat &st);
+
+#ifdef WIN32
+	static void FillAttributesWindows(StreamableMemBlock &rOutputBlock, 
+		const char* pFilename, struct stat &rStat,
+		bool ZeroModificationTimes);
+	void WriteAttributesWindows(const char *Filename) const;
+#endif
+
 	void WriteExtendedAttr(const char *Filename, int xattrOffset) const;
 
 	void RemoveClear() const;
@@ -65,6 +136,13 @@
 	static StreamableMemBlock *MakeClear(const StreamableMemBlock &rEncrypted);
 	void EncryptAttr(const StreamableMemBlock &rToEncrypt);
 
+public:
+	const StreamableMemBlock& GetAttributes()
+	{
+		EnsureClearAvailable();
+		return *mpClearAttributes;
+	}
+
 private:
 	mutable StreamableMemBlock *mpClearAttributes;
 };

Modified: box/chris/acl/lib/backupclient/BackupClientRestore.cpp
===================================================================
--- box/chris/acl/lib/backupclient/BackupClientRestore.cpp	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/lib/backupclient/BackupClientRestore.cpp	2006-08-20 22:52:09 UTC (rev 806)
@@ -325,7 +325,12 @@
 	// Apply attributes to the directory
 	const StreamableMemBlock &dirAttrBlock(dir.GetAttributes());
 	BackupClientFileAttributes dirAttr(dirAttrBlock);
+
+	// too early to apply attributes on Win32: 
+	// might prevent us from accessing or writing to the directory
+#ifndef WIN32
 	dirAttr.WriteAttributes(rLocalDirectoryName.c_str());
+#endif
 
 	int64_t bytesWrittenSinceLastRestoreInfoSave = 0;
 	
@@ -438,6 +443,10 @@
 		}
 	}
 
+#ifdef WIN32
+	dirAttr.WriteAttributes(rLocalDirectoryName.c_str());
+#endif
+
 	return Restore_Complete;
 }
 

Modified: box/chris/acl/lib/backupclient/BackupStoreException.txt
===================================================================
--- box/chris/acl/lib/backupclient/BackupStoreException.txt	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/lib/backupclient/BackupStoreException.txt	2006-08-20 22:52:09 UTC (rev 806)
@@ -68,3 +68,4 @@
 IncompatibleFromAndDiffFiles	65	Attempt to use a diff and a from file together, when they're not related
 DiffFromIDNotFoundInDirectory	66	When uploading via a diff, the diff from file must be in the same directory
 PatchChainInfoBadInDirectory	67	A directory contains inconsistent information. Run bbstoreaccounts check to fix it.
+LookupAccountFailed		68	The file's security information refers to an account that could not be found

Copied: box/chris/acl/lib/common/DiscardStream.cpp (from rev 532, box/chris/general/lib/common/CollectInBufferStream.cpp)
===================================================================
--- box/chris/acl/lib/common/DiscardStream.cpp	                        (rev 0)
+++ box/chris/acl/lib/common/DiscardStream.cpp	2006-08-20 22:52:09 UTC (rev 806)
@@ -0,0 +1,131 @@
+// --------------------------------------------------------------------------
+//
+// File
+//		Name:    DiscardStream.cpp
+//		Purpose: Discards data written to it
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+
+#include <string.h>
+
+#include "DiscardStream.h"
+#include "CommonException.h"
+
+#include "MemLeakFindOn.h"
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    DiscardStream::DiscardStream()
+//		Purpose: Constructor
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+DiscardStream::DiscardStream()
+{
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    DiscardStream::~DiscardStream()
+//		Purpose: Destructor
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+DiscardStream::~DiscardStream()
+{
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    DiscardStream::Read(void *, int, int)
+//		Purpose: As interface. Never reads anything :-)
+//		Created: 2003/08/26
+//
+// --------------------------------------------------------------------------
+int DiscardStream::Read(void *pBuffer, int NBytes, int Timeout)
+{
+	return 0;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    DiscardStream::BytesLeftToRead()
+//		Purpose: As interface. Never anything to read.
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+IOStream::pos_type DiscardStream::BytesLeftToRead()
+{
+	return 0;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    DiscardStream::Write(void *, int)
+//		Purpose: As interface. Dicards written data.
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+void DiscardStream::Write(const void *pBuffer, int NBytes)
+{
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    DiscardStream::GetPosition()
+//		Purpose: As interface. Always returns 0.
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+IOStream::pos_type DiscardStream::GetPosition() const
+{
+	return 0;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    DiscardStream::Seek(pos_type, int)
+//		Purpose: As interface. Does nothing.
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+void DiscardStream::Seek(pos_type Offset, int SeekType)
+{
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    DiscardStream::StreamDataLeft()
+//		Purpose: As interface. Always returns false.
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+bool DiscardStream::StreamDataLeft()
+{
+	return false;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    DiscardStream::StreamClosed()
+//		Purpose: As interface. Always returns false.
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+bool DiscardStream::StreamClosed()
+{
+	return false;
+}
+

Copied: box/chris/acl/lib/common/DiscardStream.h (from rev 532, box/chris/general/lib/common/CollectInBufferStream.h)
===================================================================
--- box/chris/acl/lib/common/DiscardStream.h	                        (rev 0)
+++ box/chris/acl/lib/common/DiscardStream.h	2006-08-20 22:52:09 UTC (rev 806)
@@ -0,0 +1,44 @@
+// --------------------------------------------------------------------------
+//
+// File
+//		Name:    DiscardStream.h
+//		Purpose: Discards data written to it
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+
+#ifndef DISCARDSTREAM__H
+#define DISCARDSTREAM__H
+
+#include "IOStream.h"
+
+// --------------------------------------------------------------------------
+//
+// Class
+//		Name:    DiscardStream
+//		Purpose: Discards data written to it
+//		Created: 2006/03/06
+//
+// --------------------------------------------------------------------------
+class DiscardStream : public IOStream
+{
+public:
+	DiscardStream();
+	~DiscardStream();
+private:
+	// No copying
+	DiscardStream(const DiscardStream &);
+	DiscardStream(const IOStream &);
+public:
+
+	virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite);
+	virtual pos_type BytesLeftToRead();
+	virtual void Write(const void *pBuffer, int NBytes);
+	virtual pos_type GetPosition() const;
+	virtual void Seek(pos_type Offset, int SeekType);
+	virtual bool StreamDataLeft();
+	virtual bool StreamClosed();
+};
+
+#endif // DISCARDSTREAM__H
+

Modified: box/chris/acl/lib/win32/emu.cpp
===================================================================
--- box/chris/acl/lib/win32/emu.cpp	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/lib/win32/emu.cpp	2006-08-20 22:52:09 UTC (rev 806)
@@ -663,6 +663,43 @@
 // --------------------------------------------------------------------------
 //
 // Function
+//		Name:    GetFileNameWide(const std::string& rIn, std::wstring&
+//			rOut)
+//		Purpose: Converts filename to Unicode and returns it
+//			in rOut, returning true. In case of error, 
+//			sets errno, logs the error and returns false.
+//		Created: 2006/03/13
+//
+// --------------------------------------------------------------------------
+bool GetFileNameWide(const std::string& rIn, std::wstring& rOut)
+{
+	std::string AbsPathWithUnicode = 
+		ConvertPathToAbsoluteUnicode(rIn.c_str());
+	
+	if (AbsPathWithUnicode.size() == 0)
+	{
+		// error already logged by ConvertPathToAbsoluteUnicode()
+		return false;
+	}
+	
+	WCHAR* pBuffer = ConvertUtf8ToWideString(AbsPathWithUnicode.c_str());
+	// We are responsible for freeing pBuffer
+	
+	if (pBuffer == NULL)
+	{
+		// error already logged by ConvertUtf8ToWideString()
+		return false;
+	}
+
+	rOut = pBuffer;
+	delete [] pBuffer;
+	return true;
+}
+
+
+// --------------------------------------------------------------------------
+//
+// Function
 //		Name:    OpenFileByNameUtf8
 //		Purpose: Converts filename to Unicode and returns 
 //			a handle to it. In case of error, sets errno,

Modified: box/chris/acl/lib/win32/emu.h
===================================================================
--- box/chris/acl/lib/win32/emu.h	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/lib/win32/emu.h	2006-08-20 22:52:09 UTC (rev 806)
@@ -365,6 +365,10 @@
 // replacement for _cgetws which requires a relatively recent C runtime lib
 int console_read(char* pBuffer, size_t BufferSize);
 
+// used by BackupClientFileAttributes to get Windows file attributes
+HANDLE OpenFileByNameUtf8(const char* pFileName);
+bool GetFileNameWide(const std::string& rIn, std::wstring& rOut);
+
 struct iovec {
 	void *iov_base;   /* Starting address */
 	size_t iov_len;   /* Number of bytes */

Modified: box/chris/acl/test/win32/testlibwin32.cpp
===================================================================
--- box/chris/acl/test/win32/testlibwin32.cpp	2006-08-20 22:46:33 UTC (rev 805)
+++ box/chris/acl/test/win32/testlibwin32.cpp	2006-08-20 22:52:09 UTC (rev 806)
@@ -329,6 +329,12 @@
 
 	closelog();
 
+	struct stat st;
+	assert(!emu_stat("c:\\cygwin\\home\\administrator\\pcre-6.3\\.libs", &st));
+	printf("%lld\n", (long long)st.st_size);
+	assert(!emu_stat("c:\\cygwin\\home\\administrator\\pcre-6.3\\.libs", &st));
+	printf("%lld\n", (long long)st.st_size);
+
 	/*
 	//first off get the path name for the default 
 	char buf[MAX_PATH];