windows-nt/Source/XPSP1/NT/admin/pchealth/sr/utils/ntfs.cpp
2020-09-26 16:20:57 +08:00

1009 lines
29 KiB
C++

/******************************************************************************
Copyright (c) 2000 Microsoft Corporation
Module Name:
ntfs.cpp
Abstract:
This file contains the common utility functions for NTFS file operations,
e.g. CopyNTFSFile to copy a file overriding ACL and EFS.
Revision History:
Seong Kook Khang (SKKhang) 08/16/00
created
******************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winioctl.h>
#include "srdefs.h"
#include "utils.h"
#include <dbgtrace.h>
#include <stdio.h>
#include <objbase.h>
#include <ntlsa.h>
#include <accctrl.h>
#include <aclapi.h>
#include <malloc.h>
#include <regstr.h>
#include <shlwapi.h>
#include <commctrl.h>
#include <shellapi.h>
#include <srapi.h>
#define TRACEID 9875
BOOL IsFileEncrypted(const WCHAR * cszDst);
/////////////////////////////////////////////////////////////////////////////
//
// ClearFileAttribute
//
// Check the attribute of file and clear it if necessary.
//
/////////////////////////////////////////////////////////////////////////////
DWORD
ClearFileAttribute( LPCWSTR cszFile, DWORD dwMask )
{
TraceFunctEnter("ClearFileAttribute");
DWORD dwRet = ERROR_SUCCESS;
DWORD dwErr;
LPCWSTR cszErr;
DWORD dwAttr;
// Check if file exists, ignore if not exist
dwAttr = ::GetFileAttributes( cszFile );
if ( dwAttr == 0xFFFFFFFF )
goto Exit;
// If file exist, clear the given flags
if ( ( dwAttr & dwMask ) != 0 )
{
// This will always succeed even if the file is ACL protected or
// encrypted, so don't worry about it...
if ( !::SetFileAttributes( cszFile, dwAttr & ~dwMask ) )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::SetFileAttributes failed - %ls", cszErr);
ErrorTrace(0, "Src='%ls'", cszFile);
goto Exit;
}
}
Exit:
TraceFunctLeave();
return( dwRet );
}
/////////////////////////////////////////////////////////////////////////////
//
// TakeOwnership
//
/////////////////////////////////////////////////////////////////////////////
DWORD
TakeOwnership( LPCWSTR cszPath )
{
TraceFunctEnter("TakeOwnership");
DWORD dwRet;
LPCWSTR cszErr;
HANDLE hTokenProcess = NULL;
TOKEN_USER *pUser = NULL;
DWORD dwSize;
if ( !::OpenProcessToken( ::GetCurrentProcess(), TOKEN_QUERY, &hTokenProcess ) )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::OpenProcessToken failed - %ls", cszErr);
goto Exit;
}
if ( !::GetTokenInformation( hTokenProcess, TokenUser, NULL, 0, &dwSize ) )
{
dwRet = ::GetLastError();
if ( dwRet != ERROR_INSUFFICIENT_BUFFER )
{
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::GetTokenInformation(query) failed - %ls", cszErr);
goto Exit;
}
else
dwRet = ERROR_SUCCESS;
}
pUser = (TOKEN_USER*) new BYTE[dwSize];
if ( pUser == NULL )
{
FatalTrace(0, "Insufficient memory...");
dwRet = ERROR_NOT_ENOUGH_MEMORY;
goto Exit;
}
if ( !::GetTokenInformation( hTokenProcess, TokenUser, pUser, dwSize, &dwSize ) )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::GetTokenInformation(get) failed - %ls", cszErr);
goto Exit;
}
dwRet = ::SetNamedSecurityInfo( (LPWSTR)cszPath,
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION,
pUser->User.Sid, NULL, NULL, NULL );
if ( dwRet != ERROR_SUCCESS )
{
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::SetNamedSecurityInfo failed - %ls", cszErr);
goto Exit;
}
Exit:
if ( pUser != NULL )
delete [] (BYTE*)pUser;
if ( hTokenProcess != NULL )
::CloseHandle( hTokenProcess );
TraceFunctLeave();
return( dwRet );
}
/////////////////////////////////////////////////////////////////////////////
//
// Copy File Routines
//
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// CopyACLProtectedFile
BYTE s_pBuf[4096];
DWORD
CopyACLProtectedFile( LPCWSTR cszSrc, LPCWSTR cszDst )
{
TraceFunctEnter("CopyACLProtectedFile");
DWORD dwRet = ERROR_SUCCESS;
LPCWSTR cszErr;
HANDLE hfSrc = INVALID_HANDLE_VALUE;
HANDLE hfDst = INVALID_HANDLE_VALUE;
LPVOID lpCtxRead = NULL;
LPVOID lpCtxWrite = NULL;
DWORD dwRead;
DWORD dwCopied;
DWORD dwWritten;
hfSrc = ::CreateFile( cszSrc, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL );
if ( hfSrc == INVALID_HANDLE_VALUE )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::CreateFile() failed - %ls", cszErr);
ErrorTrace(0, "cszSrc='%ls'", cszSrc);
goto Exit;
}
hfDst = ::CreateFile( cszDst, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL );
if ( hfDst == INVALID_HANDLE_VALUE )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::CreateFile() failed - %ls", cszErr);
ErrorTrace(0, "cszDst='%ls'", cszDst);
goto Exit;
}
for ( ;; )
{
if ( !::BackupRead( hfSrc, s_pBuf, sizeof(s_pBuf), &dwRead, FALSE, FALSE, &lpCtxRead ) )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::BackupRead() failed - %ls", cszErr);
goto Exit;
}
if ( dwRead == 0 )
break;
for ( dwCopied = 0; dwCopied < dwRead; dwCopied += dwWritten )
{
if ( !::BackupWrite( hfDst, s_pBuf+dwCopied, dwRead-dwCopied, &dwWritten, FALSE, FALSE, &lpCtxWrite ) )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::BackupWrite() failed - %ls", cszErr);
goto Exit;
}
}
}
Exit:
if ( lpCtxWrite != NULL )
if ( !::BackupWrite( hfDst, NULL, 0, NULL, TRUE, FALSE, &lpCtxWrite ) )
{
cszErr = ::GetSysErrStr();
ErrorTrace(0, "::BackupWrite(TRUE) failed - %ls", cszErr);
// Ignore the error
}
if ( lpCtxRead != NULL )
if ( !::BackupRead( hfSrc, NULL, 0, NULL, TRUE, FALSE, &lpCtxRead ) )
{
cszErr = ::GetSysErrStr();
ErrorTrace(0, "::BackupRead(TRUE) failed - %ls", cszErr);
// Ignore the error
}
if ( hfDst != INVALID_HANDLE_VALUE )
::CloseHandle( hfDst );
if ( hfSrc != INVALID_HANDLE_VALUE )
::CloseHandle( hfSrc );
TraceFunctLeave();
return( dwRet );
}
/////////////////////////////////////////////////////////////////////////////
// CopyEncryptedFile
DWORD WINAPI
FEExportFunc( PBYTE pbData, PVOID param, ULONG ulLen )
{
TraceFunctEnter("FEExportFunc");
DWORD dwRet = ERROR_SUCCESS;
LPCWSTR cszErr;
HANDLE hfTmp = (HANDLE)param;
DWORD dwRes;
if ( !::WriteFile( hfTmp, pbData, ulLen, &dwRes, NULL ) )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::WriteFile failed - %ls", cszErr);
goto Exit;
}
Exit:
TraceFunctLeave();
return( dwRet );
}
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
FEImportFunc( PBYTE pbData, PVOID param, PULONG pulLen )
{
TraceFunctEnter("FEImportFunc");
DWORD dwRet = ERROR_SUCCESS;
LPCWSTR cszErr;
HANDLE hfTmp = (HANDLE)param;
DWORD dwRes;
if ( !::ReadFile( hfTmp, pbData, *pulLen, &dwRes, NULL ) )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::ReadFile failed - %ls", cszErr);
goto Exit;
}
*pulLen = dwRes;
Exit:
TraceFunctLeave();
return( dwRet );
}
void GetVolumeName(const WCHAR * pszFileName,
WCHAR * pszVolumeName)
{
WCHAR * pszPastVolumeName;
pszPastVolumeName = ReturnPastVolumeName(pszFileName);
// now copy everything upto the volume name into the buffer
wcsncpy(pszVolumeName, pszFileName, pszPastVolumeName - pszFileName);
pszVolumeName[pszPastVolumeName - pszFileName]=L'\0';
}
/////////////////////////////////////////////////////////////////////////////
LPCWSTR s_cszEncTmpDir = L"encrypt.tmp";
LPCWSTR s_cszEncTmpExtension = L"ExistingMoved";
// this file moves the file to a temp file. This is done to prevent MoveFile
// from failing if the destination file already exists.
// Returns true if the file existed and was moved
BOOL MoveExistingFile(const WCHAR * pszFile,
WCHAR * pszTempFile) // this is the file to use as the
// template file for the move
// destination
{
TraceFunctEnter("MoveExistingFile");
BOOL fReturn=FALSE;
DWORD dwError;
if (DoesFileExist(pszFile))
{
WCHAR szNewFileName[MAX_PATH];
// create new file name
wsprintf(szNewFileName, L"%s.%s", pszTempFile, s_cszEncTmpExtension);
// Delete existing file (if it exists)
DeleteFile(szNewFileName);
// Now do the move
if (MoveFile(pszFile, szNewFileName))
{
fReturn=TRUE;
}
else
{
dwError = GetLastError();
DebugTrace(0, "Failed to move file ec=%d %s %s",dwError,
pszFile, szNewFileName);
}
}
TraceFunctLeave();
return fReturn;
}
BOOL MoveExistingFileBack(const WCHAR * pszFile,
WCHAR * pszTempFile) // this is the file to use as
// the template file for the move
// destination
{
TraceFunctEnter("MoveExistingFileBack");
BOOL fReturn=FALSE;
DWORD dwError;
WCHAR szNewFileName[MAX_PATH];
// create new file name
wsprintf(szNewFileName, L"%s.%s", pszTempFile, s_cszEncTmpExtension);
if (DoesFileExist(szNewFileName))
{
// Now do the move
if (MoveFile(szNewFileName, pszFile))
{
fReturn=TRUE;
}
else
{
dwError = GetLastError();
DebugTrace(0, "Failed to move file ec=%d %s %s",dwError,
szNewFileName, pszFile);
}
}
TraceFunctLeave();
return fReturn;
}
void DeleteMovedFile( WCHAR * pszTempFile) // this is the file to use as
// the template file for the move
// destination (the file to delete)
{
TraceFunctEnter("DeleteMovedFile");
WCHAR szNewFileName[MAX_PATH];
// create new file name
wsprintf(szNewFileName, L"%s.%s", pszTempFile, s_cszEncTmpExtension);
DeleteFile(szNewFileName);
TraceFunctLeave();
return;
}
DWORD SRCreateSubdirectory ( LPCWSTR cszDst, LPSECURITY_ATTRIBUTES pSecAttr )
{
TraceFunctEnter("SRCreateSubdirectory");
WCHAR szDrv[MAX_PATH];
WCHAR szTmpPath[MAX_PATH];
DWORD dwRet = ERROR_SUCCESS;
DWORD dwAttr = ::GetFileAttributes( cszDst );
if ( dwAttr != 0xFFFFFFFF )
{
dwRet = ERROR_ALREADY_EXISTS;
goto Exit;
}
// Prepare temporary directory (must be non-encrypted), create if necessary
GetVolumeName(cszDst, szDrv );
::MakeRestorePath( szTmpPath, szDrv, s_cszEncTmpDir );
if ( ::GetFileAttributes( szTmpPath ) == 0xFFFFFFFF )
{
if ( !::CreateDirectory( szTmpPath, NULL ) )
{
ErrorTrace(0, "::CreateDirectory(tmp-in-DS) failed - %d",
GetLastError());
::PathCombine( szTmpPath, szDrv, s_cszEncTmpDir );
if ( ::GetFileAttributes( szTmpPath ) == 0xFFFFFFFF )
{
if ( !::CreateDirectory( szTmpPath, NULL ) )
{
ErrorTrace(0, "::CreateDirectory(tmp-in-root) failed -%d",
GetLastError());
// use the root directory as last restort
::lstrcpy( szTmpPath, szDrv );
}
}
}
}
lstrcat (szTmpPath, L"\\"); // create the subdirectory to be renamed
lstrcat (szTmpPath, s_cszEncTmpDir);
if ( !::CreateDirectory( szTmpPath, pSecAttr) )
{
dwRet = GetLastError();
ErrorTrace(0, "::CreateDirectory failed - %d", dwRet);
goto Exit;
}
// now rename szTmpPath into the destination
if ( !::MoveFile( szTmpPath, cszDst ) )
{
dwRet = ::GetLastError();
ErrorTrace(0, "::MoveFile failed - %d", dwRet);
ErrorTrace(0, " szTmp ='%ls'", szTmpPath);
ErrorTrace(0, " cszDst='%ls'", cszDst);
RemoveDirectory (szTmpPath); // clean up
goto Exit;
}
Exit:
TraceFunctLeave();
return dwRet;
}
//
// NOTE (10/05/00 skkhang)
// Somehow, OpenEncryptedFileRaw(write) fails if directory is encrypted (and
// probably context is SYSTEM, which is true for restoration.)
// To work around this problem, the encrypted file in data store will be
// copied to a non-encrypted directory (temporary one in data store), and
// then moved into the real target location.
//
DWORD
CopyEncryptedFile( LPCWSTR cszSrc, LPCWSTR cszDst )
{
TraceFunctEnter("CopyEncryptedFile");
DWORD dwRet = ERROR_SUCCESS;
LPCWSTR cszErr;
DWORD dwAttr;
WCHAR szDrv[MAX_PATH];
WCHAR szTmpPath[MAX_PATH]=L"";
WCHAR szTmp[MAX_PATH]=L"";
WCHAR szEnc[MAX_PATH]=L"";
HANDLE hfTmp = INVALID_HANDLE_VALUE;
LPVOID lpContext = NULL;
// BOOL fMovedDestination;
dwAttr = ::GetFileAttributes( cszDst );
if ( dwAttr != 0xFFFFFFFF )
{
// If dest file already exist, it may protected by ACL and
// causes OpenEncryptedFileRaw to fail. Just delete the dest
// file, even though it'll create two log entries.
if (ERROR_SUCCESS == ::ClearFileAttribute( cszDst, FILE_ATTRIBUTE_READONLY ) )
{
if ( !::DeleteFile( cszDst ) )
{
cszErr = ::GetSysErrStr();
ErrorTrace(0, "::DeleteFile failed - %ls", cszErr);
}
}
// Ignore any error, OpenEncryptedFileRaw might succeed.
}
// Prepare temporary directory (must be non-encrypted), create if necessary
/*
// strip filename
lstrcpy(szTmpPath, cszDst);
LPWSTR pszFileName = wcsrchr(szTmpPath, L'\\');
if (pszFileName)
*(++pszFileName) = L'\0';
trace(0, "szTmpPath = %S", szTmpPath);
*/
GetVolumeName(cszDst, szDrv );
::MakeRestorePath( szTmpPath, szDrv, s_cszEncTmpDir );
if ( ::GetFileAttributes( szTmpPath ) == 0xFFFFFFFF )
{
if ( !::CreateDirectory( szTmpPath, NULL ) )
{
cszErr = ::GetSysErrStr();
ErrorTrace(0, "::CreateDirectory(tmp-in-DS) failed - %ls", cszErr);
::PathCombine( szTmpPath, szDrv, s_cszEncTmpDir );
if ( ::GetFileAttributes( szTmpPath ) == 0xFFFFFFFF )
{
if ( !::CreateDirectory( szTmpPath, NULL ) )
{
cszErr = ::GetSysErrStr();
ErrorTrace(0, "::CreateDirectory(tmp-in-root) failed -%ls",
cszErr);
// Use root directory, as a last resort...
::lstrcpy( szTmpPath, szDrv );
}
}
}
}
// Prepare temporary file to store the raw data.
// "cef" means Copy Encrypted File.
if ( ::GetTempFileName( szTmpPath, L"cef", 0, szTmp ) == 0 )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::GetTempFileName failed - %ls", cszErr);
goto Exit;
}
hfTmp = ::CreateFile( szTmp, GENERIC_READ|GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL );
if ( hfTmp == INVALID_HANDLE_VALUE )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::CreateFile failed - %ls", cszErr);
ErrorTrace(0, "szTmp='%ls'", szTmp);
goto Exit;
}
DebugTrace(0, "szTmp='%ls'", szTmp);
// Prepare temporary encrypted file.
// "ief" means Intermediate Encrypted File.
if ( ::GetTempFileName( szTmpPath, L"ief", 0, szEnc ) == 0 )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::GetTempFileName failed - %ls", cszErr);
goto Exit;
}
DebugTrace(0, "szEnc='%ls'", szEnc);
// now check to see if the temp file is encrypted. If it is not,
// then just use CopyFile to copy the temp file.
if (IsFileEncrypted(cszSrc))
{
// Read encrypted raw data from the source file.
dwRet = ::OpenEncryptedFileRaw( cszSrc, 0, &lpContext );
if ( dwRet != ERROR_SUCCESS )
{
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::OpenEncryptedFileRaw(read) failed - %ls", cszErr);
ErrorTrace(0, "szSrc='%ls'", cszSrc);
goto Exit;
}
dwRet = ::ReadEncryptedFileRaw( FEExportFunc, hfTmp, lpContext );
if ( dwRet != ERROR_SUCCESS )
{
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::ReadEncryptedFileRaw() failed - %ls", cszErr);
goto Exit;
}
::CloseEncryptedFileRaw( lpContext );
lpContext = NULL;
// Rewind the temporary file.
if ( ::SetFilePointer( hfTmp, 0, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::SetFilePointer failed - %ls", cszErr);
goto Exit;
}
// Write encrypted raw data to the destination file.
dwRet = ::OpenEncryptedFileRaw( szEnc, CREATE_FOR_IMPORT, &lpContext );
if ( dwRet != ERROR_SUCCESS )
{
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::OpenEncryptedFileRaw(write) failed - %ls",cszErr);
ErrorTrace(0, "szEnc='%ls'", szEnc);
goto Exit;
}
dwRet = ::WriteEncryptedFileRaw( FEImportFunc, hfTmp, lpContext );
if ( dwRet != ERROR_SUCCESS )
{
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::WriteEncryptedFileRaw() failed - %ls", cszErr);
goto Exit;
}
::CloseEncryptedFileRaw( lpContext );
lpContext = NULL;
}
else
{
// the temp file is not an encrypted file. Just copy this
// file in the temp location.
dwRet = SRCopyFile(cszSrc, szEnc);
if ( dwRet != ERROR_SUCCESS )
{
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::SRCopyFile() failed - %ls", cszErr);
goto Exit;
}
}
/*
// before moving the file to the destination, move the
// destination file if it already exists to another location.
fMovedDestination = MoveExistingFile(cszDst,
szEnc); // this is the file
// to use as the template
// file for the move
// destination
*/
// Rename intermediate file into the real target file.
if ( !::MoveFile( szEnc, cszDst ) )
{
dwRet = ::GetLastError();
cszErr = ::GetSysErrStr(dwRet);
ErrorTrace(0, "::MoveFile failed - %ls", cszErr);
ErrorTrace(0, " szEnc ='%ls'", szEnc);
ErrorTrace(0, " cszDst='%ls'", cszDst);
/*
if (TRUE==fMovedDestination)
{
MoveExistingFileBack(cszDst,
szEnc); // this is the file to use as the
// template file for the move
}
*/
goto Exit;
}
Exit:
/*
if (TRUE==fMovedDestination)
{
DeleteMovedFile(szEnc); // this is the file to use as the template
// file for the delete
}
*/
if ( lpContext != NULL )
::CloseEncryptedFileRaw( lpContext );
if ( hfTmp != INVALID_HANDLE_VALUE )
::CloseHandle( hfTmp );
DeleteFile(szEnc);
RemoveDirectory(szTmpPath);
TraceFunctLeave();
return( dwRet );
}
DWORD CopyFileTimes( LPCWSTR cszSrc, LPCWSTR cszDst )
{
TraceFunctEnter("CopyFileTimes");
DWORD dwErr, dwReturn = ERROR_INTERNAL_ERROR;
FILETIME CreationTime, LastWriteTime, LastAccessTime;
HANDLE hSrcFile=INVALID_HANDLE_VALUE, hDestFile=INVALID_HANDLE_VALUE;
// open the source file
// paulmcd: 1/2001: only need to get FILE_READ_ATTRIBUTES in order
// to call GetFileTimes, only do this as the file might be EFS
// and we can't get GENERIC_READ .
//
hSrcFile=CreateFile(cszSrc, // file name
FILE_READ_ATTRIBUTES, // access mode
FILE_SHARE_DELETE| FILE_SHARE_READ| FILE_SHARE_WRITE,
// share mode
NULL, // SD
OPEN_EXISTING, // how to create
FILE_FLAG_BACKUP_SEMANTICS, // file attributes
NULL); // handle to template file
if (INVALID_HANDLE_VALUE == hSrcFile)
{
dwErr = GetLastError();
if (ERROR_SUCCESS != dwErr)
{
dwReturn = dwErr;
}
ErrorTrace(0, "CreateFile of src failed ec=%d", dwErr);
LogDSFileTrace(0,L"File was ", cszSrc);
goto cleanup;
}
// open the destination file
// paulmcd: 1/2001: only need to get FILE_WRITE_ATTRIBUTES in order
// to call SetFileTimes, only do this as the file might be EFS
// and we can't get GENERIC_READ .
//
hDestFile=CreateFile(cszDst, // file name
FILE_WRITE_ATTRIBUTES, // access mode
FILE_SHARE_DELETE| FILE_SHARE_READ| FILE_SHARE_WRITE,
// share mode
NULL, // SD
OPEN_EXISTING, // how to create
FILE_FLAG_BACKUP_SEMANTICS, // file attributes
NULL); // handle to template file
if (INVALID_HANDLE_VALUE == hDestFile)
{
dwErr = GetLastError();
if (ERROR_SUCCESS != dwErr)
{
dwReturn = dwErr;
}
ErrorTrace(0, "CreateFile of dst failed ec=%d", dwErr);
LogDSFileTrace(0,L"File was ", cszDst);
goto cleanup;
}
// Call getfiletimes on the source file
if (FALSE == GetFileTime(hSrcFile,// handle to file
&CreationTime, // creation time
&LastAccessTime, // last access time
&LastWriteTime)) // last write time
{
dwErr = GetLastError();
if (ERROR_SUCCESS != dwErr)
{
dwReturn = dwErr;
}
ErrorTrace(0, "GetFileTime of src failed ec=%d", dwErr);
LogDSFileTrace(0,L"File was ", cszSrc);
goto cleanup;
}
// call SetFileTimes on the destination file
if (FALSE == SetFileTime(hDestFile,// handle to file
&CreationTime, // creation time
&LastAccessTime, // last access time
&LastWriteTime)) // last write time
{
dwErr = GetLastError();
if (ERROR_SUCCESS != dwErr)
{
dwReturn = dwErr;
}
ErrorTrace(0, "SetFileTime of dest file failed ec=%d", dwErr);
LogDSFileTrace(0,L"File was ", cszDst);
goto cleanup;
}
dwReturn = ERROR_SUCCESS;
cleanup:
if (INVALID_HANDLE_VALUE != hDestFile)
{
_VERIFY(CloseHandle(hDestFile));
}
if (INVALID_HANDLE_VALUE != hSrcFile)
{
_VERIFY(CloseHandle(hSrcFile));
}
TraceFunctLeave();
return dwReturn;
}
BOOL IsFileEncrypted(const WCHAR * cszDst)
{
TraceFunctEnter("IsFileEncrypted");
BOOL fReturn=FALSE;
DWORD dwAttr, dwError;
dwAttr = ::GetFileAttributes( cszDst );
if ( dwAttr == 0xFFFFFFFF )
{
dwError=GetLastError();
DebugTrace(0, "! GetFileAttributes ec=%d", dwError);
goto cleanup;
}
if (dwAttr & FILE_ATTRIBUTE_ENCRYPTED )
{
DebugTrace(0, " File is encrypted %S", cszDst);
fReturn = TRUE;
}
cleanup:
TraceFunctLeave();
return fReturn;
}
// this function checks to see if the parent directory under the
// specified file name is encrypted
BOOL IsParentDirectoryEncrypted(const WCHAR * pszFileName)
{
TraceFunctEnter("IsParentDirectoryEncrypted");
WCHAR * pszParentDir = new WCHAR[SR_MAX_FILENAME_LENGTH];
BOOL fReturn = FALSE;
DWORD dwError;
if (!pszParentDir)
{
ErrorTrace(0, "Cannot allocate memory");
goto cleanup;
}
lstrcpy(pszParentDir, pszFileName);
// get the parent directory
RemoveTrailingFilename(pszParentDir, L'\\');
if (TRUE == IsFileEncrypted(pszParentDir))
{
fReturn = TRUE;
}
else
{
fReturn = FALSE;
}
cleanup:
if (pszParentDir)
delete [] pszParentDir;
TraceFunctLeave();
return fReturn;
}
/////////////////////////////////////////////////////////////////////////////
// SRCopyFile
DWORD
SRCopyFile( LPCWSTR cszSrc, LPCWSTR cszDst )
{
TraceFunctEnter("SRCopyFile");
DWORD dwRet = ERROR_SUCCESS;
DWORD dwErr;
LPCWSTR cszErr;
DWORD dwAttr, dwAttrDest;
BOOL fDestinationEncrypted;
DebugTrace(TRACEID, "Source %S", cszSrc);
DebugTrace(TRACEID, "Dest %S", cszDst);
dwAttr = ::GetFileAttributes( cszSrc );
if ( dwAttr == 0xFFFFFFFF )
{
ErrorTrace(0, "Source file does not exist...???");
ErrorTrace(0, "Src='%ls'", cszSrc);
dwRet = ERROR_FILE_NOT_FOUND;
goto Exit;
}
fDestinationEncrypted = FALSE;
if (IsFileEncrypted(cszDst) || IsParentDirectoryEncrypted(cszDst))
{
fDestinationEncrypted =TRUE;
}
// Check Encrypted File.
if ( (dwAttr & FILE_ATTRIBUTE_ENCRYPTED ) ||
(TRUE == fDestinationEncrypted) )
{
// Assuming Encryption APIs would override ACL settings and
// file attributes...
dwRet = ::CopyEncryptedFile( cszSrc, cszDst );
goto Exit;
}
// Check attribute of destination file and clear it if necessary.
dwRet = ::ClearFileAttribute( cszDst, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM );
if ( dwRet != ERROR_SUCCESS )
goto Exit;
// Try Normal Copy.
if ( ::CopyFile( cszSrc, cszDst, FALSE ) )
goto Exit;
dwRet = GetLastError();
cszErr = ::GetSysErrStr();
DebugTrace(0, "::CopyFile failed - %ls", cszErr);
// Now Try to Override ACL. - however - do not do this in case of
// disk full
if (ERROR_DISK_FULL != dwRet)
{
dwRet = ::CopyACLProtectedFile( cszSrc, cszDst );
}
Exit:
if (ERROR_SUCCESS == dwRet)
{
//
// CopyFileTimes may fail for read-only files
// so ignore error here
//
CopyFileTimes(cszSrc, cszDst );
}
TraceFunctLeave();
return( dwRet );
}
DWORD SetShortFileName(const WCHAR * pszFile,
const WCHAR * pszShortName)
{
TraceFunctEnter("SetShortFileName");
HANDLE hFile=INVALID_HANDLE_VALUE;// access mode
DWORD dwRet=ERROR_INTERNAL_ERROR;
if (NULL == pszShortName)
{
goto cleanup;
}
// first open the file
// paulmcd: 1/2001, you need DELETE|FILE_WRITE_ATTRIBUTES access
// in order to call SetFileShortName, don't ask for more as the
// file might be EFS and we might not be able to get read/write .
//
hFile = ::CreateFile( pszFile,
FILE_WRITE_ATTRIBUTES|DELETE,// access mode
FILE_SHARE_READ| FILE_SHARE_WRITE,// share mode
NULL, // security attributes
OPEN_EXISTING, // how to create
FILE_FLAG_BACKUP_SEMANTICS, // to override ACLs
NULL );// handle to template file
if ( hFile == INVALID_HANDLE_VALUE )
{
dwRet = ::GetLastError();
ErrorTrace(0, "::CreateFile() failed - %d", dwRet);
ErrorTrace(0, "File was=%S", pszFile);
goto cleanup;
}
// now set the short file name
if (FALSE==SetFileShortName(hFile,
pszShortName))
{
dwRet = ::GetLastError();
ErrorTrace(0, "!SetFileShortName (it is a FAT drive?) %d %S",
dwRet, pszShortName);
ErrorTrace(0, "File was=%S", pszFile);
goto cleanup;
}
dwRet = ERROR_SUCCESS;
cleanup:
// close the file
if ( hFile != INVALID_HANDLE_VALUE )
{
_VERIFY(TRUE==CloseHandle(hFile));
}
TraceFunctLeave();
return dwRet;
}
// end of file