JNIEXPORT jstring JNICALL Java_com_microsoft_tfs_jni_internal_filesystem_NativeFileSystem_nativeGetOwner(JNIEnv *env,
    jclass cls, jstring jPath)
{
    const WCHAR * path = NULL;
    DWORD result = 0;
    PSECURITY_DESCRIPTOR securityDescriptor = NULL;
    PSID ownerSID = NULL;
    WCHAR * ownerSIDString = NULL;
    jstring jOwnerSIDString = NULL;

    if (jPath == NULL)
    {
		throwRuntimeExceptionString(env, "path must not be null");
        goto cleanup;
    }

    if ((path = javaStringToPlatformChars(env, jPath)) == NULL)
	{
		// String allocation failed, exception already thrown
		goto cleanup;
	}

	// Get sid, which points into securityDescriptor
	result = GetNamedSecurityInfoW(path, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, 
		&ownerSID, NULL, NULL, NULL, &securityDescriptor);
    if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error getting file security info for %S", path);
		goto cleanup;
	}

	// Convert to string SID
	if (ConvertSidToStringSidW(ownerSID, &ownerSIDString) == FALSE)
    {
		throwRuntimeExceptionCode(env, GetLastError(), "Error converting sid to string sid");
		goto cleanup;
	}

    jOwnerSIDString = platformCharsToJavaString(env, ownerSIDString);

cleanup:

	if (path != NULL)
	{
	    releasePlatformChars(env, jPath, path);
	}
	// ownerSID points inside securityDescriptor
	if (securityDescriptor != NULL)
	{
		LocalFree(securityDescriptor);
	}
	if (ownerSIDString != NULL)
	{
		LocalFree(ownerSIDString);
	}

    return jOwnerSIDString;
}
void throwRuntimeExceptionCode(_In_ JNIEnv * env, platform_error errorCode, _Printf_format_string_ const char * prefixFormat, ...)
{
	va_list ap;
	char * prefix = NULL;
#if defined(__solaris__) || defined(__hpux__)
    char * tmpErrorString = NULL;
#endif
	char errorString[MESSAGE_BUFFER_SIZE];
	char message[MESSAGE_BUFFER_SIZE];

	if (prefixFormat != NULL)
	{
		va_start(ap, prefixFormat);
		prefix = tee_vsprintf(prefixFormat, ap);
		va_end(ap);
	}

	// Format errorString
#ifdef _WIN32
	// String is single-byte wide.  Use FormatMessageA instead of Unicode method.
	if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode, 0, errorString, 
		MESSAGE_BUFFER_SIZE, NULL) == 0)
	{
		sprintf_s(errorString, MESSAGE_BUFFER_SIZE, "Unknown error: %d", errorCode);
	}
#elif defined(__solaris__) || defined(__hpux__)
    /*
      strerror_r is not available on Solaris, but strerror is thread-safe.
      It's marked "obsolescent" on HP-UX, and strerror is thread-safe there, 
      too.
    */
    tmpErrorString = strerror(errorCode);
    if (tmpErrorString != NULL)
    {
        snprintf(errorString, MESSAGE_BUFFER_SIZE, "%s", tmpErrorString);
    }
    else
    {
        snprintf(errorString, MESSAGE_BUFFER_SIZE, "Unknown error: %d", errorCode);
    }
#else
	if (strerror_r(errorCode, errorString, MESSAGE_BUFFER_SIZE) != 0)
	{
		snprintf(errorString, MESSAGE_BUFFER_SIZE, "Unknown error: %d", errorCode);
	}
#endif

	// Format with a prefix if it was provided
	if (prefix != NULL)
	{
#ifdef _WIN32
		sprintf_s(message, MESSAGE_BUFFER_SIZE, "%s: %s", prefix, errorString);
#else
		snprintf(message, MESSAGE_BUFFER_SIZE, "%s: %s", prefix, errorString);
#endif
		free(prefix);

		throwRuntimeExceptionString(env, message);
	}
	else
	{
		throwRuntimeExceptionString(env, errorString);
	}
}
JNIEXPORT void JNICALL Java_com_microsoft_tfs_jni_internal_filesystem_NativeFileSystem_nativeSetOwner(JNIEnv *env,
	jclass cls, jstring jPath, jstring jOwnerSIDString)
{
    const WCHAR * path= NULL;
    const WCHAR * ownerSIDString = NULL;
    PSID ownerSID = NULL;
    DWORD result = 0;

    if (jPath == NULL)
    {
       	throwRuntimeExceptionString(env, "path must not be null");
		goto cleanup;
    }

	if (jOwnerSIDString == NULL)
	{
		throwRuntimeExceptionString(env, "user must not be null");
		goto cleanup;
	}

    if ((ownerSIDString = javaStringToPlatformChars(env, jOwnerSIDString)) == NULL)
	{
		// String allocation failed, exception already thrown
		goto cleanup;
	}
    
	if (ConvertStringSidToSidW(ownerSIDString, &ownerSID) == FALSE)
	{
		throwRuntimeExceptionCode(env, GetLastError(), "Error converting string %S sid to sid", ownerSIDString);
		goto cleanup;
	}

    if ((path = javaStringToPlatformChars(env, jPath)) == NULL)
	{
		// String allocation failed, exception already thrown
		goto cleanup;
	}

	result = SetNamedSecurityInfoW((WCHAR *) path, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, 
		ownerSID, NULL, NULL, NULL);
    if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error getting file security info for %S", path);
		goto cleanup;
	}

