/*++ Copyright (c) 1996 Microsoft Corporation Module Name: copyfile.c Abstract: File copy functions The code in this source file traverses a drive tree and calls an external callback function for each file. An INF can be provided to exclude files and/or directories from enumeration. Author: Mike Condra 16-Aug-1996 Revision History: calinn 29-Ian-1998 Modified CopyFileCallback to reset directory attributes for delete op. jimschm 20-Dec-1996 Modified return codes --*/ #include "no_pch.h" #include "fileenum.h" #include #ifndef UNICODE #ifdef DEBUG #error UNICODE required for DEBUGMSG macro #endif #endif BOOL CALLBACK CopyFileCallbackA( LPCSTR szFullFileSpecIn, LPCSTR DontCare, WIN32_FIND_DATAA *pFindData, DWORD dwEnumHandle, LPVOID pVoid, PDWORD CurrentDirData ) /* This function is the built-in callback for CopyTree. Its purpose is to build the target filespec, give the user-supplied callback a chance to veto the copy, then perform the copy and any directory creation it requires. The signature of this function is the generic callback used for EnumerateTree. */ { COPYTREE_PARAMSA *pCopyParams = (COPYTREE_PARAMSA*)pVoid; int nCharsInFullFileSpec = ByteCountA (szFullFileSpecIn); INT rc; // Set return code if (COPYTREE_IGNORE_ERRORS & pCopyParams->flags) rc = CALLBACK_CONTINUE; else rc = CALLBACK_FAILED; // Build output path if (pCopyParams->szEnumRootOutWack) { StringCopyA(pCopyParams->szFullFileSpecOut, pCopyParams->szEnumRootOutWack); StringCatA (pCopyParams->szFullFileSpecOut, szFullFileSpecIn + pCopyParams->nCharsInRootInWack); } // // If a callback was supplied, give it a chance to veto the copy. This callback is // different from the one given to an EnumerateTree function, since the latter can // terminate enumeration by returning FALSE. // if (pCopyParams->pfnCallback) { if (!pCopyParams->pfnCallback( szFullFileSpecIn, pCopyParams->szFullFileSpecOut, pFindData, dwEnumHandle, pVoid, CurrentDirData )) { return CALLBACK_CONTINUE; } } // Copy, move or delete the file if requested if ((COPYTREE_DOCOPY & pCopyParams->flags) || (COPYTREE_DOMOVE & pCopyParams->flags)) { BOOL fNoOverwrite = (0 != (COPYTREE_NOOVERWRITE & pCopyParams->flags)); // // Create the directory. The function we call expects a full filename, // and considers the directory to end at the last wack. If this object // is a directory, we need to add at least a wack to make sure the last // path element is treated as part of the directory, not as a filename. // { CHAR strTemp[MAX_MBCHAR_PATH]; StringCopyA(strTemp, pCopyParams->szFullFileSpecOut); if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { AppendUncWackA(strTemp); } if (ERROR_SUCCESS != MakeSurePathExistsA(strTemp,FALSE)) { return rc; } } // // Copy or move the file // if (0 == (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if (COPYTREE_DOCOPY & pCopyParams->flags) { if (!CopyFileA( szFullFileSpecIn, pCopyParams->szFullFileSpecOut, fNoOverwrite )) { if (!fNoOverwrite) { return rc; } if (ERROR_FILE_EXISTS != GetLastError()) { return rc; } } } else if (COPYTREE_DOMOVE & pCopyParams->flags) { // If allowed to overwrite, delete the target if it exists if (!fNoOverwrite && DoesFileExistA(pCopyParams->szFullFileSpecOut)) { SetFileAttributesA (pCopyParams->szFullFileSpecOut, FILE_ATTRIBUTE_NORMAL); if (!DeleteFileA(pCopyParams->szFullFileSpecOut)) { return rc; } } // Move the file if (!MoveFileA( szFullFileSpecIn, pCopyParams->szFullFileSpecOut )) { return rc; } } } // // Copy the source file-or-directory's attributes to the target // SetFileAttributesA(pCopyParams->szFullFileSpecOut, pFindData->dwFileAttributes); } else if (COPYTREE_DODELETE & pCopyParams->flags) { SetFileAttributesA (szFullFileSpecIn, FILE_ATTRIBUTE_NORMAL); if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // // We don't care about the error. We won't stop the enumeration just // because we could not delete something. // RemoveDirectoryA (szFullFileSpecIn); } else { // // We don't care about the error. We won't stop the enumeration just // because we could not delete something. // DeleteFileA (szFullFileSpecIn); } } return CALLBACK_CONTINUE; } BOOL CALLBACK CopyFileCallbackW( LPCWSTR szFullFileSpecIn, LPCWSTR DontCare, WIN32_FIND_DATAW *pFindData, DWORD dwEnumHandle, LPVOID pVoid, PDWORD CurrentDirData ) { COPYTREE_PARAMSW *pCopyParams = (COPYTREE_PARAMSW*)pVoid; int nCharsInFullFileSpec = wcslen (szFullFileSpecIn); INT rc; // Set return code if (COPYTREE_IGNORE_ERRORS & pCopyParams->flags) rc = CALLBACK_CONTINUE; else rc = CALLBACK_FAILED; // Build output path if (pCopyParams->szEnumRootOutWack) { StringCopyW (pCopyParams->szFullFileSpecOut, pCopyParams->szEnumRootOutWack); StringCatW (pCopyParams->szFullFileSpecOut, szFullFileSpecIn + pCopyParams->nCharsInRootInWack); } // // If a callback was supplied, give it a chance to veto the copy. This callback is // different from the one given to an EnumerateTree function, since the latter can // terminate enumeration by returning FALSE. // if (pCopyParams->pfnCallback) { if (!pCopyParams->pfnCallback( szFullFileSpecIn, pCopyParams->szFullFileSpecOut, pFindData, dwEnumHandle, pVoid, CurrentDirData )) { return CALLBACK_CONTINUE; } } // Copy or move the file if requested if ((COPYTREE_DOCOPY & pCopyParams->flags) || (COPYTREE_DOMOVE & pCopyParams->flags)) { BOOL fNoOverwrite = (0 != (COPYTREE_NOOVERWRITE & pCopyParams->flags)); // // Create the directory. The function we call expects a full filename, // and considers the directory to end at the last wack. If this object // is a directory, we need to add at least a wack to make sure the last // path element is treated as part of the directory, not as a filename. // { WCHAR strTemp[MAX_WCHAR_PATH]; StringCopyW(strTemp, pCopyParams->szFullFileSpecOut); if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { AppendUncWackW(strTemp); } if (ERROR_SUCCESS != MakeSurePathExistsW(strTemp,FALSE)) { return rc; } } // // Copy or move the file // if (0 == (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { if (COPYTREE_DOCOPY & pCopyParams->flags) { DEBUGMSG ((DBG_NAUSEA, "Copying %s to %s", szFullFileSpecIn, pCopyParams->szFullFileSpecOut)); if (!CopyFileW( szFullFileSpecIn, pCopyParams->szFullFileSpecOut, fNoOverwrite )) { if (!fNoOverwrite) { LOG ((LOG_ERROR, "CopyFileW failed. Could not copy %s to %s", szFullFileSpecIn, pCopyParams->szFullFileSpecOut)); return rc; } if (ERROR_FILE_EXISTS != GetLastError()) { LOG ((LOG_ERROR, "CopyFileW failed. Could not copy %s to %s", szFullFileSpecIn, pCopyParams->szFullFileSpecOut)); return rc; } } } else if (COPYTREE_DOMOVE & pCopyParams->flags) { // If allowed to overwrite, delete the target if it exists if (!fNoOverwrite && DoesFileExistW(pCopyParams->szFullFileSpecOut)) { SetFileAttributesW (pCopyParams->szFullFileSpecOut, FILE_ATTRIBUTE_NORMAL); if (!DeleteFileW(pCopyParams->szFullFileSpecOut)) { LOG ((LOG_ERROR, "DeleteFileW failed. Could remove %s before moving", pCopyParams->szFullFileSpecOut)); return rc; } } // Move the file if (!MoveFileW( szFullFileSpecIn, pCopyParams->szFullFileSpecOut )) { LOG ((LOG_ERROR, "MoveFileW failed. Could not move %s to %s", szFullFileSpecIn, pCopyParams->szFullFileSpecOut)); return rc; } } } // // Copy the source file-or-directory's attributes to the target // SetFileAttributesW(pCopyParams->szFullFileSpecOut, pFindData->dwFileAttributes); } else if (COPYTREE_DODELETE & pCopyParams->flags) { SetFileAttributesW (szFullFileSpecIn, FILE_ATTRIBUTE_NORMAL); if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { DEBUGMSG ((DBG_NAUSEA, "Delete dir %ls", szFullFileSpecIn)); // // We don't care about the error. We won't stop the enumeration just // because we could not delete something. // RemoveDirectoryW (szFullFileSpecIn); } else { DEBUGMSG ((DBG_NAUSEA, "Delete file %ls", szFullFileSpecIn)); // // We don't care about the error. We won't stop the enumeration just // because we could not delete something. // DeleteFileW (szFullFileSpecIn); } } return CALLBACK_CONTINUE; } BOOL CopyTreeA( IN LPCSTR szEnumRootIn, IN LPCSTR szEnumRootOut, IN DWORD dwEnumHandle, IN DWORD flags, IN DWORD Levels, IN DWORD AttributeFilter, IN PEXCLUDEINFA ExcludeInfStruct, OPTIONAL IN FILEENUMPROCA pfnCallback, OPTIONAL IN FILEENUMFAILPROCA pfnFailCallback OPTIONAL ) /* This function enumerates a subtree of a disk drive, and optionally copies it to another location. No check is made to ensure the target is not contained within the source tree -- this condition could lead to unpredictable results. The parameters are a superset of those for EnumerateTree. The caller- supplied optional callback function can veto the copying of individual files, but cannot (as of 9/10) end the enumeration. Directories will be created as necessary to complete the copy. */ { COPYTREE_PARAMSA copyParams; CHAR szEnumRootInWack[MAX_MBCHAR_PATH]; CHAR szEnumRootOutWack[MAX_MBCHAR_PATH]; // // Build wacked copies of paths for use in parameter block. // // // Input path // StringCopyA(szEnumRootInWack, szEnumRootIn); AppendUncWackA(szEnumRootInWack); copyParams.szEnumRootInWack = szEnumRootInWack; copyParams.nCharsInRootInWack = ByteCountA(szEnumRootInWack); // // If output path is NULL, store 0 length and a NULL ptr in param block. // if (NULL != szEnumRootOut) { StringCopyA(szEnumRootOutWack, szEnumRootOut); AppendUncWackA(szEnumRootOutWack); copyParams.szEnumRootOutWack = szEnumRootOutWack; copyParams.nCharsInRootOutWack = ByteCountA(szEnumRootOutWack); } else { copyParams.szEnumRootOutWack = NULL; copyParams.nCharsInRootOutWack = 0; } copyParams.pfnCallback = pfnCallback; copyParams.flags = flags; if ((flags & COPYTREE_DOCOPY) && (flags & COPYTREE_DOMOVE)) { return ERROR_INVALID_PARAMETER; } if (flags & COPYTREE_DODELETE) { AttributeFilter |= FILTER_DIRS_LAST; } return EnumerateTreeA( szEnumRootInWack, CopyFileCallbackA, pfnFailCallback, dwEnumHandle, (LPVOID)©Params, Levels, ExcludeInfStruct, AttributeFilter); } BOOL CopyTreeW( IN LPCWSTR szEnumRootIn, IN LPCWSTR szEnumRootOut, IN DWORD dwEnumHandle, IN DWORD flags, IN DWORD Levels, IN DWORD AttributeFilter, IN PEXCLUDEINFW ExcludeInfStruct, OPTIONAL IN FILEENUMPROCW pfnCallback, OPTIONAL IN FILEENUMFAILPROCW pfnFailCallback OPTIONAL ) { COPYTREE_PARAMSW copyParams; WCHAR szEnumRootInWack[MAX_WCHAR_PATH]; WCHAR szEnumRootOutWack[MAX_WCHAR_PATH]; // // Place wacked copies of paths in parameter block. // // // Input Path // StringCopyW(szEnumRootInWack, szEnumRootIn); AppendUncWackW(szEnumRootInWack); copyParams.szEnumRootInWack = szEnumRootInWack; copyParams.nCharsInRootInWack = wcslen(szEnumRootInWack); // // If output path is NULL, put 0 length and NULL ptr in param block. // if (NULL != szEnumRootOut) { StringCopyW(szEnumRootOutWack, szEnumRootOut); AppendUncWackW(szEnumRootOutWack); copyParams.szEnumRootOutWack = szEnumRootOutWack; copyParams.nCharsInRootOutWack = wcslen(szEnumRootOutWack); } else { copyParams.szEnumRootOutWack = NULL; copyParams.nCharsInRootOutWack = 0; } copyParams.pfnCallback = pfnCallback; copyParams.flags = flags; if ((flags & COPYTREE_DOCOPY) && (flags & COPYTREE_DOMOVE)) { return ERROR_INVALID_PARAMETER; } if (flags & COPYTREE_DODELETE) { AttributeFilter |= FILTER_DIRS_LAST; } return EnumerateTreeW( szEnumRootInWack, CopyFileCallbackW, pfnFailCallback, dwEnumHandle, (LPVOID)©Params, Levels, ExcludeInfStruct, AttributeFilter); } DWORD CreateEmptyDirectoryA ( PCSTR Dir ) { DWORD rc; if (!DeleteDirectoryContentsA (Dir)) { rc = GetLastError(); if (rc != ERROR_PATH_NOT_FOUND) return rc; } if (!RemoveDirectoryA (Dir)) { rc = GetLastError(); if (rc != ERROR_PATH_NOT_FOUND) { return rc; } } return MakeSurePathExistsA (Dir, TRUE); } DWORD CreateEmptyDirectoryW ( PCWSTR Dir ) { DWORD rc; if (!DeleteDirectoryContentsW (Dir)) { rc = GetLastError(); if (rc != ERROR_PATH_NOT_FOUND) return rc; } if (!RemoveDirectoryW (Dir)) { rc = GetLastError(); if (rc != ERROR_PATH_NOT_FOUND) { return rc; } } return MakeSurePathExistsW (Dir, TRUE); }