copy.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 273k
Category:

Windows Kernel

Development Platform:

Visual C++

  1.         TCHAR  cTemp;
  2.         SHNAMEMAPPING FAR* prp = DSA_GetItemPtr(hdsaRenamePairs, i);
  3.         
  4.         //  I don't call StrCmpNI 'cause I already know cchOldPath, and
  5.         //  it has to do a couple of lstrlen()s to calculate it.
  6.         cTemp = pszTarget[prp->cchOldPath];
  7.         pszTarget[prp->cchOldPath] = 0;
  8.         
  9.         //  Does the target match this collision renaming entry?
  10.         // NOTE: We are trying to compare a path to a path.  prp->pszOldPath
  11.         // does not have a trailing "" character, so this isn't covered
  12.         // by the lstrcmpi below.  As such, cTemp had best be the path
  13.         // seperator character to ensure that the modified pszTarget is actually
  14.         // a path and not a filename or a longer path name that doesn't match
  15.         // but happens to start with the same characters as prp->pszOldPath.
  16.         if ((cTemp == TEXT('\')) && !lstrcmpi(pszTarget, prp->pszOldPath))
  17.         {
  18.             // Get subtree string of the target.
  19.             TCHAR *pszSubTree = &(pszTarget[prp->cchOldPath + 1]);
  20.             
  21.             // Generate the new target path.
  22.             PathCombine(pszTarget, prp->pszNewPath, pszSubTree);
  23.             
  24.             break;
  25.         }
  26.         else
  27.         {
  28.             // Restore the trounced character.
  29.             pszTarget[prp->cchOldPath] = cTemp;
  30.         }
  31.     }
  32. }
  33. /* Sets the status dialog item in the modeless status dialog box. */
  34. // used for both the drag drop status dialogs and the manual user
  35. // entry dialogs so be careful what you change
  36. void SetProgressText(COPY_STATE *pcs, LPCTSTR pszFrom, LPCTSTR pszTo)
  37. {
  38.     if (pcs->hwndProgress && !(pcs->fFlags & FOF_SIMPLEPROGRESS)) 
  39.     {
  40.         TCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
  41.         LPTSTR pszMsg = NULL;
  42.         SetDlgItemText(pcs->hwndProgress, IDD_NAME,
  43.             PathFindFileName((pcs->fFlags & FOF_MULTIDESTFILES) ? pszTo : pszFrom));
  44.         
  45.         lstrcpy(szFrom, pszFrom);
  46.         if (szFrom[0]) 
  47.         {
  48.             PathRemoveFileSpec(szFrom);
  49.             if (pszTo)
  50.             {
  51.                 lstrcpy(szTo, pszTo);
  52.                 PathRemoveFileSpec(szTo);
  53.             }
  54.             
  55.             pszMsg = ShellConstructMessageString(HINST_THISDLL,
  56.                 pszTo ? MAKEINTRESOURCE(IDS_FROMTO) : MAKEINTRESOURCE(IDS_FROM),
  57.                 PathFindFileName(szFrom),
  58.                 pszTo ? PathFindFileName(szTo) : NULL);
  59.         } 
  60.         else if (!pcs->fDTBuilt) 
  61.         {
  62.             TCHAR szFunc[80];
  63.             if (LoadString(HINST_THISDLL, FOFuncToStringID(pcs->lpfo->wFunc),
  64.                 szFunc, ARRAYSIZE(szFunc))) 
  65.             {
  66.                 pszMsg = ShellConstructMessageString(HINST_THISDLL,
  67.                     MAKEINTRESOURCE(IDS_PREPARINGTO), szFunc);
  68.             }
  69.         }
  70.         
  71.         if (pszMsg)
  72.         {
  73.             SetDlgItemText(pcs->hwndProgress, IDD_TONAME, pszMsg);
  74.             LocalFree(pszMsg);
  75.         }
  76.     }
  77. }
  78. void SetProgressTimeEst(COPY_STATE *pcs, DWORD dwTimeLeft)
  79. {
  80.     TCHAR szFmt[60];
  81.     TCHAR szOut[70];
  82.     DWORD dwTime;
  83.     
  84.     if (pcs->hwndProgress) 
  85.     {
  86.         // BUGBUG: how well does this localize?
  87.         if (dwTimeLeft > 60)
  88.         {
  89.             // Note that dwTime is at least 2, so we only need a plural form
  90.             LoadString(HINST_THISDLL, IDS_TIMEEST_MINUTES, szFmt, ARRAYSIZE(szFmt));
  91.             dwTime = (dwTimeLeft / 60) + 1;
  92.         }
  93.         else
  94.         {
  95.             LoadString(HINST_THISDLL, IDS_TIMEEST_SECONDS, szFmt, ARRAYSIZE(szFmt));
  96.             // Round up to 5 seconds so it doesn't look so random
  97.             dwTime = ((dwTimeLeft+4) / 5) * 5;
  98.         }
  99.         
  100.         wsprintf(szOut, szFmt, dwTime);
  101.         
  102.         SetDlgItemText(pcs->hwndProgress, IDD_TIMEEST, szOut);
  103.     }
  104. }
  105. // this updates the animation, which could change because we could switch between 
  106. // doing a move to recycle bin and really nuke if the file/folder was bigger that
  107. // the allowable size of the recycle bin.
  108. void UpdateProgressAnimation(COPY_STATE *pcs)
  109. {
  110.     if (pcs->hwndProgress && pcs->lpfo)
  111.     {
  112.         INT_PTR idAni, idAniCurrent;
  113.         HWND hwndAnimation;
  114.         switch (pcs->lpfo->wFunc) 
  115.         {
  116.         case FO_DELETE:
  117.             if ((pcs->lpfo->lpszProgressTitle == MAKEINTRESOURCE(IDS_BB_EMPTYINGWASTEBASKET)) ||
  118.                 (pcs->lpfo->lpszProgressTitle == MAKEINTRESOURCE(IDS_BB_DELETINGWASTEBASKETFILES))) 
  119.             {
  120.                 idAni = IDA_FILENUKE;
  121.                 break;
  122.             } 
  123.             else if (!(pcs->fFlags & FOF_ALLOWUNDO)) 
  124.             {
  125.                 idAni = IDA_FILEDELREAL;
  126.                 break;
  127.             } // else fall through to default
  128.             
  129.         default:
  130.             idAni = (IDA_FILEMOVE + (int)pcs->lpfo->wFunc - FO_MOVE);
  131.         }
  132.         
  133.         hwndAnimation = GetDlgItem(pcs->hwndProgress,IDD_ANIMATE);
  134.         
  135.         idAniCurrent = (INT_PTR) GetProp(hwndAnimation, TEXT("AnimationID"));
  136.         
  137.         if (idAni != idAniCurrent)
  138.         {
  139.             // the one we should be using is different from the one we have, 
  140.             // so update it
  141.             
  142.             // close the old clip
  143.             Animate_Close(hwndAnimation);
  144.             
  145.             // open the new one
  146.             Animate_Open(hwndAnimation, idAni);
  147.             
  148.             // if the window is enabled, start the new animation playing
  149.             if (IsWindowEnabled(pcs->hwndProgress))
  150.                 Animate_Play(hwndAnimation, -1, -1, -1);
  151.             
  152.             // set the current idAni
  153.             SetProp(hwndAnimation, TEXT("AnimationID"), (HANDLE)idAni);
  154.             
  155.             // at the same time we update the animation, we also update the text,
  156.             // so that the two will always be in sync
  157.             SetProgressText(pcs, pcs->dth.szSrcPath, pcs->lpfo->wFunc == FO_DELETE ? NULL : pcs->dth.szDestPath);
  158.         }
  159.     }
  160. }
  161. void SendProgressMessage(COPY_STATE *pcs, UINT uMsg, WPARAM wParam, LPARAM lParam)
  162. {
  163.     if (pcs->hwndProgress)
  164.         SendDlgItemMessage(pcs->hwndProgress, IDD_PROBAR, uMsg, wParam, lParam);
  165. }
  166. // see if this file is loaded by kernel, thus something we don't
  167. // want to fuck with.
  168. //
  169. // pszPath      fully qualified path name
  170. //
  171. BOOL IsWindowsFileEx(LPCTSTR pszFile, BOOL bWin32)
  172. {
  173.     LPCTSTR pszSpec = PathFindFileName(pszFile);
  174.     if (pszSpec)
  175.     {
  176.         HMODULE hMod = bWin32 ? GetModuleHandle(pszSpec)
  177.             : GetModuleHandle16(pszSpec);
  178.         if (hMod)
  179.         {
  180.             TCHAR szModule[MAX_PATH];
  181.             
  182.             bWin32 ? GetModuleFileName(hMod, szModule, ARRAYSIZE(szModule))
  183.                 : GetModuleFileName16(hMod, szModule, ARRAYSIZE(szModule));
  184.             
  185.             return !lstrcmpi(pszFile, szModule);
  186.         }
  187.     }
  188.     return FALSE;
  189. }
  190. BOOL IsWindowsFile(LPCTSTR pszFile)
  191. {
  192.     return IsWindowsFileEx(pszFile, TRUE) || IsWindowsFileEx(pszFile, FALSE);
  193. }
  194. // verify that we can see the contents of a newly created folder
  195. // this is to deal with the case where net drives have an unknown path
  196. // limit.
  197. //
  198. // assumes:
  199. //      folder exists and is empty (newly created)
  200. //
  201. // returns:
  202. //      ERROR_SUCCESS       everything is fine
  203. //      ERROR_*             failure
  204. int VerifyFolderVisible(HWND hwnd, LPCTSTR pszPath)
  205. {
  206.     int res = ERROR_SUCCESS;
  207.     
  208.     ASSERT(PathIsDirectory(pszPath));   // must exist and be a folder
  209.     
  210.     if (PathIsUNC(pszPath) || IsRemoteDrive(DRIVEID(pszPath))) 
  211.     {
  212.         TCHAR szTest[MAX_PATH];
  213.         HANDLE hfile;
  214.         BOOL bFoundFile = FALSE;
  215.         
  216.         PathCombine(szTest, pszPath, TEXT("TESTDIR.TMP"));
  217.         
  218.         hfile = CreateFile(szTest, GENERIC_READ | GENERIC_WRITE,
  219.                 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 
  220.                 FILE_ATTRIBUTE_TEMPORARY | 
  221.                 FILE_ATTRIBUTE_HIDDEN | 
  222.                 FILE_ATTRIBUTE_SYSTEM | 
  223.                 FILE_FLAG_DELETE_ON_CLOSE, NULL);
  224.         if (hfile != INVALID_HANDLE_VALUE) 
  225.         {
  226.             WIN32_FIND_DATA fd;
  227.             HANDLE hfind;
  228.             
  229.             PathRemoveFileSpec(szTest);         // replace file with "*"
  230.             PathAppend(szTest, c_szStar);
  231.             
  232.             hfind = FindFirstFile(szTest, &fd);
  233.             if (hfind != INVALID_HANDLE_VALUE) 
  234.             {
  235.                 do {
  236.                     if (!lstrcmpi(fd.cFileName, TEXT("TESTDIR.TMP"))) 
  237.                     {
  238.                         bFoundFile = TRUE;
  239.                         break;
  240.                     }
  241.                 } while (FindNextFile(hfind, &fd));
  242.                 FindClose(hfind);
  243.             }
  244.             CloseHandle(hfile); // FILE_FLAG_DELETE_ON_CLOSE does the DeleteFile()
  245.         }
  246.         
  247.         if (!bFoundFile) 
  248.         {
  249.             RECT rcMonitor;
  250.             GetMonitorRect(MonitorFromWindow(hwnd, TRUE), &rcMonitor);
  251.             
  252.             PathRemoveFileSpec(szTest); // remove "*"
  253.             PathCompactPath(NULL, szTest, (rcMonitor.right - rcMonitor.left) / 3);
  254.             
  255.             if (!hwnd ||
  256.                 ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_CREATELONGDIR),
  257.                 MAKEINTRESOURCE(IDS_CREATELONGDIRTITLE),
  258.                 MB_SETFOREGROUND | MB_ICONHAND | MB_YESNO, (LPTSTR)szTest) != IDYES)
  259.             {
  260.                 Win32RemoveDirectory(pszPath);
  261.                 res = ERROR_CANCELLED;
  262.             }
  263.         }
  264.     }
  265.     return res;
  266. }
  267. //
  268. // creates folder and all parts of the path if necessary (parent does not need
  269. // to exists) and verifies that the contents of the folder will be visibile.
  270. //
  271. // in:
  272. //    hwnd      hwnd to post UI on
  273. //    pszPath   full path to create
  274. //    psa       security attributes
  275. //
  276. // returns:
  277. //      ERROR_SUCCESS (0)   success
  278. //      ERROR_              failure
  279. //
  280. STDAPI_(int) SHCreateDirectoryEx(HWND hwnd, LPCTSTR pszPath, SECURITY_ATTRIBUTES *psa)
  281. {
  282.     int ret = ERROR_SUCCESS;
  283.     
  284.     if (PathIsRelative(pszPath))
  285.     {
  286.         // if not a "full" path bail
  287.         // to ensure that we dont create a dir in the current working directory
  288.         SetLastError(ERROR_BAD_PATHNAME);
  289.         return ERROR_BAD_PATHNAME;
  290.     }
  291.     
  292.     if (!Win32CreateDirectory(pszPath, psa)) 
  293.     {
  294.         TCHAR *pEnd, *pSlash, szTemp[MAX_PATH + 1];  // +1 for PathAddBackslash()
  295.         
  296.         ret = GetLastError();
  297.         
  298.         // There are certain error codes that we should bail out here
  299.         // before going through and walking up the tree...
  300.         switch (ret)
  301.         {
  302.         case ERROR_FILENAME_EXCED_RANGE:
  303.         case ERROR_FILE_EXISTS:
  304.         case ERROR_ALREADY_EXISTS:
  305.             return ret;
  306.         }
  307.         
  308.         lstrcpyn(szTemp, pszPath, ARRAYSIZE(szTemp) - 1);
  309.         pEnd = PathAddBackslash(szTemp); // for the loop below
  310.         
  311.         // assume we have 'X:' to start this should even work
  312.         // on UNC names because will will ignore the first error
  313.         
  314.         pSlash = szTemp + 3;
  315.         
  316.         // create each part of the dir in order
  317.         
  318.         while (*pSlash) 
  319.         {
  320.             while (*pSlash && *pSlash != TEXT('\'))
  321.                 pSlash = CharNext(pSlash);
  322.             
  323.             if (*pSlash) 
  324.             {
  325.                 ASSERT(*pSlash == TEXT('\'));
  326.                 
  327.                 *pSlash = 0;    // terminate path at seperator
  328.                 
  329.                 ret = Win32CreateDirectory(szTemp, pSlash + 1 == pEnd ? psa : NULL) ? ERROR_SUCCESS : GetLastError();
  330.                 
  331.             }
  332.             *pSlash++ = TEXT('\');     // put the seperator back
  333.         }
  334.     }
  335.     if (ERROR_SUCCESS == ret)
  336.     {
  337.         // we succeeded in creating the folder, lets see if its visible
  338.         if (PathIsUNC(pszPath) || IsRemoteDrive(DRIVEID(pszPath)))
  339.             ret = VerifyFolderVisible(hwnd, pszPath);
  340.     }
  341.     else    
  342.     {
  343.         // We failed, so let's try to display error UI.
  344.         if ( hwnd && ERROR_CANCELLED != ret )
  345.         {               
  346.             SHSysErrorMessageBox(hwnd, NULL, IDS_CANNOTCREATEFOLDER, ret,
  347.                                  pszPath ? PathFindFileName(pszPath) : NULL, 
  348.                                  MB_OK | MB_ICONEXCLAMATION);
  349.                                  
  350.             ret = ERROR_CANCELLED; // Indicate we already displayed Error UI.
  351.         }
  352.     }   
  353.     return ret;
  354. }
  355. STDAPI_(int) SHCreateDirectory(HWND hwnd, LPCTSTR pszPath)
  356. {
  357.     return SHCreateDirectoryEx(hwnd, pszPath, NULL);
  358. }
  359. #ifdef UNICODE
  360. STDAPI_(int) SHCreateDirectoryExA(HWND hwnd, LPCSTR pszPath, SECURITY_ATTRIBUTES *psa)
  361. {
  362.     WCHAR wsz[MAX_PATH];
  363.     SHAnsiToUnicode(pszPath, wsz, SIZECHARS(wsz));
  364.     return SHCreateDirectoryEx(hwnd, wsz, psa);
  365. }
  366. #else
  367. STDAPI_(int) SHCreateDirectoryExW(HWND hwnd, LPCWSTR pszPath, SECURITY_ATTRIBUTES *psa)
  368. {
  369.     char sz[MAX_PATH];
  370.     SHUnicodeToAnsi(pszPath, sz, SIZECHARS(sz));
  371.     return SHCreateDirectoryEx(hwnd, sz, psa);
  372. }
  373. #endif
  374. #ifndef COPY_USE_COPYFILEEX
  375. // in:
  376. //
  377. // returns:
  378. #define REGPATH_RSN TEXT("Software\Microsoft\Windows\CurrentVersion\FileCopy")
  379. #define REGVAL_RSN  TEXT("ReadShareNetware")
  380. BOOL OpenDestFile(LPCTSTR pszDest, HFILE *phf, DWORD dwAttribs, BOOL fCreateAlways)
  381. {
  382.     HFILE fh;
  383.     
  384.     DWORD dwFlag = 0;
  385.     DWORD dwSize = sizeof( DWORD );
  386.     DWORD dwType = REG_BINARY;
  387.     
  388.     // NB Some networks will fail writes if you open the file readonly.
  389.     dwAttribs &= ~FILE_ATTRIBUTE_READONLY;
  390.     
  391.     // NB Open the dest file without read sharing. That way, if the source and the
  392.     // dest are the same file and we didn't detect it (because of UNC weirdness) we'll
  393.     // get a sharing violation instead of trashing the file.
  394.     // Warning: FILE_SHARE_READ needed without this we can have data loss if running on netware client
  395.     // and doing move where the target runs out of disk space
  396.     
  397.     //
  398.     // IEQFE #665:  We're damned either way with FILE_SHARE_READ, so use it or not based on the
  399.     // value of HKLMSoftwareMicrosoftWindowsCurrentVersionFileCopyReadShareNetware.
  400.     //
  401.     
  402.     if ( ERROR_SUCCESS != SHGetValue( HKEY_LOCAL_MACHINE, REGPATH_RSN, REGVAL_RSN, &dwType, &dwFlag, &dwSize ))
  403.         dwFlag = 0;  // make sure
  404.     
  405.     fh = (HFILE)CreateFile(pszDest, GENERIC_WRITE, dwFlag ? FILE_SHARE_READ : 0, 0L, fCreateAlways ? CREATE_ALWAYS : CREATE_NEW, dwAttribs, NULL);
  406.     
  407.     if (GetLastError() == ERROR_ACCESS_DENIED)
  408.     {
  409.         // If the file is readonly, reset the readonly attribute
  410.         // and have another go at it
  411.         
  412.         DWORD dwAttributes = GetFileAttributes(pszDest);
  413.         if (0xFFFFFFFF != dwAttributes)
  414.         {
  415.             dwAttributes &= ~FILE_ATTRIBUTE_READONLY;
  416.             if (SetFileAttributes(pszDest, dwAttributes))
  417.             {
  418.                 fh = (HFILE)CreateFile(pszDest, GENERIC_WRITE, dwFlag ? FILE_SHARE_READ : 0, 0L, fCreateAlways ? CREATE_ALWAYS : CREATE_NEW, dwAttribs, NULL);
  419.             }
  420.         }
  421.         else
  422.         {
  423.             // The last error obtained from trying to create the
  424.             // destination file needs to be preserved.
  425.             *phf = (HFILE) ERROR_ACCESS_DENIED;
  426.             return FALSE;
  427.         }
  428.     }
  429.     if (fh == HFILE_ERROR) 
  430.     {
  431.         *phf = (HFILE)GetLastError();
  432.         return FALSE;
  433.     }
  434.     *phf = fh;
  435.     return TRUE;
  436. }
  437. #endif      // COPY_USE_COPYFILEEX
  438. // call MPR to find out the speed of a given path
  439. //
  440. // returns
  441. //        0 for unknown
  442. //      144 for 14.4 modems
  443. //       96 for 9600
  444. //       24 for 2400
  445. //
  446. // if the device does not return a speed we return 0
  447. //
  448. DWORD GetPathSpeed(LPCTSTR pszPath)
  449. {
  450.     NETCONNECTINFOSTRUCT nci;
  451.     NETRESOURCE nr;
  452.     TCHAR szPath[MAX_PATH];
  453.     
  454.     lstrcpyn(szPath, pszPath, ARRAYSIZE(szPath));
  455.     PathStripToRoot(szPath);    // get a root to this path
  456.     
  457.     memset(&nci, 0, SIZEOF(nci));
  458.     nci.cbStructure = SIZEOF(nci);
  459.     
  460.     memset(&nr, 0, SIZEOF(nr));
  461.     if (PathIsUNC(szPath))
  462.         nr.lpRemoteName = szPath;
  463.     else
  464.     {
  465.         // Don't bother for local drives
  466.         if (!IsRemoteDrive(DRIVEID(szPath)))
  467.             return 0;
  468.         
  469.         // we are passing in a local drive and MPR does not like us to pass a
  470.         // local name as Z: but only wants Z:
  471.         szPath[2] = TEXT('');   // Strip off after character and :
  472.         nr.lpLocalName = szPath;
  473.     }
  474.     
  475.     // dwSpeed is returned by MultinetGetConnectionPerformance
  476.     MultinetGetConnectionPerformance(&nr, &nci);
  477.     
  478.     return nci.dwSpeed;
  479. }
  480. #ifndef COPY_USE_COPYFILEEX
  481. // This function determines the size of the copy buffer, depending
  482. // on the speed of the connection.  (for slow connections)
  483. //
  484. // in:
  485. //      pszSource       fully qualified source path (ANSI)
  486. //      pszDest         fully qualified destination path (ANSI)
  487. //
  488. // returns:
  489. //      optimal buffer size (optimized for approximately 1 sec bursts)
  490. //      with a maximum size of COPYMAXBUFFERSIZE
  491. UINT SizeFromLinkSpeed(LPCTSTR pszSource, LPCTSTR pszDest, BOOL *pbFlushWrites)
  492. {
  493.     DWORD dwSize, dwSpeed, dwSrc, dwDst;
  494.     
  495.     dwSrc = GetPathSpeed(pszSource);
  496.     dwDst = GetPathSpeed(pszDest);
  497.     
  498.     if ((dwSrc == 0) || (dwDst == 0))
  499.     {
  500.         dwSpeed = dwSrc == 0 ? dwDst : dwSrc;
  501.     }
  502.     else
  503.     {
  504.         dwSpeed = min(dwSrc, dwDst);
  505.     }
  506.     
  507.     dwSize = (dwSpeed * 100 / 8);    // convert 100 bps to bytes for 1 second
  508.     
  509.     // round up to a sector size (512 == 0x200)
  510.     dwSize = (dwSize + 511) & ~511;
  511.     
  512.     if (dwSize == 0 || dwSize > COPYMAXBUFFERSIZE)
  513.         dwSize = COPYMAXBUFFERSIZE;
  514.     
  515.     // If the destination is on some type of slow link, we should flush
  516.     // per write as to make it such that the user can cancel out of the operation
  517.     // This is a guess for what size should be the threshold...
  518.     //
  519.     *pbFlushWrites =  (dwDst > 0) && (dwDst < 0x500);
  520.     
  521.     DebugMsg(TF_DEBUGCOPY, TEXT("Copy Size = %d, Copy Speed = %d, Flush = %x"), dwSize, dwSpeed, *pbFlushWrites);
  522.     return dwSize;
  523. }
  524. #endif  !COPY_USE_COPYFILEEX
  525. #ifdef COPY_USE_COPYFILEEX
  526. DWORD CopyCallbackProc(LARGE_INTEGER liTotSize, LARGE_INTEGER liBytes,
  527.                        LARGE_INTEGER liStreamSize, LARGE_INTEGER liStreamBytes,
  528.                        DWORD dwStream, DWORD dwCallback,
  529.                        HANDLE hSource, HANDLE hDest, void *pv)
  530. {
  531.     COPY_STATE *pcs = (COPY_STATE *)pv;
  532.     DWORD dwBytesRead = (DWORD)liBytes.QuadPart;
  533.     
  534.     DebugMsg(DM_TRACE, TEXT("CopyCallbackProc[%08lX], totsize=%08lX, bytes=%08lX"),
  535.         dwCallback,  liTotSize.LowPart, liBytes.LowPart);
  536.     
  537.     if (FOQueryAbort(pcs))
  538.         return PROGRESS_CANCEL;
  539.     
  540.     DTSetFileCopyProgress(&pcs->dth, dwBytesRead);
  541.     
  542.     if (pcs->fInitialize)
  543.     {
  544.         // preserve the create date when moving across volumes, otherwise use the
  545.         // create date the file system picked when we did the CreateFile()
  546.         // always preserve modified date (ftLastWriteTime)
  547.         // bummer is we loose accuracy when going to VFAT compared to NT servers
  548.         
  549.         SetFileTime((HANDLE)hDest, (pcs->lpfo->wFunc == FO_MOVE) ? &pcs->pfd->ftCreationTime : NULL,
  550.             NULL, &pcs->pfd->ftLastWriteTime);
  551.         
  552.         pcs->fInitialize = FALSE;
  553.     }
  554.     
  555.     switch(dwCallback)
  556.     {
  557.     case CALLBACK_STREAM_SWITCH:
  558.         break;
  559.     case CALLBACK_CHUNK_FINISHED:
  560.         break;
  561.     default:
  562.         break;
  563.     }
  564.     return PROGRESS_CONTINUE;
  565. }
  566. #endif
  567. #ifdef WINNT
  568. // copy the SECURITY_DESCRIPTOR for two files
  569. //
  570. // in:
  571. //      pszSource       fully qualified source path
  572. //      pszDest         fully qualified destination path
  573. //
  574. // returns:
  575. //      0       ERROR_SUCCESS
  576. //      WIN32 error codes
  577. //
  578. DWORD 
  579. CopyFileSecurity(LPCTSTR pszSource, LPCTSTR pszDest)
  580. {
  581.     DWORD err = ERROR_SUCCESS;
  582.     BOOL fRet = TRUE;
  583.     BYTE buf[512];  //  BUGBUG arbitrary default size here...
  584.     
  585.     //  BUGBUG arbitrarily saying do everything we can
  586.     //    except SACL_SECURITY_INFORMATION because
  587.     SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
  588.     PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) buf;
  589.     DWORD cbPsd = SIZEOF(buf);
  590.     
  591.     if (!SHRestricted(REST_FORCECOPYACLWITHFILE))
  592.     {
  593.         // shell restriction so return access denied?
  594.         return ERROR_ACCESS_DENIED;
  595.     }    
  596.     
  597.     fRet = GetFileSecurity(pszSource, si, psd, cbPsd, &cbPsd);
  598.     if (!fRet)
  599.     {
  600.         err = GetLastError();
  601.         if (ERROR_INSUFFICIENT_BUFFER == err)
  602.         {
  603.             // just need to resize the buffer and try again
  604.             // ASSERT(FALSE);  // BUGBUGREMOVE
  605.             psd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, cbPsd);
  606.             if (psd)
  607.                 fRet = GetFileSecurity(pszSource, si, psd, cbPsd, &cbPsd);
  608.             else
  609.                 err = ERROR_NOT_ENOUGH_MEMORY;
  610.         }
  611.     }
  612.     
  613.     if (fRet)
  614.     {
  615.         fRet = SetFileSecurity(pszDest, si, psd);
  616.         if (!fRet)
  617.             err = GetLastError();
  618.     }
  619.     
  620.     if (psd && psd != buf)
  621.         LocalFree(psd);
  622.     
  623.     if (fRet)
  624.         return ERROR_SUCCESS;
  625.     
  626.     return err;
  627. }
  628. #endif // WINNT
  629. // This function queues copies. If the queue is full the queue is purged.
  630. //
  631. // in:
  632. //      hwnd            Window to report things to.
  633. //      pszSource       fully qualified source path (ANSI)
  634. //      pszDest         fully qualified destination path (ANSI)
  635. //      pfd             source file find data (size/date/time/attribs)
  636. //
  637. // returns:
  638. //      0       success
  639. //      dos error code for failure
  640. //
  641. #ifdef WINNT
  642. // We'll GetProcAddress the MoveFileWithProgress API on NT5+ only
  643. typedef BOOL (WINAPI *PFNMOVEFILEWITHPROGRESS)(LPCWSTR lpExistingFileName,
  644.                                                LPCWSTR lpNewFileName,
  645.                                                LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
  646.                                                void *lpData OPTIONAL,
  647.                                                DWORD dwFlags);
  648. #endif
  649. UINT FileCopy(COPY_STATE *pcs, LPCTSTR pszSource, LPCTSTR pszDest, const WIN32_FIND_DATA *pfd, BOOL fCreateAlways)
  650. {
  651.     UINT iRet = ERROR_CANCELLED;
  652.     HFILE hSource = HFILE_ERROR;
  653.     HFILE hDest   = HFILE_ERROR;
  654.     int iLastError;
  655. #ifdef COPY_USE_COPYFILEEX
  656.     BOOL fRetryPath = FALSE;
  657.     BOOL fRetryAttr = FALSE;
  658.     BOOL fCopyOrMoveSucceeded = FALSE;
  659. #else
  660.     HFILE fh;
  661.     DWORD dwRead, dwWrite;
  662.     DWORD dwBytesLeft;
  663.     DWORD dwBytesRead;
  664. #endif
  665.     
  666. #ifdef WINNT
  667.     
  668.     BOOL fSecurityObtained = FALSE;
  669.     
  670.     // Buffers for security info
  671.     
  672.     BYTE rgbSecurityDescriptor[512];
  673.     SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
  674.     PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) rgbSecurityDescriptor;
  675.     DWORD cbPsd = SIZEOF(rgbSecurityDescriptor);
  676. #endif
  677.     
  678. #ifdef COPY_USE_COPYFILEEX
  679.     
  680.     // The MoveFileWithProgressW function pointer
  681.     
  682.     BOOL fUseMoveFileWithProgress = FALSE;
  683.     HMODULE hinstKernel32 = NULL;
  684.     static PFNMOVEFILEWITHPROGRESS pfnMoveFileWithProgress = NULL;
  685. #endif
  686.     
  687.     // Make sure we can start
  688.     if (FOQueryAbort(pcs))
  689.         return ERROR_CANCELLED;
  690.     
  691. #ifdef COPY_USE_COPYFILEEX // {
  692.     
  693.     //
  694.     // Now do the file copy/move
  695.     //
  696.     
  697. #ifdef WINNT // {
  698.     
  699.     // Get the security info from the source file.  If there is a problem
  700.     // (e.g. the file is on FAT) we ignore it and proceed with the copy/move.
  701.     
  702.     if (!(pcs->fFlags & FOF_NOCOPYSECURITYATTRIBS))
  703.     {
  704.         if (SHRestricted(REST_FORCECOPYACLWITHFILE))
  705.         {
  706.             if ( GetFileSecurity(pszSource, si, psd, cbPsd, &cbPsd ))
  707.             {
  708.                 fSecurityObtained = TRUE;
  709.             }
  710.             else
  711.             {
  712.                 if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
  713.                 {
  714.                     psd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, cbPsd);
  715.                     if ( psd && !GetFileSecurity(pszSource, si, psd, cbPsd, &cbPsd) )
  716.                         fSecurityObtained = TRUE;
  717.                 }
  718.             }
  719.         }
  720.     }
  721.     
  722.     // Attempt to get the NT5 MoveFileWithProgress API
  723.     
  724.     if ( g_bRunOnNT5 && FO_MOVE == pcs->lpfo->wFunc )
  725.     {
  726.         if ( NULL != pfnMoveFileWithProgress )
  727.         {
  728.             fUseMoveFileWithProgress = TRUE;
  729.         }
  730.         else
  731.         {
  732.             hinstKernel32 = GetModuleHandle( TEXT("kernel32.dll") );
  733.             if ( NULL == hinstKernel32 )
  734.             {
  735.                 DebugMsg( DM_ERROR, TEXT("FileCopy couldn't get kernel32.dll (%lu)"), GetLastError() );
  736.             }
  737.             else
  738.             {
  739.                 pfnMoveFileWithProgress = (PFNMOVEFILEWITHPROGRESS)
  740.                     GetProcAddress( hinstKernel32, "MoveFileWithProgressW" );
  741.                 if ( NULL == pfnMoveFileWithProgress )
  742.                     DebugMsg( DM_ERROR, TEXT("FileCopy couldn't get MoveFileWithProgressW"), GetLastError() );
  743.                 else
  744.                     fUseMoveFileWithProgress = TRUE;
  745.             }
  746.         }
  747.     }   // if ( g_fNewTrack )
  748.     
  749. #endif  // } #ifdef WINNT
  750.     
  751. TryCopyAgain:
  752.     pcs->fInitialize = TRUE;
  753.     pcs->pfd = pfd;
  754.     SetProgressText(pcs, pszSource, pszDest);
  755.     fCopyOrMoveSucceeded = fUseMoveFileWithProgress
  756.         ? pfnMoveFileWithProgress( pszSource, pszDest, CopyCallbackProc, pcs, MOVEFILE_COPY_ALLOWED | (fCreateAlways ? MOVEFILE_REPLACE_EXISTING : 0) )
  757.         : CopyFileEx(pszSource, pszDest, CopyCallbackProc, pcs, &pcs->bAbort, fCreateAlways? 0 : COPY_FILE_FAIL_IF_EXISTS );
  758.     
  759.     if (!fCopyOrMoveSucceeded)  
  760.     {
  761.         iLastError = (int)GetLastError();
  762.         DebugMsg( TF_DEBUGCOPY, TEXT("FileCopy() failed, get last error returned 0x%08x"), iLastError );
  763. #ifdef WINNT
  764.         // HACKHACK: workaround for bug in NT4 CopyFileEx (see IE4#51085)
  765.         if ((iLastError == ERROR_SHARING_VIOLATION) && !StrCmpC(pszSource, pszDest))
  766.         {
  767.             iLastError = ERROR_FILE_EXISTS;        
  768.             DebugMsg( TF_DEBUGCOPY, TEXT("Error code converted to 0x%08x (ERROR_FILE_EXISTS)"), iLastError );
  769.         }
  770. #endif // WINNT
  771.         
  772.         switch(iLastError)
  773.         {
  774.             // Let the caller handle this one
  775.         case ERROR_FILE_EXISTS:
  776.         case ERROR_ALREADY_EXISTS: // nt5 221893 CopyFileEx now returns this for some reason...
  777.             iRet = ERROR_FILE_EXISTS;
  778.             goto Exit;
  779.             
  780.         case ERROR_DISK_FULL:
  781.             if (!IsRemovableDrive(DRIVEID(pszDest))
  782.                 || PathIsSameRoot(pszDest,pszSource))
  783.             {
  784.                 break;
  785.             }
  786.             
  787.             iLastError = DE_NODISKSPACE;
  788.             // Fall through
  789.             
  790.         case ERROR_PATH_NOT_FOUND:
  791.             if (!fRetryPath)
  792.             {
  793.                 // ask the user to stick in another disk or empty wastebasket
  794.                 iLastError = CopyMoveRetry(pcs, pszDest, iLastError, pfd->nFileSizeLow);
  795.                 if (!iLastError)
  796.                 {
  797.                     fRetryPath = TRUE;
  798.                     goto TryCopyAgain;
  799.                 }
  800.                 CopyError(pcs, pszSource, pszDest, (UINT)iLastError | ERRORONDEST, FO_COPY, OPER_DOFILE);
  801.                 iRet = ERROR_CANCELLED;
  802.                 goto Exit;
  803.             }
  804.             break;
  805.         case ERROR_ACCESS_DENIED:
  806.             {
  807.                 // check if the filename is too long
  808.                 if ( lstrlen(PathFindFileName(pszSource)) + lstrlen(pszDest) >= MAX_PATH )
  809.                 {
  810.                     iLastError = DE_FILENAMETOOLONG;
  811.                 }
  812.                 else if (!fRetryAttr)
  813.                 {
  814.                     // If the file is readonly, reset the readonly attribute
  815.                     // and have another go at it
  816.                     DWORD dwAttributes = GetFileAttributes(pszDest);
  817.                     if (0xFFFFFFFF != dwAttributes)
  818.                     {
  819.                         dwAttributes &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
  820.                         if (SetFileAttributes(pszDest, dwAttributes))
  821.                         {
  822.                             fRetryAttr = TRUE;
  823.                             goto TryCopyAgain;
  824.                         }
  825.                     }
  826.                     
  827.                     // GetFileAttributes() 10 lines above clobers GetLastError() and CopyError()
  828.                     // needs it.
  829.                     SetLastError(iLastError);
  830.                 }
  831.             }
  832.             break;
  833.         }
  834.         
  835.         if (!pcs->bAbort)
  836.         {
  837.             CopyError(pcs, pszSource, pszDest, iLastError, FO_COPY, OPER_DOFILE);
  838.         }
  839.         
  840.         iRet = ERROR_CANCELLED;  // error already reported
  841.         goto Exit;
  842.     }
  843.     
  844. #else // }{ #ifdef COPY_USE_COPYFILEEX
  845.     
  846.     // SizeFromLinkSpeed assumes there is a connection already established
  847.     if (!pcs->lpCopyBuffer)
  848.     {
  849.         pcs->uSize = SizeFromLinkSpeed(pszSource, pszDest,  &pcs->fFlushWrites);
  850.         
  851.         // BUGBUG: For wildcard or dir copies/moves, we calculate link speed and
  852.         //          allocate the buffers for each file!
  853.         pcs->lpCopyBuffer = (void*)LocalAlloc(LPTR, pcs->uSize);
  854.         if (!pcs->lpCopyBuffer)
  855.         {
  856.             DebugMsg(DM_WARNING, TEXT("insuf. mem for lpCopyBuffer"));
  857.             return DE_INSMEM;   // memory failure
  858.         }
  859.     }
  860.     
  861.     // Still ok to continue?
  862.     if (FOQueryAbort(pcs))
  863.         return ERROR_CANCELLED;
  864.     
  865.     hSource = (HFILE)CreateFile(pszSource, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, 0L, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  866.     
  867.     if (hSource == HFILE_ERROR)
  868.     {
  869.         CopyError(pcs, pszSource, pszDest, (int)GetLastError(), FO_COPY, OPER_DOFILE);
  870.         return ERROR_CANCELLED;  // error already reported
  871.     }
  872.     
  873.     // open destination files
  874.     
  875.     if (FOQueryAbort(pcs))
  876.         goto CloseSource;
  877.     
  878. TryOpen:
  879.     
  880.     if (!OpenDestFile(pszDest, &hDest, pfd->dwFileAttributes, fCreateAlways))
  881.     {
  882.         // error operning/creating destinaton file
  883.         
  884.         fh = hDest;
  885.         
  886.         if (fh == ERROR_FILE_EXISTS && !fCreateAlways)
  887.         {
  888.             iRet = ERROR_FILE_EXISTS;
  889.             goto CloseSource;
  890.         }
  891.         
  892.         if (fh == ERROR_PATH_NOT_FOUND)
  893.         {
  894. TryOpenDestAgain:
  895.             // ask the user to stick in another disk or empty wastebasket
  896.         
  897.             fh = CopyMoveRetry(pcs, pszDest, fh, pfd->nFileSizeLow);
  898.             if (!fh)
  899.             {
  900.                 goto TryOpen;
  901.             }
  902.         }
  903.         
  904.         // can't recover ... bail!
  905.         CopyError(pcs, pszSource, pszDest, (UINT)fh | ERRORONDEST, FO_COPY, OPER_DOFILE);
  906.         goto CloseSource;
  907.     }
  908.     
  909.     // BUGBUG - BobDay - Copying more than 4 gig will not be done correctly here
  910.     dwBytesLeft = pfd->nFileSizeLow;
  911.     dwBytesRead = 0;
  912.     
  913.     /* Now copy between the open files */
  914.     
  915.     SetProgressText(pcs, pszSource, pszDest);
  916.     
  917.     //SendProgressMessage(pcs, PBM_SETRANGE, 0, MAKELONG(0, (WORD)((pfd->nFileSizeLow + pcs->uSize - 1) / pcs->uSize)));
  918.     
  919.     dwRead = (DWORD)pcs->uSize;
  920.     
  921.     
  922.     // initialzie the file to the full size
  923.     // this takes 3 dos calls, so only do it if the file is big
  924.     if (pfd->nFileSizeLow > (COPYMAXBUFFERSIZE * 3))
  925.     {
  926.         // if there's a problem, bail
  927.         if ((_llseek(hDest, pfd->nFileSizeLow, 0L) == HFILE_ERROR) ||
  928.             (!SetEndOfFile((HANDLE)hDest)))
  929.         {
  930.             iLastError = GetLastError();
  931.             goto ErrorOnWrite;
  932.         }
  933.         else
  934.         {
  935.             _llseek(hDest, 0, 0L);
  936.         }
  937.     }
  938.     
  939.     /* Now copy between the open files */
  940.     
  941.     do
  942.     {
  943.         iLastError = 0;
  944.         
  945.         if (FOQueryAbort(pcs))
  946.             goto OpCancelled;
  947.         
  948.         //dwRead = _lread(hSource, pcs->lpCopyBuffer, pcs->uSize);
  949.         
  950.         if (! ReadFile((HANDLE)hSource, pcs->lpCopyBuffer, pcs->uSize, &dwRead, NULL))
  951.         {
  952.             // Error during file read
  953.             CopyError(pcs, pszSource, pszDest, (int)GetLastError(), FO_COPY, OPER_DOFILE);
  954.             goto OpCancelled;
  955.         }
  956.         
  957.         //SendProgressMessage(pcs, PBM_DELTAPOS, 1, 0);
  958.         
  959.         //wWrite = _lwrite(hDest, pcs->lpCopyBuffer, wRead);
  960.         if (! WriteFile((HANDLE)hDest, pcs->lpCopyBuffer, dwRead, &dwWrite, NULL))
  961.             dwWrite = (DWORD)-1;
  962.         
  963.         // write did not complete and removable drive?
  964.         if (dwRead != dwWrite)
  965.         {
  966.             iLastError = GetLastError();
  967. #ifndef WRITEFILE_SETSLASTERROR_ON_DISKFULL
  968.             // if no error set and we couldn't write, assume disk full
  969.             if (!iLastError)
  970.                 iLastError = DE_NODISKSPACE;
  971. #endif
  972.         }
  973.         
  974.         if (pcs->fFlushWrites)
  975.             FlushFileBuffers((HANDLE)hDest);
  976.         
  977. ErrorOnWrite:
  978.         if ((iLastError == DE_NODISKSPACE) && IsRemovableDrive(DRIVEID(pszDest)) &&
  979.             !PathIsSameRoot(pszDest, pszSource))
  980.         {
  981.             
  982.             // seek back to the start of the source.
  983.             
  984.             _llseek(hSource, 0L, 0);
  985.             
  986.             // destination disk must be full. close all
  987.             // destination files and delete those that
  988.             // have not been copied yet then
  989.             // give the user the option to insert a new disk.
  990.             
  991.             _lclose(hDest);
  992.             hDest = (HFILE)-1;
  993.             
  994.             Win32DeleteFile(pszDest);
  995.             
  996.             fh = DE_NODISKSPACE;
  997.             goto TryOpenDestAgain;      // and try to create the destiations
  998.             
  999.         }
  1000.         else if (iLastError)
  1001.         {
  1002.             // error writing file
  1003.             CopyError(pcs, pszSource, pszDest, (int)iLastError | ERRORONDEST, FO_COPY, OPER_DOFILE);
  1004.             goto OpCancelled;
  1005.         }
  1006.         
  1007.         // Reduce by ammount copied
  1008.         dwBytesLeft -= dwRead;
  1009.         // Add to so far read pile
  1010.         dwBytesRead += dwRead;
  1011.         
  1012.         DTSetFileCopyProgress(&pcs->dth, dwBytesRead);
  1013.     } while (dwRead && dwBytesLeft);
  1014.     
  1015.     // Close all destination files, set date time attribs
  1016.     
  1017.     // preserve the create date when moving across volumes, otherwise use the
  1018.     // create date the file system picked when we did the CreateFile()
  1019.     // always preserve modified date (ftLastWriteTime)
  1020.     // bummer is we loose accuracy when going to VFAT compared to NT servers
  1021.     
  1022.     SetFileTime((HANDLE)hDest, (pcs->lpfo->wFunc == FO_MOVE) ? &pfd->ftCreationTime : NULL,
  1023.         NULL, &pfd->ftLastWriteTime);
  1024.     
  1025.     _lclose(hDest);
  1026.     
  1027.     // NB We may have opened the destination with different attributes than the source
  1028.     // so reset them now.
  1029.     if (pfd->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
  1030.         SetFileAttributes(pszDest, pfd->dwFileAttributes);
  1031.     
  1032.     _lclose(hSource);
  1033.     
  1034. #endif  // } #ifdef COPY_USE_COPYFILEEX ... #else
  1035.     
  1036.     
  1037. #ifdef WINNT
  1038.     
  1039.     // Set the source's security on the destination, ignoring any error.
  1040.     if (SHRestricted(REST_FORCECOPYACLWITHFILE))
  1041.     {
  1042.         if ( fSecurityObtained )
  1043.             SetFileSecurity(pszDest, si, psd);
  1044.     }
  1045. #endif // #ifdef WINNT
  1046.     
  1047.     SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, pszDest, NULL);
  1048.     
  1049.     if (pcs->lpfo->wFunc == FO_MOVE)
  1050.     {
  1051.         
  1052. #ifdef COPY_USE_COPYFILEEX
  1053.         if (fUseMoveFileWithProgress)
  1054.         {
  1055.             // Let windows waiting on notifications of Source know of change.  We have to check
  1056.             // to see if the file is actually gone in order to tell if it actually moved or not.
  1057.             
  1058.             if (!PathFileExists(pszSource))
  1059.                 SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, pszSource, NULL);
  1060.             
  1061.             iRet = 0;
  1062.         }
  1063.         else
  1064.         {
  1065. #endif
  1066.             // BUGBUG, will this fail if source attribs are readonly?
  1067.             iRet = Win32DeleteFile(pszSource) ? 0 : GetLastError();
  1068.             if (iRet == ERROR_ACCESS_DENIED)
  1069.             {
  1070.                 // We may need to make the file not read-only
  1071.                 
  1072.                 SetFileAttributes(pszSource, FILE_ATTRIBUTE_NORMAL);
  1073.                 iRet = Win32DeleteFile(pszSource) ? 0 : GetLastError();
  1074.             }
  1075. #ifdef COPY_USE_COPYFILEEX
  1076.         }
  1077. #endif
  1078.     }
  1079.     else
  1080.     {
  1081.         iRet = 0;
  1082.     }
  1083.     
  1084.     
  1085. #ifdef COPY_USE_COPYFILEEX
  1086.     // this line has been taken out because it causes us to not report the erro when we failed to delete the source...
  1087.     // iRet = 0
  1088.     goto Exit;
  1089. #else
  1090.     return iRet;            // success
  1091. #endif
  1092.     
  1093. #ifndef COPY_USE_COPYFILEEX
  1094. OpCancelled:
  1095.     if (hDest != HFILE_ERROR)
  1096.         _lclose(hDest);
  1097.     Win32DeleteFile(pszDest);
  1098.     
  1099. CloseSource:
  1100.     if (hSource != HFILE_ERROR)
  1101.         _lclose(hSource);
  1102.     
  1103.     if (pcs->lpCopyBuffer)
  1104.     {
  1105.         LocalFree((HLOCAL)pcs->lpCopyBuffer);
  1106.         pcs->lpCopyBuffer = NULL;
  1107.     }
  1108.     return iRet;
  1109. #endif      // #ifndef COPY_USE_COPYFILEEX
  1110.     
  1111. #ifdef COPY_USE_COPYFILEEX
  1112.     
  1113. Exit:
  1114.     
  1115. #ifdef WINNT
  1116.     
  1117.     // If we had to alloc a buffer for the security descriptor,
  1118.     // free it now.
  1119.     
  1120.     if ( NULL != psd && rgbSecurityDescriptor != psd )
  1121.         LocalFree(psd);
  1122. #endif  // #ifdef WINNT
  1123.     
  1124.     return iRet;
  1125.     
  1126. #endif  // #ifdef COPY_USE_COPYFILEEX
  1127.     
  1128. }
  1129. // note: this is a very slow call
  1130. DWORD GetFreeClusters(LPCTSTR szPath)
  1131. {
  1132.     DWORD dwFreeClus;
  1133.     DWORD dwTemp;
  1134.     
  1135.     if (GetDiskFreeSpace(szPath,
  1136.         &dwTemp,       // Don't care
  1137.         &dwTemp,       // Don't care
  1138.         &dwFreeClus,
  1139.         &dwTemp))      // Don't care
  1140.         return dwFreeClus;
  1141.     else
  1142.         return (DWORD)-1;
  1143. }
  1144. // note: this is a very slow call
  1145. DWORD TotalCapacity(LPCTSTR szPath)
  1146. {
  1147.     int idDrive = PathGetDriveNumber(szPath);
  1148.     if (idDrive != -1) 
  1149.     {
  1150.         DWORD dwSecPerClus, dwBytesPerSec, dwClusters, dwTemp;
  1151.         TCHAR szDrive[5];
  1152.         
  1153.         PathBuildRoot(szDrive, idDrive);
  1154.         
  1155.         if (GetDiskFreeSpace(szDrive, &dwSecPerClus, &dwBytesPerSec, &dwTemp, &dwClusters))
  1156.             return dwSecPerClus * dwBytesPerSec * dwClusters;
  1157.     }
  1158.     
  1159.     return 0;
  1160. }
  1161. typedef struct
  1162. {
  1163.     LPTSTR pszTitle;
  1164.     LPTSTR pszText;
  1165. } DISKERRORPARAM;
  1166. BOOL_PTR CALLBACK DiskErrDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
  1167. {
  1168.     switch(uMessage)
  1169.     {
  1170.     case WM_INITDIALOG:
  1171.         {
  1172.             DISKERRORPARAM *pDiskError = (DISKERRORPARAM *) lParam;
  1173.             if (pDiskError)
  1174.             {
  1175.                 SetWindowText(hDlg, pDiskError->pszTitle);
  1176.                 SetDlgItemText(hDlg, IDC_DISKERR_EXPLAIN, pDiskError->pszText);
  1177.             }
  1178.             Static_SetIcon(GetDlgItem(hDlg, IDC_DISKERR_STOPICON), 
  1179.                 LoadIcon(NULL, IDI_HAND));
  1180.         }
  1181.         break;
  1182.         
  1183.     case WM_COMMAND:
  1184.         switch (LOWORD(wParam))
  1185.         {
  1186.         case IDOK:
  1187.         case IDCANCEL:
  1188.         case IDC_DISKERR_LAUNCHCLEANUP:
  1189.             EndDialog (hDlg, LOWORD(wParam));
  1190.             break;
  1191.             
  1192.         default:
  1193.             return FALSE;
  1194.         }
  1195.         break;
  1196.         
  1197.         default:
  1198.             return FALSE;
  1199.     }
  1200.     return TRUE;
  1201. }
  1202. void DisplayFileOperationError(HWND hParent, int idVerb, int wFunc, int nError, LPCTSTR szReason, LPCTSTR szPath, LPCTSTR szDest)
  1203. {
  1204.     TCHAR szBuffer[80];
  1205.     DISKERRORPARAM diskparams;
  1206.     int idDrive;
  1207.     
  1208.     // Grab title from resource 
  1209.     if (LoadString(HINST_THISDLL, IDS_FILEERROR + wFunc, szBuffer, ARRAYSIZE(szBuffer)))
  1210.     {
  1211.         diskparams.pszTitle = szBuffer;
  1212.     }
  1213.     else
  1214.     { 
  1215.         diskparams.pszTitle = NULL;
  1216.     }
  1217.     
  1218.     // Build Message to display
  1219.     diskparams.pszText = ShellConstructMessageString(HINST_THISDLL, 
  1220.         MAKEINTRESOURCE(idVerb), szReason, PathFindFileName(szPath));
  1221.     
  1222.     if (diskparams.pszText)
  1223.     {
  1224.         idDrive = DriveIDFromBBPath(szDest);
  1225.         //if we want to show Disk cleanup do our stuff, otherwise do MessageBox
  1226.         if (nError == DE_NODISKSPACE && 
  1227.             IsBitBucketableDrive(idDrive) &&
  1228.             GetDiskCleanupPath(NULL, 0))
  1229.         {
  1230.             if (DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_DISKERR), hParent,
  1231.                 DiskErrDlgProc,(LPARAM) &diskparams) == IDC_DISKERR_LAUNCHCLEANUP)
  1232.             {
  1233.                 LaunchDiskCleanup(hParent, idDrive);
  1234.             }
  1235.         }
  1236.         else
  1237.         {
  1238.             MessageBox(hParent, diskparams.pszText, diskparams.pszTitle, 
  1239.                 MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
  1240.         }
  1241.         LocalFree(diskparams.pszText);
  1242.     }
  1243. }
  1244. /***********************************************************************
  1245.     DESCRIPTION:
  1246.         We received an SHARINGVIOLATION or ACCESSDENIED error.  We want
  1247.     to generate the most accruate error message for the user to inform
  1248.     them better.  These are the cases we care about:
  1249.     DE_ACCESSDENIEDSRC: This is the legacy case with the message:
  1250.                         "Access is denied. The source file may be in use."
  1251.     DE_DEST_IS_CDROM:  This is displayed in case the user copies a file to
  1252.                         their cd-rom drive.
  1253.     DE_DEST_IS_DVD:  This is displayed in case the user copies a file to
  1254.                         their DVD drive
  1255.     DE_SHARING_VIOLATION: The file can't be copied because it's open by someone
  1256.                      who doesn't allow others to read the file while they
  1257.                      use it.
  1258.     DE_PERMISSIONDENIED:  This should be displayed if the user doesn't have
  1259.                          the ACLs (security permissions) to read/copy the file.
  1260. ***********************************************************************/
  1261. int GenAccessDeniedError(LPCTSTR pszSource, LPCTSTR pszDest, int nError)
  1262. {
  1263.     int nErrorMsg = DE_ACCESSDENIEDSRC;
  1264.     int iDrive = PathGetDriveNumber(pszDest);
  1265.     if (iDrive != -1)
  1266.     {
  1267.         if (IsCDRomDrive(iDrive))
  1268.             nErrorMsg = DE_DEST_IS_CDROM;
  1269.         if (DriveIsDVD(iDrive))
  1270.             nErrorMsg = DE_DEST_IS_DVD;
  1271.     }
  1272.     // TODO: DE_SHARING_VIOLATION, DE_PERMISSIONDENIED
  1273.     return nErrorMsg;
  1274. }
  1275. //
  1276. // The following function reports errors for the copy engine
  1277. //
  1278. // Parameters
  1279. //      pszSource       source file name
  1280. //      pszDest         destination file name
  1281. //      nError          dos (or our exteneded) error code
  1282. //                      0xFFFF for special case NET error
  1283. //      wFunc           FO_* values
  1284. //      nOper           OPER_* values, operation being performed
  1285. //
  1286. void CopyError(LPCOPY_STATE pcs, LPCTSTR pszSource, LPCTSTR pszDest, int nError, UINT wFunc, int nOper)
  1287. {
  1288.     TCHAR szReason[200];
  1289.     TCHAR szFile[MAX_PATH];
  1290.     int idVerb;
  1291.     BOOL bDest;
  1292.     BOOL fSysError = FALSE;
  1293.     DWORD dwError = GetLastError();       // get Extended error now before we blow it away.
  1294.     
  1295.     if (!pcs || (pcs->fFlags & FOF_NOERRORUI))
  1296.         return;      // caller doesn't want to report errors
  1297.     
  1298.     bDest = nError & ERRORONDEST;        // was dest file cause of error
  1299.     nError &= ~ERRORONDEST;              // clear the dest bit
  1300.     
  1301.     // We also may need to remap some new error codes into old error codes
  1302.     //
  1303.     if (nError == ERROR_BAD_PATHNAME)
  1304.         nError = DE_INVALIDFILES;
  1305.     
  1306.     if (nError == ERROR_CANCELLED)        // user abort
  1307.         return;
  1308.     
  1309.     lstrcpyn(szFile, bDest ? pszDest : pszSource, ARRAYSIZE(szFile));
  1310.     if (!szFile[0])
  1311.     {
  1312.         LoadString(HINST_THISDLL, IDS_FILE, szFile, ARRAYSIZE(szFile));
  1313.     }
  1314.     else
  1315.     {
  1316.         // make the path fits on the screen
  1317.         RECT rcMonitor;
  1318.         HWND hwnd = pcs->hwndProgress;
  1319.         
  1320.         if (!hwnd)
  1321.         {
  1322.             hwnd = pcs->hwndDlgParent;
  1323.         }
  1324.         
  1325.         GetMonitorRect(MonitorFromWindow(hwnd, TRUE), &rcMonitor);
  1326.         
  1327.         PathCompactPath(NULL, szFile,
  1328.             (rcMonitor.right - rcMonitor.left) / 3);
  1329.     }
  1330.     
  1331.     // get the verb string
  1332.     // since we now recycle folders as well as files, added OPER_ENTERDIR check here
  1333.     if ((nOper == OPER_DOFILE) || (nOper == OPER_ENTERDIR) || (nOper == 0))
  1334.     {
  1335.         if ((nError != -1) && bDest)
  1336.         {
  1337.             idVerb = IDS_REPLACING;
  1338.         }
  1339.         else
  1340.         {
  1341.             idVerb = IDS_VERBS + wFunc;
  1342.         }
  1343.     }
  1344.     else
  1345.     {
  1346.         idVerb = IDS_ACTIONS + (nOper >> 8);
  1347.     }
  1348.     
  1349.     // get the reason string
  1350.     if (nError == 0xFFFF)
  1351.     {
  1352.         DWORD dw;
  1353.         WNetGetLastError(&dw, szReason, ARRAYSIZE(szReason), NULL , 0);
  1354.     }
  1355.     else
  1356.     {
  1357.         // transform some error cases
  1358.         
  1359.         if (bDest)
  1360.         {
  1361.             // BUGBUG:: This caseing of error codes is error prone.. it would
  1362.             //          be better to find the explicit ones we wish to map to
  1363.             //          this one instead of trying to guess all the ones
  1364.             //          we don't want to map...
  1365.             if ((nError == ERROR_DISK_FULL) ||
  1366.                 ((nError != ERROR_ACCESS_DENIED) &&
  1367.                 (nError != ERROR_NETWORK_ACCESS_DENIED) &&
  1368.                 (nError != ERROR_WRITE_PROTECT) &&
  1369.                 (nError != ERROR_BAD_NET_NAME) &&
  1370.                 (GetFreeClusters(pszDest) == 0L)))
  1371.             {
  1372.                 nError = DE_NODISKSPACE;
  1373.             }
  1374.             else if (dwError == DE_WRITEFAULT)
  1375.             {
  1376.                 nError = DE_WRITEFAULT;
  1377.             }
  1378.         }
  1379.         else
  1380.         {
  1381.             if (nError == ERROR_ACCESS_DENIED)
  1382.             {
  1383.                 // Check the extended error for more info about the error...
  1384.                 // We just map these errors to something generic that
  1385.                 // tells the user something weird is going on.
  1386.                 switch (dwError)
  1387.                 {
  1388.                 case DE_CRCDATAERROR:
  1389.                 case DE_SEEKERROR:
  1390.                 case DE_SECTORNOTFOUND:
  1391.                 case DE_READFAULT:
  1392.                 case ERROR_GEN_FAILURE:
  1393.                     nError = ERROR_GEN_FAILURE;
  1394.                     break;
  1395.                 // Whoever wrote this code needs to come to my office for a good old ass-kicking.  
  1396.                 // We can't test for ERROR_FILE_NOT_FOUND because in the case where we copy to
  1397.                 // a write-protected dest we check to see if the reason we got access denied was
  1398.                 // because there's already a read-only file there.  If there isn't _that_ test is
  1399.                 // going to SetLastError() to ERROR_FILE_NOT_FOUND and that's what we're going to
  1400.                 // report as an error. [davepl]
  1401.                 // 
  1402.                 // case ERROR_FILE_NOT_FOUND:
  1403.                 //    nError = ERROR_GEN_FAILURE;
  1404.                 //    break;
  1405.                 case DE_SHARINGVIOLATION:
  1406.                 case DE_ACCESSDENIED:
  1407.                     nError = GenAccessDeniedError(pszSource, pszDest, nError);
  1408.                     break;
  1409.                 default:
  1410.                     TraceMsg(TF_WARNING, "CopyEngine: hit error %x , not currently special cased", dwError);
  1411.                     break;
  1412.                 }
  1413.             }
  1414.             else
  1415.             {
  1416.                 // This error occures when a user drags & drops a file from point a to
  1417.                 // point b twice.  The second time fails because the first time hasn't finished.
  1418.                 if (nError == (OPER_ERROR | DE_FILENOTFOUND))
  1419.                 {
  1420.                     nError = ERROR_GEN_FAILURE;
  1421.                 }
  1422.             }
  1423.         }
  1424.     }
  1425.     
  1426.     // BUGBUG: This is total bullshit. fSysError might as well be a random number after this call.
  1427.     // But that't ok because nError is already a random number to start with.  Sigh.
  1428.     // This should be a range check instead of hoping that LoadString will fail.
  1429.     fSysError = !LoadString(HINST_THISDLL, IDS_REASONS + nError, szReason, ARRAYSIZE(szReason));
  1430.     
  1431.     if (nOper == OPER_DOFILE)
  1432.     {
  1433.         PathRemoveExtension(szFile);
  1434.     }
  1435.     
  1436.     if (fSysError)
  1437.     {
  1438.         SHSysErrorMessageBox(pcs->hwndDlgParent, MAKEINTRESOURCE(IDS_FILEERROR + wFunc),
  1439.             idVerb, nError, PathFindFileName(szFile),
  1440.             MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
  1441.     }
  1442.     else
  1443.     {
  1444.         DisplayFileOperationError(pcs->hwndDlgParent, idVerb, wFunc, nError, szReason, szFile, pszDest);
  1445.     }
  1446. }
  1447. //
  1448. // The following function is used to retry failed move/copy operations
  1449. // due to out of disk situations or path not found errors
  1450. // on the destination.
  1451. //
  1452. // parameters:
  1453. //      pszDest         Fully qualified path to destination file (ANSI)
  1454. //      nError          type of error: DE_NODISKSPACE or ERROR_PATH_NOT_FOUND
  1455. //      dwFileSize      amount of space needed for this file if DE_NODISKSPACE
  1456. //
  1457. // returns:
  1458. //      0       success (destination path has been created)
  1459. //      != 0    dos error code including ERROR_CANCELLED
  1460. //
  1461. int CopyMoveRetry(COPY_STATE *pcs, LPCTSTR pszDest, int nError, DWORD dwFileSize)
  1462. {
  1463.     UINT wFlags;
  1464.     int  result;
  1465.     LPCTSTR wID;
  1466.     TCHAR szTemp[MAX_PATH];
  1467.     BOOL fFirstRetry = TRUE;
  1468.     
  1469.     if (pcs->fFlags & FOF_NOERRORUI)
  1470.     {
  1471.         result = ERROR_CANCELLED;
  1472.         goto ErrorExit;
  1473.     }
  1474.     
  1475.     lstrcpyn(szTemp, pszDest, ARRAYSIZE(szTemp));
  1476.     PathRemoveFileSpec(szTemp);
  1477.     
  1478.     do
  1479.     {
  1480.         // until the destination path has been created
  1481.         if (nError == ERROR_PATH_NOT_FOUND)
  1482.         {
  1483.             if (!( pcs->fFlags & FOF_NOCONFIRMMKDIR))
  1484.             {
  1485.                 wID = MAKEINTRESOURCE(IDS_PATHNOTTHERE);
  1486.                 wFlags = MB_ICONEXCLAMATION | MB_YESNO;
  1487.             }
  1488.             else
  1489.             {
  1490.                 wID = 0;
  1491.             }
  1492.         }
  1493.         else  // DE_NODISKSPACE
  1494.         {
  1495.             wFlags = MB_ICONEXCLAMATION | MB_RETRYCANCEL;
  1496.             if (dwFileSize > TotalCapacity(pszDest))
  1497.             {
  1498.                 wID = MAKEINTRESOURCE(IDS_FILEWONTFIT);
  1499.             }
  1500.             else
  1501.             {
  1502.                 wID = MAKEINTRESOURCE(IDS_DESTFULL);
  1503.             }
  1504.         }
  1505.         
  1506.         if (wID)
  1507.         {
  1508.             // szTemp will be ignored if there's no %1%s in the string.
  1509.             result = ShellMessageBox(HINST_THISDLL, pcs->hwndDlgParent, wID, MAKEINTRESOURCE(IDS_UNDO_FILEOP + pcs->lpfo->wFunc), wFlags, (LPTSTR)szTemp);
  1510.         }
  1511.         else
  1512.         {
  1513.             result = IDYES;
  1514.         }
  1515.         
  1516.         if (result == IDRETRY || result == IDYES)
  1517.         {
  1518.             TCHAR szDrive[5];
  1519.             int idDrive;
  1520.             
  1521.             // Allow the disk to be formatted
  1522.             // REVIEW, could this be FO_MOVE as well?
  1523.             if (FAILED(SHPathPrepareForWrite(((pcs->fFlags & FOF_NOERRORUI) ? NULL : pcs->hwndDlgParent), NULL, szTemp, SHPPFW_DEFAULT)))
  1524.                 return ERROR_CANCELLED;
  1525.             
  1526.             idDrive = PathGetDriveNumber(szTemp);
  1527.             if (idDrive != -1)
  1528.                 PathBuildRoot(szDrive, idDrive);
  1529.             else
  1530.                 szDrive[0] = 0;
  1531.             
  1532.             // if we're not copying to the root
  1533.             if (lstrcmpi(szTemp, szDrive))
  1534.             {
  1535.                 result = SHCreateDirectory(pcs->hwndDlgParent, szTemp);
  1536.                 
  1537.                 if (result == ERROR_CANCELLED)
  1538.                     goto ErrorExit;
  1539.                 if ( result == ERROR_ALREADY_EXISTS )
  1540.                 {
  1541.                     // if SHPathPrepareForWrite created the directory we shouldn't treat this as an error
  1542.                     result = 0;
  1543.                 }
  1544.                 else if (result && (nError == ERROR_PATH_NOT_FOUND))
  1545.                 {
  1546.                     result |= ERRORONDEST;
  1547.                     
  1548.                     //  We try twice to allow the recyclebin to be flushed.
  1549.                     if (fFirstRetry)
  1550.                         fFirstRetry = FALSE;
  1551.                     else
  1552.                         goto ErrorExit;
  1553.                 }
  1554.             }
  1555.             else
  1556.             {
  1557.                 result = 0;
  1558.             }
  1559.         }
  1560.         else
  1561.         {
  1562.             result = ERROR_CANCELLED;
  1563.             goto ErrorExit;
  1564.         }
  1565.     } while (result);
  1566.     
  1567. ErrorExit:
  1568.     return result;            // success
  1569. }
  1570. // BUGBUG: This function is bogus because PathIsInvalid is bogus.
  1571. BOOL ValidFilenames(LPCTSTR pList)
  1572. {
  1573.     if ( !*pList )
  1574.         return FALSE;
  1575.     for (; *pList; pList += lstrlen(pList) + 1)
  1576.     {
  1577.         if (PathIsInvalid(pList))
  1578.         {
  1579.             return FALSE;
  1580.         }
  1581.     }
  1582.     
  1583.     return TRUE;
  1584. }
  1585. void AddRenamePairToHDSA(LPCTSTR pszOldPath, LPCTSTR pszNewPath, HDSA* phdsaRenamePairs)
  1586. {
  1587.     //
  1588.     //  Update our collision mapping table
  1589.     //
  1590.     if (!*phdsaRenamePairs)
  1591.         *phdsaRenamePairs = DSA_Create(SIZEOF(SHNAMEMAPPING), 4);
  1592.     
  1593.     if (*phdsaRenamePairs)
  1594.     {
  1595.         SHNAMEMAPPING rp;
  1596.         rp.cchOldPath = lstrlen(pszOldPath);
  1597.         rp.cchNewPath = lstrlen(pszNewPath);
  1598.         
  1599.         if (NULL != (rp.pszOldPath = Alloc((rp.cchOldPath + 1) * SIZEOF(TCHAR))))
  1600.         {
  1601.             if (NULL != (rp.pszNewPath = Alloc((rp.cchNewPath + 1) * SIZEOF(TCHAR))))
  1602.             {
  1603.                 lstrcpy(rp.pszOldPath, pszOldPath);
  1604.                 lstrcpy(rp.pszNewPath, pszNewPath);
  1605.                 
  1606.                 if (DSA_InsertItem(*phdsaRenamePairs,
  1607.                     DSA_GetItemCount(*phdsaRenamePairs),
  1608.                     &rp) == -1)
  1609.                 {
  1610.                     Free(rp.pszOldPath);
  1611.                     Free(rp.pszNewPath);
  1612.                 }
  1613.             }
  1614.             else
  1615.             {
  1616.                 Free(rp.pszOldPath);
  1617.             }
  1618.         }
  1619.     }
  1620. }
  1621. BOOL _HandleRename(LPCTSTR pszSource, LPTSTR pszDest, FILEOP_FLAGS fFlags, COPY_STATE * pcs)
  1622. {
  1623.     TCHAR *pszConflictingName = PathFindFileName(pszSource);
  1624.     TCHAR szTemp[MAX_PATH];
  1625.     TCHAR szTemplate[MAX_PATH];
  1626.     LPTSTR lpszLongPlate;
  1627.     
  1628.     PathRemoveFileSpec(pszDest);
  1629.     
  1630.     if (LoadString(HINST_THISDLL, IDS_COPYLONGPLATE, szTemplate, ARRAYSIZE(szTemplate)))
  1631.     {
  1632.         LPTSTR lpsz;
  1633.         lpsz = pszConflictingName;
  1634.         lpszLongPlate = szTemplate;
  1635.         // see if the first part of the template is the same as the name "Copy #"
  1636.         while (*lpsz && *lpszLongPlate &&
  1637.             *lpsz == *lpszLongPlate &&
  1638.             *lpszLongPlate != TEXT('('))
  1639.         {
  1640.             lpsz++;
  1641.             lpszLongPlate++;
  1642.         }
  1643.         
  1644.         if (*lpsz == TEXT('(') && *lpszLongPlate == TEXT('('))
  1645.         {
  1646.             // conflicting name already in the template, use it instead
  1647.             lpszLongPlate = pszConflictingName;
  1648.         }
  1649.         else
  1650.         {
  1651.             // otherwise build our own
  1652.             // We need to make sure not to overflow a max buffer.
  1653.             int ichFixed = lstrlen(szTemplate) + lstrlen(pszDest) + 5;
  1654.             lpszLongPlate = szTemplate;
  1655.             
  1656.             if ((ichFixed + lstrlen(pszConflictingName)) <= MAX_PATH)
  1657.             {
  1658.                 lstrcat(lpszLongPlate, pszConflictingName);
  1659.             }
  1660.             else
  1661.             {
  1662.                 // Need to remove some of the name
  1663.                 LPTSTR pszExt = StrRChr(pszConflictingName, NULL, TEXT('.'));
  1664.                 if (pszExt)
  1665.                 {
  1666.                     lstrcpyn(lpszLongPlate + lstrlen(lpszLongPlate),
  1667.                         pszConflictingName,
  1668.                         MAX_PATH - ichFixed - lstrlen(pszExt));
  1669.                     lstrcat(lpszLongPlate, pszExt);
  1670.                 }
  1671.                 else
  1672.                 {
  1673.                     lstrcpyn(lpszLongPlate + lstrlen(lpszLongPlate),
  1674.                         pszConflictingName,
  1675.                         MAX_PATH - ichFixed);
  1676.                 }
  1677.             }
  1678.         }
  1679.     }
  1680.     else
  1681.     {
  1682.         lpszLongPlate = NULL;
  1683.     }
  1684.     
  1685.     if (PathYetAnotherMakeUniqueName(szTemp, pszDest, pszConflictingName, lpszLongPlate))
  1686.     {
  1687.         //
  1688.         //  If there are any other files in the queue which are to
  1689.         //  be copied into a subtree of pszDest, we must update them
  1690.         //  as well.
  1691.         //
  1692.         
  1693.         //  Put the new (renamed) target in pszDest.
  1694.         lstrcpy(pszDest, szTemp);
  1695.         
  1696.         //  Rebuild the old dest name and put it in szTemp.
  1697.         //  I'm going for minimum stack usage here, so I don't want more
  1698.         //  than one MAX_PATH lying around.
  1699.         PathRemoveFileSpec(szTemp);
  1700.         PathAppend(szTemp, pszConflictingName);
  1701.         
  1702.         AddRenamePairToHDSA(szTemp, pszDest, &pcs->dth.hdsaRenamePairs);
  1703.         
  1704.         return(TRUE);
  1705.     }
  1706.     return(FALSE);
  1707. }
  1708. // test input for "multiple" filespec
  1709. //
  1710. // examples:
  1711. //      1       foo.bar                 (single non directory file)
  1712. //      -1      *.exe                   (wild card on any of the files)
  1713. //      n       foo.bar bletch.txt      (number of files)
  1714. //
  1715. int CountFiles(LPCTSTR pInput)
  1716. {
  1717.     int count;
  1718.     for (count = 0; *pInput; pInput += lstrlen(pInput) + 1, count++) {
  1719.         // wild cards imply multiple files
  1720.         if (PathIsWild(pInput))
  1721.             return -1;
  1722.     }
  1723.     return count;
  1724.     
  1725. }
  1726. // set the attribs of a folder, but blow it off if there are no
  1727. // special attributes set
  1728. void SetDirAttributes(LPCTSTR szDest, DWORD dwFileAttributes)
  1729. {
  1730.     if (dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN))
  1731.         SetFileAttributes(szDest, dwFileAttributes & ~FILE_ATTRIBUTE_DIRECTORY);
  1732. }
  1733. #define ISDIGIT(c)  ((c) >= TEXT('0') && (c) <= TEXT('9'))
  1734. BOOL IsCompressedVolume(LPCTSTR pszSource, DWORD dwAttributes)
  1735. {
  1736.     int i;
  1737.     LPTSTR pszFileName, pszExtension;
  1738.     TCHAR szPath[MAX_PATH];
  1739.     
  1740.     // must be marked system and hidden
  1741.     if (!IS_SYSTEM_HIDDEN(dwAttributes))
  1742.         return FALSE;
  1743.     
  1744.     lstrcpy(szPath, pszSource);
  1745.     pszFileName = PathFindFileName(szPath);
  1746.     pszExtension = PathFindExtension(pszFileName);
  1747.     
  1748.     // make sure the extension is a 3 digit number
  1749.     if (!*pszExtension)
  1750.         return FALSE;       // no extension
  1751.     
  1752.     for (i = 1; i < 4; i++) 
  1753.     {
  1754.         if (!pszExtension[i] || !ISDIGIT(pszExtension[i]))
  1755.             return FALSE;
  1756.     }
  1757.     
  1758.     // make sure it's null terminated here
  1759.     if (pszExtension[4])
  1760.         return FALSE;
  1761.     
  1762.     // now knock off the extension and make sure the stem matches
  1763.     *pszExtension = 0;
  1764.     if (lstrcmpi(pszFileName, TEXT("DRVSPACE")) &&
  1765.         lstrcmpi(pszFileName, TEXT("DBLSPACE"))) 
  1766.     {
  1767.         return FALSE;
  1768.     }
  1769.     
  1770.     // make sure it's in the root
  1771.     PathRemoveFileSpec(szPath);
  1772.     if (!PathIsRoot(szPath)) 
  1773.     {
  1774.         return FALSE;
  1775.     }
  1776.     
  1777.     return TRUE;        // passed all tests!
  1778. }
  1779. void _DeferMoveDlgItem(HDWP hdwp, HWND hDlg, int nItem, int x, int y)
  1780. {
  1781.     RECT rc;
  1782.     HWND hwnd = GetDlgItem(hDlg, nItem);
  1783.     GetClientRect(hwnd, &rc);
  1784.     MapWindowPoints(hwnd, hDlg, (LPPOINT) &rc, 2);
  1785.     DeferWindowPos(hdwp, hwnd, 0, rc.left + x, rc.top + y, 0, 0,
  1786.         SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
  1787. }
  1788. void _RecalcWindowHeight(HWND hWnd, LPTSTR lpszText)
  1789. {
  1790.     HDC hdc = GetDC(hWnd);
  1791.     RECT rc;
  1792.     HWND hwndText = GetDlgItem(hWnd,IDC_MBC_TEXT);
  1793.     HDWP hdwp;
  1794.     int iHeightDelta, cx;
  1795.     // Get the starting rect of the text area (for the width)
  1796.     GetClientRect(hwndText, &rc);
  1797.     MapWindowPoints(hwndText, hWnd, (LPPOINT) &rc, 2);
  1798.     // Calc how high the static text area needs to be, given the above width
  1799.     iHeightDelta = RECTHEIGHT(rc);
  1800.     cx = RECTWIDTH(rc);
  1801.     DrawText(hdc, lpszText, -1, &rc, DT_CALCRECT | DT_WORDBREAK | DT_LEFT | DT_INTERNAL | DT_EDITCONTROL);
  1802.     
  1803.     iHeightDelta = RECTHEIGHT(rc) - iHeightDelta;
  1804.     cx = RECTWIDTH(rc) - cx; // Should only change for really long words w/o spaces
  1805.     ReleaseDC(hWnd, hdc);
  1806.     hdwp = BeginDeferWindowPos(4);
  1807.     DeferWindowPos(hdwp, hwndText, 0, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER | SWP_NOACTIVATE);
  1808.     _DeferMoveDlgItem(hdwp, hWnd, IDC_MESSAGEBOXCHECKEX, 0, iHeightDelta);
  1809.     _DeferMoveDlgItem(hdwp, hWnd, IDYES, cx, iHeightDelta);
  1810.     _DeferMoveDlgItem(hdwp, hWnd, IDNO, cx, iHeightDelta);
  1811.     EndDeferWindowPos(hdwp);
  1812.     GetWindowRect(hWnd, &rc);
  1813.     SetWindowPos(hWnd, 0, rc.left - (cx/2), rc.top - (iHeightDelta/2), RECTWIDTH(rc)+cx, RECTHEIGHT(rc)+iHeightDelta, SWP_NOZORDER | SWP_NOACTIVATE);
  1814.     return;
  1815. }
  1816. BOOL_PTR CALLBACK RenameMsgBoxCheckDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1817. {
  1818.     switch (uMsg)
  1819.     {
  1820.         // we only handle the WM_INITDIALOG so that we can resize the dialog
  1821.         // approprately and to set the default button to IDNO
  1822.         case WM_INITDIALOG:
  1823.         {
  1824.             HWND hwndNO = GetDlgItem(hDlg, IDNO);
  1825.             
  1826.             _RecalcWindowHeight(hDlg, (LPTSTR)lParam);
  1827.             SetDlgItemText(hDlg,IDC_MBC_TEXT,(LPTSTR)lParam);
  1828.             
  1829.             SendMessage(hDlg, DM_SETDEFID, IDNO, 0);
  1830.             SetFocus(hwndNO);
  1831.             
  1832.             return (FALSE); // we set the focus, so return false
  1833.         }
  1834.     }
  1835.     
  1836.     // didnt handle this message
  1837.     return FALSE;
  1838. }
  1839. int ConfirmRenameOfConnectedItem(COPY_STATE *pcs, WIN32_FIND_DATA *pfd, LPTSTR szSource)
  1840. {
  1841.     int result = IDYES; //For non-connected elements, the default is IDYES!
  1842.     LPTSTR  pszMessage;
  1843.     LPTSTR  lpConnectedItem, lpConnectOrigin;
  1844.     LPTSTR  lpStringID;
  1845.     //Check if this item being renamed has a connected item.
  1846.     if (DTNIsConnectOrigin(pcs->dth.pdtnCurrent))
  1847.     {
  1848.         //Yes! It has a connected element! Form the strings to create the confirmation dialog!
  1849.         //Get the name of the connected element
  1850.         lpConnectedItem = PathFindFileName(pcs->dth.pdtnCurrent->pdtnConnected->szName);
  1851.         lpConnectOrigin = PathFindFileName(pcs->dth.pFrom);
  1852.         // Mark the connected item as dummy as this will never get renamed.
  1853.         // (Note that this connected node could be a folder. It is still OK to mark it as 
  1854.         // dummy because for rename operation, a folder is treated just like a file in 
  1855.         // DTGotoNextNode()).
  1856.         pcs->dth.pdtnCurrent->pdtnConnected->fDummy = TRUE;
  1857.         
  1858.         if (pfd && (pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  1859.             lpStringID = MAKEINTRESOURCE(IDS_HTML_FOLDER_RENAME);
  1860.         else
  1861.             lpStringID = MAKEINTRESOURCE(IDS_HTML_FILE_RENAME);
  1862.         //Load the confirmation message and format it!
  1863.         pszMessage = ShellConstructMessageString(HINST_THISDLL, lpStringID, 
  1864.                         lpConnectedItem, lpConnectOrigin);
  1865.         if (pszMessage)
  1866.         {
  1867.             //Get the confirmation from the end-user;
  1868.             result = SHMessageBoxCheckEx(pcs->hwndDlgParent, HINST_THISDLL, 
  1869.                                             MAKEINTRESOURCE(DLG_RENAME_MESSAGEBOXCHECK), 
  1870.                                             RenameMsgBoxCheckDlgProc,
  1871.                                             (void *)pszMessage,
  1872.                                             IDYES, 
  1873.                                             REG_VAL_GENERAL_RENAMEHTMLFILE);
  1874.             //It is possible we get IDCANCEL if the "X" in the caption is clicked to clost
  1875.             // the dialog. The following code makes sure we get one of the return code that we want.
  1876.             if ((result != IDYES) && (result != IDNO))
  1877.                 result = IDNO;
  1878.             SHFree(pszMessage);
  1879.         }
  1880.         else
  1881.             result = IDNO;  //For connected elements, the default is "Don't rename";
  1882.     }
  1883.     else
  1884.     {
  1885.         if (DTNIsConnected(pcs->dth.pdtnCurrent))
  1886.             result = IDNO;  //Connected elements, do not get renamed.
  1887.     }
  1888.     return result;
  1889. }
  1890. int AllConfirmations(COPY_STATE *pcs, WIN32_FIND_DATA *pfd, UINT oper, UINT wFunc,
  1891.                      LPTSTR szSource, LPTSTR szDest,
  1892.                      WIN32_FIND_DATA *pfdDest, LPINT lpret)
  1893. {
  1894.     int result = IDYES;
  1895.     LPTSTR p;
  1896.     LPTSTR pszStatusDest = NULL;
  1897.     CONFIRM_FLAG fConfirm;
  1898.     WIN32_FIND_DATA *pfdUse1 = NULL;
  1899.     WIN32_FIND_DATA *pfdUse2;
  1900.     BOOL fSetProgress = FALSE;
  1901.     BOOL fShowConfirm = FALSE;
  1902.     switch (oper | wFunc)
  1903.     {
  1904.     case OPER_ENTERDIR | FO_MOVE:
  1905.         if (PathIsSameRoot(szSource, szDest))
  1906.         {
  1907.             fConfirm = CONFIRM_MOVE_FOLDER;
  1908.             pfdUse1 = pfd;
  1909.             pfdUse2 = pfdDest;
  1910.             fShowConfirm = TRUE;
  1911.         }
  1912.         break;
  1913.     case OPER_ENTERDIR | FO_DELETE:
  1914.         // Confirm removal of directory on this pass.  The directories
  1915.         // are actually removed on the OPER_LEAVEDIR pass
  1916.         if (DTNIsRootNode(pcs->dth.pdtnCurrent))
  1917.             fSetProgress = TRUE;        
  1918.         if ( !PathIsRoot(szSource) )
  1919.         {
  1920.             fShowConfirm = TRUE;
  1921.             pfdUse2 = pfd;
  1922.             fConfirm = CONFIRM_DELETE_FOLDER;
  1923.             szDest = NULL;
  1924.         }
  1925.         break;
  1926.     case OPER_DOFILE | FO_RENAME:
  1927.         // pszStatusDest = szDest;
  1928.         fSetProgress = TRUE;
  1929.         p = PathFindFileName(szSource);
  1930.         if (!IntlStrEqNI(szSource, szDest, (int)(p - szSource)))
  1931.         {
  1932.             result = DE_DIFFDIR;
  1933.         }
  1934.         else
  1935.         {
  1936.             if (pfd && (pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  1937.                 fConfirm = CONFIRM_RENAME_FOLDER;
  1938.             else
  1939.                 fConfirm =  CONFIRM_RENAME_FILE;
  1940.             
  1941.             if (PathIsRoot(szSource) || (PathIsRoot(szDest)))
  1942.             {
  1943.                 result = DE_ROOTDIR | ERRORONDEST;
  1944.             }
  1945.             else
  1946.             {
  1947.                 // We need to bring up a special confirmation dialog if this file/folder being
  1948.                 // renamed has a connected element (if "foo.htm" or "foo files" is renamed that
  1949.                 // will break the links).
  1950.                 result = ConfirmRenameOfConnectedItem(pcs, pfd, szSource);
  1951.                 if (result != IDNO)
  1952.                 {
  1953.                     fShowConfirm = TRUE;
  1954.                     pfdUse2 = pfdDest;
  1955.                     pfdUse1 = pfd;
  1956.                 }
  1957.             }
  1958.         }
  1959.         break;
  1960.     case OPER_DOFILE | FO_MOVE:
  1961.         
  1962.         fSetProgress = TRUE;
  1963.         pszStatusDest = szDest;
  1964.         if (PathIsRoot(szSource))
  1965.         {
  1966.             result = DE_ROOTDIR;
  1967.         }
  1968.         else if (PathIsRoot(szDest))
  1969.         {
  1970.             result = DE_ROOTDIR | ERRORONDEST;
  1971.         }
  1972.         else
  1973.         {
  1974.             fConfirm = CONFIRM_MOVE_FILE;
  1975.             fShowConfirm = TRUE;
  1976.             pfdUse2 = pfdDest;
  1977.             pfdUse1 = pfd;
  1978.         }
  1979.         break;
  1980.     case OPER_DOFILE | FO_DELETE:
  1981.         fSetProgress = TRUE;
  1982.         if (IsCompressedVolume(szSource, pfd->dwFileAttributes))
  1983.         {
  1984.             CopyError(pcs, szSource, szDest, DE_COMPRESSEDVOLUME, wFunc, oper);
  1985.             result = IDNO;
  1986.         }
  1987.         else if (IsWindowsFile(szSource))
  1988.         {
  1989.             CopyError(pcs, szSource, szDest, DE_WINDOWSFILE, wFunc, oper);
  1990.             result = IDNO;
  1991.         }
  1992.         else
  1993.         {
  1994.             fShowConfirm = TRUE;
  1995.             szDest = NULL;
  1996.             pfdUse2 = pfd;
  1997.             fConfirm = CONFIRM_DELETE_FILE;
  1998.         }
  1999.         break;
  2000.         
  2001.     }
  2002.     
  2003.     if (fShowConfirm)
  2004.     {
  2005.         result = CachedConfirmFileOp(pcs->hwndDlgParent, pcs, &pcs->cd, pcs->nSourceFiles, !DTNIsRootNode(pcs->dth.pdtnCurrent), fConfirm,
  2006.             szSource, pfdUse1, szDest, pfdUse2, NULL);
  2007.     }
  2008.     
  2009. #ifdef WINNT
  2010.     if (oper == OPER_DOFILE || oper == OPER_ENTERDIR)
  2011.     {
  2012.         if ((wFunc == FO_MOVE) || (wFunc == FO_COPY))
  2013.         {
  2014.             if ((result != IDNO) && (result != IDCANCEL))
  2015.             {   
  2016.                 LPTSTR pszDataToBeLost;
  2017.                 WCHAR wszDestDir[MAX_PATH];
  2018.                 BOOL  bNoStreamLossThisDir = FALSE;
  2019.                 lstrcpy(wszDestDir, szDest);
  2020.                 PathRemoveFileSpec(wszDestDir);
  2021.                 
  2022.                 // Files with multiple streams will suffer stream loss on a downlevel
  2023.                 // copy, but CopyFile special-cases native structure storage.
  2024.                 pszDataToBeLost = GetDownlevelCopyDataLossText(szSource, wszDestDir, (oper == OPER_ENTERDIR), &bNoStreamLossThisDir);
  2025.                 if (pszDataToBeLost)
  2026.                 {
  2027.                     fConfirm     = CONFIRM_STREAMLOSS;
  2028.                     pfdUse2      = pfd;
  2029.                     
  2030.                     result = CachedConfirmFileOp(pcs->hwndDlgParent, pcs, &pcs->cd, pcs->nSourceFiles, !DTNIsRootNode(pcs->dth.pdtnCurrent), fConfirm,
  2031.                         szSource, pfdUse1, szDest, pfdUse2, pszDataToBeLost);
  2032.                     LocalFree(pszDataToBeLost);
  2033.                 }
  2034.                 else if (bNoStreamLossThisDir)
  2035.                 {
  2036.                     // pcs->bStreamLossPossible = FALSE;                    
  2037.                 }
  2038.             }
  2039.         }   
  2040.     }
  2041. #endif
  2042.     
  2043.     // We only really care about OPER_ENTERDIR when deleting and
  2044.     // OPER_DOFILE when renaming, but I guess the hook will figure it out
  2045.     
  2046.     if ((result == IDYES) &&
  2047.         ISDIRFINDDATA(*pfd) &&
  2048.         (oper==OPER_ENTERDIR || oper==OPER_DOFILE))
  2049.     {
  2050.         
  2051.         result = CallFileCopyHooks(pcs->hwndDlgParent, wFunc, pcs->fFlags,
  2052.             szSource, pfd->dwFileAttributes,
  2053.             szDest, pfdDest->dwFileAttributes);
  2054.     }
  2055.     
  2056.     if ((result != IDCANCEL) && (result != IDNO) && fSetProgress)
  2057.         SetProgressText(pcs, szSource, pszStatusDest);
  2058.     
  2059.     return result;
  2060. }
  2061. // return TRUE if they're the same file
  2062. // assumes that given two file specs, the short name will
  2063. // be identical (except case)
  2064. BOOL SameFile(LPTSTR pszSource, LPTSTR pszDest)
  2065. {
  2066.     TCHAR szShortSrc[MAX_PATH];
  2067.     TCHAR szShortDest[MAX_PATH];
  2068.     
  2069.     GetShortPathName(pszSource, szShortSrc, ARRAYSIZE(szShortSrc));
  2070.     GetShortPathName(pszDest, szShortDest, ARRAYSIZE(szShortDest));
  2071.     return !lstrcmpi(szShortSrc, szShortDest);
  2072. }
  2073. // make sure we aren't operating on the current dir to avoid
  2074. // ERROR_CURRENT_DIRECTORY kinda errors
  2075. void AvoidCurrentDirectory(LPCTSTR p)
  2076. {
  2077.     TCHAR szTemp[MAX_PATH];
  2078.     
  2079.     GetCurrentDirectory(ARRAYSIZE(szTemp), szTemp);
  2080.     if (lstrcmpi(szTemp, p) == 0)
  2081.     {
  2082.         DebugMsg(TF_DEBUGCOPY, TEXT("operating on current dir(%s), cd .."), p);
  2083.         PathRemoveFileSpec(szTemp);
  2084.         SetCurrentDirectory(szTemp);
  2085.     }
  2086. }
  2087. // this resolves short/long name collisions such as moving
  2088. // "NewFolde" onto a dir with "New Folder" whose short name is "NEWFOLDE"
  2089. //
  2090. // we resolve this by renaming "New Folder" to a unique short name (like TMP1)
  2091. //
  2092. // making a temporary file of name "NEWFOLDE"
  2093. //
  2094. // renaming TMP1 back to "New Folder"  (at which point it will have a new short
  2095. // name like "NEWFOL~1"
  2096. // BUGBUG, it'd be faster if we didn't make the temporary file, but that
  2097. // would require that we rename the file back to the long name at the
  2098. // end of the operation.. which would mean we'd need to queue them all up..
  2099. // too much for right now.
  2100. BOOL ResolveShortNameCollisions(LPCTSTR lpszDest, WIN32_FIND_DATA *pfd)
  2101. {
  2102.     BOOL fRet = FALSE;
  2103.     
  2104.     // first verify that we're in the name collision.
  2105.     // we are if lpszDest is the same as the pfd's short name which is different
  2106.     // than it's long name.
  2107.     
  2108.     if (!lstrcmpi(PathFindFileName(lpszDest), pfd->cAlternateFileName) &&
  2109.         lstrcmpi(pfd->cAlternateFileName, pfd->cFileName))
  2110.     {
  2111.         // yes... do the renaming
  2112.         TCHAR szTemp[MAX_PATH];
  2113.         TCHAR szLongName[MAX_PATH];
  2114.         
  2115.         lstrcpy(szTemp, lpszDest);
  2116.         PathRemoveFileSpec(szTemp);
  2117.         
  2118.         // build the original long name
  2119.         lstrcpy(szLongName, szTemp);
  2120.         PathAppend(szLongName, pfd->cFileName);
  2121.         
  2122.         GetTempFileName(szTemp, c_szNULL, 1, szTemp);
  2123.         DebugMsg(TF_DEBUGCOPY, TEXT("Got %s as a temp file"), szTemp);
  2124.         // rename "New Folder" to "tmp1"
  2125.         if (Win32MoveFile(szLongName, szTemp, ISDIRFINDDATA(*pfd)))
  2126.         {
  2127.             // make a temporary "NewFolde"
  2128.             fRet = CreateWriteCloseFile(NULL, lpszDest, NULL, 0);
  2129.             ASSERT(fRet);
  2130.             
  2131.             // move it back...
  2132.             
  2133.             if (!Win32MoveFile(szTemp, szLongName, ISDIRFINDDATA(*pfd)))
  2134.             {
  2135.                 //
  2136.                 //  Can't move it back, so delete the empty dir and then
  2137.                 //  move it back.  Return FALSE to denote failure.
  2138.                 //
  2139.                 DeleteFile(lpszDest);
  2140.                 Win32MoveFile(szTemp, szLongName, ISDIRFINDDATA(*pfd));
  2141.                 fRet = FALSE;
  2142.             }
  2143.             else
  2144.             {
  2145.                 // send this out because we could have confused views
  2146.                 // with this swapping files around...  by the time they get the first
  2147.                 // move file notification, the temp file is likely gone
  2148.                 // so they could blow that off.. which would screw up the rest of this.
  2149.                 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, szLongName, NULL);
  2150.                 //
  2151.                 //  We've now created an empty dir entry of this name type.
  2152.                 //
  2153.                 Win32DeleteFile(lpszDest);
  2154.             }
  2155.             
  2156.             DebugMsg(TF_DEBUGCOPY, TEXT("ResolveShortNameCollision: %s = original, %s = destination,n %s = temp file, %d = return"), szLongName, lpszDest, szTemp, fRet);
  2157.         }
  2158.     }
  2159.     return fRet;
  2160. }
  2161. // return values.
  2162. //
  2163. // IDCANCEL = bail out of all operations
  2164. // IDNO = skip this one
  2165. // IDRETRY = try operation again
  2166. // IDUNKNOWN = this (collision) is not the problem
  2167. #define IDUNKNOWN IDOK
  2168. int CheckForRenameCollision(COPY_STATE *pcs, UINT oper, LPTSTR pszSource, LPTSTR pszDest,
  2169.                             WIN32_FIND_DATA *pfdDest, WIN32_FIND_DATA* pfd)
  2170. {
  2171.     int iRet = IDUNKNOWN;
  2172.     
  2173.     ASSERT((pcs->lpfo->wFunc != FO_DELETE) && (oper != OPER_LEAVEDIR));
  2174.     
  2175.     
  2176.     /* Check to see if we are overwriting an existing file or
  2177.     directory.  If so, better confirm */
  2178.     
  2179.     // we only have a potential for collision of we're at the top level or doing a merge
  2180.     if ((pcs->fMerge || DTNIsRootNode(pcs->dth.pdtnCurrent)) &&
  2181.         (oper == OPER_DOFILE) ||
  2182.         ((oper == OPER_ENTERDIR) && (pcs->fFlags & FOF_RENAMEONCOLLISION)))
  2183.     {
  2184.         HANDLE  hfindT;
  2185.         
  2186.         // REVIEW this slows things down checking for the dest file
  2187.         if ((hfindT = FindFirstFile(pszDest, pfdDest)) != INVALID_HANDLE_VALUE)
  2188.         {
  2189.             FindClose(hfindT);
  2190.             
  2191.             iRet = IDCANCEL;
  2192.             
  2193.             if (pcs->lpfo->wFunc != FO_RENAME || !SameFile(pszSource, pszDest))
  2194.             {
  2195.                 
  2196.                 if (!ResolveShortNameCollisions(pszDest, pfdDest))
  2197.                 {
  2198.                     if (pcs->fFlags & FOF_RENAMEONCOLLISION)
  2199.                     {
  2200.                         //  The client wants us to generate a new name for the
  2201.                         //  source file to avoid a collision at the destination
  2202.                         //  dir.  Must also update the current queue and the
  2203.                         //  copy root.
  2204.                         _HandleRename(pszSource, pszDest, pcs->fFlags, pcs);
  2205.                         iRet = IDRETRY;
  2206.                     }
  2207.                     else
  2208.                     {
  2209.                         int result;
  2210.                         
  2211.                         if (pcs->lpfo->wFunc == FO_RENAME)
  2212.                         {
  2213.                             return ERROR_ALREADY_EXISTS;
  2214.                         }
  2215.                         
  2216.                         if (IsWindowsFile(pszDest))
  2217.                         {
  2218.                             CopyError(pcs, pszSource, pszDest, DE_WINDOWSFILE | ERRORONDEST, pcs->lpfo->wFunc, oper);
  2219.                             iRet = IDNO;
  2220.                         }
  2221.                         // REVIEW, if the destination file we are copying over
  2222.                         // is actually a directory we are doomed.  we can
  2223.                         // try to remove the dir but that will fail if there
  2224.                         // are files there.  we probably need a special error message
  2225.                         // for this case.
  2226.                         
  2227.                         result = CachedConfirmFileOp(pcs->hwndDlgParent, pcs, &pcs->cd, pcs->nSourceFiles, !DTNIsRootNode(pcs->dth.pdtnCurrent), CONFIRM_REPLACE_FILE, pszSource, pfd, pszDest, pfdDest, NULL);
  2228.                         switch (result)
  2229.                         {
  2230.                         case IDYES:
  2231.                             
  2232.                             if ((pcs->lpfo->wFunc == FO_MOVE) && (PathIsSameRoot(pszSource, pszDest)))
  2233.                             {
  2234.                                 int ret;
  2235.                                 // For FO_MOVE we need to delete the
  2236.                                 // destination first.  Do that now.
  2237.                                 
  2238.                                 // bugbug, this replace options should be undable
  2239.                                 ret = Win32DeleteFile(pszDest) ? 0 : GetLastError();
  2240.                                 
  2241.                                 if (ret)
  2242.                                 {
  2243.                                     ret |= ERRORONDEST;
  2244.                                     result = ret;
  2245.                                 }
  2246.                             }
  2247.                             if (pcs->lpua)
  2248.                                 FOUndo_Release(pcs->lpua);
  2249.                             iRet = IDRETRY;
  2250.                             break;
  2251.                             
  2252.                         case IDNO:
  2253.                         case IDCANCEL:
  2254.                             pcs->lpfo->fAnyOperationsAborted = TRUE;
  2255.                             iRet = result;
  2256.                             break;
  2257.                             
  2258.                         default:
  2259.                             iRet = result;
  2260.                             break;
  2261.                         }
  2262.                     }
  2263.                 }
  2264.                 else
  2265.                 {
  2266.                     iRet = IDRETRY;
  2267.                 }
  2268.             }
  2269.         }
  2270.     }
  2271.     
  2272.     return iRet;
  2273. }
  2274. int LeaveDir_Delete(COPY_STATE *pcs, LPTSTR pszSource)
  2275. {
  2276.     int ret;
  2277.     if (PathIsRoot(pszSource))
  2278.         return 0;
  2279.     
  2280.     AvoidCurrentDirectory(pszSource);
  2281.     
  2282.     // We already confirmed the delete at MKDIR time, so attempt
  2283.     // to delete the directory
  2284.     
  2285.     ret = Win32RemoveDirectory(pszSource) ? 0 : GetLastError();
  2286.     if (!ret)
  2287.     {
  2288.         FOUndo_FileReallyDeleted(pszSource);
  2289.     }
  2290.     return ret;
  2291. }
  2292. int EnterDir_Copy(COPY_STATE* pcs, LPTSTR pszSource, LPTSTR pszDest,
  2293.                   WIN32_FIND_DATA *pfd, WIN32_FIND_DATA * pfdDest, BOOL fRenameTried)
  2294. {
  2295.     int ret;
  2296.     int result;
  2297.     DWORD dwSourceAttrib = pfd->dwFileAttributes;
  2298.     
  2299.     // Whenever we enter a directory, we need to reset the bStreamLossPossible flag,
  2300.     // since we could have stepped out from an NTFS->NTFS to NTFS->FAT scenario via
  2301.     // a junction point
  2302.     pcs->bStreamLossPossible = TRUE;
  2303. #ifdef WINNT
  2304.     // SHMoveFile restricts the based on path length. To be consistent, we make the same
  2305.     // restricton on Copy directory also.
  2306.     if(IsDirPathTooLongForCreateDir(pszDest))
  2307.         ret = ERROR_FILENAME_EXCED_RANGE;
  2308.     else
  2309.     {
  2310.         ret = CreateDirectoryEx(pszSource, pszDest, NULL);
  2311.         
  2312.         if (ret)
  2313.         {
  2314.             SHChangeNotify(SHCNE_MKDIR, SHCNF_PATH, pszDest, NULL);
  2315.             ret = 0;
  2316.         }
  2317.         else
  2318.         {
  2319.             // Save the LastError value
  2320.             int iRetTmp = GetLastError();
  2321.             BOOL fCD = FALSE;
  2322.             if (ERROR_ALREADY_EXISTS != iRetTmp)
  2323.             {
  2324.                 if (DE_INVFUNCTION == iRetTmp)
  2325.                 {
  2326.                     //
  2327.                     // We hit this path when copying an offline directory with
  2328.                     // extended attributes (EAs).  The CSC cache doesn't support
  2329.                     // EAs so CreateDirectoryEx fails with GLE == DE_INVFUNCTION.
  2330.                     // 
  2331.                     fCD = Win32CreateDirectory(pszDest, NULL);
  2332.                     if (!fCD)
  2333.                     {
  2334.                         //
  2335.                         // Win32CreateDirectory failed.  
  2336.                         // This error is what we want to report.
  2337.                         //
  2338.                         iRetTmp = ret = GetLastError();
  2339.                     }
  2340.                 }
  2341.                 // Did we hit a junction point (reparse point)?
  2342.                 else if ((0xFFFFFFFF != dwSourceAttrib) && (dwSourceAttrib & FILE_ATTRIBUTE_REPARSE_POINT))
  2343.                 {
  2344.                     // Yes, most probably we've copied something from NTFS 5 to FAT
  2345.                     // or some other FS that does not support FILE_ATTRIBUTES_REPARSE_POINT
  2346.                     int iGLE = 0;
  2347.                     fCD = Win32CreateDirectory(pszDest, NULL);
  2348.                     // Did we failed?
  2349.                     if (!fCD)
  2350.                     {
  2351.                         // Yes
  2352.                         iGLE = GetLastError();
  2353.                         // For the FILE_ATTRIBUTE_REPARSE_POINT case, the fct fails but still the dir
  2354.                         // is created, so we get ERROR_ALREADY_EXISTS.
  2355.                         if (ERROR_ALREADY_EXISTS == iGLE)
  2356.                         {
  2357.                             // This is expected as mentionned above, set it to success
  2358.                             ret = 0;
  2359.                             dwSourceAttrib &= ~FILE_ATTRIBUTE_REPARSE_POINT;
  2360.                         }
  2361.                     }
  2362.                     else
  2363.                     {
  2364.                         // No, remove this attribute, since it's not supported
  2365.                         ret = 0;
  2366.                         dwSourceAttrib &= ~FILE_ATTRIBUTE_REPARSE_POINT;
  2367.                     }
  2368.                 }
  2369.                 else
  2370.                 {
  2371.                     ret = iRetTmp;
  2372.                 }
  2373.             }
  2374.             else
  2375.             {
  2376.                 ret = iRetTmp;
  2377.             }
  2378.             if (!fCD && (0 != ret))
  2379.             {
  2380.                 // We failed with an error that we do not handle, set ret to the LastError of
  2381.                 // the initial operation to mimic the behavior of before this change.
  2382.                 ret = iRetTmp;
  2383.             }
  2384.         }
  2385.     }
  2386.     
  2387.     // Massage goofy NT error code in the source == dest case
  2388.     if (ret == ERROR_INVALID_NAME)
  2389.         ret = ERROR_ALREADY_EXISTS;
  2390. #else
  2391.     ret = SHCreateDirectory(pcs->hwndDlgParent, pszDest);
  2392. #endif
  2393.     
  2394.     switch (ret) {
  2395.     case 0:     // successful folder creation (or it already exists)
  2396.         // propogate the attributes (if there are any)
  2397.         SetDirAttributes(pszDest, dwSourceAttrib);
  2398.         
  2399. #ifdef WINNT
  2400.         //
  2401.         //  we should set the security ACLs here on NT
  2402.         //  we ignore any kind of failure though, is that OK?
  2403.         //
  2404.         CopyFileSecurity(pszSource, pszDest);
  2405. #endif WINNT
  2406.         
  2407.         // add to the undo atom
  2408.         if (pcs->lpua)
  2409.         {
  2410.             if (DTNIsRootNode(pcs->dth.pdtnCurrent) && !DTNIsConnected(pcs->dth.pdtnCurrent))
  2411.                 FOUndo_AddInfo(pcs->lpua, pszSource, pszDest, 0);
  2412.         }
  2413.         break;
  2414.         
  2415.     case ERROR_ALREADY_EXISTS:
  2416.     case ERROR_DISK_FULL:
  2417.     case ERROR_ACCESS_DENIED:
  2418.         {
  2419.             DWORD dwFileAttributes;
  2420.             if (!fRenameTried)
  2421.             {
  2422.                 int result = CheckForRenameCollision(pcs, OPER_ENTERDIR, pszSource, pszDest, pfdDest, pfd);
  2423.                 switch (result)
  2424.                 {
  2425.                 case IDUNKNOWN:
  2426.                     break;
  2427.                     
  2428.                 case IDRETRY:
  2429.                     return EnterDir_Copy(pcs, pszSource, pszDest, pfd, pfdDest, TRUE);
  2430.                     
  2431.                 case IDCANCEL:
  2432.                     pcs->bAbort = TRUE;
  2433.                     return result;
  2434.                     
  2435.                 case IDNO:
  2436.                     return result;
  2437.                     
  2438.                 default:
  2439.                     return result;
  2440.                 }
  2441.             }
  2442.             
  2443.             dwFileAttributes = GetFileAttributes(pszDest);
  2444.             
  2445.             if (dwFileAttributes == (DWORD)-1)
  2446.             {
  2447.                 // The dir does not exist, so it looks like a problem
  2448.                 // with a read-only drive or disk full
  2449.                 
  2450.                 if (IsRemovableDrive(DRIVEID(pszDest)) && !PathIsSameRoot(pszDest, pszSource) &&
  2451.                     (ret == ERROR_DISK_FULL))
  2452.                 {
  2453.                     ret = CopyMoveRetry(pcs, pszDest, DE_NODISKSPACE, 0);
  2454.                     if (!ret)
  2455.                     {
  2456.                         return EnterDir_Copy(pcs, pszSource, pszDest, pfd, pfdDest, fRenameTried);
  2457.                     }
  2458.                     else
  2459.                     {
  2460.                         pcs->bAbort = TRUE;
  2461.                         return ret;
  2462.                     }
  2463.                 }
  2464.                 else
  2465.                 {
  2466.                     CopyError(pcs, pszSource, pszDest, ERROR_ACCESS_DENIED | ERRORONDEST, FO_COPY, OPER_DOFILE);
  2467.                     pcs->bAbort = TRUE;
  2468.                     return ret;
  2469.                 }
  2470.                 
  2471.             }
  2472.             else if (!(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  2473.             {
  2474.                 // A file with this name already exists
  2475.                 CopyError(pcs, pszSource, pszDest, DE_FLDDESTISFILE | ERRORONDEST, FO_COPY, OPER_DOFILE);
  2476.                 pcs->bAbort = TRUE;
  2477.                 return ret;
  2478.             }
  2479.             
  2480.             result = CachedConfirmFileOp(pcs->hwndDlgParent, pcs, &pcs->cd, pcs->nSourceFiles, !DTNIsRootNode(pcs->dth.pdtnCurrent), CONFIRM_REPLACE_FOLDER, pszSource, pfd, pszDest, pfdDest, NULL);
  2481.             switch (result) {
  2482.             case IDYES:
  2483.                 ret = 0;    // convert to no error
  2484.                 pcs->fMerge = TRUE;
  2485.                 if (pcs->lpua)
  2486.                     FOUndo_Release(pcs->lpua);
  2487.                 break;
  2488.                 
  2489.             case IDNO:
  2490.                 DTAbortCurrentNode(&pcs->dth);    // so we don't recurse down this folder
  2491.                 pcs->lpfo->fAnyOperationsAborted = TRUE;
  2492.                 ret = IDNO;  // Don't put up error message on this one...
  2493.                 // Since the end-user cancelled the copy operation on this folder, we can cancel the 
  2494.                 // copy operation on the corresponding connected file too!
  2495.                 if (DTNIsConnectOrigin(pcs->dth.pdtnCurrent))
  2496.                     pcs->dth.pdtnCurrent->pdtnConnected->fDummy = TRUE;
  2497.                 break;
  2498.                 
  2499.             case IDCANCEL:
  2500.                 pcs->lpfo->fAnyOperationsAborted = TRUE;
  2501.                 pcs->bAbort = TRUE;
  2502.                 // Since the end-user cancelled the copy operation on this folder, we can cancel the 
  2503.                 // copy operation on the corresponding connected file too!
  2504.                 if (DTNIsConnectOrigin(pcs->dth.pdtnCurrent))
  2505.                     pcs->dth.pdtnCurrent->pdtnConnected->fDummy = TRUE;
  2506.                 break;
  2507.                 
  2508.             default:
  2509.                 result = ret;
  2510.                 break;
  2511.             }
  2512.             break;
  2513.         }
  2514.         
  2515.     case ERROR_CANCELLED:
  2516.         pcs->bAbort = TRUE;
  2517.         break;
  2518.     case ERROR_FILENAME_EXCED_RANGE:
  2519.         DTAbortCurrentNode(&pcs->dth);    // so we don't recurse down this folder
  2520.         break;
  2521.         
  2522.     default:    // ret != 0 (dos error code)
  2523.         ret |= ERRORONDEST;
  2524.         break;
  2525.     }
  2526.     
  2527.     return ret;
  2528. }
  2529. int EnterDir_Move(COPY_STATE* pcs, LPTSTR pszSource, LPTSTR pszDest,
  2530.                   WIN32_FIND_DATA *pfd, WIN32_FIND_DATA * pfdDest, BOOL fRenameTried)
  2531. {
  2532.     int ret;