cleanup:

	if (ownerSIDString != NULL)
	{
	   releasePlatformChars(env, jOwnerSIDString, ownerSIDString);
	}
	if (path != NULL)
	{
		releasePlatformChars(env, jPath, path);
	}
	if (ownerSID != NULL)
	{
		LocalFree(ownerSID);
	}
}
JNIEXPORT void JNICALL Java_com_microsoft_tfs_jni_internal_filesystem_NativeFileSystem_nativeCopyExplicitDACLEntries(
    JNIEnv *env, jclass cls, jstring jSourcePath, jstring jTargetPath)
{
	const WCHAR * sourcePath = NULL;
	const WCHAR * targetPath = NULL;
	DWORD result = 0;
	PACL sourceDACL = NULL;
	PACL targetDACL = NULL;
	PACL newDACL = NULL;
	PSECURITY_DESCRIPTOR sourceSecurityDescriptor = NULL;
	PSECURITY_DESCRIPTOR targetSecurityDescriptor = NULL;
	PEXPLICIT_ACCESS sourceExplicitEntries = NULL;
	ULONG sourceExplicitEntriesCount = 0;

	if (jSourcePath == NULL)
    {
       	throwRuntimeExceptionString(env, "source path must not be null");
		goto cleanup;
    }

	if (jTargetPath == NULL)
	{
		throwRuntimeExceptionString(env, "target path must not be null");
		goto cleanup;
	}

	if ((sourcePath = javaStringToPlatformChars(env, jSourcePath)) == NULL)
	{
		// String allocation failed, exception already thrown
		goto cleanup;
	}

	if ((targetPath = javaStringToPlatformChars(env, jTargetPath)) == NULL)
	{
		// String allocation failed, exception already thrown
		goto cleanup;
	}

	// Get source's DACL
	result = GetNamedSecurityInfo(sourcePath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, 
		NULL, NULL, &sourceDACL, NULL, &sourceSecurityDescriptor);
	if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error getting security info for %S", sourcePath);
		goto cleanup;
	}

	// Get the explicit entries in the source DACL
	result = GetExplicitEntriesFromAcl(sourceDACL, &sourceExplicitEntriesCount, &sourceExplicitEntries);
	if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error getting ACL entries");
		goto cleanup;
	}

	if (sourceExplicitEntries == 0)
	{
		goto cleanup;
	}

	// Get target's DACL
	result = GetNamedSecurityInfo(targetPath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, 
		NULL, NULL, &targetDACL, NULL, &targetSecurityDescriptor);
	if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error getting security info for %S", targetPath);
		goto cleanup;
	}

	// Merge the source entries into the target list
	result = SetEntriesInAcl(sourceExplicitEntriesCount, sourceExplicitEntries, targetDACL, &newDACL);
    if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error setting entries in ACL");
        goto cleanup;
    }

	// Set the list on the target path
	result = SetNamedSecurityInfo((WCHAR *) targetPath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
		NULL, NULL, newDACL, NULL);
	if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error setting security info for %S", targetPath);
        goto cleanup;
	}

