Code/Resource
Windows Develop
Linux-Unix program
Internet-Socket-Network
Web Server
Browser Client
Ftp Server
Ftp Client
Browser Plugins
Proxy Server
Email Server
Email Client
WEB Mail
Firewall-Security
Telnet Server
Telnet Client
ICQ-IM-Chat
Search Engine
Sniffer Package capture
Remote Control
xml-soap-webservice
P2P
WEB(ASP,PHP,...)
TCP/IP Stack
SNMP
Grid Computing
SilverLight
DNS
Cluster Service
Network Security
Communication-Mobile
Game Program
Editor
Multimedia program
Graph program
Compiler program
Compress-Decompress algrithms
Crypt_Decrypt algrithms
Mathimatics-Numerical algorithms
MultiLanguage
Disk/Storage
Java Develop
assembly language
Applications
Other systems
Database system
Embeded-SCM Develop
FlashMX/Flex
source in ebook
Delphi VCL
OS Develop
MiddleWare
MPI
MacOS develop
LabView
ELanguage
Software/Tools
E-Books
Artical/Document
copy.c
Package: shell.rar [view]
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 273k
Category:
Windows Kernel
Development Platform:
Visual C++
- TCHAR cTemp;
- SHNAMEMAPPING FAR* prp = DSA_GetItemPtr(hdsaRenamePairs, i);
- // I don't call StrCmpNI 'cause I already know cchOldPath, and
- // it has to do a couple of lstrlen()s to calculate it.
- cTemp = pszTarget[prp->cchOldPath];
- pszTarget[prp->cchOldPath] = 0;
- // Does the target match this collision renaming entry?
- // NOTE: We are trying to compare a path to a path. prp->pszOldPath
- // does not have a trailing "" character, so this isn't covered
- // by the lstrcmpi below. As such, cTemp had best be the path
- // seperator character to ensure that the modified pszTarget is actually
- // a path and not a filename or a longer path name that doesn't match
- // but happens to start with the same characters as prp->pszOldPath.
- if ((cTemp == TEXT('\')) && !lstrcmpi(pszTarget, prp->pszOldPath))
- {
- // Get subtree string of the target.
- TCHAR *pszSubTree = &(pszTarget[prp->cchOldPath + 1]);
- // Generate the new target path.
- PathCombine(pszTarget, prp->pszNewPath, pszSubTree);
- break;
- }
- else
- {
- // Restore the trounced character.
- pszTarget[prp->cchOldPath] = cTemp;
- }
- }
- }
- /* Sets the status dialog item in the modeless status dialog box. */
- // used for both the drag drop status dialogs and the manual user
- // entry dialogs so be careful what you change
- void SetProgressText(COPY_STATE *pcs, LPCTSTR pszFrom, LPCTSTR pszTo)
- {
- if (pcs->hwndProgress && !(pcs->fFlags & FOF_SIMPLEPROGRESS))
- {
- TCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
- LPTSTR pszMsg = NULL;
- SetDlgItemText(pcs->hwndProgress, IDD_NAME,
- PathFindFileName((pcs->fFlags & FOF_MULTIDESTFILES) ? pszTo : pszFrom));
- lstrcpy(szFrom, pszFrom);
- if (szFrom[0])
- {
- PathRemoveFileSpec(szFrom);
- if (pszTo)
- {
- lstrcpy(szTo, pszTo);
- PathRemoveFileSpec(szTo);
- }
- pszMsg = ShellConstructMessageString(HINST_THISDLL,
- pszTo ? MAKEINTRESOURCE(IDS_FROMTO) : MAKEINTRESOURCE(IDS_FROM),
- PathFindFileName(szFrom),
- pszTo ? PathFindFileName(szTo) : NULL);
- }
- else if (!pcs->fDTBuilt)
- {
- TCHAR szFunc[80];
- if (LoadString(HINST_THISDLL, FOFuncToStringID(pcs->lpfo->wFunc),
- szFunc, ARRAYSIZE(szFunc)))
- {
- pszMsg = ShellConstructMessageString(HINST_THISDLL,
- MAKEINTRESOURCE(IDS_PREPARINGTO), szFunc);
- }
- }
- if (pszMsg)
- {
- SetDlgItemText(pcs->hwndProgress, IDD_TONAME, pszMsg);
- LocalFree(pszMsg);
- }
- }
- }
- void SetProgressTimeEst(COPY_STATE *pcs, DWORD dwTimeLeft)
- {
- TCHAR szFmt[60];
- TCHAR szOut[70];
- DWORD dwTime;
- if (pcs->hwndProgress)
- {
- // BUGBUG: how well does this localize?
- if (dwTimeLeft > 60)
- {
- // Note that dwTime is at least 2, so we only need a plural form
- LoadString(HINST_THISDLL, IDS_TIMEEST_MINUTES, szFmt, ARRAYSIZE(szFmt));
- dwTime = (dwTimeLeft / 60) + 1;
- }
- else
- {
- LoadString(HINST_THISDLL, IDS_TIMEEST_SECONDS, szFmt, ARRAYSIZE(szFmt));
- // Round up to 5 seconds so it doesn't look so random
- dwTime = ((dwTimeLeft+4) / 5) * 5;
- }
- wsprintf(szOut, szFmt, dwTime);
- SetDlgItemText(pcs->hwndProgress, IDD_TIMEEST, szOut);
- }
- }
- // this updates the animation, which could change because we could switch between
- // doing a move to recycle bin and really nuke if the file/folder was bigger that
- // the allowable size of the recycle bin.
- void UpdateProgressAnimation(COPY_STATE *pcs)
- {
- if (pcs->hwndProgress && pcs->lpfo)
- {
- INT_PTR idAni, idAniCurrent;
- HWND hwndAnimation;
- switch (pcs->lpfo->wFunc)
- {
- case FO_DELETE:
- if ((pcs->lpfo->lpszProgressTitle == MAKEINTRESOURCE(IDS_BB_EMPTYINGWASTEBASKET)) ||
- (pcs->lpfo->lpszProgressTitle == MAKEINTRESOURCE(IDS_BB_DELETINGWASTEBASKETFILES)))
- {
- idAni = IDA_FILENUKE;
- break;
- }
- else if (!(pcs->fFlags & FOF_ALLOWUNDO))
- {
- idAni = IDA_FILEDELREAL;
- break;
- } // else fall through to default
- default:
- idAni = (IDA_FILEMOVE + (int)pcs->lpfo->wFunc - FO_MOVE);
- }
- hwndAnimation = GetDlgItem(pcs->hwndProgress,IDD_ANIMATE);
- idAniCurrent = (INT_PTR) GetProp(hwndAnimation, TEXT("AnimationID"));
- if (idAni != idAniCurrent)
- {
- // the one we should be using is different from the one we have,
- // so update it
- // close the old clip
- Animate_Close(hwndAnimation);
- // open the new one
- Animate_Open(hwndAnimation, idAni);
- // if the window is enabled, start the new animation playing
- if (IsWindowEnabled(pcs->hwndProgress))
- Animate_Play(hwndAnimation, -1, -1, -1);
- // set the current idAni
- SetProp(hwndAnimation, TEXT("AnimationID"), (HANDLE)idAni);
- // at the same time we update the animation, we also update the text,
- // so that the two will always be in sync
- SetProgressText(pcs, pcs->dth.szSrcPath, pcs->lpfo->wFunc == FO_DELETE ? NULL : pcs->dth.szDestPath);
- }
- }
- }
- void SendProgressMessage(COPY_STATE *pcs, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- if (pcs->hwndProgress)
- SendDlgItemMessage(pcs->hwndProgress, IDD_PROBAR, uMsg, wParam, lParam);
- }
- // see if this file is loaded by kernel, thus something we don't
- // want to fuck with.
- //
- // pszPath fully qualified path name
- //
- BOOL IsWindowsFileEx(LPCTSTR pszFile, BOOL bWin32)
- {
- LPCTSTR pszSpec = PathFindFileName(pszFile);
- if (pszSpec)
- {
- HMODULE hMod = bWin32 ? GetModuleHandle(pszSpec)
- : GetModuleHandle16(pszSpec);
- if (hMod)
- {
- TCHAR szModule[MAX_PATH];
- bWin32 ? GetModuleFileName(hMod, szModule, ARRAYSIZE(szModule))
- : GetModuleFileName16(hMod, szModule, ARRAYSIZE(szModule));
- return !lstrcmpi(pszFile, szModule);
- }
- }
- return FALSE;
- }
- BOOL IsWindowsFile(LPCTSTR pszFile)
- {
- return IsWindowsFileEx(pszFile, TRUE) || IsWindowsFileEx(pszFile, FALSE);
- }
- // verify that we can see the contents of a newly created folder
- // this is to deal with the case where net drives have an unknown path
- // limit.
- //
- // assumes:
- // folder exists and is empty (newly created)
- //
- // returns:
- // ERROR_SUCCESS everything is fine
- // ERROR_* failure
- int VerifyFolderVisible(HWND hwnd, LPCTSTR pszPath)
- {
- int res = ERROR_SUCCESS;
- ASSERT(PathIsDirectory(pszPath)); // must exist and be a folder
- if (PathIsUNC(pszPath) || IsRemoteDrive(DRIVEID(pszPath)))
- {
- TCHAR szTest[MAX_PATH];
- HANDLE hfile;
- BOOL bFoundFile = FALSE;
- PathCombine(szTest, pszPath, TEXT("TESTDIR.TMP"));
- hfile = CreateFile(szTest, GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
- FILE_ATTRIBUTE_TEMPORARY |
- FILE_ATTRIBUTE_HIDDEN |
- FILE_ATTRIBUTE_SYSTEM |
- FILE_FLAG_DELETE_ON_CLOSE, NULL);
- if (hfile != INVALID_HANDLE_VALUE)
- {
- WIN32_FIND_DATA fd;
- HANDLE hfind;
- PathRemoveFileSpec(szTest); // replace file with "*"
- PathAppend(szTest, c_szStar);
- hfind = FindFirstFile(szTest, &fd);
- if (hfind != INVALID_HANDLE_VALUE)
- {
- do {
- if (!lstrcmpi(fd.cFileName, TEXT("TESTDIR.TMP")))
- {
- bFoundFile = TRUE;
- break;
- }
- } while (FindNextFile(hfind, &fd));
- FindClose(hfind);
- }
- CloseHandle(hfile); // FILE_FLAG_DELETE_ON_CLOSE does the DeleteFile()
- }
- if (!bFoundFile)
- {
- RECT rcMonitor;
- GetMonitorRect(MonitorFromWindow(hwnd, TRUE), &rcMonitor);
- PathRemoveFileSpec(szTest); // remove "*"
- PathCompactPath(NULL, szTest, (rcMonitor.right - rcMonitor.left) / 3);
- if (!hwnd ||
- ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CREATELONGDIR),
- MAKEINTRESOURCE(IDS_CREATELONGDIRTITLE),
- MB_SETFOREGROUND | MB_ICONHAND | MB_YESNO, (LPTSTR)szTest) != IDYES)
- {
- Win32RemoveDirectory(pszPath);
- res = ERROR_CANCELLED;
- }
- }
- }
- return res;
- }
- //
- // creates folder and all parts of the path if necessary (parent does not need
- // to exists) and verifies that the contents of the folder will be visibile.
- //
- // in:
- // hwnd hwnd to post UI on
- // pszPath full path to create
- // psa security attributes
- //
- // returns:
- // ERROR_SUCCESS (0) success
- // ERROR_ failure
- //
- STDAPI_(int) SHCreateDirectoryEx(HWND hwnd, LPCTSTR pszPath, SECURITY_ATTRIBUTES *psa)
- {
- int ret = ERROR_SUCCESS;
- if (PathIsRelative(pszPath))
- {
- // if not a "full" path bail
- // to ensure that we dont create a dir in the current working directory
- SetLastError(ERROR_BAD_PATHNAME);
- return ERROR_BAD_PATHNAME;
- }
- if (!Win32CreateDirectory(pszPath, psa))
- {
- TCHAR *pEnd, *pSlash, szTemp[MAX_PATH + 1]; // +1 for PathAddBackslash()
- ret = GetLastError();
- // There are certain error codes that we should bail out here
- // before going through and walking up the tree...
- switch (ret)
- {
- case ERROR_FILENAME_EXCED_RANGE:
- case ERROR_FILE_EXISTS:
- case ERROR_ALREADY_EXISTS:
- return ret;
- }
- lstrcpyn(szTemp, pszPath, ARRAYSIZE(szTemp) - 1);
- pEnd = PathAddBackslash(szTemp); // for the loop below
- // assume we have 'X:' to start this should even work
- // on UNC names because will will ignore the first error
- pSlash = szTemp + 3;
- // create each part of the dir in order
- while (*pSlash)
- {
- while (*pSlash && *pSlash != TEXT('\'))
- pSlash = CharNext(pSlash);
- if (*pSlash)
- {
- ASSERT(*pSlash == TEXT('\'));
- *pSlash = 0; // terminate path at seperator
- ret = Win32CreateDirectory(szTemp, pSlash + 1 == pEnd ? psa : NULL) ? ERROR_SUCCESS : GetLastError();
- }
- *pSlash++ = TEXT('\'); // put the seperator back
- }
- }
- if (ERROR_SUCCESS == ret)
- {
- // we succeeded in creating the folder, lets see if its visible
- if (PathIsUNC(pszPath) || IsRemoteDrive(DRIVEID(pszPath)))
- ret = VerifyFolderVisible(hwnd, pszPath);
- }
- else
- {
- // We failed, so let's try to display error UI.
- if ( hwnd && ERROR_CANCELLED != ret )
- {
- SHSysErrorMessageBox(hwnd, NULL, IDS_CANNOTCREATEFOLDER, ret,
- pszPath ? PathFindFileName(pszPath) : NULL,
- MB_OK | MB_ICONEXCLAMATION);
- ret = ERROR_CANCELLED; // Indicate we already displayed Error UI.
- }
- }
- return ret;
- }
- STDAPI_(int) SHCreateDirectory(HWND hwnd, LPCTSTR pszPath)
- {
- return SHCreateDirectoryEx(hwnd, pszPath, NULL);
- }
- #ifdef UNICODE
- STDAPI_(int) SHCreateDirectoryExA(HWND hwnd, LPCSTR pszPath, SECURITY_ATTRIBUTES *psa)
- {
- WCHAR wsz[MAX_PATH];
- SHAnsiToUnicode(pszPath, wsz, SIZECHARS(wsz));
- return SHCreateDirectoryEx(hwnd, wsz, psa);
- }
- #else
- STDAPI_(int) SHCreateDirectoryExW(HWND hwnd, LPCWSTR pszPath, SECURITY_ATTRIBUTES *psa)
- {
- char sz[MAX_PATH];
- SHUnicodeToAnsi(pszPath, sz, SIZECHARS(sz));
- return SHCreateDirectoryEx(hwnd, sz, psa);
- }
- #endif
- #ifndef COPY_USE_COPYFILEEX
- // in:
- //
- // returns:
- #define REGPATH_RSN TEXT("Software\Microsoft\Windows\CurrentVersion\FileCopy")
- #define REGVAL_RSN TEXT("ReadShareNetware")
- BOOL OpenDestFile(LPCTSTR pszDest, HFILE *phf, DWORD dwAttribs, BOOL fCreateAlways)
- {
- HFILE fh;
- DWORD dwFlag = 0;
- DWORD dwSize = sizeof( DWORD );
- DWORD dwType = REG_BINARY;
- // NB Some networks will fail writes if you open the file readonly.
- dwAttribs &= ~FILE_ATTRIBUTE_READONLY;
- // NB Open the dest file without read sharing. That way, if the source and the
- // dest are the same file and we didn't detect it (because of UNC weirdness) we'll
- // get a sharing violation instead of trashing the file.
- // Warning: FILE_SHARE_READ needed without this we can have data loss if running on netware client
- // and doing move where the target runs out of disk space
- //
- // IEQFE #665: We're damned either way with FILE_SHARE_READ, so use it or not based on the
- // value of HKLMSoftwareMicrosoftWindowsCurrentVersionFileCopyReadShareNetware.
- //
- if ( ERROR_SUCCESS != SHGetValue( HKEY_LOCAL_MACHINE, REGPATH_RSN, REGVAL_RSN, &dwType, &dwFlag, &dwSize ))
- dwFlag = 0; // make sure
- fh = (HFILE)CreateFile(pszDest, GENERIC_WRITE, dwFlag ? FILE_SHARE_READ : 0, 0L, fCreateAlways ? CREATE_ALWAYS : CREATE_NEW, dwAttribs, NULL);
- if (GetLastError() == ERROR_ACCESS_DENIED)
- {
- // If the file is readonly, reset the readonly attribute
- // and have another go at it
- DWORD dwAttributes = GetFileAttributes(pszDest);
- if (0xFFFFFFFF != dwAttributes)
- {
- dwAttributes &= ~FILE_ATTRIBUTE_READONLY;
- if (SetFileAttributes(pszDest, dwAttributes))
- {
- fh = (HFILE)CreateFile(pszDest, GENERIC_WRITE, dwFlag ? FILE_SHARE_READ : 0, 0L, fCreateAlways ? CREATE_ALWAYS : CREATE_NEW, dwAttribs, NULL);
- }
- }
- else
- {
- // The last error obtained from trying to create the
- // destination file needs to be preserved.
- *phf = (HFILE) ERROR_ACCESS_DENIED;
- return FALSE;
- }
- }
- if (fh == HFILE_ERROR)
- {
- *phf = (HFILE)GetLastError();
- return FALSE;
- }
- *phf = fh;
- return TRUE;
- }
- #endif // COPY_USE_COPYFILEEX
- // call MPR to find out the speed of a given path
- //
- // returns
- // 0 for unknown
- // 144 for 14.4 modems
- // 96 for 9600
- // 24 for 2400
- //
- // if the device does not return a speed we return 0
- //
- DWORD GetPathSpeed(LPCTSTR pszPath)
- {
- NETCONNECTINFOSTRUCT nci;
- NETRESOURCE nr;
- TCHAR szPath[MAX_PATH];
- lstrcpyn(szPath, pszPath, ARRAYSIZE(szPath));
- PathStripToRoot(szPath); // get a root to this path
- memset(&nci, 0, SIZEOF(nci));
- nci.cbStructure = SIZEOF(nci);
- memset(&nr, 0, SIZEOF(nr));
- if (PathIsUNC(szPath))
- nr.lpRemoteName = szPath;
- else
- {
- // Don't bother for local drives
- if (!IsRemoteDrive(DRIVEID(szPath)))
- return 0;
- // we are passing in a local drive and MPR does not like us to pass a
- // local name as Z: but only wants Z:
- szPath[2] = TEXT(''); // Strip off after character and :
- nr.lpLocalName = szPath;
- }
- // dwSpeed is returned by MultinetGetConnectionPerformance
- MultinetGetConnectionPerformance(&nr, &nci);
- return nci.dwSpeed;
- }
- #ifndef COPY_USE_COPYFILEEX
- // This function determines the size of the copy buffer, depending
- // on the speed of the connection. (for slow connections)
- //
- // in:
- // pszSource fully qualified source path (ANSI)
- // pszDest fully qualified destination path (ANSI)
- //
- // returns:
- // optimal buffer size (optimized for approximately 1 sec bursts)
- // with a maximum size of COPYMAXBUFFERSIZE
- UINT SizeFromLinkSpeed(LPCTSTR pszSource, LPCTSTR pszDest, BOOL *pbFlushWrites)
- {
- DWORD dwSize, dwSpeed, dwSrc, dwDst;
- dwSrc = GetPathSpeed(pszSource);
- dwDst = GetPathSpeed(pszDest);
- if ((dwSrc == 0) || (dwDst == 0))
- {
- dwSpeed = dwSrc == 0 ? dwDst : dwSrc;
- }
- else
- {
- dwSpeed = min(dwSrc, dwDst);
- }
- dwSize = (dwSpeed * 100 / 8); // convert 100 bps to bytes for 1 second
- // round up to a sector size (512 == 0x200)
- dwSize = (dwSize + 511) & ~511;
- if (dwSize == 0 || dwSize > COPYMAXBUFFERSIZE)
- dwSize = COPYMAXBUFFERSIZE;
- // If the destination is on some type of slow link, we should flush
- // per write as to make it such that the user can cancel out of the operation
- // This is a guess for what size should be the threshold...
- //
- *pbFlushWrites = (dwDst > 0) && (dwDst < 0x500);
- DebugMsg(TF_DEBUGCOPY, TEXT("Copy Size = %d, Copy Speed = %d, Flush = %x"), dwSize, dwSpeed, *pbFlushWrites);
- return dwSize;
- }
- #endif !COPY_USE_COPYFILEEX
- #ifdef COPY_USE_COPYFILEEX
- DWORD CopyCallbackProc(LARGE_INTEGER liTotSize, LARGE_INTEGER liBytes,
- LARGE_INTEGER liStreamSize, LARGE_INTEGER liStreamBytes,
- DWORD dwStream, DWORD dwCallback,
- HANDLE hSource, HANDLE hDest, void *pv)
- {
- COPY_STATE *pcs = (COPY_STATE *)pv;
- DWORD dwBytesRead = (DWORD)liBytes.QuadPart;
- DebugMsg(DM_TRACE, TEXT("CopyCallbackProc[%08lX], totsize=%08lX, bytes=%08lX"),
- dwCallback, liTotSize.LowPart, liBytes.LowPart);
- if (FOQueryAbort(pcs))
- return PROGRESS_CANCEL;
- DTSetFileCopyProgress(&pcs->dth, dwBytesRead);
- if (pcs->fInitialize)
- {
- // preserve the create date when moving across volumes, otherwise use the
- // create date the file system picked when we did the CreateFile()
- // always preserve modified date (ftLastWriteTime)
- // bummer is we loose accuracy when going to VFAT compared to NT servers
- SetFileTime((HANDLE)hDest, (pcs->lpfo->wFunc == FO_MOVE) ? &pcs->pfd->ftCreationTime : NULL,
- NULL, &pcs->pfd->ftLastWriteTime);
- pcs->fInitialize = FALSE;
- }
- switch(dwCallback)
- {
- case CALLBACK_STREAM_SWITCH:
- break;
- case CALLBACK_CHUNK_FINISHED:
- break;
- default:
- break;
- }
- return PROGRESS_CONTINUE;
- }
- #endif
- #ifdef WINNT
- // copy the SECURITY_DESCRIPTOR for two files
- //
- // in:
- // pszSource fully qualified source path
- // pszDest fully qualified destination path
- //
- // returns:
- // 0 ERROR_SUCCESS
- // WIN32 error codes
- //
- DWORD
- CopyFileSecurity(LPCTSTR pszSource, LPCTSTR pszDest)
- {
- DWORD err = ERROR_SUCCESS;
- BOOL fRet = TRUE;
- BYTE buf[512]; // BUGBUG arbitrary default size here...
- // BUGBUG arbitrarily saying do everything we can
- // except SACL_SECURITY_INFORMATION because
- SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
- PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) buf;
- DWORD cbPsd = SIZEOF(buf);
- if (!SHRestricted(REST_FORCECOPYACLWITHFILE))
- {
- // shell restriction so return access denied?
- return ERROR_ACCESS_DENIED;
- }
- fRet = GetFileSecurity(pszSource, si, psd, cbPsd, &cbPsd);
- if (!fRet)
- {
- err = GetLastError();
- if (ERROR_INSUFFICIENT_BUFFER == err)
- {
- // just need to resize the buffer and try again
- // ASSERT(FALSE); // BUGBUGREMOVE
- psd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, cbPsd);
- if (psd)
- fRet = GetFileSecurity(pszSource, si, psd, cbPsd, &cbPsd);
- else
- err = ERROR_NOT_ENOUGH_MEMORY;
- }
- }
- if (fRet)
- {
- fRet = SetFileSecurity(pszDest, si, psd);
- if (!fRet)
- err = GetLastError();
- }
- if (psd && psd != buf)
- LocalFree(psd);
- if (fRet)
- return ERROR_SUCCESS;
- return err;
- }
- #endif // WINNT
- // This function queues copies. If the queue is full the queue is purged.
- //
- // in:
- // hwnd Window to report things to.
- // pszSource fully qualified source path (ANSI)
- // pszDest fully qualified destination path (ANSI)
- // pfd source file find data (size/date/time/attribs)
- //
- // returns:
- // 0 success
- // dos error code for failure
- //
- #ifdef WINNT
- // We'll GetProcAddress the MoveFileWithProgress API on NT5+ only
- typedef BOOL (WINAPI *PFNMOVEFILEWITHPROGRESS)(LPCWSTR lpExistingFileName,
- LPCWSTR lpNewFileName,
- LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
- void *lpData OPTIONAL,
- DWORD dwFlags);
- #endif
- UINT FileCopy(COPY_STATE *pcs, LPCTSTR pszSource, LPCTSTR pszDest, const WIN32_FIND_DATA *pfd, BOOL fCreateAlways)
- {
- UINT iRet = ERROR_CANCELLED;
- HFILE hSource = HFILE_ERROR;
- HFILE hDest = HFILE_ERROR;
- int iLastError;
- #ifdef COPY_USE_COPYFILEEX
- BOOL fRetryPath = FALSE;
- BOOL fRetryAttr = FALSE;
- BOOL fCopyOrMoveSucceeded = FALSE;
- #else
- HFILE fh;
- DWORD dwRead, dwWrite;
- DWORD dwBytesLeft;
- DWORD dwBytesRead;
- #endif
- #ifdef WINNT
- BOOL fSecurityObtained = FALSE;
- // Buffers for security info
- BYTE rgbSecurityDescriptor[512];
- SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
- PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) rgbSecurityDescriptor;
- DWORD cbPsd = SIZEOF(rgbSecurityDescriptor);
- #endif
- #ifdef COPY_USE_COPYFILEEX
- // The MoveFileWithProgressW function pointer
- BOOL fUseMoveFileWithProgress = FALSE;
- HMODULE hinstKernel32 = NULL;
- static PFNMOVEFILEWITHPROGRESS pfnMoveFileWithProgress = NULL;
- #endif
- // Make sure we can start
- if (FOQueryAbort(pcs))
- return ERROR_CANCELLED;
- #ifdef COPY_USE_COPYFILEEX // {
- //
- // Now do the file copy/move
- //
- #ifdef WINNT // {
- // Get the security info from the source file. If there is a problem
- // (e.g. the file is on FAT) we ignore it and proceed with the copy/move.
- if (!(pcs->fFlags & FOF_NOCOPYSECURITYATTRIBS))
- {
- if (SHRestricted(REST_FORCECOPYACLWITHFILE))
- {
- if ( GetFileSecurity(pszSource, si, psd, cbPsd, &cbPsd ))
- {
- fSecurityObtained = TRUE;
- }
- else
- {
- if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
- {
- psd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, cbPsd);
- if ( psd && !GetFileSecurity(pszSource, si, psd, cbPsd, &cbPsd) )
- fSecurityObtained = TRUE;
- }
- }
- }
- }
- // Attempt to get the NT5 MoveFileWithProgress API
- if ( g_bRunOnNT5 && FO_MOVE == pcs->lpfo->wFunc )
- {
- if ( NULL != pfnMoveFileWithProgress )
- {
- fUseMoveFileWithProgress = TRUE;
- }
- else
- {
- hinstKernel32 = GetModuleHandle( TEXT("kernel32.dll") );
- if ( NULL == hinstKernel32 )
- {
- DebugMsg( DM_ERROR, TEXT("FileCopy couldn't get kernel32.dll (%lu)"), GetLastError() );
- }
- else
- {
- pfnMoveFileWithProgress = (PFNMOVEFILEWITHPROGRESS)
- GetProcAddress( hinstKernel32, "MoveFileWithProgressW" );
- if ( NULL == pfnMoveFileWithProgress )
- DebugMsg( DM_ERROR, TEXT("FileCopy couldn't get MoveFileWithProgressW"), GetLastError() );
- else
- fUseMoveFileWithProgress = TRUE;
- }
- }
- } // if ( g_fNewTrack )
- #endif // } #ifdef WINNT
- TryCopyAgain:
- pcs->fInitialize = TRUE;
- pcs->pfd = pfd;
- SetProgressText(pcs, pszSource, pszDest);
- fCopyOrMoveSucceeded = fUseMoveFileWithProgress
- ? pfnMoveFileWithProgress( pszSource, pszDest, CopyCallbackProc, pcs, MOVEFILE_COPY_ALLOWED | (fCreateAlways ? MOVEFILE_REPLACE_EXISTING : 0) )
- : CopyFileEx(pszSource, pszDest, CopyCallbackProc, pcs, &pcs->bAbort, fCreateAlways? 0 : COPY_FILE_FAIL_IF_EXISTS );
- if (!fCopyOrMoveSucceeded)
- {
- iLastError = (int)GetLastError();
- DebugMsg( TF_DEBUGCOPY, TEXT("FileCopy() failed, get last error returned 0x%08x"), iLastError );
- #ifdef WINNT
- // HACKHACK: workaround for bug in NT4 CopyFileEx (see IE4#51085)
- if ((iLastError == ERROR_SHARING_VIOLATION) && !StrCmpC(pszSource, pszDest))
- {
- iLastError = ERROR_FILE_EXISTS;
- DebugMsg( TF_DEBUGCOPY, TEXT("Error code converted to 0x%08x (ERROR_FILE_EXISTS)"), iLastError );
- }
- #endif // WINNT
- switch(iLastError)
- {
- // Let the caller handle this one
- case ERROR_FILE_EXISTS:
- case ERROR_ALREADY_EXISTS: // nt5 221893 CopyFileEx now returns this for some reason...
- iRet = ERROR_FILE_EXISTS;
- goto Exit;
- case ERROR_DISK_FULL:
- if (!IsRemovableDrive(DRIVEID(pszDest))
- || PathIsSameRoot(pszDest,pszSource))
- {
- break;
- }
- iLastError = DE_NODISKSPACE;
- // Fall through
- case ERROR_PATH_NOT_FOUND:
- if (!fRetryPath)
- {
- // ask the user to stick in another disk or empty wastebasket
- iLastError = CopyMoveRetry(pcs, pszDest, iLastError, pfd->nFileSizeLow);
- if (!iLastError)
- {
- fRetryPath = TRUE;
- goto TryCopyAgain;
- }
- CopyError(pcs, pszSource, pszDest, (UINT)iLastError | ERRORONDEST, FO_COPY, OPER_DOFILE);
- iRet = ERROR_CANCELLED;
- goto Exit;
- }
- break;
- case ERROR_ACCESS_DENIED:
- {
- // check if the filename is too long
- if ( lstrlen(PathFindFileName(pszSource)) + lstrlen(pszDest) >= MAX_PATH )
- {
- iLastError = DE_FILENAMETOOLONG;
- }
- else if (!fRetryAttr)
- {
- // If the file is readonly, reset the readonly attribute
- // and have another go at it
- DWORD dwAttributes = GetFileAttributes(pszDest);
- if (0xFFFFFFFF != dwAttributes)
- {
- dwAttributes &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
- if (SetFileAttributes(pszDest, dwAttributes))
- {
- fRetryAttr = TRUE;
- goto TryCopyAgain;
- }
- }
- // GetFileAttributes() 10 lines above clobers GetLastError() and CopyError()
- // needs it.
- SetLastError(iLastError);
- }
- }
- break;
- }
- if (!pcs->bAbort)
- {
- CopyError(pcs, pszSource, pszDest, iLastError, FO_COPY, OPER_DOFILE);
- }
- iRet = ERROR_CANCELLED; // error already reported
- goto Exit;
- }
- #else // }{ #ifdef COPY_USE_COPYFILEEX
- // SizeFromLinkSpeed assumes there is a connection already established
- if (!pcs->lpCopyBuffer)
- {
- pcs->uSize = SizeFromLinkSpeed(pszSource, pszDest, &pcs->fFlushWrites);
- // BUGBUG: For wildcard or dir copies/moves, we calculate link speed and
- // allocate the buffers for each file!
- pcs->lpCopyBuffer = (void*)LocalAlloc(LPTR, pcs->uSize);
- if (!pcs->lpCopyBuffer)
- {
- DebugMsg(DM_WARNING, TEXT("insuf. mem for lpCopyBuffer"));
- return DE_INSMEM; // memory failure
- }
- }
- // Still ok to continue?
- if (FOQueryAbort(pcs))
- return ERROR_CANCELLED;
- hSource = (HFILE)CreateFile(pszSource, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, 0L, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
- if (hSource == HFILE_ERROR)
- {
- CopyError(pcs, pszSource, pszDest, (int)GetLastError(), FO_COPY, OPER_DOFILE);
- return ERROR_CANCELLED; // error already reported
- }
- // open destination files
- if (FOQueryAbort(pcs))
- goto CloseSource;
- TryOpen:
- if (!OpenDestFile(pszDest, &hDest, pfd->dwFileAttributes, fCreateAlways))
- {
- // error operning/creating destinaton file
- fh = hDest;
- if (fh == ERROR_FILE_EXISTS && !fCreateAlways)
- {
- iRet = ERROR_FILE_EXISTS;
- goto CloseSource;
- }
- if (fh == ERROR_PATH_NOT_FOUND)
- {
- TryOpenDestAgain:
- // ask the user to stick in another disk or empty wastebasket
- fh = CopyMoveRetry(pcs, pszDest, fh, pfd->nFileSizeLow);
- if (!fh)
- {
- goto TryOpen;
- }
- }
- // can't recover ... bail!
- CopyError(pcs, pszSource, pszDest, (UINT)fh | ERRORONDEST, FO_COPY, OPER_DOFILE);
- goto CloseSource;
- }
- // BUGBUG - BobDay - Copying more than 4 gig will not be done correctly here
- dwBytesLeft = pfd->nFileSizeLow;
- dwBytesRead = 0;
- /* Now copy between the open files */
- SetProgressText(pcs, pszSource, pszDest);
- //SendProgressMessage(pcs, PBM_SETRANGE, 0, MAKELONG(0, (WORD)((pfd->nFileSizeLow + pcs->uSize - 1) / pcs->uSize)));
- dwRead = (DWORD)pcs->uSize;
- // initialzie the file to the full size
- // this takes 3 dos calls, so only do it if the file is big
- if (pfd->nFileSizeLow > (COPYMAXBUFFERSIZE * 3))
- {
- // if there's a problem, bail
- if ((_llseek(hDest, pfd->nFileSizeLow, 0L) == HFILE_ERROR) ||
- (!SetEndOfFile((HANDLE)hDest)))
- {
- iLastError = GetLastError();
- goto ErrorOnWrite;
- }
- else
- {
- _llseek(hDest, 0, 0L);
- }
- }
- /* Now copy between the open files */
- do
- {
- iLastError = 0;
- if (FOQueryAbort(pcs))
- goto OpCancelled;
- //dwRead = _lread(hSource, pcs->lpCopyBuffer, pcs->uSize);
- if (! ReadFile((HANDLE)hSource, pcs->lpCopyBuffer, pcs->uSize, &dwRead, NULL))
- {
- // Error during file read
- CopyError(pcs, pszSource, pszDest, (int)GetLastError(), FO_COPY, OPER_DOFILE);
- goto OpCancelled;
- }
- //SendProgressMessage(pcs, PBM_DELTAPOS, 1, 0);
- //wWrite = _lwrite(hDest, pcs->lpCopyBuffer, wRead);
- if (! WriteFile((HANDLE)hDest, pcs->lpCopyBuffer, dwRead, &dwWrite, NULL))
- dwWrite = (DWORD)-1;
- // write did not complete and removable drive?
- if (dwRead != dwWrite)
- {
- iLastError = GetLastError();
- #ifndef WRITEFILE_SETSLASTERROR_ON_DISKFULL
- // if no error set and we couldn't write, assume disk full
- if (!iLastError)
- iLastError = DE_NODISKSPACE;
- #endif
- }
- if (pcs->fFlushWrites)
- FlushFileBuffers((HANDLE)hDest);
- ErrorOnWrite:
- if ((iLastError == DE_NODISKSPACE) && IsRemovableDrive(DRIVEID(pszDest)) &&
- !PathIsSameRoot(pszDest, pszSource))
- {
- // seek back to the start of the source.
- _llseek(hSource, 0L, 0);
- // destination disk must be full. close all
- // destination files and delete those that
- // have not been copied yet then
- // give the user the option to insert a new disk.
- _lclose(hDest);
- hDest = (HFILE)-1;
- Win32DeleteFile(pszDest);
- fh = DE_NODISKSPACE;
- goto TryOpenDestAgain; // and try to create the destiations
- }
- else if (iLastError)
- {
- // error writing file
- CopyError(pcs, pszSource, pszDest, (int)iLastError | ERRORONDEST, FO_COPY, OPER_DOFILE);
- goto OpCancelled;
- }
- // Reduce by ammount copied
- dwBytesLeft -= dwRead;
- // Add to so far read pile
- dwBytesRead += dwRead;
- DTSetFileCopyProgress(&pcs->dth, dwBytesRead);
- } while (dwRead && dwBytesLeft);
- // Close all destination files, set date time attribs
- // preserve the create date when moving across volumes, otherwise use the
- // create date the file system picked when we did the CreateFile()
- // always preserve modified date (ftLastWriteTime)
- // bummer is we loose accuracy when going to VFAT compared to NT servers
- SetFileTime((HANDLE)hDest, (pcs->lpfo->wFunc == FO_MOVE) ? &pfd->ftCreationTime : NULL,
- NULL, &pfd->ftLastWriteTime);
- _lclose(hDest);
- // NB We may have opened the destination with different attributes than the source
- // so reset them now.
- if (pfd->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
- SetFileAttributes(pszDest, pfd->dwFileAttributes);
- _lclose(hSource);
- #endif // } #ifdef COPY_USE_COPYFILEEX ... #else
- #ifdef WINNT
- // Set the source's security on the destination, ignoring any error.
- if (SHRestricted(REST_FORCECOPYACLWITHFILE))
- {
- if ( fSecurityObtained )
- SetFileSecurity(pszDest, si, psd);
- }
- #endif // #ifdef WINNT
- SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, pszDest, NULL);
- if (pcs->lpfo->wFunc == FO_MOVE)
- {
- #ifdef COPY_USE_COPYFILEEX
- if (fUseMoveFileWithProgress)
- {
- // Let windows waiting on notifications of Source know of change. We have to check
- // to see if the file is actually gone in order to tell if it actually moved or not.
- if (!PathFileExists(pszSource))
- SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, pszSource, NULL);
- iRet = 0;
- }
- else
- {
- #endif
- // BUGBUG, will this fail if source attribs are readonly?
- iRet = Win32DeleteFile(pszSource) ? 0 : GetLastError();
- if (iRet == ERROR_ACCESS_DENIED)
- {
- // We may need to make the file not read-only
- SetFileAttributes(pszSource, FILE_ATTRIBUTE_NORMAL);
- iRet = Win32DeleteFile(pszSource) ? 0 : GetLastError();
- }
- #ifdef COPY_USE_COPYFILEEX
- }
- #endif
- }
- else
- {
- iRet = 0;
- }
- #ifdef COPY_USE_COPYFILEEX
- // this line has been taken out because it causes us to not report the erro when we failed to delete the source...
- // iRet = 0
- goto Exit;
- #else
- return iRet; // success
- #endif
- #ifndef COPY_USE_COPYFILEEX
- OpCancelled:
- if (hDest != HFILE_ERROR)
- _lclose(hDest);
- Win32DeleteFile(pszDest);
- CloseSource:
- if (hSource != HFILE_ERROR)
- _lclose(hSource);
- if (pcs->lpCopyBuffer)
- {
- LocalFree((HLOCAL)pcs->lpCopyBuffer);
- pcs->lpCopyBuffer = NULL;
- }
- return iRet;
- #endif // #ifndef COPY_USE_COPYFILEEX
- #ifdef COPY_USE_COPYFILEEX
- Exit:
- #ifdef WINNT
- // If we had to alloc a buffer for the security descriptor,
- // free it now.
- if ( NULL != psd && rgbSecurityDescriptor != psd )
- LocalFree(psd);
- #endif // #ifdef WINNT
- return iRet;
- #endif // #ifdef COPY_USE_COPYFILEEX
- }
- // note: this is a very slow call
- DWORD GetFreeClusters(LPCTSTR szPath)
- {
- DWORD dwFreeClus;
- DWORD dwTemp;
- if (GetDiskFreeSpace(szPath,
- &dwTemp, // Don't care
- &dwTemp, // Don't care
- &dwFreeClus,
- &dwTemp)) // Don't care
- return dwFreeClus;
- else
- return (DWORD)-1;
- }
- // note: this is a very slow call
- DWORD TotalCapacity(LPCTSTR szPath)
- {
- int idDrive = PathGetDriveNumber(szPath);
- if (idDrive != -1)
- {
- DWORD dwSecPerClus, dwBytesPerSec, dwClusters, dwTemp;
- TCHAR szDrive[5];
- PathBuildRoot(szDrive, idDrive);
- if (GetDiskFreeSpace(szDrive, &dwSecPerClus, &dwBytesPerSec, &dwTemp, &dwClusters))
- return dwSecPerClus * dwBytesPerSec * dwClusters;
- }
- return 0;
- }
- typedef struct
- {
- LPTSTR pszTitle;
- LPTSTR pszText;
- } DISKERRORPARAM;
- BOOL_PTR CALLBACK DiskErrDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
- {
- switch(uMessage)
- {
- case WM_INITDIALOG:
- {
- DISKERRORPARAM *pDiskError = (DISKERRORPARAM *) lParam;
- if (pDiskError)
- {
- SetWindowText(hDlg, pDiskError->pszTitle);
- SetDlgItemText(hDlg, IDC_DISKERR_EXPLAIN, pDiskError->pszText);
- }
- Static_SetIcon(GetDlgItem(hDlg, IDC_DISKERR_STOPICON),
- LoadIcon(NULL, IDI_HAND));
- }
- break;
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDOK:
- case IDCANCEL:
- case IDC_DISKERR_LAUNCHCLEANUP:
- EndDialog (hDlg, LOWORD(wParam));
- break;
- default:
- return FALSE;
- }
- break;
- default:
- return FALSE;
- }
- return TRUE;
- }
- void DisplayFileOperationError(HWND hParent, int idVerb, int wFunc, int nError, LPCTSTR szReason, LPCTSTR szPath, LPCTSTR szDest)
- {
- TCHAR szBuffer[80];
- DISKERRORPARAM diskparams;
- int idDrive;
- // Grab title from resource
- if (LoadString(HINST_THISDLL, IDS_FILEERROR + wFunc, szBuffer, ARRAYSIZE(szBuffer)))
- {
- diskparams.pszTitle = szBuffer;
- }
- else
- {
- diskparams.pszTitle = NULL;
- }
- // Build Message to display
- diskparams.pszText = ShellConstructMessageString(HINST_THISDLL,
- MAKEINTRESOURCE(idVerb), szReason, PathFindFileName(szPath));
- if (diskparams.pszText)
- {
- idDrive = DriveIDFromBBPath(szDest);
- //if we want to show Disk cleanup do our stuff, otherwise do MessageBox
- if (nError == DE_NODISKSPACE &&
- IsBitBucketableDrive(idDrive) &&
- GetDiskCleanupPath(NULL, 0))
- {
- if (DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_DISKERR), hParent,
- DiskErrDlgProc,(LPARAM) &diskparams) == IDC_DISKERR_LAUNCHCLEANUP)
- {
- LaunchDiskCleanup(hParent, idDrive);
- }
- }
- else
- {
- MessageBox(hParent, diskparams.pszText, diskparams.pszTitle,
- MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
- }
- LocalFree(diskparams.pszText);
- }
- }
- /***********************************************************************
- DESCRIPTION:
- We received an SHARINGVIOLATION or ACCESSDENIED error. We want
- to generate the most accruate error message for the user to inform
- them better. These are the cases we care about:
- DE_ACCESSDENIEDSRC: This is the legacy case with the message:
- "Access is denied. The source file may be in use."
- DE_DEST_IS_CDROM: This is displayed in case the user copies a file to
- their cd-rom drive.
- DE_DEST_IS_DVD: This is displayed in case the user copies a file to
- their DVD drive
- DE_SHARING_VIOLATION: The file can't be copied because it's open by someone
- who doesn't allow others to read the file while they
- use it.
- DE_PERMISSIONDENIED: This should be displayed if the user doesn't have
- the ACLs (security permissions) to read/copy the file.
- ***********************************************************************/
- int GenAccessDeniedError(LPCTSTR pszSource, LPCTSTR pszDest, int nError)
- {
- int nErrorMsg = DE_ACCESSDENIEDSRC;
- int iDrive = PathGetDriveNumber(pszDest);
- if (iDrive != -1)
- {
- if (IsCDRomDrive(iDrive))
- nErrorMsg = DE_DEST_IS_CDROM;
- if (DriveIsDVD(iDrive))
- nErrorMsg = DE_DEST_IS_DVD;
- }
- // TODO: DE_SHARING_VIOLATION, DE_PERMISSIONDENIED
- return nErrorMsg;
- }
- //
- // The following function reports errors for the copy engine
- //
- // Parameters
- // pszSource source file name
- // pszDest destination file name
- // nError dos (or our exteneded) error code
- // 0xFFFF for special case NET error
- // wFunc FO_* values
- // nOper OPER_* values, operation being performed
- //
- void CopyError(LPCOPY_STATE pcs, LPCTSTR pszSource, LPCTSTR pszDest, int nError, UINT wFunc, int nOper)
- {
- TCHAR szReason[200];
- TCHAR szFile[MAX_PATH];
- int idVerb;
- BOOL bDest;
- BOOL fSysError = FALSE;
- DWORD dwError = GetLastError(); // get Extended error now before we blow it away.
- if (!pcs || (pcs->fFlags & FOF_NOERRORUI))
- return; // caller doesn't want to report errors
- bDest = nError & ERRORONDEST; // was dest file cause of error
- nError &= ~ERRORONDEST; // clear the dest bit
- // We also may need to remap some new error codes into old error codes
- //
- if (nError == ERROR_BAD_PATHNAME)
- nError = DE_INVALIDFILES;
- if (nError == ERROR_CANCELLED) // user abort
- return;
- lstrcpyn(szFile, bDest ? pszDest : pszSource, ARRAYSIZE(szFile));
- if (!szFile[0])
- {
- LoadString(HINST_THISDLL, IDS_FILE, szFile, ARRAYSIZE(szFile));
- }
- else
- {
- // make the path fits on the screen
- RECT rcMonitor;
- HWND hwnd = pcs->hwndProgress;
- if (!hwnd)
- {
- hwnd = pcs->hwndDlgParent;
- }
- GetMonitorRect(MonitorFromWindow(hwnd, TRUE), &rcMonitor);
- PathCompactPath(NULL, szFile,
- (rcMonitor.right - rcMonitor.left) / 3);
- }
- // get the verb string
- // since we now recycle folders as well as files, added OPER_ENTERDIR check here
- if ((nOper == OPER_DOFILE) || (nOper == OPER_ENTERDIR) || (nOper == 0))
- {
- if ((nError != -1) && bDest)
- {
- idVerb = IDS_REPLACING;
- }
- else
- {
- idVerb = IDS_VERBS + wFunc;
- }
- }
- else
- {
- idVerb = IDS_ACTIONS + (nOper >> 8);
- }
- // get the reason string
- if (nError == 0xFFFF)
- {
- DWORD dw;
- WNetGetLastError(&dw, szReason, ARRAYSIZE(szReason), NULL , 0);
- }
- else
- {
- // transform some error cases
- if (bDest)
- {
- // BUGBUG:: This caseing of error codes is error prone.. it would
- // be better to find the explicit ones we wish to map to
- // this one instead of trying to guess all the ones
- // we don't want to map...
- if ((nError == ERROR_DISK_FULL) ||
- ((nError != ERROR_ACCESS_DENIED) &&
- (nError != ERROR_NETWORK_ACCESS_DENIED) &&
- (nError != ERROR_WRITE_PROTECT) &&
- (nError != ERROR_BAD_NET_NAME) &&
- (GetFreeClusters(pszDest) == 0L)))
- {
- nError = DE_NODISKSPACE;
- }
- else if (dwError == DE_WRITEFAULT)
- {
- nError = DE_WRITEFAULT;
- }
- }
- else
- {
- if (nError == ERROR_ACCESS_DENIED)
- {
- // Check the extended error for more info about the error...
- // We just map these errors to something generic that
- // tells the user something weird is going on.
- switch (dwError)
- {
- case DE_CRCDATAERROR:
- case DE_SEEKERROR:
- case DE_SECTORNOTFOUND:
- case DE_READFAULT:
- case ERROR_GEN_FAILURE:
- nError = ERROR_GEN_FAILURE;
- break;
- // Whoever wrote this code needs to come to my office for a good old ass-kicking.
- // We can't test for ERROR_FILE_NOT_FOUND because in the case where we copy to
- // a write-protected dest we check to see if the reason we got access denied was
- // because there's already a read-only file there. If there isn't _that_ test is
- // going to SetLastError() to ERROR_FILE_NOT_FOUND and that's what we're going to
- // report as an error. [davepl]
- //
- // case ERROR_FILE_NOT_FOUND:
- // nError = ERROR_GEN_FAILURE;
- // break;
- case DE_SHARINGVIOLATION:
- case DE_ACCESSDENIED:
- nError = GenAccessDeniedError(pszSource, pszDest, nError);
- break;
- default:
- TraceMsg(TF_WARNING, "CopyEngine: hit error %x , not currently special cased", dwError);
- break;
- }
- }
- else
- {
- // This error occures when a user drags & drops a file from point a to
- // point b twice. The second time fails because the first time hasn't finished.
- if (nError == (OPER_ERROR | DE_FILENOTFOUND))
- {
- nError = ERROR_GEN_FAILURE;
- }
- }
- }
- }
- // BUGBUG: This is total bullshit. fSysError might as well be a random number after this call.
- // But that't ok because nError is already a random number to start with. Sigh.
- // This should be a range check instead of hoping that LoadString will fail.
- fSysError = !LoadString(HINST_THISDLL, IDS_REASONS + nError, szReason, ARRAYSIZE(szReason));
- if (nOper == OPER_DOFILE)
- {
- PathRemoveExtension(szFile);
- }
- if (fSysError)
- {
- SHSysErrorMessageBox(pcs->hwndDlgParent, MAKEINTRESOURCE(IDS_FILEERROR + wFunc),
- idVerb, nError, PathFindFileName(szFile),
- MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
- }
- else
- {
- DisplayFileOperationError(pcs->hwndDlgParent, idVerb, wFunc, nError, szReason, szFile, pszDest);
- }
- }
- //
- // The following function is used to retry failed move/copy operations
- // due to out of disk situations or path not found errors
- // on the destination.
- //
- // parameters:
- // pszDest Fully qualified path to destination file (ANSI)
- // nError type of error: DE_NODISKSPACE or ERROR_PATH_NOT_FOUND
- // dwFileSize amount of space needed for this file if DE_NODISKSPACE
- //
- // returns:
- // 0 success (destination path has been created)
- // != 0 dos error code including ERROR_CANCELLED
- //
- int CopyMoveRetry(COPY_STATE *pcs, LPCTSTR pszDest, int nError, DWORD dwFileSize)
- {
- UINT wFlags;
- int result;
- LPCTSTR wID;
- TCHAR szTemp[MAX_PATH];
- BOOL fFirstRetry = TRUE;
- if (pcs->fFlags & FOF_NOERRORUI)
- {
- result = ERROR_CANCELLED;
- goto ErrorExit;
- }
- lstrcpyn(szTemp, pszDest, ARRAYSIZE(szTemp));
- PathRemoveFileSpec(szTemp);
- do
- {
- // until the destination path has been created
- if (nError == ERROR_PATH_NOT_FOUND)
- {
- if (!( pcs->fFlags & FOF_NOCONFIRMMKDIR))
- {
- wID = MAKEINTRESOURCE(IDS_PATHNOTTHERE);
- wFlags = MB_ICONEXCLAMATION | MB_YESNO;
- }
- else
- {
- wID = 0;
- }
- }
- else // DE_NODISKSPACE
- {
- wFlags = MB_ICONEXCLAMATION | MB_RETRYCANCEL;
- if (dwFileSize > TotalCapacity(pszDest))
- {
- wID = MAKEINTRESOURCE(IDS_FILEWONTFIT);
- }
- else
- {
- wID = MAKEINTRESOURCE(IDS_DESTFULL);
- }
- }
- if (wID)
- {
- // szTemp will be ignored if there's no %1%s in the string.
- result = ShellMessageBox(HINST_THISDLL, pcs->hwndDlgParent, wID, MAKEINTRESOURCE(IDS_UNDO_FILEOP + pcs->lpfo->wFunc), wFlags, (LPTSTR)szTemp);
- }
- else
- {
- result = IDYES;
- }
- if (result == IDRETRY || result == IDYES)
- {
- TCHAR szDrive[5];
- int idDrive;
- // Allow the disk to be formatted
- // REVIEW, could this be FO_MOVE as well?
- if (FAILED(SHPathPrepareForWrite(((pcs->fFlags & FOF_NOERRORUI) ? NULL : pcs->hwndDlgParent), NULL, szTemp, SHPPFW_DEFAULT)))
- return ERROR_CANCELLED;
- idDrive = PathGetDriveNumber(szTemp);
- if (idDrive != -1)
- PathBuildRoot(szDrive, idDrive);
- else
- szDrive[0] = 0;
- // if we're not copying to the root
- if (lstrcmpi(szTemp, szDrive))
- {
- result = SHCreateDirectory(pcs->hwndDlgParent, szTemp);
- if (result == ERROR_CANCELLED)
- goto ErrorExit;
- if ( result == ERROR_ALREADY_EXISTS )
- {
- // if SHPathPrepareForWrite created the directory we shouldn't treat this as an error
- result = 0;
- }
- else if (result && (nError == ERROR_PATH_NOT_FOUND))
- {
- result |= ERRORONDEST;
- // We try twice to allow the recyclebin to be flushed.
- if (fFirstRetry)
- fFirstRetry = FALSE;
- else
- goto ErrorExit;
- }
- }
- else
- {
- result = 0;
- }
- }
- else
- {
- result = ERROR_CANCELLED;
- goto ErrorExit;
- }
- } while (result);
- ErrorExit:
- return result; // success
- }
- // BUGBUG: This function is bogus because PathIsInvalid is bogus.
- BOOL ValidFilenames(LPCTSTR pList)
- {
- if ( !*pList )
- return FALSE;
- for (; *pList; pList += lstrlen(pList) + 1)
- {
- if (PathIsInvalid(pList))
- {
- return FALSE;
- }
- }
- return TRUE;
- }
- void AddRenamePairToHDSA(LPCTSTR pszOldPath, LPCTSTR pszNewPath, HDSA* phdsaRenamePairs)
- {
- //
- // Update our collision mapping table
- //
- if (!*phdsaRenamePairs)
- *phdsaRenamePairs = DSA_Create(SIZEOF(SHNAMEMAPPING), 4);
- if (*phdsaRenamePairs)
- {
- SHNAMEMAPPING rp;
- rp.cchOldPath = lstrlen(pszOldPath);
- rp.cchNewPath = lstrlen(pszNewPath);
- if (NULL != (rp.pszOldPath = Alloc((rp.cchOldPath + 1) * SIZEOF(TCHAR))))
- {
- if (NULL != (rp.pszNewPath = Alloc((rp.cchNewPath + 1) * SIZEOF(TCHAR))))
- {
- lstrcpy(rp.pszOldPath, pszOldPath);
- lstrcpy(rp.pszNewPath, pszNewPath);
- if (DSA_InsertItem(*phdsaRenamePairs,
- DSA_GetItemCount(*phdsaRenamePairs),
- &rp) == -1)
- {
- Free(rp.pszOldPath);
- Free(rp.pszNewPath);
- }
- }
- else
- {
- Free(rp.pszOldPath);
- }
- }
- }
- }
- BOOL _HandleRename(LPCTSTR pszSource, LPTSTR pszDest, FILEOP_FLAGS fFlags, COPY_STATE * pcs)
- {
- TCHAR *pszConflictingName = PathFindFileName(pszSource);
- TCHAR szTemp[MAX_PATH];
- TCHAR szTemplate[MAX_PATH];
- LPTSTR lpszLongPlate;
- PathRemoveFileSpec(pszDest);
- if (LoadString(HINST_THISDLL, IDS_COPYLONGPLATE, szTemplate, ARRAYSIZE(szTemplate)))
- {
- LPTSTR lpsz;
- lpsz = pszConflictingName;
- lpszLongPlate = szTemplate;
- // see if the first part of the template is the same as the name "Copy #"
- while (*lpsz && *lpszLongPlate &&
- *lpsz == *lpszLongPlate &&
- *lpszLongPlate != TEXT('('))
- {
- lpsz++;
- lpszLongPlate++;
- }
- if (*lpsz == TEXT('(') && *lpszLongPlate == TEXT('('))
- {
- // conflicting name already in the template, use it instead
- lpszLongPlate = pszConflictingName;
- }
- else
- {
- // otherwise build our own
- // We need to make sure not to overflow a max buffer.
- int ichFixed = lstrlen(szTemplate) + lstrlen(pszDest) + 5;
- lpszLongPlate = szTemplate;
- if ((ichFixed + lstrlen(pszConflictingName)) <= MAX_PATH)
- {
- lstrcat(lpszLongPlate, pszConflictingName);
- }
- else
- {
- // Need to remove some of the name
- LPTSTR pszExt = StrRChr(pszConflictingName, NULL, TEXT('.'));
- if (pszExt)
- {
- lstrcpyn(lpszLongPlate + lstrlen(lpszLongPlate),
- pszConflictingName,
- MAX_PATH - ichFixed - lstrlen(pszExt));
- lstrcat(lpszLongPlate, pszExt);
- }
- else
- {
- lstrcpyn(lpszLongPlate + lstrlen(lpszLongPlate),
- pszConflictingName,
- MAX_PATH - ichFixed);
- }
- }
- }
- }
- else
- {
- lpszLongPlate = NULL;
- }
- if (PathYetAnotherMakeUniqueName(szTemp, pszDest, pszConflictingName, lpszLongPlate))
- {
- //
- // If there are any other files in the queue which are to
- // be copied into a subtree of pszDest, we must update them
- // as well.
- //
- // Put the new (renamed) target in pszDest.
- lstrcpy(pszDest, szTemp);
- // Rebuild the old dest name and put it in szTemp.
- // I'm going for minimum stack usage here, so I don't want more
- // than one MAX_PATH lying around.
- PathRemoveFileSpec(szTemp);
- PathAppend(szTemp, pszConflictingName);
- AddRenamePairToHDSA(szTemp, pszDest, &pcs->dth.hdsaRenamePairs);
- return(TRUE);
- }
- return(FALSE);
- }
- // test input for "multiple" filespec
- //
- // examples:
- // 1 foo.bar (single non directory file)
- // -1 *.exe (wild card on any of the files)
- // n foo.bar bletch.txt (number of files)
- //
- int CountFiles(LPCTSTR pInput)
- {
- int count;
- for (count = 0; *pInput; pInput += lstrlen(pInput) + 1, count++) {
- // wild cards imply multiple files
- if (PathIsWild(pInput))
- return -1;
- }
- return count;
- }
- // set the attribs of a folder, but blow it off if there are no
- // special attributes set
- void SetDirAttributes(LPCTSTR szDest, DWORD dwFileAttributes)
- {
- if (dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN))
- SetFileAttributes(szDest, dwFileAttributes & ~FILE_ATTRIBUTE_DIRECTORY);
- }
- #define ISDIGIT(c) ((c) >= TEXT('0') && (c) <= TEXT('9'))
- BOOL IsCompressedVolume(LPCTSTR pszSource, DWORD dwAttributes)
- {
- int i;
- LPTSTR pszFileName, pszExtension;
- TCHAR szPath[MAX_PATH];
- // must be marked system and hidden
- if (!IS_SYSTEM_HIDDEN(dwAttributes))
- return FALSE;
- lstrcpy(szPath, pszSource);
- pszFileName = PathFindFileName(szPath);
- pszExtension = PathFindExtension(pszFileName);
- // make sure the extension is a 3 digit number
- if (!*pszExtension)
- return FALSE; // no extension
- for (i = 1; i < 4; i++)
- {
- if (!pszExtension[i] || !ISDIGIT(pszExtension[i]))
- return FALSE;
- }
- // make sure it's null terminated here
- if (pszExtension[4])
- return FALSE;
- // now knock off the extension and make sure the stem matches
- *pszExtension = 0;
- if (lstrcmpi(pszFileName, TEXT("DRVSPACE")) &&
- lstrcmpi(pszFileName, TEXT("DBLSPACE")))
- {
- return FALSE;
- }
- // make sure it's in the root
- PathRemoveFileSpec(szPath);
- if (!PathIsRoot(szPath))
- {
- return FALSE;
- }
- return TRUE; // passed all tests!
- }
- void _DeferMoveDlgItem(HDWP hdwp, HWND hDlg, int nItem, int x, int y)
- {
- RECT rc;
- HWND hwnd = GetDlgItem(hDlg, nItem);
- GetClientRect(hwnd, &rc);
- MapWindowPoints(hwnd, hDlg, (LPPOINT) &rc, 2);
- DeferWindowPos(hdwp, hwnd, 0, rc.left + x, rc.top + y, 0, 0,
- SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
- }
- void _RecalcWindowHeight(HWND hWnd, LPTSTR lpszText)
- {
- HDC hdc = GetDC(hWnd);
- RECT rc;
- HWND hwndText = GetDlgItem(hWnd,IDC_MBC_TEXT);
- HDWP hdwp;
- int iHeightDelta, cx;
- // Get the starting rect of the text area (for the width)
- GetClientRect(hwndText, &rc);
- MapWindowPoints(hwndText, hWnd, (LPPOINT) &rc, 2);
- // Calc how high the static text area needs to be, given the above width
- iHeightDelta = RECTHEIGHT(rc);
- cx = RECTWIDTH(rc);
- DrawText(hdc, lpszText, -1, &rc, DT_CALCRECT | DT_WORDBREAK | DT_LEFT | DT_INTERNAL | DT_EDITCONTROL);
- iHeightDelta = RECTHEIGHT(rc) - iHeightDelta;
- cx = RECTWIDTH(rc) - cx; // Should only change for really long words w/o spaces
- ReleaseDC(hWnd, hdc);
- hdwp = BeginDeferWindowPos(4);
- DeferWindowPos(hdwp, hwndText, 0, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER | SWP_NOACTIVATE);
- _DeferMoveDlgItem(hdwp, hWnd, IDC_MESSAGEBOXCHECKEX, 0, iHeightDelta);
- _DeferMoveDlgItem(hdwp, hWnd, IDYES, cx, iHeightDelta);
- _DeferMoveDlgItem(hdwp, hWnd, IDNO, cx, iHeightDelta);
- EndDeferWindowPos(hdwp);
- GetWindowRect(hWnd, &rc);
- SetWindowPos(hWnd, 0, rc.left - (cx/2), rc.top - (iHeightDelta/2), RECTWIDTH(rc)+cx, RECTHEIGHT(rc)+iHeightDelta, SWP_NOZORDER | SWP_NOACTIVATE);
- return;
- }
- BOOL_PTR CALLBACK RenameMsgBoxCheckDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- switch (uMsg)
- {
- // we only handle the WM_INITDIALOG so that we can resize the dialog
- // approprately and to set the default button to IDNO
- case WM_INITDIALOG:
- {
- HWND hwndNO = GetDlgItem(hDlg, IDNO);
- _RecalcWindowHeight(hDlg, (LPTSTR)lParam);
- SetDlgItemText(hDlg,IDC_MBC_TEXT,(LPTSTR)lParam);
- SendMessage(hDlg, DM_SETDEFID, IDNO, 0);
- SetFocus(hwndNO);
- return (FALSE); // we set the focus, so return false
- }
- }
- // didnt handle this message
- return FALSE;
- }
- int ConfirmRenameOfConnectedItem(COPY_STATE *pcs, WIN32_FIND_DATA *pfd, LPTSTR szSource)
- {
- int result = IDYES; //For non-connected elements, the default is IDYES!
- LPTSTR pszMessage;
- LPTSTR lpConnectedItem, lpConnectOrigin;
- LPTSTR lpStringID;
- //Check if this item being renamed has a connected item.
- if (DTNIsConnectOrigin(pcs->dth.pdtnCurrent))
- {
- //Yes! It has a connected element! Form the strings to create the confirmation dialog!
- //Get the name of the connected element
- lpConnectedItem = PathFindFileName(pcs->dth.pdtnCurrent->pdtnConnected->szName);
- lpConnectOrigin = PathFindFileName(pcs->dth.pFrom);
- // Mark the connected item as dummy as this will never get renamed.
- // (Note that this connected node could be a folder. It is still OK to mark it as
- // dummy because for rename operation, a folder is treated just like a file in
- // DTGotoNextNode()).
- pcs->dth.pdtnCurrent->pdtnConnected->fDummy = TRUE;
- if (pfd && (pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- lpStringID = MAKEINTRESOURCE(IDS_HTML_FOLDER_RENAME);
- else
- lpStringID = MAKEINTRESOURCE(IDS_HTML_FILE_RENAME);
- //Load the confirmation message and format it!
- pszMessage = ShellConstructMessageString(HINST_THISDLL, lpStringID,
- lpConnectedItem, lpConnectOrigin);
- if (pszMessage)
- {
- //Get the confirmation from the end-user;
- result = SHMessageBoxCheckEx(pcs->hwndDlgParent, HINST_THISDLL,
- MAKEINTRESOURCE(DLG_RENAME_MESSAGEBOXCHECK),
- RenameMsgBoxCheckDlgProc,
- (void *)pszMessage,
- IDYES,
- REG_VAL_GENERAL_RENAMEHTMLFILE);
- //It is possible we get IDCANCEL if the "X" in the caption is clicked to clost
- // the dialog. The following code makes sure we get one of the return code that we want.
- if ((result != IDYES) && (result != IDNO))
- result = IDNO;
- SHFree(pszMessage);
- }
- else
- result = IDNO; //For connected elements, the default is "Don't rename";
- }
- else
- {
- if (DTNIsConnected(pcs->dth.pdtnCurrent))
- result = IDNO; //Connected elements, do not get renamed.
- }
- return result;
- }
- int AllConfirmations(COPY_STATE *pcs, WIN32_FIND_DATA *pfd, UINT oper, UINT wFunc,
- LPTSTR szSource, LPTSTR szDest,
- WIN32_FIND_DATA *pfdDest, LPINT lpret)
- {
- int result = IDYES;
- LPTSTR p;
- LPTSTR pszStatusDest = NULL;
- CONFIRM_FLAG fConfirm;
- WIN32_FIND_DATA *pfdUse1 = NULL;
- WIN32_FIND_DATA *pfdUse2;
- BOOL fSetProgress = FALSE;
- BOOL fShowConfirm = FALSE;
- switch (oper | wFunc)
- {
- case OPER_ENTERDIR | FO_MOVE:
- if (PathIsSameRoot(szSource, szDest))
- {
- fConfirm = CONFIRM_MOVE_FOLDER;
- pfdUse1 = pfd;
- pfdUse2 = pfdDest;
- fShowConfirm = TRUE;
- }
- break;
- case OPER_ENTERDIR | FO_DELETE:
- // Confirm removal of directory on this pass. The directories
- // are actually removed on the OPER_LEAVEDIR pass
- if (DTNIsRootNode(pcs->dth.pdtnCurrent))
- fSetProgress = TRUE;
- if ( !PathIsRoot(szSource) )
- {
- fShowConfirm = TRUE;
- pfdUse2 = pfd;
- fConfirm = CONFIRM_DELETE_FOLDER;
- szDest = NULL;
- }
- break;
- case OPER_DOFILE | FO_RENAME:
- // pszStatusDest = szDest;
- fSetProgress = TRUE;
- p = PathFindFileName(szSource);
- if (!IntlStrEqNI(szSource, szDest, (int)(p - szSource)))
- {
- result = DE_DIFFDIR;
- }
- else
- {
- if (pfd && (pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- fConfirm = CONFIRM_RENAME_FOLDER;
- else
- fConfirm = CONFIRM_RENAME_FILE;
- if (PathIsRoot(szSource) || (PathIsRoot(szDest)))
- {
- result = DE_ROOTDIR | ERRORONDEST;
- }
- else
- {
- // We need to bring up a special confirmation dialog if this file/folder being
- // renamed has a connected element (if "foo.htm" or "foo files" is renamed that
- // will break the links).
- result = ConfirmRenameOfConnectedItem(pcs, pfd, szSource);
- if (result != IDNO)
- {
- fShowConfirm = TRUE;
- pfdUse2 = pfdDest;
- pfdUse1 = pfd;
- }
- }
- }
- break;
- case OPER_DOFILE | FO_MOVE:
- fSetProgress = TRUE;
- pszStatusDest = szDest;
- if (PathIsRoot(szSource))
- {
- result = DE_ROOTDIR;
- }
- else if (PathIsRoot(szDest))
- {
- result = DE_ROOTDIR | ERRORONDEST;
- }
- else
- {
- fConfirm = CONFIRM_MOVE_FILE;
- fShowConfirm = TRUE;
- pfdUse2 = pfdDest;
- pfdUse1 = pfd;
- }
- break;
- case OPER_DOFILE | FO_DELETE:
- fSetProgress = TRUE;
- if (IsCompressedVolume(szSource, pfd->dwFileAttributes))
- {
- CopyError(pcs, szSource, szDest, DE_COMPRESSEDVOLUME, wFunc, oper);
- result = IDNO;
- }
- else if (IsWindowsFile(szSource))
- {
- CopyError(pcs, szSource, szDest, DE_WINDOWSFILE, wFunc, oper);
- result = IDNO;
- }
- else
- {
- fShowConfirm = TRUE;
- szDest = NULL;
- pfdUse2 = pfd;
- fConfirm = CONFIRM_DELETE_FILE;
- }
- break;
- }
- if (fShowConfirm)
- {
- result = CachedConfirmFileOp(pcs->hwndDlgParent, pcs, &pcs->cd, pcs->nSourceFiles, !DTNIsRootNode(pcs->dth.pdtnCurrent), fConfirm,
- szSource, pfdUse1, szDest, pfdUse2, NULL);
- }
- #ifdef WINNT
- if (oper == OPER_DOFILE || oper == OPER_ENTERDIR)
- {
- if ((wFunc == FO_MOVE) || (wFunc == FO_COPY))
- {
- if ((result != IDNO) && (result != IDCANCEL))
- {
- LPTSTR pszDataToBeLost;
- WCHAR wszDestDir[MAX_PATH];
- BOOL bNoStreamLossThisDir = FALSE;
- lstrcpy(wszDestDir, szDest);
- PathRemoveFileSpec(wszDestDir);
- // Files with multiple streams will suffer stream loss on a downlevel
- // copy, but CopyFile special-cases native structure storage.
- pszDataToBeLost = GetDownlevelCopyDataLossText(szSource, wszDestDir, (oper == OPER_ENTERDIR), &bNoStreamLossThisDir);
- if (pszDataToBeLost)
- {
- fConfirm = CONFIRM_STREAMLOSS;
- pfdUse2 = pfd;
- result = CachedConfirmFileOp(pcs->hwndDlgParent, pcs, &pcs->cd, pcs->nSourceFiles, !DTNIsRootNode(pcs->dth.pdtnCurrent), fConfirm,
- szSource, pfdUse1, szDest, pfdUse2, pszDataToBeLost);
- LocalFree(pszDataToBeLost);
- }
- else if (bNoStreamLossThisDir)
- {
- // pcs->bStreamLossPossible = FALSE;
- }
- }
- }
- }
- #endif
- // We only really care about OPER_ENTERDIR when deleting and
- // OPER_DOFILE when renaming, but I guess the hook will figure it out
- if ((result == IDYES) &&
- ISDIRFINDDATA(*pfd) &&
- (oper==OPER_ENTERDIR || oper==OPER_DOFILE))
- {
- result = CallFileCopyHooks(pcs->hwndDlgParent, wFunc, pcs->fFlags,
- szSource, pfd->dwFileAttributes,
- szDest, pfdDest->dwFileAttributes);
- }
- if ((result != IDCANCEL) && (result != IDNO) && fSetProgress)
- SetProgressText(pcs, szSource, pszStatusDest);
- return result;
- }
- // return TRUE if they're the same file
- // assumes that given two file specs, the short name will
- // be identical (except case)
- BOOL SameFile(LPTSTR pszSource, LPTSTR pszDest)
- {
- TCHAR szShortSrc[MAX_PATH];
- TCHAR szShortDest[MAX_PATH];
- GetShortPathName(pszSource, szShortSrc, ARRAYSIZE(szShortSrc));
- GetShortPathName(pszDest, szShortDest, ARRAYSIZE(szShortDest));
- return !lstrcmpi(szShortSrc, szShortDest);
- }
- // make sure we aren't operating on the current dir to avoid
- // ERROR_CURRENT_DIRECTORY kinda errors
- void AvoidCurrentDirectory(LPCTSTR p)
- {
- TCHAR szTemp[MAX_PATH];
- GetCurrentDirectory(ARRAYSIZE(szTemp), szTemp);
- if (lstrcmpi(szTemp, p) == 0)
- {
- DebugMsg(TF_DEBUGCOPY, TEXT("operating on current dir(%s), cd .."), p);
- PathRemoveFileSpec(szTemp);
- SetCurrentDirectory(szTemp);
- }
- }
- // this resolves short/long name collisions such as moving
- // "NewFolde" onto a dir with "New Folder" whose short name is "NEWFOLDE"
- //
- // we resolve this by renaming "New Folder" to a unique short name (like TMP1)
- //
- // making a temporary file of name "NEWFOLDE"
- //
- // renaming TMP1 back to "New Folder" (at which point it will have a new short
- // name like "NEWFOL~1"
- // BUGBUG, it'd be faster if we didn't make the temporary file, but that
- // would require that we rename the file back to the long name at the
- // end of the operation.. which would mean we'd need to queue them all up..
- // too much for right now.
- BOOL ResolveShortNameCollisions(LPCTSTR lpszDest, WIN32_FIND_DATA *pfd)
- {
- BOOL fRet = FALSE;
- // first verify that we're in the name collision.
- // we are if lpszDest is the same as the pfd's short name which is different
- // than it's long name.
- if (!lstrcmpi(PathFindFileName(lpszDest), pfd->cAlternateFileName) &&
- lstrcmpi(pfd->cAlternateFileName, pfd->cFileName))
- {
- // yes... do the renaming
- TCHAR szTemp[MAX_PATH];
- TCHAR szLongName[MAX_PATH];
- lstrcpy(szTemp, lpszDest);
- PathRemoveFileSpec(szTemp);
- // build the original long name
- lstrcpy(szLongName, szTemp);
- PathAppend(szLongName, pfd->cFileName);
- GetTempFileName(szTemp, c_szNULL, 1, szTemp);
- DebugMsg(TF_DEBUGCOPY, TEXT("Got %s as a temp file"), szTemp);
- // rename "New Folder" to "tmp1"
- if (Win32MoveFile(szLongName, szTemp, ISDIRFINDDATA(*pfd)))
- {
- // make a temporary "NewFolde"
- fRet = CreateWriteCloseFile(NULL, lpszDest, NULL, 0);
- ASSERT(fRet);
- // move it back...
- if (!Win32MoveFile(szTemp, szLongName, ISDIRFINDDATA(*pfd)))
- {
- //
- // Can't move it back, so delete the empty dir and then
- // move it back. Return FALSE to denote failure.
- //
- DeleteFile(lpszDest);
- Win32MoveFile(szTemp, szLongName, ISDIRFINDDATA(*pfd));
- fRet = FALSE;
- }
- else
- {
- // send this out because we could have confused views
- // with this swapping files around... by the time they get the first
- // move file notification, the temp file is likely gone
- // so they could blow that off.. which would screw up the rest of this.
- SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, szLongName, NULL);
- //
- // We've now created an empty dir entry of this name type.
- //
- Win32DeleteFile(lpszDest);
- }
- DebugMsg(TF_DEBUGCOPY, TEXT("ResolveShortNameCollision: %s = original, %s = destination,n %s = temp file, %d = return"), szLongName, lpszDest, szTemp, fRet);
- }
- }
- return fRet;
- }
- // return values.
- //
- // IDCANCEL = bail out of all operations
- // IDNO = skip this one
- // IDRETRY = try operation again
- // IDUNKNOWN = this (collision) is not the problem
- #define IDUNKNOWN IDOK
- int CheckForRenameCollision(COPY_STATE *pcs, UINT oper, LPTSTR pszSource, LPTSTR pszDest,
- WIN32_FIND_DATA *pfdDest, WIN32_FIND_DATA* pfd)
- {
- int iRet = IDUNKNOWN;
- ASSERT((pcs->lpfo->wFunc != FO_DELETE) && (oper != OPER_LEAVEDIR));
- /* Check to see if we are overwriting an existing file or
- directory. If so, better confirm */
- // we only have a potential for collision of we're at the top level or doing a merge
- if ((pcs->fMerge || DTNIsRootNode(pcs->dth.pdtnCurrent)) &&
- (oper == OPER_DOFILE) ||
- ((oper == OPER_ENTERDIR) && (pcs->fFlags & FOF_RENAMEONCOLLISION)))
- {
- HANDLE hfindT;
- // REVIEW this slows things down checking for the dest file
- if ((hfindT = FindFirstFile(pszDest, pfdDest)) != INVALID_HANDLE_VALUE)
- {
- FindClose(hfindT);
- iRet = IDCANCEL;
- if (pcs->lpfo->wFunc != FO_RENAME || !SameFile(pszSource, pszDest))
- {
- if (!ResolveShortNameCollisions(pszDest, pfdDest))
- {
- if (pcs->fFlags & FOF_RENAMEONCOLLISION)
- {
- // The client wants us to generate a new name for the
- // source file to avoid a collision at the destination
- // dir. Must also update the current queue and the
- // copy root.
- _HandleRename(pszSource, pszDest, pcs->fFlags, pcs);
- iRet = IDRETRY;
- }
- else
- {
- int result;
- if (pcs->lpfo->wFunc == FO_RENAME)
- {
- return ERROR_ALREADY_EXISTS;
- }
- if (IsWindowsFile(pszDest))
- {
- CopyError(pcs, pszSource, pszDest, DE_WINDOWSFILE | ERRORONDEST, pcs->lpfo->wFunc, oper);
- iRet = IDNO;
- }
- // REVIEW, if the destination file we are copying over
- // is actually a directory we are doomed. we can
- // try to remove the dir but that will fail if there
- // are files there. we probably need a special error message
- // for this case.
- result = CachedConfirmFileOp(pcs->hwndDlgParent, pcs, &pcs->cd, pcs->nSourceFiles, !DTNIsRootNode(pcs->dth.pdtnCurrent), CONFIRM_REPLACE_FILE, pszSource, pfd, pszDest, pfdDest, NULL);
- switch (result)
- {
- case IDYES:
- if ((pcs->lpfo->wFunc == FO_MOVE) && (PathIsSameRoot(pszSource, pszDest)))
- {
- int ret;
- // For FO_MOVE we need to delete the
- // destination first. Do that now.
- // bugbug, this replace options should be undable
- ret = Win32DeleteFile(pszDest) ? 0 : GetLastError();
- if (ret)
- {
- ret |= ERRORONDEST;
- result = ret;
- }
- }
- if (pcs->lpua)
- FOUndo_Release(pcs->lpua);
- iRet = IDRETRY;
- break;
- case IDNO:
- case IDCANCEL:
- pcs->lpfo->fAnyOperationsAborted = TRUE;
- iRet = result;
- break;
- default:
- iRet = result;
- break;
- }
- }
- }
- else
- {
- iRet = IDRETRY;
- }
- }
- }
- }
- return iRet;
- }
- int LeaveDir_Delete(COPY_STATE *pcs, LPTSTR pszSource)
- {
- int ret;
- if (PathIsRoot(pszSource))
- return 0;
- AvoidCurrentDirectory(pszSource);
- // We already confirmed the delete at MKDIR time, so attempt
- // to delete the directory
- ret = Win32RemoveDirectory(pszSource) ? 0 : GetLastError();
- if (!ret)
- {
- FOUndo_FileReallyDeleted(pszSource);
- }
- return ret;
- }
- int EnterDir_Copy(COPY_STATE* pcs, LPTSTR pszSource, LPTSTR pszDest,
- WIN32_FIND_DATA *pfd, WIN32_FIND_DATA * pfdDest, BOOL fRenameTried)
- {
- int ret;
- int result;
- DWORD dwSourceAttrib = pfd->dwFileAttributes;
- // Whenever we enter a directory, we need to reset the bStreamLossPossible flag,
- // since we could have stepped out from an NTFS->NTFS to NTFS->FAT scenario via
- // a junction point
- pcs->bStreamLossPossible = TRUE;
- #ifdef WINNT
- // SHMoveFile restricts the based on path length. To be consistent, we make the same
- // restricton on Copy directory also.
- if(IsDirPathTooLongForCreateDir(pszDest))
- ret = ERROR_FILENAME_EXCED_RANGE;
- else
- {
- ret = CreateDirectoryEx(pszSource, pszDest, NULL);
- if (ret)
- {
- SHChangeNotify(SHCNE_MKDIR, SHCNF_PATH, pszDest, NULL);
- ret = 0;
- }
- else
- {
- // Save the LastError value
- int iRetTmp = GetLastError();
- BOOL fCD = FALSE;
- if (ERROR_ALREADY_EXISTS != iRetTmp)
- {
- if (DE_INVFUNCTION == iRetTmp)
- {
- //
- // We hit this path when copying an offline directory with
- // extended attributes (EAs). The CSC cache doesn't support
- // EAs so CreateDirectoryEx fails with GLE == DE_INVFUNCTION.
- //
- fCD = Win32CreateDirectory(pszDest, NULL);
- if (!fCD)
- {
- //
- // Win32CreateDirectory failed.
- // This error is what we want to report.
- //
- iRetTmp = ret = GetLastError();
- }
- }
- // Did we hit a junction point (reparse point)?
- else if ((0xFFFFFFFF != dwSourceAttrib) && (dwSourceAttrib & FILE_ATTRIBUTE_REPARSE_POINT))
- {
- // Yes, most probably we've copied something from NTFS 5 to FAT
- // or some other FS that does not support FILE_ATTRIBUTES_REPARSE_POINT
- int iGLE = 0;
- fCD = Win32CreateDirectory(pszDest, NULL);
- // Did we failed?
- if (!fCD)
- {
- // Yes
- iGLE = GetLastError();
- // For the FILE_ATTRIBUTE_REPARSE_POINT case, the fct fails but still the dir
- // is created, so we get ERROR_ALREADY_EXISTS.
- if (ERROR_ALREADY_EXISTS == iGLE)
- {
- // This is expected as mentionned above, set it to success
- ret = 0;
- dwSourceAttrib &= ~FILE_ATTRIBUTE_REPARSE_POINT;
- }
- }
- else
- {
- // No, remove this attribute, since it's not supported
- ret = 0;
- dwSourceAttrib &= ~FILE_ATTRIBUTE_REPARSE_POINT;
- }
- }
- else
- {
- ret = iRetTmp;
- }
- }
- else
- {
- ret = iRetTmp;
- }
- if (!fCD && (0 != ret))
- {
- // We failed with an error that we do not handle, set ret to the LastError of
- // the initial operation to mimic the behavior of before this change.
- ret = iRetTmp;
- }
- }
- }
- // Massage goofy NT error code in the source == dest case
- if (ret == ERROR_INVALID_NAME)
- ret = ERROR_ALREADY_EXISTS;
- #else
- ret = SHCreateDirectory(pcs->hwndDlgParent, pszDest);
- #endif
- switch (ret) {
- case 0: // successful folder creation (or it already exists)
- // propogate the attributes (if there are any)
- SetDirAttributes(pszDest, dwSourceAttrib);
- #ifdef WINNT
- //
- // we should set the security ACLs here on NT
- // we ignore any kind of failure though, is that OK?
- //
- CopyFileSecurity(pszSource, pszDest);
- #endif WINNT
- // add to the undo atom
- if (pcs->lpua)
- {
- if (DTNIsRootNode(pcs->dth.pdtnCurrent) && !DTNIsConnected(pcs->dth.pdtnCurrent))
- FOUndo_AddInfo(pcs->lpua, pszSource, pszDest, 0);
- }
- break;
- case ERROR_ALREADY_EXISTS:
- case ERROR_DISK_FULL:
- case ERROR_ACCESS_DENIED:
- {
- DWORD dwFileAttributes;
- if (!fRenameTried)
- {
- int result = CheckForRenameCollision(pcs, OPER_ENTERDIR, pszSource, pszDest, pfdDest, pfd);
- switch (result)
- {
- case IDUNKNOWN:
- break;
- case IDRETRY:
- return EnterDir_Copy(pcs, pszSource, pszDest, pfd, pfdDest, TRUE);
- case IDCANCEL:
- pcs->bAbort = TRUE;
- return result;
- case IDNO:
- return result;
- default:
- return result;
- }
- }
- dwFileAttributes = GetFileAttributes(pszDest);
- if (dwFileAttributes == (DWORD)-1)
- {
- // The dir does not exist, so it looks like a problem
- // with a read-only drive or disk full
- if (IsRemovableDrive(DRIVEID(pszDest)) && !PathIsSameRoot(pszDest, pszSource) &&
- (ret == ERROR_DISK_FULL))
- {
- ret = CopyMoveRetry(pcs, pszDest, DE_NODISKSPACE, 0);
- if (!ret)
- {
- return EnterDir_Copy(pcs, pszSource, pszDest, pfd, pfdDest, fRenameTried);
- }
- else
- {
- pcs->bAbort = TRUE;
- return ret;
- }
- }
- else
- {
- CopyError(pcs, pszSource, pszDest, ERROR_ACCESS_DENIED | ERRORONDEST, FO_COPY, OPER_DOFILE);
- pcs->bAbort = TRUE;
- return ret;
- }
- }
- else if (!(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
- {
- // A file with this name already exists
- CopyError(pcs, pszSource, pszDest, DE_FLDDESTISFILE | ERRORONDEST, FO_COPY, OPER_DOFILE);
- pcs->bAbort = TRUE;
- return ret;
- }
- result = CachedConfirmFileOp(pcs->hwndDlgParent, pcs, &pcs->cd, pcs->nSourceFiles, !DTNIsRootNode(pcs->dth.pdtnCurrent), CONFIRM_REPLACE_FOLDER, pszSource, pfd, pszDest, pfdDest, NULL);
- switch (result) {
- case IDYES:
- ret = 0; // convert to no error
- pcs->fMerge = TRUE;
- if (pcs->lpua)
- FOUndo_Release(pcs->lpua);
- break;
- case IDNO:
- DTAbortCurrentNode(&pcs->dth); // so we don't recurse down this folder
- pcs->lpfo->fAnyOperationsAborted = TRUE;
- ret = IDNO; // Don't put up error message on this one...
- // Since the end-user cancelled the copy operation on this folder, we can cancel the
- // copy operation on the corresponding connected file too!
- if (DTNIsConnectOrigin(pcs->dth.pdtnCurrent))
- pcs->dth.pdtnCurrent->pdtnConnected->fDummy = TRUE;
- break;
- case IDCANCEL:
- pcs->lpfo->fAnyOperationsAborted = TRUE;
- pcs->bAbort = TRUE;
- // Since the end-user cancelled the copy operation on this folder, we can cancel the
- // copy operation on the corresponding connected file too!
- if (DTNIsConnectOrigin(pcs->dth.pdtnCurrent))
- pcs->dth.pdtnCurrent->pdtnConnected->fDummy = TRUE;
- break;
- default:
- result = ret;
- break;
- }
- break;
- }
- case ERROR_CANCELLED:
- pcs->bAbort = TRUE;
- break;
- case ERROR_FILENAME_EXCED_RANGE:
- DTAbortCurrentNode(&pcs->dth); // so we don't recurse down this folder
- break;
- default: // ret != 0 (dos error code)
- ret |= ERRORONDEST;
- break;
- }
- return ret;
- }
- int EnterDir_Move(COPY_STATE* pcs, LPTSTR pszSource, LPTSTR pszDest,
- WIN32_FIND_DATA *pfd, WIN32_FIND_DATA * pfdDest, BOOL fRenameTried)
- {
- int ret;