/*++ Copyright (c) 1992-1993 Microsoft Corporation Module Name: CopyTree.c Abstract: ReplCopyTree() does a recursive file/directory copy. Author: John Rogers (JohnRo) 25-Mar-1992 Environment: User mode only. Uses Win32 APIs. Requires ANSI C extensions: slash-slash comments, long external names. Tab size is set to 4. Revision History: 25-Mar-1992 JohnRo Created. 26-Mar-1992 JohnRo Removed assumption about using the current directory. New ReplFind routines interface. 27-Mar-1992 JohnRo Create dest directories. Copy single file if asked to. 11-Aug-1992 JohnRo RAID 3288: repl svc should preserve ACLs on copy. 25-Feb-1993 JohnRo RAID 12237: replicator tree depth exceeded. Improve thread documentation. Use NetpKdPrint() where possible. Use PREFIX_ equates. 22-Apr-1993 JohnRo RAID 7157: replicator does not stop while in recursive tree copy. Make sure attributes, EAs, etc. are updated for each directory. Added error logging. Random cleanup. Prepare for >32 bits someday. --*/ #include // IN, LPTSTR, etc. #include // NET_API_STATUS, PATHLEN, etc. #include // ReplCopyTree(). #include // REPL_WIN32_FIND_DATA. #include // NELOG_ equates. #include // NetpKdPrint(), FORMAT_ equates, etc. #include // PREFIX_ equates. #include // IF_DEBUG(), STAR_DOT_STAR, etc. #include // ReplGlobal variables. #include // STRLEN(), TCHAR_EOS, etc. DBGSTATIC NET_API_STATUS ReplCopyRestOfTree( IN LPTSTR SourcePath, IN LPTSTR DestPath ); NET_API_STATUS ReplCopyTree( IN LPTSTR SourcePath, IN LPTSTR DestPath ) /*++ Routine Description: Front-end for ReplCopyRestOfTree(). See ReplCopyRestOfTree(). Arguments: See ReplCopyRestOfTree(). Return Value: NET_API_STATUS - NO_ERROR (copy complete). - ERROR_OPERATION_ABORTED (service is stopping). - other errors. Threads: Used by client and syncer threads. --*/ { NET_API_STATUS ApiStatus; DWORD Attributes; TCHAR FullSourceBuffer[PATHLEN+1]; TCHAR FullDestBuffer[PATHLEN+1]; NetpAssert( SourcePath != NULL ); NetpAssert( (*SourcePath) != TCHAR_EOS ); NetpAssert( STRLEN(SourcePath) <= PATHLEN ); NetpAssert( DestPath != NULL ); NetpAssert( (*DestPath) != TCHAR_EOS ); NetpAssert( STRLEN(DestPath) <= PATHLEN ); Attributes = GetFileAttributes( SourcePath ); if ( Attributes == (DWORD) -1 ) { // // Source doesn't exist, bad syntax, or something along those lines. // ApiStatus = (NET_API_STATUS) GetLastError(); NetpAssert( ApiStatus != NO_ERROR ); // Log this! ReplErrorLog( NULL, // local (no server name) NELOG_ReplUpdateError, // log code ApiStatus, DestPath, SourcePath ); // BUGBUG: Log on remote server too if we got UNC name. } else { if ((Attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // // Simple case: just a single file. // ReplCopyFile() will copy data, attributes, everything! // ApiStatus = ReplCopyFile( SourcePath, DestPath, FALSE ); // Don't fail if exists. // (Error already logged by ReplCopyFile.) } else { // // It's a directory tree. // Set up large buffers and call the worker to handle this. // (void) STRCPY( FullSourceBuffer, SourcePath ); (void) STRCPY( FullDestBuffer, DestPath ); ApiStatus = ReplCopyRestOfTree( FullSourceBuffer, FullDestBuffer ); // (Error already logged by ReplCopyRestOfTree.) } } IF_DEBUG( SYNC ) { NetpKdPrint(( PREFIX_REPL_CLIENT "ReplCopyTree: tree copy of " FORMAT_LPTSTR " to " FORMAT_LPTSTR " gave status " FORMAT_API_STATUS ".\n", SourcePath, DestPath, ApiStatus )); } // No need to log error here, as it has already been done. return (ApiStatus); } // ReplCopyTree. DBGSTATIC NET_API_STATUS ReplCopyRestOfTree( IN LPTSTR SourcePath, IN LPTSTR DestPath ) /*++ Routine Description: Scans recursivly thru an entire sub-tree copying files and directories. Uses a depth-first algorithm. Arguments: BUGBUG Note - path of dir to be scanned. Must be alloc'ed as TCHAR[PATHLEN+1], as this routine uses the space at the end for temporary stuff. Return Value: NET_API_STATUS Threads: Used by client and syncer threads. --*/ { NET_API_STATUS ApiStatus; DWORD DestAttributes; DWORD DestPathIndex; REPL_WIN32_FIND_DATA SearchBuf; LPREPL_FIND_HANDLE SearchHandle = INVALID_REPL_HANDLE; DWORD SourcePathIndex; #define UNEXPECTED( apiName ) \ { \ NetpKdPrint(( PREFIX_REPL_CLIENT \ "ReplCopyRestOfTree: Unexpected status from " \ apiName " (" FORMAT_DWORD ").\n", ApiStatus )); \ } SourcePathIndex = STRLEN(SourcePath); NetpAssert( SourcePathIndex < PATHLEN ); DestPathIndex = STRLEN(DestPath); NetpAssert( DestPathIndex < PATHLEN ); // // Prevent trashing a file with this directory. // DestAttributes = GetFileAttributes( DestPath ); if ((DestAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { ApiStatus = ERROR_ALREADY_EXISTS; NetpKdPrint(( PREFIX_REPL_CLIENT "ReplCopyRestOfTree: *ERROR* " "Copying dir to file - invalid.\n" )); goto Cleanup; } IF_DEBUG( MAJOR ) { if (DestAttributes == (DWORD)-1) { NetpKdPrint(( PREFIX_REPL_CLIENT "ReplCopyRestOfTree: Creating directory " FORMAT_LPTSTR "...\n", DestPath )); } else { NetpKdPrint(( PREFIX_REPL_CLIENT "ReplCopyRestOfTree: Updating directory " FORMAT_LPTSTR "...\n", DestPath )); } } // // Make sure dest directory exists, with right attributes, EAs, etc. // ApiStatus = ReplCopyDirectoryItself( SourcePath, // place to copy security, timestamp, etc from. DestPath, // Name of the new directory. FALSE); // Don't fail if it already exists. if (ApiStatus != NO_ERROR) { UNEXPECTED( "ReplCopyDirectoryItself" ); goto Cleanup; } // // Setup to scan this directory for files and directories. // (void) STRCAT( SourcePath, SLASH ); (void) STRCAT( SourcePath, STAR_DOT_STAR ); IF_DEBUG( SYNC ) { NetpKdPrint(( PREFIX_REPL_CLIENT "ReplCopyRestOfTree: Processing source " FORMAT_LPTSTR ", dest " FORMAT_LPTSTR ".\n", SourcePath, DestPath )); } SearchHandle = ReplFindFirstFile( SourcePath, &SearchBuf); SourcePath[SourcePathIndex] = TCHAR_EOS; if (SearchHandle == INVALID_REPL_HANDLE) { ApiStatus = (NET_API_STATUS) GetLastError(); UNEXPECTED( "ReplFindFirstFile" ); goto Cleanup; } // // Loop for each files and directory in this directory. // do { // // Quit if service is stopping. // if (ReplGlobalIsServiceStopping) { ApiStatus = ERROR_OPERATION_ABORTED; goto Cleanup; } // // Ignore REPL.INI, USERLOCK.*, ., .., and so on. // if ( ReplIgnoreDirOrFileName( SearchBuf.fdFound.cFileName ) ) { continue; // Skip to next one. } // Append "\". SourcePath[SourcePathIndex] = TCHAR_BACKSLASH; DestPath[DestPathIndex] = TCHAR_BACKSLASH; // Append sub-dir or file name name. (void) STRCPY( SourcePath + SourcePathIndex + 1, SearchBuf.fdFound.cFileName); NetpAssert( STRLEN( SourcePath ) <= PATHLEN ); (void) STRCPY( DestPath + DestPathIndex + 1, SearchBuf.fdFound.cFileName); NetpAssert( STRLEN( DestPath ) <= PATHLEN ); if (SearchBuf.fdFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // // Recursively copy this subdirectory. // ApiStatus = ReplCopyRestOfTree( SourcePath, DestPath ); if (ApiStatus != NO_ERROR) { UNEXPECTED( "ReplCopyRestOfTree" ); goto Cleanup; } } else { IF_DEBUG( SYNC ) { NetpKdPrint(( PREFIX_REPL_CLIENT "ReplCopyRestOfTree: Copying file " FORMAT_LPTSTR " to " FORMAT_LPTSTR ".\n", SourcePath, DestPath )); } // // Copy this individual file; don't fail if it already exists. // ReplCopyFile() will copy data, attributes, everything. // ApiStatus = ReplCopyFile(SourcePath, DestPath, FALSE); if (ApiStatus != NO_ERROR) { UNEXPECTED( "ReplCopyFile" ); goto Cleanup; } } // Reset path names for next pass through the loop. SourcePath[SourcePathIndex] = TCHAR_EOS; DestPath[DestPathIndex] = TCHAR_EOS; } while (ReplFindNextFile(SearchHandle, &SearchBuf)); ApiStatus = NO_ERROR; Cleanup: IF_DEBUG( SYNC ) { NetpKdPrint(( PREFIX_REPL_CLIENT "ReplCopyRestOfTree() is done, stat=" FORMAT_API_STATUS ".\n", ApiStatus )); } if (SearchHandle != INVALID_REPL_HANDLE) { if( !ReplFindClose(SearchHandle) ) { ApiStatus = (NET_API_STATUS) GetLastError(); UNEXPECTED( "ReplFindClose" ); NetpAssert( ApiStatus != NO_ERROR ); } } // // Log error. // if (ApiStatus != NO_ERROR) { // Log this locally. ReplErrorLog( NULL, // local (no server name) NELOG_ReplUpdateError, // log code ApiStatus, DestPath, SourcePath ); // BUGBUG: Log error on remote server too, if we got UNC name. } // // Reset path names, now that we've logged them. // SourcePath[SourcePathIndex] = TCHAR_EOS; DestPath[DestPathIndex] = TCHAR_EOS; return (ApiStatus); } // ReplCopyRestOfTree