/****************************************************************************** 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 #include #include #include #include #include "srdefs.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #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