cleanup:

	if (sourcePath != NULL)
	{
		releasePlatformChars(env, jSourcePath, sourcePath);
	}
	if (targetPath != NULL)
	{
		releasePlatformChars(env, jTargetPath, targetPath);
	}
	// sourceDACL points into sourceSecurityDescriptor
	if (sourceSecurityDescriptor != NULL)
	{
		LocalFree(sourceSecurityDescriptor);
	}
	// targetDACL points into targetSecurityDescriptor
	if (targetSecurityDescriptor != NULL)
	{
		LocalFree(targetSecurityDescriptor);
	}
	if (sourceExplicitEntries != NULL)
	{
		LocalFree(sourceExplicitEntries);
	}
	if (newDACL != NULL)
	{
		LocalFree(newDACL);
	}
}
JNIEXPORT void JNICALL Java_com_microsoft_tfs_jni_internal_filesystem_NativeFileSystem_nativeRemoveExplicitAllowEntries(
    JNIEnv *env, jclass cls, jstring jPath, jstring jUserSIDString)
{
	const WCHAR * path = NULL;
	const WCHAR * userSIDString = NULL;
	PSID userSID = NULL;
	DWORD result = 0;
	PACL dacl = NULL;
	PSECURITY_DESCRIPTOR securityDescriptor = NULL;
	ACL_SIZE_INFORMATION aclSizeInfo;
	ULONG aceCount = 0;
	BOOL modifiedDACL = FALSE;

	if (jPath == NULL)
    {
       	throwRuntimeExceptionString(env, "path must not be null");
		goto cleanup;
    }

	if (jUserSIDString == NULL)
    {
       	throwRuntimeExceptionString(env, "user must not be null");
		goto cleanup;
    }
	
	// Convert the SID string to a struct
	if ((userSIDString = javaStringToPlatformChars(env, jUserSIDString)) == NULL)
	{
		// String allocation failed, exception already thrown
		goto cleanup;
	}

	if (ConvertStringSidToSidW(userSIDString, &userSID) == FALSE)
	{
		throwRuntimeExceptionCode(env, GetLastError(), "Error converting string sid %S to sid", userSIDString);
		goto cleanup;
	}

	if ((path = javaStringToPlatformChars(env, jPath)) == NULL)
	{
		// String allocation failed, exception already thrown
		goto cleanup;
	}

	// Get file's DACL
	result = GetNamedSecurityInfo(path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, 
		NULL, NULL, &dacl, NULL, &securityDescriptor);
	if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error getting file security info for %S", path);
		goto cleanup;
	}

	// Get the count of entries int the DACL
	if (GetAclInformation(dacl, &aclSizeInfo, sizeof(aclSizeInfo), AclSizeInformation) == 0)
	{
		throwRuntimeExceptionCode(env, GetLastError(), "Error getting DACL");
		goto cleanup;
	}

	// Loop over the DACL backwards, removing matching entries
	for (aceCount = aclSizeInfo.AceCount; aceCount > 0; aceCount--)
	{
		ULONG aceIndex = aceCount - 1;
		ACCESS_ALLOWED_ACE * ace = NULL;
		PSID sid = NULL;

		if (GetAce(dacl, aceIndex, (LPVOID *) &ace) == 0)
		{
			throwRuntimeExceptionCode(env, GetLastError(), "Error getting ACE at index %d", aceIndex);
			goto cleanup;
		}

		// Skip inherited (non-explicit) entries
		if ((((ACE_HEADER *) ace)->AceFlags & INHERITED_ACE) == INHERITED_ACE)
		{
			continue;
		}

		// Extract the SID for "allow" types
		switch(((ACE_HEADER *) ace)->AceType)
		{
			case ACCESS_ALLOWED_ACE_TYPE:
				sid = (PSID) &((ACCESS_ALLOWED_ACE *) ace)->SidStart;
				break;
			case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
				sid = (PSID) &((ACCESS_ALLOWED_CALLBACK_ACE *) ace)->SidStart;
				break;
			case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
				sid = (PSID) &((ACCESS_ALLOWED_CALLBACK_OBJECT_ACE *) ace)->SidStart;
				break;
			case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
				sid = (PSID) &((ACCESS_ALLOWED_OBJECT_ACE *) ace)->SidStart;
				break;
			default:
				// These are "deny" or other entries
				break;
		}

		if (sid != NULL && EqualSid(sid, userSID))
		{
			if (DeleteAce(dacl, aceIndex) == 0)
			{
				throwRuntimeExceptionCode(env, GetLastError(), "Error deleting ACE at index %d", aceIndex);
				goto cleanup;
			}

			modifiedDACL = TRUE;
		}

		// Nothing to free in the loop, all pointers are into dacl
	}

	if (modifiedDACL)
	{
		result = SetNamedSecurityInfo((WCHAR *) path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
			NULL, NULL, dacl, NULL);
		if (result != ERROR_SUCCESS)
		{
			throwRuntimeExceptionCode(env, result, "Error setting security info for %S", path);
			goto cleanup;
		}
	}

