You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
937 lines
28 KiB
937 lines
28 KiB
/******************************************************************************
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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
|