cleanup:

	if (path != NULL)
	{
		releasePlatformChars(env, jPath, path);
	}
	if (userSID != NULL)
	{
		LocalFree(userSID);
	}
	if (userSIDString != NULL)
	{
	   releasePlatformChars(env, jUserSIDString, userSIDString);
	}
	// dacl points inside securityDescriptor
	if (securityDescriptor != NULL)
	{
		LocalFree(securityDescriptor);
	}
}
JNIEXPORT void JNICALL Java_com_microsoft_tfs_jni_internal_filesystem_NativeFileSystem_nativeGrantInheritableFullControl(
    JNIEnv *env, jclass cls, jstring jPath, jstring jUserSIDString, jstring jCopyExplicitRulesFromPath)
{
	const WCHAR * path = NULL;
	const WCHAR * userSIDString = NULL;
	const WCHAR * copyExplicitRulesFromPath = NULL;
	DWORD result = 0;
	PACL existingDACL = NULL;
	PACL newDACL = NULL;
	PSECURITY_DESCRIPTOR securityDescriptor = NULL;
	PSID userSID = NULL;
	EXPLICIT_ACCESS fullControl;
	
	if (jPath == NULL)
    {
       	throwRuntimeExceptionString(env, "path must not be null");
		goto cleanup;
    }

	if (jUserSIDString == NULL)
	{
		throwRuntimeExceptionString(env, "user must not be null");
		goto cleanup;
	}

	// Get the existing DACL entries
	if (jCopyExplicitRulesFromPath != NULL)
	{
		if ((copyExplicitRulesFromPath = javaStringToPlatformChars(env, jCopyExplicitRulesFromPath)) == NULL)
		{
			// String allocation failed, exception already thrown
			goto cleanup;
		}

		result = GetNamedSecurityInfo(copyExplicitRulesFromPath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, 
			NULL, NULL, &existingDACL, NULL, &securityDescriptor);

		if (result != ERROR_SUCCESS)
		{
			throwRuntimeExceptionCode(env, result, "Error getting file security info for %S", copyExplicitRulesFromPath);
			goto cleanup;
	    }
	}

	// Convert the string SID to a structure
	if ((userSIDString = javaStringToPlatformChars(env, jUserSIDString)) == NULL)
	{
		// String allocation failed, exception already thrown
		goto cleanup;
	}

	if (ConvertStringSidToSidW(userSIDString, &userSID) == FALSE)
	{
		throwRuntimeExceptionCode(env, GetLastError(), "Error converting string sid %S to sid", userSIDString);
		goto cleanup;
	}

	/*
	 * Create a new explicit access entry with rights equivalent to .NET's 
	 * FileSystemRights.FullControl (0x1F01FF; see FileSecurity.cs) and
	 * full inheritance.
	 */
	ZeroMemory(&fullControl, sizeof(EXPLICIT_ACCESS));
    fullControl.grfAccessPermissions = 0x1F01FF;
    fullControl.grfAccessMode = GRANT_ACCESS;
    fullControl.grfInheritance= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
    fullControl.Trustee.TrusteeForm = TRUSTEE_IS_SID;
	fullControl.Trustee.TrusteeType = TRUSTEE_IS_USER;
    fullControl.Trustee.ptstrName = userSID;
	
	// Merge new entry with old entries into a new list
	result = SetEntriesInAcl(1, &fullControl, existingDACL, &newDACL);
    if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error setting entries in ACL");
        goto cleanup;
    }

	// Set the list on the path
	if ((path = javaStringToPlatformChars(env, jPath)) == NULL)
	{
		// String allocation failed, exception already thrown
		goto cleanup;
	}

	result = SetNamedSecurityInfo((WCHAR *) path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
		NULL, NULL, newDACL, NULL);
	if (result != ERROR_SUCCESS)
	{
		throwRuntimeExceptionCode(env, result, "Error setting file security info for %S", path);
        goto cleanup;
	}

cleanup:

	if (path != NULL)
	{
		releasePlatformChars(env, jPath, path);
	}
	if (userSIDString != NULL)
	{
		releasePlatformChars(env, jUserSIDString, userSIDString);
	}
	if (copyExplicitRulesFromPath != NULL)
	{
		releasePlatformChars(env, jCopyExplicitRulesFromPath, copyExplicitRulesFromPath);
	}
	if (securityDescriptor != NULL)
	{
		LocalFree(securityDescriptor);
	}
	if (userSID != NULL)
	{
		LocalFree(userSID);
	}
	if (newDACL != NULL)
	{
		LocalFree(newDACL);
	}
	// existingDACL points inside securityDescriptor
}