Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3719 lines
124 KiB

  1. //*************************************************************
  2. //
  3. // Copyright (c) Microsoft Corporation 1998
  4. // All rights reserved
  5. //
  6. // util.cxx
  7. //
  8. //*************************************************************
  9. #include "fdeploy.hxx"
  10. #define PCOMMON_IMPL
  11. #include "pcommon.h"
  12. WCHAR* NTPrivs[] = {
  13. SE_TAKE_OWNERSHIP_NAME, //we only need take ownership privileges
  14. SE_RESTORE_NAME, //we only need to be able to assign owners
  15. L"\0"
  16. };
  17. //+--------------------------------------------------------------------------
  18. //
  19. // Member: CCopyFailData::CCopyFailData
  20. //
  21. // Synopsis: constructor for the object that contains data about
  22. // copy failures.
  23. //
  24. // Arguments: none.
  25. //
  26. // Returns: nothing.
  27. //
  28. // History: 1/25/2000 RahulTh created
  29. //
  30. // Notes:
  31. //
  32. //---------------------------------------------------------------------------
  33. CCopyFailData::CCopyFailData () : m_bCopyFailed (FALSE), m_dwSourceBufLen (0),
  34. m_pwszSourceName (NULL), m_dwDestBufLen (0),
  35. m_pwszDestName (NULL)
  36. {
  37. }
  38. //+--------------------------------------------------------------------------
  39. //
  40. // Member: CCopyFailData::~CCopyFailData
  41. //
  42. // Synopsis: destructor for the object that contains data about the last
  43. // copy failure
  44. //
  45. // Arguments: none.
  46. //
  47. // Returns: nothing.
  48. //
  49. // History: 1/25/2000 RahulTh created
  50. //
  51. // Notes:
  52. //
  53. //---------------------------------------------------------------------------
  54. CCopyFailData::~CCopyFailData ()
  55. {
  56. if (m_dwSourceBufLen)
  57. delete [] m_pwszSourceName;
  58. if (m_dwDestBufLen)
  59. delete [] m_pwszDestName;
  60. }
  61. //+--------------------------------------------------------------------------
  62. //
  63. // Member: CCopyFailData::RegisterFailure
  64. //
  65. // Synopsis: registers information about a failed copy.
  66. //
  67. // Arguments: [in] pwszSource : the source file for the copy.
  68. // [in] pwszDest : the destination file for the copy.
  69. //
  70. // Returns: ERROR_SUCCESS : on succesful registration.
  71. // an error code otherwise.
  72. //
  73. // History: 1/25/2000 RahulTh created
  74. //
  75. // Notes: if another failure has already been registered in this
  76. // object, it is not overwritten with the new info. We only
  77. // keep track of the first failure. Since folder redirection
  78. // anyway bails out on the first copy failure, we don't really
  79. // expect this function to be called more than once.
  80. //
  81. //---------------------------------------------------------------------------
  82. DWORD CCopyFailData::RegisterFailure (LPCTSTR pwszSource, LPCTSTR pwszDest)
  83. {
  84. DWORD dwStatus = ERROR_SUCCESS;
  85. HRESULT hr = S_OK;
  86. DWORD dwFromLen = 0;
  87. DWORD dwToLen = 0;
  88. //bail out if another copy failure has already been registered.
  89. if (m_bCopyFailed)
  90. return dwStatus;
  91. //first copy the source info.
  92. dwFromLen = wcslen (pwszSource);
  93. if (dwFromLen >= m_dwSourceBufLen)
  94. {
  95. //we need a bigger buffer.
  96. delete [] m_pwszSourceName;
  97. m_dwSourceBufLen = 0;
  98. m_pwszSourceName = new WCHAR [dwFromLen + 1];
  99. if (m_pwszSourceName)
  100. m_dwSourceBufLen = dwFromLen + 1;
  101. else
  102. dwStatus = ERROR_OUTOFMEMORY;
  103. }
  104. if (ERROR_SUCCESS == dwStatus)
  105. (void) StringCchCopy(m_pwszSourceName, m_dwSourceBufLen, pwszSource);
  106. //now copy the destination info.
  107. if (ERROR_SUCCESS == dwStatus)
  108. {
  109. dwToLen = wcslen (pwszDest);
  110. if (dwToLen >= m_dwDestBufLen)
  111. {
  112. //we need a bigger buffer
  113. delete [] m_pwszDestName;
  114. m_dwDestBufLen = 0;
  115. m_pwszDestName = new WCHAR [dwToLen + 1];
  116. if (m_pwszDestName)
  117. m_dwDestBufLen = dwToLen + 1;
  118. else
  119. dwStatus = ERROR_OUTOFMEMORY;
  120. }
  121. }
  122. if (ERROR_SUCCESS == dwStatus)
  123. (void) StringCchCopy(m_pwszDestName, m_dwDestBufLen, pwszDest);
  124. //register the fact that the copy fail data has been
  125. //successfully incorporated into the object
  126. if (ERROR_SUCCESS == dwStatus)
  127. m_bCopyFailed = TRUE;
  128. return dwStatus;
  129. }
  130. //+--------------------------------------------------------------------------
  131. //
  132. // Member: CCopyFailData::IsCopyFailure
  133. //
  134. // Synopsis: indicates if copy failure data exists within the object.
  135. //
  136. // Arguments: none.
  137. //
  138. // Returns: TRUE / FALSE : self-explanatory
  139. //
  140. // History: 1/25/2000 RahulTh created
  141. //
  142. // Notes:
  143. //
  144. //---------------------------------------------------------------------------
  145. BOOL CCopyFailData::IsCopyFailure (void)
  146. {
  147. return m_bCopyFailed;
  148. }
  149. //+--------------------------------------------------------------------------
  150. //
  151. // Member: CCopyFailData::GetSourceName
  152. //
  153. // Synopsis: gets the name of the source file of the failed copy.
  154. //
  155. // Arguments: none.
  156. //
  157. // Returns: name of the source file of the failed copy.
  158. //
  159. // History: 1/25/2000 RahulTh created
  160. //
  161. // Notes: returns NULL if the data does not exist or if a
  162. // copy failure has not been incorporated into the object
  163. //
  164. //---------------------------------------------------------------------------
  165. LPCTSTR CCopyFailData::GetSourceName (void)
  166. {
  167. if (! m_bCopyFailed)
  168. return NULL;
  169. if (! m_dwSourceBufLen)
  170. return NULL;
  171. return m_pwszSourceName;
  172. }
  173. //+--------------------------------------------------------------------------
  174. //
  175. // Member: CCopyFailData::GetDestName
  176. //
  177. // Synopsis: gets the name of the destination file of the failed copy.
  178. //
  179. // Arguments: none.
  180. //
  181. // Returns: name of the destination file of the failed copy.
  182. //
  183. // History: 1/25/2000 RahulTh created
  184. //
  185. // Notes: returns NULL if the data does not exist or if a copy failure
  186. // has not been incorporated into the object.
  187. //
  188. //---------------------------------------------------------------------------
  189. LPCTSTR CCopyFailData::GetDestName (void)
  190. {
  191. if (! m_bCopyFailed)
  192. return NULL;
  193. if (! m_dwDestBufLen)
  194. return NULL;
  195. return m_pwszDestName;
  196. }
  197. //+--------------------------------------------------------------------------
  198. //
  199. // Function: IsOnNTFS
  200. //
  201. // Synopsis: this function determines whether a given file/folder lies
  202. // on an NTFS volume or not.
  203. //
  204. // Arguments: [in] pwszPath : the full pathname of the file.
  205. //
  206. // Returns: ERROR_SUCCESS : if it is on NTFS
  207. // ERROR_NO_SECURITY_ON_OBJECT : if it is on FAT
  208. // other error codes if something goes wrong
  209. //
  210. // History: 9/4/1998 RahulTh created
  211. //
  212. // Notes:
  213. // 1. the full pathname is required in order to determine
  214. // if the file/folder is on an NTFS volume
  215. // 2. If the file/folder lies on a network share, then the
  216. // share must be online when this function is executed.
  217. // if it is offline and CSC is turned on, then even NTFS
  218. // volumes will show up as FAT volumes.
  219. //
  220. //---------------------------------------------------------------------------
  221. DWORD IsOnNTFS (const WCHAR* pwszPath)
  222. {
  223. WCHAR* szName = 0;
  224. DWORD Status;
  225. size_t len;
  226. BOOL bAddSlash = FALSE;
  227. BOOL bStatus;
  228. DWORD dwFlags;
  229. WCHAR* szLastSlash;
  230. WCHAR* pwszSuccess = NULL;
  231. // Basic sanity checks
  232. if (NULL == pwszPath || L'\0' == *pwszPath)
  233. {
  234. return ERROR_BAD_PATHNAME;
  235. }
  236. //GetVolumeInformation requires its 1st argument to be terminated by a slash
  237. //so we first make sure that this is the case.
  238. len = wcslen (pwszPath);
  239. if ('\\' != pwszPath[len-1])
  240. {
  241. len++;
  242. bAddSlash = TRUE;
  243. }
  244. __try {
  245. szName = (WCHAR*) alloca ((len + 1)*sizeof(WCHAR));
  246. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  247. _resetstkoflw();
  248. return ERROR_OUTOFMEMORY;
  249. }
  250. //obtain the absolute path
  251. pwszSuccess = _wfullpath (szName, pwszPath, len + 1);
  252. if (!pwszSuccess)
  253. {
  254. return ERROR_BAD_PATHNAME; //_wfullpath will very rarely fail, but
  255. //never hurts to take precautions
  256. }
  257. if (bAddSlash)
  258. {
  259. szName[len] = '\0';
  260. szName[len-1] = '\\';
  261. }
  262. //now our path name is terminated by a slash, and we have the absolute path
  263. //too, so we don't have to worry about errors generating from weird paths
  264. //like \\server\share\hello\..\.\ etc...
  265. for (szLastSlash = szName + len - 1;;)
  266. {
  267. bStatus = GetVolumeInformation (szName, 0, 0, 0, 0, &dwFlags, 0, 0);
  268. if (!bStatus)
  269. {
  270. Status = GetLastError();
  271. if (ERROR_DIR_NOT_ROOT != Status)
  272. {
  273. return Status;
  274. }
  275. //GetVolumeInformation requires that the path provided to it be
  276. //the root of the volume. So if we are here, it means that the
  277. //function returned ERROR_DIR_NOT_ROOT. So we remove the last
  278. //component from the path and try with the smaller path. We repeat
  279. //this until we either succeed or end up with no path in which
  280. //case we return ERROR_INVALID_NAME
  281. *szLastSlash = '\0';
  282. szLastSlash = wcsrchr (szName, '\\');
  283. if (NULL == szLastSlash)
  284. return ERROR_INVALID_NAME; //we have run out of components
  285. else
  286. szLastSlash[1] = '\0'; //get rid of the last component.
  287. }
  288. else
  289. break;
  290. }
  291. if (dwFlags & FS_PERSISTENT_ACLS)
  292. return ERROR_SUCCESS; //NTFS supports persistent ACLs
  293. //if we are here, then GetVolumeInformation succeeded, but the volume
  294. //does not support persistent ACLs. So it must be a FAT volume.
  295. return ERROR_NO_SECURITY_ON_OBJECT;
  296. }
  297. //+--------------------------------------------------------------------------
  298. //
  299. // Function: RestrictMyDocsRedirection
  300. //
  301. // Synopsis: Disables/Enables the ability of users to redirect the
  302. // "My Documents" folder
  303. //
  304. // Arguments: [in] fRestrict : Disable if TRUE, Enable if FALSE
  305. //
  306. // Returns: ERROR_SUCCESS : on success
  307. // *OR* other Win32 error codes based on the error that occurred
  308. //
  309. // History: 8/25/1998 RahulTh created
  310. //
  311. // Notes:
  312. //
  313. //---------------------------------------------------------------------------
  314. DWORD RestrictMyDocsRedirection (
  315. HANDLE hToken,
  316. HKEY hKeyRoot,
  317. BOOL fRestrict
  318. )
  319. {
  320. HKEY hkRoot;
  321. HANDLE hUserToken;
  322. HKEY hkPolicies;
  323. HKEY hkExplorer;
  324. DWORD Status;
  325. hkRoot = hKeyRoot;
  326. hUserToken = hToken;
  327. //
  328. // This policies key is secured, so we must do this as LocalSystem.
  329. //
  330. RevertToSelf();
  331. Status = RegCreateKeyEx(
  332. hkRoot,
  333. L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies",
  334. 0,
  335. NULL,
  336. REG_OPTION_NON_VOLATILE,
  337. KEY_READ | KEY_WRITE,
  338. NULL,
  339. &hkPolicies,
  340. NULL );
  341. if ( ERROR_SUCCESS == Status )
  342. {
  343. Status = RegCreateKeyEx(
  344. hkPolicies,
  345. L"Explorer",
  346. 0,
  347. NULL,
  348. REG_OPTION_NON_VOLATILE,
  349. KEY_READ | KEY_WRITE,
  350. NULL,
  351. &hkExplorer,
  352. NULL );
  353. RegCloseKey( hkPolicies );
  354. }
  355. if ( ERROR_SUCCESS == Status )
  356. {
  357. if ( fRestrict )
  358. {
  359. Status = RegSetValueEx(
  360. hkExplorer,
  361. L"DisablePersonalDirChange",
  362. 0,
  363. REG_DWORD,
  364. (PBYTE) &fRestrict,
  365. sizeof(fRestrict) );
  366. }
  367. else
  368. {
  369. RegDeleteValue( hkExplorer, L"DisablePersonalDirChange" );
  370. }
  371. RegCloseKey( hkExplorer );
  372. }
  373. //now that the keys have been modified, return to impersonation.
  374. if (!ImpersonateLoggedOnUser( hUserToken ))
  375. Status = GetLastError();
  376. if ( ERROR_SUCCESS == Status )
  377. {
  378. if ( fRestrict )
  379. {
  380. DebugMsg((DM_VERBOSE, IDS_MYDOCSRESTRICT_ON));
  381. }
  382. else
  383. {
  384. DebugMsg((DM_VERBOSE, IDS_MYDOCSRESTRICT_OFF));
  385. }
  386. }
  387. return Status;
  388. }
  389. //+--------------------------------------------------------------------------
  390. //
  391. // Function: GroupInList
  392. //
  393. // Synopsis: given a group sid in string format, and a list of group sids
  394. // in PTOKEN_GROUPS format, this function figures out if the
  395. // give sid belongs to that list
  396. //
  397. // Arguments: [in] pwszSid : the given sid in string format
  398. // [in] PTOKEN_GROUPS : a list of group sids
  399. //
  400. // Returns: TRUE : if the group is found in the list
  401. // FALSE : otherwise. FALSE is also returned if an error occurs
  402. //
  403. // History: 10/6/1998 RahulTh created
  404. //
  405. // Notes:
  406. //
  407. //---------------------------------------------------------------------------
  408. BOOL GroupInList (WCHAR * pwszSid, PTOKEN_GROUPS pGroups)
  409. {
  410. ASSERT (pwszSid);
  411. PSID pSid = 0;
  412. DWORD Status;
  413. BOOL bStatus = FALSE;
  414. DWORD i;
  415. //optimization for the basic case
  416. if (2 == CompareString( LOCALE_INVARIANT, NORM_IGNORECASE, pwszSid, -1, L"s-1-1-0", -1)) //if the user is an earthling
  417. {
  418. bStatus = TRUE;
  419. goto GroupInListEnd;
  420. }
  421. Status = AllocateAndInitSidFromString (pwszSid, &pSid);
  422. if (ERROR_SUCCESS != Status)
  423. goto GroupInListEnd;
  424. for (i = 0, bStatus = FALSE;
  425. i < pGroups->GroupCount && !bStatus;
  426. i++
  427. )
  428. {
  429. bStatus = RtlEqualSid (pSid, pGroups->Groups[i].Sid);
  430. }
  431. GroupInListEnd:
  432. if (pSid)
  433. RtlFreeSid (pSid);
  434. return bStatus;
  435. }
  436. //+--------------------------------------------------------------------------
  437. //
  438. // Function: AllocateAndInitSidFromString
  439. //
  440. // Synopsis: given the string representation of a SID, this function
  441. // allocate and initializes a SID which the string represents
  442. // For more information on the string representation of SIDs
  443. // refer to ntseapi.h & ntrtl.h
  444. //
  445. // Arguments: [in] lpszSidStr : the string representation of the SID
  446. // [out] pSID : the actual SID structure created from the string
  447. //
  448. // Returns: STATUS_SUCCESS : if the sid structure was successfully created
  449. // or an error code based on errors that might occur
  450. //
  451. // History: 10/6/1998 RahulTh created
  452. //
  453. // Notes:
  454. //
  455. //---------------------------------------------------------------------------
  456. NTSTATUS AllocateAndInitSidFromString (const WCHAR* lpszSidStr, PSID* ppSid)
  457. {
  458. WCHAR * pSidStr = 0;
  459. size_t sidStrSize = 0;
  460. WCHAR* pString = 0;
  461. NTSTATUS Status;
  462. WCHAR* pEnd = 0;
  463. int count;
  464. BYTE SubAuthCount;
  465. DWORD SubAuths[8] = {0, 0, 0, 0, 0, 0, 0, 0};
  466. ULONG n;
  467. SID_IDENTIFIER_AUTHORITY Auth;
  468. sidStrSize = lstrlen (lpszSidStr) + 1;
  469. pSidStr = new WCHAR [sidStrSize];
  470. if (!pSidStr)
  471. {
  472. Status = STATUS_NO_MEMORY;
  473. goto AllocAndInitSidFromStr_End;
  474. }
  475. (void) StringCchCopy(pSidStr, sidStrSize, lpszSidStr);
  476. pString = pSidStr;
  477. *ppSid = NULL;
  478. count = 0;
  479. do
  480. {
  481. pString = wcschr (pString, '-');
  482. if (NULL == pString)
  483. break;
  484. count++;
  485. pString++;
  486. } while (1);
  487. SubAuthCount = (BYTE)(count - 2);
  488. if (0 > SubAuthCount || 8 < SubAuthCount)
  489. {
  490. Status = ERROR_INVALID_SID;
  491. goto AllocAndInitSidFromStr_End;
  492. }
  493. pString = wcschr (pSidStr, L'-');
  494. pString++;
  495. pString = wcschr (pString, L'-'); //ignore the revision #
  496. pString++;
  497. pEnd = wcschr (pString, L'-'); //go to the beginning of subauths.
  498. if (NULL != pEnd) *pEnd = L'\0';
  499. Status = LoadSidAuthFromString (pString, &Auth);
  500. if (STATUS_SUCCESS != Status)
  501. goto AllocAndInitSidFromStr_End;
  502. for (count = 0; count < SubAuthCount; count++)
  503. {
  504. pString = pEnd + 1;
  505. pEnd = wcschr (pString, L'-');
  506. if (pEnd)
  507. *pEnd = L'\0';
  508. Status = GetIntFromUnicodeString (pString, 10, &n);
  509. if (STATUS_SUCCESS != Status)
  510. goto AllocAndInitSidFromStr_End;
  511. SubAuths[count] = n;
  512. }
  513. Status = RtlAllocateAndInitializeSid (&Auth, SubAuthCount,
  514. SubAuths[0], SubAuths[1], SubAuths[2],
  515. SubAuths[3], SubAuths[4], SubAuths[5],
  516. SubAuths[6], SubAuths[7], ppSid);
  517. AllocAndInitSidFromStr_End:
  518. if (pSidStr)
  519. delete [] pSidStr;
  520. return Status;
  521. }
  522. //+--------------------------------------------------------------------------
  523. //
  524. // Function: LoadSidAuthFromString
  525. //
  526. // Synopsis: given a string representing the SID authority (as it is
  527. // normally represented in string format, fill the SID_AUTH..
  528. // structure. For more details on the format of the string
  529. // representation of the sid authority, refer to ntseapi.h and
  530. // ntrtl.h
  531. //
  532. // Arguments: [in] pString : pointer to the unicode string
  533. // [out] pSidAuth : pointer to the SID_IDENTIFIER_AUTH.. that is
  534. // desired
  535. //
  536. // Returns: STATUS_SUCCESS if it succeeds
  537. // or an error code
  538. //
  539. // History: 9/29/1998 RahulTh created
  540. //
  541. // Notes:
  542. //
  543. //---------------------------------------------------------------------------
  544. NTSTATUS LoadSidAuthFromString (const WCHAR* pString,
  545. PSID_IDENTIFIER_AUTHORITY pSidAuth)
  546. {
  547. size_t len;
  548. int i;
  549. NTSTATUS Status;
  550. const ULONG LowByteMask = 0xFF;
  551. ULONG n;
  552. len = wcslen (pString);
  553. if (len > 2 && 'x' == pString[1])
  554. {
  555. //this is in hex.
  556. //so we must have exactly 14 characters
  557. //(2 each for each of the 6 bytes) + 2 for the leading 0x
  558. if (14 != len)
  559. {
  560. Status = ERROR_INVALID_SID;
  561. goto LoadAuthEnd;
  562. }
  563. for (i=0; i < 6; i++)
  564. {
  565. pString += 2; //we need to skip the leading 0x
  566. pSidAuth->Value[i] = (UCHAR)(((pString[0] - L'0') << 4) +
  567. (pString[1] - L'0'));
  568. }
  569. }
  570. else
  571. {
  572. //this is in decimal
  573. Status = GetIntFromUnicodeString (pString, 10, &n);
  574. if (Status != STATUS_SUCCESS)
  575. goto LoadAuthEnd;
  576. pSidAuth->Value[0] = pSidAuth->Value[1] = 0;
  577. for (i = 5; i >=2; i--, n>>=8)
  578. pSidAuth->Value[i] = (UCHAR)(n & LowByteMask);
  579. }
  580. Status = STATUS_SUCCESS;
  581. LoadAuthEnd:
  582. return Status;
  583. }
  584. //+--------------------------------------------------------------------------
  585. //
  586. // Function: GetIntfromUnicodeString
  587. //
  588. // Synopsis: converts a unicode string into an integer
  589. //
  590. // Arguments: [in] szNum : the number represented as a unicode string
  591. // [in] Base : the base in which the resultant int is desired
  592. // [out] pValue : pointer to the integer representation of the
  593. // number
  594. //
  595. // Returns: STATUS_SUCCESS if successful.
  596. // or some other error code
  597. //
  598. // History: 9/29/1998 RahulTh created
  599. //
  600. // Notes:
  601. //
  602. //---------------------------------------------------------------------------
  603. NTSTATUS GetIntFromUnicodeString (const WCHAR* szNum, ULONG Base, PULONG pValue)
  604. {
  605. WCHAR * pwszNumStr = 0;
  606. UNICODE_STRING StringW;
  607. size_t len;
  608. NTSTATUS Status;
  609. len = lstrlen (szNum);
  610. pwszNumStr = new WCHAR [len + 1];
  611. if (!pwszNumStr)
  612. {
  613. Status = STATUS_NO_MEMORY;
  614. goto GetNumEnd;
  615. }
  616. (void) StringCchCopy(pwszNumStr, len + 1, szNum);
  617. StringW.Length = len * sizeof(WCHAR);
  618. StringW.MaximumLength = StringW.Length + sizeof (WCHAR);
  619. StringW.Buffer = pwszNumStr;
  620. Status = RtlUnicodeStringToInteger (&StringW, Base, pValue);
  621. GetNumEnd:
  622. if (pwszNumStr)
  623. delete [] pwszNumStr;
  624. return Status;
  625. }
  626. //+--------------------------------------------------------------------------
  627. //
  628. // Function: CopyProgressRoutine
  629. //
  630. // Synopsis: this is a callback function for PrivCopyFileExW. It is used
  631. // to track errors that are considered fatal by the folder
  632. // redirection client. In many cases, PrivCopyFileExW will succeed
  633. // even if a certain operation like encryption fails. The only
  634. // way a calling prgram can find out about this is through this
  635. // callback function by looking at the reason for the callback.
  636. // currently, the only 3 reasons that are considered fatal for
  637. // redirection are PRIVCALLBACK_ENCRYPTION_FAILED and
  638. // PRIVCALLBACK_DACL_ACCESS_DENIED and
  639. // PRIVCALLBACK_OWNER_GROUP_ACCESS_DENIED. All other reasons will
  640. // either not occur, or can be safely ignored.
  641. //
  642. // The data passed via lpData is actually a pointer to a DWORD
  643. // that this callback function uses to store an error code if one
  644. // occurs.
  645. //
  646. // Arguments: see sdk help on CopyProgressRoutine
  647. //
  648. // Returns: see sdk help on CopyProgressRoutine
  649. //
  650. // History: 10/21/1998 RahulTh created
  651. //
  652. // Notes:
  653. //
  654. //---------------------------------------------------------------------------
  655. DWORD CALLBACK CopyProgressRoutine (
  656. LARGE_INTEGER TotalFileSize,
  657. LARGE_INTEGER TotalBytesTransferred,
  658. LARGE_INTEGER StreamSize,
  659. LARGE_INTEGER StreamBytesTransferred,
  660. DWORD dwStreamNumber,
  661. DWORD dwCallbackReason,
  662. HANDLE hSourceFile,
  663. HANDLE hDestinationFile,
  664. LPVOID lpData
  665. )
  666. {
  667. LPDWORD lpStatus = (LPDWORD) lpData;
  668. //an error condition has already been registered. No need to invoke
  669. //this callback again
  670. if (ERROR_SUCCESS != *lpStatus)
  671. return PROGRESS_QUIET;
  672. switch (dwCallbackReason)
  673. {
  674. case PRIVCALLBACK_ENCRYPTION_FAILED:
  675. *lpStatus = ERROR_ENCRYPTION_FAILED;
  676. return PROGRESS_CANCEL; //no point continuing. we have already failed
  677. case PRIVCALLBACK_DACL_ACCESS_DENIED:
  678. *lpStatus = ERROR_INVALID_SECURITY_DESCR;
  679. return PROGRESS_CANCEL; //same as above
  680. case PRIVCALLBACK_OWNER_GROUP_ACCESS_DENIED:
  681. *lpStatus = ERROR_INVALID_OWNER;
  682. return PROGRESS_CANCEL;
  683. default:
  684. return PROGRESS_CONTINUE; //all other conditions can be safely ignored
  685. }
  686. }
  687. //+--------------------------------------------------------------------------
  688. //
  689. // Function: FullFileCopyW
  690. //
  691. // Synopsis: this function makes use of the internal API PrivCopyFileExW
  692. // to copy not only the contents of a file but also metadata
  693. // like encryption, compression and DACL
  694. //
  695. // This function also imposes limits on the lengths of the files
  696. // that can be copied. Anything longer than MAX_PATH is disallowed
  697. // because the shell cannot gracefully handle such paths and we
  698. // don't want to create problems for the user by redirecting their
  699. // files to paths that explorer cannot get to.
  700. //
  701. // Arguments: [in] wszSource : the path of the source file.
  702. // [in] wszDest : the path of the destination file.
  703. // [in] bFailIfExists : whether the function should fail if
  704. // the destination exists
  705. //
  706. // Returns: ERROR_SUCCESS : if successful
  707. // ERROR_ENCRYPTION_FAILED : if the source is encrypted and
  708. // the destination cannot be encrypted
  709. // ERROR_INVALID_SECURITY_DESCR: if the DACL of the source cannot
  710. // be copied over to the destination
  711. // ERROR_FILE_EXISTS / ERROR_ALREADY_EXISTS : if the destination
  712. // exists and bFailIfExists is TRUE
  713. // ERROR_INVALID_OWNER : if the owner info. cannot be copied
  714. // or other error codes.
  715. //
  716. // ERROR_FILENAME_EXCED_RANGE : if the filename is longer than MAX_PATH.
  717. //
  718. // History: 10/22/1998 RahulTh created
  719. // 12/13/2000 RahulTh added length limitations
  720. //
  721. // Notes:
  722. //
  723. //---------------------------------------------------------------------------
  724. DWORD FullFileCopyW (
  725. const WCHAR* wszSource,
  726. const WCHAR* wszDest,
  727. BOOL bFailIfExists
  728. )
  729. {
  730. DWORD Status = ERROR_SUCCESS;
  731. DWORD dwFlags = PRIVCOPY_FILE_METADATA | PRIVCOPY_FILE_OWNER_GROUP;
  732. BOOL bCancel = FALSE;
  733. BOOL bStatus;
  734. int lenSource = 0;
  735. int lenDest = 0;
  736. if (bFailIfExists)
  737. dwFlags |= COPY_FILE_FAIL_IF_EXISTS;
  738. if ( wszSource != 0 )
  739. lenSource = wcslen (wszSource);
  740. if ( wszDest != 0 )
  741. lenDest = wcslen (wszDest);
  742. //
  743. // Prevent copying of files longer than MAX_PATH characters. This limitation
  744. // needs to be added because the shell cannot handle paths longer than
  745. // MAX_PATH gracefully and we don't want to land the users into trouble by
  746. // creating files / folder that they cannot get to via explorer.
  747. //
  748. if (lenDest >= MAX_PATH || lenSource >= MAX_PATH)
  749. return ERROR_FILENAME_EXCED_RANGE;
  750. bStatus = PrivCopyFileExW (wszSource, wszDest,
  751. (LPPROGRESS_ROUTINE) CopyProgressRoutine,
  752. (LPVOID) &Status,
  753. &bCancel,
  754. dwFlags
  755. );
  756. //get the last error if PrivCopyFileExW failed
  757. //and the callback function has not already registered a fatal error
  758. if ((ERROR_SUCCESS == Status) && (!bStatus))
  759. Status = GetLastError();
  760. return Status;
  761. }
  762. //+--------------------------------------------------------------------------
  763. //
  764. // Function: FullDirCopyW
  765. //
  766. // Synopsis: creates a directory using the new PrivCopyFileExW so that
  767. // all the file metadata and ownership information is retained
  768. //
  769. // This function also imposes limits on the lengths of the folders
  770. // that can be copied. Anything longer than MAX_PATH is disallowed
  771. // because the shell cannot gracefully handle such paths and we
  772. // don't want to create problems for the user by redirecting their
  773. // files to paths that explorer cannot get to.
  774. //
  775. // Arguments: [in] pwszSource : the full path of the source directory
  776. // [in] pwszDest : the full path of the destination directory
  777. // [in] bSkipDacl : Skip DACL copying.
  778. //
  779. // Returns: ERROR_SUCCESS : if the copy was successful
  780. // ERROR_INVALID_SECURITY_DESCR : if the DACL could not be applied
  781. // ERROR_INVALID_OWNER : if the owner information could not be copied
  782. // ERROR_ENCRYPTION_FAILED : if the encryption info. could not be copied
  783. // or other error codes if some other error occurs
  784. // ERROR_FILENAME_EXCED_RANGE : if the filename is longer than MAX_PATH.
  785. //
  786. // History: 11/5/1998 RahulTh created
  787. // 12/13/2000 RahulTh added length limitations
  788. // 5/2/2002 RahulTh added the skip DACL flag
  789. //
  790. // Notes: Essentially the same as FullFileCopyW, but we have an extra
  791. // attribute to indicate that we are trying to copy a directory
  792. // Also, the FAIL_IF_EXSTS flag doesn't have any significance
  793. // when we are trying to copy directories, so that is something
  794. // we do not need here.
  795. //
  796. //---------------------------------------------------------------------------
  797. DWORD FullDirCopyW (const WCHAR* pwszSource, const WCHAR* pwszDest, BOOL bSkipDacl)
  798. {
  799. DWORD Status = ERROR_SUCCESS;
  800. DWORD dwFlags = PRIVCOPY_FILE_METADATA |
  801. PRIVCOPY_FILE_OWNER_GROUP | PRIVCOPY_FILE_DIRECTORY;
  802. BOOL bCancel = FALSE;
  803. BOOL bStatus = TRUE;
  804. int lenSource = 0;
  805. int lenDest = 0;
  806. if ( pwszSource != NULL )
  807. lenSource = wcslen (pwszSource);
  808. if ( pwszDest != NULL )
  809. lenDest = wcslen (pwszDest);
  810. //
  811. // Prevent copying of files longer than MAX_PATH characters. This limitation
  812. // needs to be added because the shell cannot handle paths longer than
  813. // MAX_PATH gracefully and we don't want to land the users into trouble by
  814. // creating files / folder that they cannot get to via explorer.
  815. //
  816. if (lenDest >= MAX_PATH || lenSource >= MAX_PATH)
  817. return ERROR_FILENAME_EXCED_RANGE;
  818. if (bSkipDacl)
  819. dwFlags |= PRIVCOPY_FILE_SKIP_DACL;
  820. bStatus = PrivCopyFileExW (pwszSource, pwszDest,
  821. (LPPROGRESS_ROUTINE) CopyProgressRoutine,
  822. (LPVOID) &Status,
  823. &bCancel,
  824. dwFlags
  825. );
  826. //get the last error if PrivCopyFileExW failed
  827. //and the callback function has not already registered a fatal error
  828. if ((ERROR_SUCCESS == Status) && (!bStatus))
  829. Status = GetLastError();
  830. return Status;
  831. }
  832. //+--------------------------------------------------------------------------
  833. //
  834. // Function: FileInDir
  835. //
  836. // Synopsis: given a file and a directory, this function determines if
  837. // a file with the same name exists in the given directory.
  838. //
  839. // Arguments: [in] pwszFile : the name of the file : it can be the full path
  840. // [in] pwszDir : the directory for which the check needs to be
  841. // performed
  842. // [out] pExists : if the function succeeds, this will contain
  843. // the result. TRUE if the file is present in the
  844. // directory. FALSE otherwise.
  845. //
  846. // Returns: ERROR_SUCCESS : if it is successful
  847. // ERROR_OUTOFMEMORY : if it runs out of memory
  848. // ERROR_BAD_NETPATH : if the network path for the given directory
  849. // cannot be found
  850. //
  851. // History: 10/28/1998 RahulTh created
  852. //
  853. // Notes: pwszDir MUST BE \ terminated
  854. //
  855. //---------------------------------------------------------------------------
  856. DWORD FileInDir (LPCWSTR pwszFile, LPCWSTR pwszDir, BOOL* pExists)
  857. {
  858. const WCHAR* pwszFileName = NULL;
  859. WCHAR* pwszDestName = NULL;
  860. int len;
  861. DWORD Status = ERROR_SUCCESS;
  862. //first get the display name of the source file
  863. pwszFileName = wcsrchr (pwszFile, L'\\');
  864. if (!pwszFileName)
  865. pwszFileName = pwszFile;
  866. else
  867. pwszFileName++; //go past the slash
  868. //the dir should be \ terminated
  869. len = wcslen (pwszFile) + wcslen (pwszDir) + 1;
  870. __try {
  871. pwszDestName = (WCHAR*) alloca (sizeof (WCHAR) * len);
  872. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  873. _resetstkoflw();
  874. return ERROR_OUTOFMEMORY;
  875. }
  876. Status = HRESULT_CODE(StringCchCopy(pwszDestName, len, pwszDir));
  877. if ( Status != ERROR_SUCCESS )
  878. {
  879. return Status;
  880. }
  881. Status = HRESULT_CODE(StringCchCat(pwszDestName, len, pwszFileName));
  882. if ( Status != ERROR_SUCCESS )
  883. {
  884. return Status;
  885. }
  886. if (0xFFFFFFFF == GetFileAttributes(pwszDestName))
  887. {
  888. //return an error if it is a bad network name. Saves us the trouble
  889. //of trying to redirect to a non-existent location later
  890. if (ERROR_BAD_NETPATH == GetLastError())
  891. return ERROR_BAD_NETPATH;
  892. else
  893. *pExists = FALSE;
  894. }
  895. else
  896. *pExists = TRUE;
  897. return ERROR_SUCCESS;
  898. }
  899. //+--------------------------------------------------------------------------
  900. //
  901. // Function: ComparePaths
  902. //
  903. // Synopsis: given 2 paths, this function compares them to check if
  904. // they are identical or if one is a descendant of the other
  905. // or if no such relationship can be deduced
  906. //
  907. // Arguments: [in] pwszSource : the first path
  908. // [in] pwszDest : the second path
  909. // [out] pResult : the result of the comparison if the function
  910. // succeeds in comparing the paths.
  911. // value of pResult may contain the following values upon
  912. // successful completion.
  913. // 0 : if the 2 paths are identical
  914. // -1 : if the second path is a descendant of the first
  915. // 1 : if no such relationship can be deduced
  916. //
  917. // Returns: ERROR_SUCCESS : if the function succeeds in comparing the paths
  918. // other error codes depending on the failure
  919. //
  920. // History: 10/28/1998 RahulTh created
  921. //
  922. // Notes: the result of the comparison is unreliable if the paths are
  923. // expressed in different formats, e.g. TCP/IP, UNC, NetBios etc.
  924. //
  925. //---------------------------------------------------------------------------
  926. DWORD ComparePaths (LPCWSTR pwszSource, LPCWSTR pwszDest, int* pResult)
  927. {
  928. DWORD Status = ERROR_SUCCESS;
  929. HRESULT hr = S_OK;
  930. BOOL bStatus;
  931. WCHAR* pwszAbsSource = NULL;
  932. WCHAR* pwszAbsDest = NULL;
  933. int lSource, lDest;
  934. size_t sourceSize, destSize;
  935. WCHAR* pwszSuccess = NULL;
  936. ASSERT (pResult);
  937. //first allocate memory for the absolute paths.
  938. //since the arguments to this function are full pathnames
  939. //the lengths of the absolute paths cannot exceed the length of
  940. //the parameters
  941. //add an extra character because we will add a \ to the end of the abs. path
  942. sourceSize = wcslen (pwszSource) + 2;
  943. __try {
  944. pwszAbsSource = (WCHAR*) alloca (sizeof(WCHAR) * sourceSize);
  945. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  946. _resetstkoflw();
  947. pwszAbsSource = NULL;
  948. Status = ERROR_OUTOFMEMORY;
  949. goto ComparePathsEnd;
  950. }
  951. //add an extra character because we will add a \ to the end of the abs. path
  952. destSize = wcslen (pwszDest) + 2;
  953. __try {
  954. pwszAbsDest = (WCHAR*) alloca (sizeof(WCHAR) * destSize);
  955. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  956. _resetstkoflw();
  957. pwszAbsDest = NULL;
  958. Status = ERROR_OUTOFMEMORY;
  959. goto ComparePathsEnd;
  960. }
  961. //first get the absolute paths. after that we will just work with the
  962. //absolute paths.
  963. //note: we need the absolute paths so that we can can check if one path
  964. //is a descendant of the other path by just using wcsncmp. without absolute
  965. //paths, wcsncmp cannot be used because we can have 2 paths like
  966. //\\server\share\hello\there and \\server\share\hello\..\hello\there\hi
  967. //in this case the second path is actually a descendant of the first, but
  968. //wcsncmp cannot detect that. getting the absolute paths will eliminate
  969. //the .., . etc.
  970. //also we must terminate the absolute paths with \, so that wcsncmp does
  971. //not mistakenly think that \\server\share\hellofubar is a descendant
  972. //of \\server\share\hello
  973. pwszSuccess = _wfullpath (pwszAbsSource, pwszSource, sourceSize);
  974. if (!pwszSuccess)
  975. {
  976. Status = ERROR_BAD_PATHNAME;
  977. goto ComparePathsEnd;
  978. }
  979. pwszSuccess = _wfullpath (pwszAbsDest, pwszDest, destSize);
  980. if (!pwszSuccess)
  981. {
  982. Status = ERROR_BAD_PATHNAME;
  983. goto ComparePathsEnd;
  984. }
  985. //update the lengths with the actual lengths of the absolute paths
  986. //not including the terminating null character
  987. lSource = wcslen (pwszAbsSource);
  988. lDest = wcslen (pwszAbsDest);
  989. //terminate the absolute paths with '\' if necessary. also make
  990. //the appropriate changes to the lengths
  991. if (L'\\' != pwszAbsSource[lSource - 1])
  992. {
  993. (void) StringCchCat(pwszAbsSource, sourceSize, L"\\"); //we won't run out of space here
  994. //because of the extra character allocation
  995. lSource++;
  996. }
  997. if (L'\\' != pwszAbsDest[lDest - 1])
  998. {
  999. (void) StringCchCat(pwszAbsDest, destSize, L"\\"); //won't run out of space here because
  1000. //of the extra allocation above
  1001. lDest++;
  1002. }
  1003. //now we are all set (finally!) to perform the comparisons
  1004. //first do a simple check of whether the paths are identical
  1005. if ((lSource == lDest) && (0 == _wcsicmp (pwszAbsSource, pwszAbsDest)))
  1006. {
  1007. *pResult = 0;
  1008. goto ComparePathsSuccess;
  1009. }
  1010. //check for recursion
  1011. if ((lDest > lSource) && (0 == _wcsnicmp (pwszAbsSource, pwszAbsDest, lSource)))
  1012. {
  1013. *pResult = -1;
  1014. goto ComparePathsSuccess;
  1015. }
  1016. //if we are here, these paths are not identical...
  1017. *pResult = 1;
  1018. ComparePathsSuccess:
  1019. Status = ERROR_SUCCESS;
  1020. goto ComparePathsEnd;
  1021. ComparePathsEnd:
  1022. return Status;
  1023. }
  1024. //+--------------------------------------------------------------------------
  1025. //
  1026. // Function: CheckIdenticalSpecial
  1027. //
  1028. // Synopsis: given 2 paths, this function determines if they are actually
  1029. // the same path expressed in 2 different formats
  1030. //
  1031. // Arguments: [in] pwszSource : path #1
  1032. // [in] pwszDest : path #2
  1033. // [out] pResult : result of the comparison
  1034. //
  1035. // Returns: ERROR_SUCCESS if the function could perform the comparison
  1036. // in this case *pResult will contain the result of comparison
  1037. // Other win32 errors, in which case *pResult should not be used
  1038. // by the calling function
  1039. //
  1040. // History: 12/1/1998 RahulTh created
  1041. //
  1042. // Notes: this function expects both pwszSource and pwszDest to exist and
  1043. // be online when it is called.
  1044. //
  1045. //---------------------------------------------------------------------------
  1046. DWORD CheckIdenticalSpecial (LPCWSTR pwszSource, LPCWSTR pwszDest, int* pResult)
  1047. {
  1048. ASSERT (pResult);
  1049. BOOL bStatus;
  1050. DWORD Status;
  1051. BOOL bTempFileCreated = FALSE;
  1052. WCHAR * pwszTempPath;
  1053. UINT lUnique = 0;
  1054. BOOL bFileExists;
  1055. WCHAR * pwszSlashTerminatedDest;
  1056. int lDest, destSize;
  1057. //first append a \ to pwszDest if it is not already \ terminated
  1058. //allocate an extra characted just in case we need it
  1059. lDest = wcslen(pwszDest);
  1060. destSize = lDest + 2;
  1061. __try {
  1062. pwszSlashTerminatedDest = (WCHAR *) alloca (sizeof (WCHAR) * destSize);
  1063. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  1064. _resetstkoflw();
  1065. pwszSlashTerminatedDest = NULL;
  1066. Status = ERROR_OUTOFMEMORY;
  1067. goto CheckIdenticalEnd;
  1068. }
  1069. (void) StringCchCopy(pwszSlashTerminatedDest, destSize, pwszDest);
  1070. if (L'\\' != pwszSlashTerminatedDest[lDest - 1])
  1071. {
  1072. (void) StringCchCat(pwszSlashTerminatedDest, destSize, L"\\");
  1073. lDest++;
  1074. }
  1075. __try {
  1076. pwszTempPath = (WCHAR*) alloca (sizeof (WCHAR) * (MAX_PATH + wcslen(pwszSource) + 2));
  1077. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  1078. _resetstkoflw();
  1079. pwszTempPath = NULL;
  1080. Status = ERROR_OUTOFMEMORY;
  1081. goto CheckIdenticalEnd;
  1082. }
  1083. if (0 == (lUnique = GetTempFileName(pwszSource, L"fde", 0, pwszTempPath)))
  1084. {
  1085. //a failure to create the temporary file would mean that the source
  1086. //and destination paths are different...
  1087. *pResult = 1;
  1088. goto CheckIdenticalSuccess;
  1089. }
  1090. //now we have created a temporary file,
  1091. bTempFileCreated = TRUE;
  1092. //check if it exists on the destination
  1093. //note: FileInDir requires that the path in the second parameter is
  1094. // slash terminated.
  1095. Status = FileInDir (pwszTempPath, pwszSlashTerminatedDest, &bFileExists);
  1096. if (Status != ERROR_SUCCESS)
  1097. goto CheckIdenticalEnd;
  1098. //if the file does not exist at the destination, we know that these 2 paths
  1099. //are different. However, if the file does exist at the destination, we
  1100. //need to watch out for the rare case that the file existed even before we
  1101. //we created the temp file at the source. To do this, we must delete the
  1102. //temp file from the source and make sure that it has indeed disappeared
  1103. //from the destination
  1104. if (!bFileExists)
  1105. {
  1106. *pResult = 1;
  1107. }
  1108. else
  1109. {
  1110. if (!DeleteFile(pwszTempPath))
  1111. goto CheckIdenticalErr;
  1112. //the file has been deleted
  1113. bTempFileCreated = FALSE;
  1114. //make sure that it has disappeared from the destination
  1115. Status = FileInDir (pwszTempPath, pwszSlashTerminatedDest, &bFileExists);
  1116. if (Status != ERROR_SUCCESS)
  1117. goto CheckIdenticalEnd;
  1118. if (bFileExists)
  1119. {
  1120. *pResult = 1; //by some quirk of fate, a file by the same name as
  1121. //the tempfile preexisted on the destination, so
  1122. //in reality they are not the same share.
  1123. }
  1124. else
  1125. {
  1126. //the file has disappeared from the dest. this means that it was
  1127. //indeed the same share
  1128. *pResult = 0;
  1129. }
  1130. }
  1131. CheckIdenticalSuccess:
  1132. Status = ERROR_SUCCESS;
  1133. goto CheckIdenticalEnd;
  1134. CheckIdenticalErr:
  1135. Status = GetLastError();
  1136. CheckIdenticalEnd:
  1137. if (bTempFileCreated)
  1138. DeleteFile (pwszTempPath); //ignore any errors here.
  1139. return Status;
  1140. }
  1141. //*************************************************************
  1142. //
  1143. // CheckSlash()
  1144. //
  1145. // Purpose: Checks for an ending slash and adds one if
  1146. // it is missing.
  1147. //
  1148. // Parameters: lpDir - directory
  1149. //
  1150. // Return: Pointer to the end of the string
  1151. //
  1152. // Comments:
  1153. //
  1154. // History: Date Author Comment
  1155. // 6/19/95 ericflo Created
  1156. //
  1157. //*************************************************************
  1158. LPTSTR CheckSlash (LPTSTR lpDir)
  1159. {
  1160. LPTSTR lpEnd;
  1161. lpEnd = lpDir + lstrlen(lpDir);
  1162. if (*(lpEnd - 1) != TEXT('\\')) {
  1163. *lpEnd = TEXT('\\');
  1164. lpEnd++;
  1165. *lpEnd = TEXT('\0');
  1166. }
  1167. return lpEnd;
  1168. }
  1169. //+--------------------------------------------------------------------------
  1170. //
  1171. // Function: GetSetOwnerPrivileges
  1172. //
  1173. // Synopsis: tries to get privileges to set ownership
  1174. //
  1175. // Arguments: [in] hToken : handle to the token for which we are trying to
  1176. // obtain the privileges
  1177. //
  1178. // Returns: nothing
  1179. //
  1180. // History: 11/6/1998 RahulTh created
  1181. //
  1182. // Notes: this function never fails. It just tries its best to get all
  1183. // NT privileges. It is not guaranteed that it will get all of
  1184. // them, as it depends on the user's rights
  1185. //
  1186. //---------------------------------------------------------------------------
  1187. void GetSetOwnerPrivileges (HANDLE hToken)
  1188. {
  1189. BOOL bStatus;
  1190. DWORD Status;
  1191. DWORD Size = 0;
  1192. DWORD i;
  1193. DWORD privCount;
  1194. PTOKEN_PRIVILEGES pPrivs = NULL;
  1195. //try to get all the windows NT privileges.
  1196. for (i=0, privCount=0; *NTPrivs[i]; i++)
  1197. privCount++;
  1198. Size = sizeof (LUID_AND_ATTRIBUTES) * (privCount - 1) +
  1199. sizeof (TOKEN_PRIVILEGES);
  1200. __try {
  1201. pPrivs = (PTOKEN_PRIVILEGES) alloca (Size);
  1202. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  1203. _resetstkoflw();
  1204. pPrivs = NULL;
  1205. goto GetAllPrivsEnd;
  1206. }
  1207. for (i=0, privCount = 0; *NTPrivs[i]; i++)
  1208. {
  1209. bStatus = LookupPrivilegeValue (NULL, NTPrivs[i],
  1210. &(pPrivs->Privileges[privCount].Luid));
  1211. if (!bStatus)
  1212. continue;
  1213. pPrivs->Privileges[privCount++].Attributes = SE_PRIVILEGE_ENABLED;
  1214. }
  1215. pPrivs->PrivilegeCount = privCount;
  1216. AdjustTokenPrivileges (hToken,
  1217. FALSE,
  1218. pPrivs,
  1219. NULL, NULL, NULL
  1220. );
  1221. GetAllPrivsEnd:
  1222. return;
  1223. }
  1224. //+--------------------------------------------------------------------------
  1225. //
  1226. // Function: SafeGetPrivateProfileStringW
  1227. //
  1228. // Synopsis: a wrapper for GetPrivateProfileString which takes care of
  1229. // all the error checks etc. so that functions that call
  1230. // this routine won't have to do it.
  1231. //
  1232. // Arguments: very similar to GetPrivateProfileStringW
  1233. //
  1234. // Returns: ERROR_SUCCESS if successful. An error code otherwise
  1235. // Also, upon successful return *pSize contains the size of
  1236. // the data copied -- not including the terminating NULL
  1237. // probably the only Error code this will ever return is
  1238. // ERROR_OUTOFMEMORY
  1239. //
  1240. // History: 11/19/1998 RahulTh created
  1241. // 12/13/2000 RahulTh made prefix happy by initializing vars.
  1242. // which anyway get set by GetPrivateProfileString
  1243. //
  1244. // Notes: if *ppwszReturnedString has been allocated memory, it might
  1245. // get freed by this function
  1246. // this function also allocates memory for the data
  1247. //
  1248. //---------------------------------------------------------------------------
  1249. DWORD SafeGetPrivateProfileStringW (
  1250. const WCHAR * pwszSection,
  1251. const WCHAR * pwszKey,
  1252. const WCHAR * pwszDefault,
  1253. WCHAR ** ppwszReturnedString,
  1254. DWORD * pSize,
  1255. const WCHAR * pwszIniFile
  1256. )
  1257. {
  1258. DWORD Status = ERROR_SUCCESS;
  1259. DWORD retVal;
  1260. *pSize = MAX_PATH;
  1261. do
  1262. {
  1263. if (*ppwszReturnedString)
  1264. delete [] *ppwszReturnedString;
  1265. *ppwszReturnedString = new WCHAR [*pSize];
  1266. if (!(*ppwszReturnedString))
  1267. {
  1268. Status = ERROR_OUTOFMEMORY;
  1269. *pSize = 0;
  1270. goto SafeGetEnd;
  1271. }
  1272. (*ppwszReturnedString)[0] = L'*';
  1273. (*ppwszReturnedString)[1] = L'\0';
  1274. retVal = GetPrivateProfileString (
  1275. pwszSection,
  1276. pwszKey,
  1277. pwszDefault,
  1278. *ppwszReturnedString,
  1279. *pSize,
  1280. pwszIniFile
  1281. );
  1282. if (*pSize - 1 != retVal)
  1283. {
  1284. *pSize = retVal;
  1285. break;
  1286. }
  1287. //if we are here, we need more memory
  1288. //try with twice of what we had
  1289. *pSize = (*pSize) * 2;
  1290. } while ( TRUE );
  1291. SafeGetEnd:
  1292. return Status;
  1293. }
  1294. //+--------------------------------------------------------------------------
  1295. //
  1296. // Function: MySidCopy
  1297. //
  1298. // Synopsis: copies sids and also allocates memory for the destination Sid
  1299. //
  1300. // Arguments: [out] ppDestSid : pointer the destination Sid;
  1301. // [in] pSourceSid : the source Sid
  1302. //
  1303. // Returns: ERROR_SUCCESS if successful. an error code otherwise
  1304. //
  1305. // History: 11/20/1998 RahulTh created
  1306. //
  1307. // Notes:
  1308. //
  1309. //---------------------------------------------------------------------------
  1310. DWORD MySidCopy (PSID * ppDestSid, PSID pSourceSid)
  1311. {
  1312. DWORD Status;
  1313. ULONG Size = 0;
  1314. *ppDestSid = 0;
  1315. if (!pSourceSid)
  1316. return ERROR_SUCCESS;
  1317. Size = RtlLengthSid (pSourceSid);
  1318. if (!Size)
  1319. return ERROR_SUCCESS;
  1320. *ppDestSid = (PSID) new BYTE [Size];
  1321. if (! (*ppDestSid))
  1322. return ERROR_OUTOFMEMORY;
  1323. NTSTATUS ntStatus;
  1324. DWORD dwError;
  1325. ntStatus = RtlCopySid (Size, *ppDestSid, pSourceSid);
  1326. dwError = RtlNtStatusToDosError(ntStatus);
  1327. if (!NT_SUCCESS(ntStatus))
  1328. {
  1329. delete [] *ppDestSid;
  1330. *ppDestSid = NULL;
  1331. }
  1332. return dwError;
  1333. }
  1334. //+--------------------------------------------------------------------------
  1335. //
  1336. // Function: GetShareStatus
  1337. //
  1338. // Synopsis: this function is a wrapper for CSCQueryFileStatus.
  1339. // basically CSCQueryFileStatus can fail if there was never a net
  1340. // use to a share. So this function tries to create a net use to
  1341. // the share if CSCQueryFileStatus fails and then re-queries the
  1342. // file status
  1343. //
  1344. // Arguments: [in] pwszShare : the share name
  1345. // [out] pdwStatus : the share Status
  1346. // [out] pdwPinCount : the pin count
  1347. // [out] pdwHints : the hints
  1348. //
  1349. // Returns: TRUE : if everything was successful.
  1350. // FALSE : if there was an error. In this case, it GetLastError()
  1351. // will contain the specific error code.
  1352. //
  1353. // History: 5/11/1999 RahulTh created
  1354. //
  1355. // Notes: it is very important that this function be passed a share name
  1356. // it does not do any parameter validation. So under no
  1357. // circumstance should this function be passed a filename.
  1358. //
  1359. //---------------------------------------------------------------------------
  1360. BOOL GetShareStatus (const WCHAR * pwszShare, DWORD * pdwStatus,
  1361. DWORD * pdwPinCount, DWORD * pdwHints)
  1362. {
  1363. NETRESOURCE nr;
  1364. DWORD dwResult;
  1365. DWORD dwErr = NO_ERROR;
  1366. BOOL bStatus;
  1367. bStatus = CSCQueryFileStatus(pwszShare, pdwStatus, pdwPinCount, pdwHints);
  1368. if (!bStatus)
  1369. {
  1370. //try to connect to the share
  1371. ZeroMemory ((PVOID) (&nr), sizeof (NETRESOURCE));
  1372. nr.dwType = RESOURCETYPE_DISK;
  1373. nr.lpLocalName = NULL;
  1374. nr.lpRemoteName = (LPTSTR) pwszShare;
  1375. nr.lpProvider = NULL;
  1376. dwErr = WNetUseConnection(NULL, &nr, NULL, NULL, 0,
  1377. NULL, NULL, &dwResult);
  1378. if (NO_ERROR == dwErr)
  1379. {
  1380. bStatus = CSCQueryFileStatus (pwszShare, pdwStatus, pdwPinCount, pdwHints);
  1381. if (!bStatus)
  1382. dwErr = GetLastError();
  1383. else
  1384. dwErr = NO_ERROR;
  1385. WNetCancelConnection2 (pwszShare, 0, FALSE);
  1386. }
  1387. else
  1388. {
  1389. bStatus = FALSE;
  1390. }
  1391. }
  1392. SetLastError(dwErr);
  1393. return bStatus;
  1394. }
  1395. //+--------------------------------------------------------------------------
  1396. //
  1397. // Function: GetCSCStatus
  1398. //
  1399. // Synopsis: given a path, finds out if it is local and if it is not
  1400. // whether it is online or offline.
  1401. //
  1402. // Arguments: [in] pwszPath : the path to the file
  1403. //
  1404. // Returns: Local/Online/Offline
  1405. //
  1406. // History: 11/20/1998 RahulTh created
  1407. //
  1408. // Notes: it is important that the path passed to this function is a
  1409. // a full path and not a relative path
  1410. //
  1411. // this function will return offline if the share is not live or
  1412. // if the share is live but CSC thinks that it is offline
  1413. //
  1414. // it will return PathLocal if the path is local or if the path
  1415. // is a network path that cannot be handled by CSC e.g. a network
  1416. // share with a pathname longer than what csc can handle or if it
  1417. // is a netware share. in this case it makes sense to return
  1418. // PathLocal because CSC won't maintain a database for these shares
  1419. // -- same as for a local path. so as far as CSC is concerned, this
  1420. // is as good as a local path.
  1421. //
  1422. //---------------------------------------------------------------------------
  1423. SHARESTATUS GetCSCStatus (const WCHAR * pwszPath)
  1424. {
  1425. WCHAR * pwszAbsPath = NULL;
  1426. WCHAR * pwszCurr = NULL;
  1427. int len;
  1428. BOOL bRetVal;
  1429. DWORD Status;
  1430. DWORD dwPinCount;
  1431. DWORD dwHints;
  1432. if (!pwszPath)
  1433. return ShareOffline; //a path must be provided
  1434. len = wcslen (pwszPath);
  1435. __try {
  1436. pwszAbsPath = (WCHAR *) alloca (sizeof (WCHAR) * (len + 1));
  1437. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  1438. _resetstkoflw();
  1439. //we are out of memory, so it is safest to return ShareOffline
  1440. //so that we can bail out of redirection.
  1441. return ShareOffline;
  1442. }
  1443. //get the absolute path
  1444. pwszCurr = _wfullpath (pwszAbsPath, pwszPath, len + 1);
  1445. if (!pwszCurr)
  1446. {
  1447. //in order for _wfullpath to fail, something really bad has to happen
  1448. //so it is best to return ShareOffline so that we can bail out of
  1449. //redirection
  1450. return ShareOffline;
  1451. }
  1452. len = wcslen (pwszAbsPath);
  1453. if (! (
  1454. (2 <= len) &&
  1455. (L'\\' == pwszAbsPath[0]) &&
  1456. (L'\\' == pwszAbsPath[1])
  1457. )
  1458. )
  1459. {
  1460. //it is a local path if it does not begin with 2 backslashes
  1461. return PathLocal;
  1462. }
  1463. //this is a UNC path; so extract the \\server\share part
  1464. pwszCurr = wcschr ( & (pwszAbsPath[2]), L'\\');
  1465. //first make sure that it is at least of the form \\server\share
  1466. //watch out for the \\server\ case
  1467. if (!pwszCurr || !pwszCurr[1])
  1468. return ShareOffline; //it is an invalid path (no share name)
  1469. //the path is of the form \\server\share
  1470. //note: the use _wfullpath automatically protects us against the \\server\\ case
  1471. pwszCurr = wcschr (&(pwszCurr[1]), L'\\');
  1472. if (pwszCurr) //if it is of the form \\server\share\...
  1473. *pwszCurr = L'\0';
  1474. //now pwszAbsPath is a share name
  1475. bRetVal = CSCCheckShareOnline (pwszAbsPath);
  1476. if (!bRetVal)
  1477. {
  1478. if (!g_bCSCEnabled)
  1479. {
  1480. //CSC has not been enabled on this machine, so the fact that
  1481. //CSC check share online failed means that the share is indeed
  1482. //offline.
  1483. return ShareOffline;
  1484. }
  1485. if (ERROR_SUCCESS != GetLastError())
  1486. {
  1487. //either there is really a problem (e.g. invalid share name) or
  1488. //it is just a share that is not handled by CSC e.g. a netware share
  1489. //or a share with a name that is longer than can be handled by CSC
  1490. //so check if the share actually exists
  1491. if (0xFFFFFFFF != GetFileAttributes(pwszAbsPath))
  1492. {
  1493. //this can still be a share that is offline since GetFileAttributes
  1494. //will return the attributes stored in the cache
  1495. Status = 0;
  1496. bRetVal = GetShareStatus (pwszAbsPath, &Status, &dwPinCount,
  1497. &dwHints);
  1498. if (! bRetVal || (! (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & Status)))
  1499. return PathLocal; //this is simply a valid path that CSC cannot handle
  1500. else if (bRetVal &&
  1501. (FLAG_CSC_SHARE_STATUS_NO_CACHING ==
  1502. (FLAG_CSC_SHARE_STATUS_CACHING_MASK & Status)))
  1503. return PathLocal; //CSC caching is not turned on for the share.
  1504. }
  1505. }
  1506. //it is indeed an inaccessble share
  1507. return ShareOffline; //for all other cases, treat this as offline
  1508. }
  1509. else
  1510. {
  1511. if (!g_bCSCEnabled)
  1512. {
  1513. //CSC has not been enabled on this machine, so the fact that
  1514. //CSCCheckShareOnline succeed means that the share is indeed
  1515. //accessible. Since nothing can be cached, we must return
  1516. //PathLocal here.
  1517. return PathLocal;
  1518. }
  1519. //if we are here, it means that the share is live, but CSC might still
  1520. //think that it is offline.
  1521. Status = 0;
  1522. bRetVal = GetShareStatus (pwszAbsPath, &Status, &dwPinCount,
  1523. &dwHints);
  1524. if (bRetVal && (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & Status))
  1525. return ShareOffline; //CSC thinks that the share is offline
  1526. else if (bRetVal &&
  1527. (FLAG_CSC_SHARE_STATUS_NO_CACHING ==
  1528. (FLAG_CSC_SHARE_STATUS_CACHING_MASK & Status)))
  1529. return PathLocal; //CSC caching is not turned on for the share
  1530. else if (!bRetVal)
  1531. return ShareOffline;
  1532. //in all other cases, consider the share as online since
  1533. //CSCCheckShareOnline has already returned TRUE
  1534. return ShareOnline;
  1535. }
  1536. }
  1537. //+--------------------------------------------------------------------------
  1538. //
  1539. // Function: MoveDirInCSC
  1540. //
  1541. // Synopsis: this function moves a directory within the CSC cache without
  1542. // prejudice. If the destination is a local path, it just deletes
  1543. // the source tree from the cache
  1544. //
  1545. // Arguments: [in] pwszSource : the source path
  1546. // [in] pwszDest : the dest path
  1547. // [in] pwszSkipSubdir : the directory to skip while moving
  1548. // [in] StatusFrom : the CSC status of the source path
  1549. // [in] StatusTo : the CSC status of the dest. path
  1550. // [in] bAllowRdrTimeout : if stuff needs to be deleted from the
  1551. // cache, we may not succeed immediately since
  1552. // the rdr keeps the handles to recently opened
  1553. // files open. This paramaters tells the function
  1554. // whether it needs to wait and retry
  1555. //
  1556. // Returns: nothing. it just tries its best.
  1557. //
  1558. // History: 11/21/1998 RahulTh created
  1559. //
  1560. // Notes: the value of bAllowRdrTimeout is always ignored for the
  1561. // in-cache rename operation. we always want to wait for
  1562. // the timeout.
  1563. //
  1564. //---------------------------------------------------------------------------
  1565. void MoveDirInCSC (const WCHAR * pwszSource, const WCHAR * pwszDest,
  1566. const WCHAR * pwszSkipSubdir,
  1567. SHARESTATUS StatusFrom, SHARESTATUS StatusTo,
  1568. BOOL bAllowRdrTimeoutForDel,
  1569. BOOL bAllowRdrTimeoutForRen)
  1570. {
  1571. WIN32_FIND_DATA findData;
  1572. DWORD dwFileStatus;
  1573. DWORD dwPinCount;
  1574. HANDLE hCSCFind;
  1575. DWORD dwHintFlags;
  1576. FILETIME origTime;
  1577. WCHAR * pwszPath;
  1578. WCHAR * pwszEnd;
  1579. int len;
  1580. size_t pathSize;
  1581. size_t endSize;
  1582. DWORD StatusCSCRen = ERROR_SUCCESS;
  1583. HRESULT hr = S_OK;
  1584. if (!g_bCSCEnabled || PathLocal == StatusFrom)
  1585. return; //there is nothing to do. nothing was cached.
  1586. if (PathLocal == StatusTo)
  1587. {
  1588. //the destination is a local path, so we should just delete the
  1589. //files from the source
  1590. DeleteCSCFileTree (pwszSource, pwszSkipSubdir, bAllowRdrTimeoutForDel);
  1591. }
  1592. else
  1593. {
  1594. len = wcslen (pwszSource);
  1595. pathSize = len + MAX_PATH + 2;
  1596. __try {
  1597. pwszPath = (WCHAR *) alloca (sizeof (WCHAR) * pathSize);
  1598. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  1599. _resetstkoflw();
  1600. return;
  1601. }
  1602. if (!pwszPath || len <= 0)
  1603. return;
  1604. (void) StringCchCopy(pwszPath, pathSize, pwszSource);
  1605. pwszEnd = pwszPath + len;
  1606. endSize = pathSize - len;
  1607. if (L'\\' != pwszEnd[-1])
  1608. {
  1609. *pwszEnd++ = L'\\';
  1610. endSize--;
  1611. }
  1612. hCSCFind = CSCFindFirstFile (pwszSource, &findData, &dwFileStatus,
  1613. &dwPinCount, &dwHintFlags, &origTime);
  1614. if (INVALID_HANDLE_VALUE != hCSCFind)
  1615. {
  1616. do
  1617. {
  1618. if (0 != _wcsicmp (L".", findData.cFileName) &&
  1619. 0 != _wcsicmp (L"..", findData.cFileName) &&
  1620. (!pwszSkipSubdir || (0 != _wcsicmp (findData.cFileName, pwszSkipSubdir))))
  1621. {
  1622. StatusCSCRen = HRESULT_CODE(StringCchCopy(pwszEnd, endSize, findData.cFileName));
  1623. if ( StatusCSCRen != ERROR_SUCCESS )
  1624. {
  1625. break;
  1626. }
  1627. if (ERROR_SUCCESS == StatusCSCRen)
  1628. {
  1629. StatusCSCRen = DoCSCRename (pwszPath, pwszDest, TRUE, bAllowRdrTimeoutForRen);
  1630. }
  1631. else
  1632. {
  1633. //here we ignore the return value since an error has already occurred.
  1634. //and we do not wish to spend any more time in timeouts in any subsequent
  1635. //rename operation.
  1636. DoCSCRename (pwszPath, pwszDest, TRUE, FALSE);
  1637. }
  1638. }
  1639. } while ( CSCFindNextFile (hCSCFind, &findData, &dwFileStatus,
  1640. &dwPinCount, &dwHintFlags, &origTime)
  1641. );
  1642. CSCFindClose (hCSCFind);
  1643. }
  1644. //merge the pin info. at the top level folder
  1645. MergePinInfo (pwszSource, pwszDest, StatusFrom, StatusTo);
  1646. }
  1647. return;
  1648. }
  1649. //+--------------------------------------------------------------------------
  1650. //
  1651. // Function: DoCSCRename
  1652. //
  1653. // Synopsis: does a file rename within the CSC cache.
  1654. //
  1655. // Arguments: [in] pwszSource : full source path
  1656. // [in] pwszDest : full path to destination directory
  1657. // [in] bOverwrite : overwrite if the file/folder exists at
  1658. // the destination
  1659. // [in] bAllowRdrTimeout : if TRUE, retry for 10 seconds on failure
  1660. // so that rdr and mem. mgr. get enough time to
  1661. // release the handles.
  1662. //
  1663. // Returns: ERROR_SUCCESS if the rename was successful.
  1664. // an error code otherwise.
  1665. //
  1666. // History: 5/26/1999 RahulTh created
  1667. //
  1668. // Notes: no validation of parameters is done. The caller is responsible
  1669. // for that
  1670. //
  1671. //---------------------------------------------------------------------------
  1672. DWORD DoCSCRename (const WCHAR * pwszSource, const WCHAR * pwszDest,
  1673. BOOL bOverwrite, BOOL bAllowRdrTimeout)
  1674. {
  1675. DWORD Status = ERROR_SUCCESS;
  1676. BOOL bStatus;
  1677. int i;
  1678. bStatus = CSCDoLocalRename (pwszSource, pwszDest, bOverwrite);
  1679. if (!bStatus)
  1680. {
  1681. Status = GetLastError();
  1682. if (ERROR_SUCCESS != Status &&
  1683. ERROR_FILE_NOT_FOUND != Status &&
  1684. ERROR_PATH_NOT_FOUND != Status &&
  1685. ERROR_INVALID_PARAMETER != Status &&
  1686. ERROR_BAD_NETPATH != Status)
  1687. {
  1688. if (bAllowRdrTimeout)
  1689. {
  1690. if (!bOverwrite && ERROR_FILE_EXISTS == Status)
  1691. {
  1692. Status = ERROR_SUCCESS;
  1693. }
  1694. else
  1695. {
  1696. for (i = 0; i < 11; i++)
  1697. {
  1698. Sleep (1000); //wait for the handle to be released
  1699. bStatus = CSCDoLocalRename (pwszSource, pwszDest, bOverwrite);
  1700. if (bStatus)
  1701. {
  1702. Status = ERROR_SUCCESS;
  1703. break;
  1704. }
  1705. }
  1706. }
  1707. }
  1708. }
  1709. else
  1710. {
  1711. Status = ERROR_SUCCESS;
  1712. }
  1713. }
  1714. if (ERROR_SUCCESS != Status)
  1715. {
  1716. DebugMsg ((DM_VERBOSE, IDS_CSCRENAME_FAIL, pwszSource, pwszDest, Status));
  1717. }
  1718. return Status;
  1719. }
  1720. //+--------------------------------------------------------------------------
  1721. //
  1722. // Function: DeleteCSCFileTree
  1723. //
  1724. // Synopsis: deletes a file tree from the CSC
  1725. //
  1726. // Arguments: [in] pwszSource : the path to the folder whose contents should
  1727. // be deleted
  1728. // [in] pwszSkipSubdir : name of the subdirectory to be skipped.
  1729. // [in] bAllowRdrTimeout : if true, makes multiple attempts to
  1730. // delete the file since the rdr may have left
  1731. // the handle open for sometime which can result
  1732. // in an ACCESS_DENIED message.
  1733. //
  1734. // Returns: ERROR_SUCCESS if the deletion was successful. An error code
  1735. // otherwise.
  1736. //
  1737. // History: 11/21/1998 RahulTh created
  1738. //
  1739. // Notes:
  1740. //
  1741. //---------------------------------------------------------------------------
  1742. DWORD DeleteCSCFileTree (const WCHAR * pwszSource, const WCHAR * pwszSkipSubdir,
  1743. BOOL bAllowRdrTimeout)
  1744. {
  1745. WIN32_FIND_DATA findData;
  1746. DWORD dwFileStatus;
  1747. DWORD dwPinCount;
  1748. HANDLE hCSCFind;
  1749. DWORD dwHintFlags;
  1750. FILETIME origTime;
  1751. WCHAR * pwszPath;
  1752. WCHAR * pwszEnd;
  1753. int len;
  1754. size_t pathSize;
  1755. size_t endSize;
  1756. DWORD Status = ERROR_SUCCESS;
  1757. DWORD dwCopyStatus = ERROR_SUCCESS;
  1758. if (! g_bCSCEnabled)
  1759. return ERROR_SUCCESS;
  1760. len = wcslen(pwszSource);
  1761. pathSize = len + MAX_PATH + 2;
  1762. __try {
  1763. pwszPath = (WCHAR *) alloca (sizeof(WCHAR) * pathSize);
  1764. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  1765. _resetstkoflw();
  1766. return ERROR_OUTOFMEMORY; //nothing much we can do if we run out of memory
  1767. }
  1768. if (len <= 0)
  1769. return ERROR_BAD_PATHNAME;
  1770. (void) StringCchCopy(pwszPath, pathSize, pwszSource);
  1771. pwszEnd = pwszPath + len;
  1772. endSize = pathSize - len;
  1773. if (L'\\' != pwszEnd[-1])
  1774. {
  1775. *pwszEnd++ = L'\\';
  1776. endSize--;
  1777. }
  1778. hCSCFind = CSCFindFirstFile (pwszSource, &findData, &dwFileStatus,
  1779. &dwPinCount, &dwHintFlags, &origTime);
  1780. if (INVALID_HANDLE_VALUE != hCSCFind)
  1781. {
  1782. do
  1783. {
  1784. if (0 != _wcsicmp (L".", findData.cFileName) &&
  1785. 0 != _wcsicmp (L"..", findData.cFileName) &&
  1786. (!pwszSkipSubdir || (0 != _wcsicmp (pwszSkipSubdir, findData.cFileName))))
  1787. {
  1788. //
  1789. // Note: Don't assign the return value directly to Status or you will
  1790. // risk losing track of the fact that we had failed earlier.
  1791. // As a side-effect of this, we will also not turn off the retry loop (bAllowRdrTimeout)
  1792. // for file deletes and thus will end up with a max. delay which is linearly proportional
  1793. // to the number of filesystem objects in the users' folder rather than a constant max. delay.
  1794. //
  1795. dwCopyStatus = HRESULT_CODE(StringCchCopy(pwszEnd, endSize, findData.cFileName));
  1796. if ( dwCopyStatus != ERROR_SUCCESS )
  1797. {
  1798. Status = dwCopyStatus;
  1799. break;
  1800. }
  1801. if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  1802. {
  1803. if (ERROR_SUCCESS != Status)
  1804. {
  1805. //no point delaying the deletes since a delete has already
  1806. //failed.
  1807. DeleteCSCFileTree (pwszPath, NULL, FALSE);
  1808. }
  1809. else
  1810. {
  1811. Status = DeleteCSCFileTree (pwszPath, NULL, bAllowRdrTimeout);
  1812. }
  1813. }
  1814. else
  1815. {
  1816. if (ERROR_SUCCESS != Status)
  1817. {
  1818. //no point delaying the delete if we have already failed.
  1819. DeleteCSCFile (pwszPath, FALSE);
  1820. }
  1821. else
  1822. {
  1823. Status = DeleteCSCFile (pwszPath, bAllowRdrTimeout);
  1824. }
  1825. }
  1826. }
  1827. } while ( CSCFindNextFile (hCSCFind, &findData, &dwFileStatus,
  1828. &dwPinCount, &dwHintFlags, &origTime)
  1829. );
  1830. CSCFindClose (hCSCFind);
  1831. }
  1832. if (ERROR_SUCCESS != Status)
  1833. {
  1834. //no point in delaying the delete if we have already failed.
  1835. DeleteCSCFile (pwszSource, FALSE);
  1836. }
  1837. else
  1838. {
  1839. Status = DeleteCSCFile (pwszSource, bAllowRdrTimeout);
  1840. }
  1841. return Status;
  1842. }
  1843. //+--------------------------------------------------------------------------
  1844. //
  1845. // Function: DeleteCSCFile
  1846. //
  1847. // Synopsis: deletes the given path. but might make repeated attempts to
  1848. // make sure that the rdr has enough time to release any handles
  1849. // that it holds.
  1850. //
  1851. // Arguments: [in] pwszPath : the path to delete.
  1852. // [in] bAllowRdrTimeout : make multiple attempts to delete the
  1853. // file with waits in between so that the rdr has
  1854. // enough time to release any held handles.
  1855. //
  1856. // Returns: ERROR_SUCCES if the delete was successful. An error code
  1857. // otherwise.
  1858. //
  1859. // History: 5/26/1999 RahulTh created
  1860. //
  1861. // Notes:
  1862. //
  1863. //---------------------------------------------------------------------------
  1864. DWORD DeleteCSCFile (const WCHAR * pwszPath, BOOL bAllowRdrTimeout)
  1865. {
  1866. BOOL bStatus;
  1867. DWORD Status = ERROR_SUCCESS;
  1868. int i;
  1869. bStatus = CSCDelete (pwszPath);
  1870. if (!bStatus)
  1871. {
  1872. Status = GetLastError();
  1873. if (ERROR_SUCCESS != Status &&
  1874. ERROR_FILE_NOT_FOUND != Status &&
  1875. ERROR_PATH_NOT_FOUND != Status &&
  1876. ERROR_INVALID_PARAMETER != Status)
  1877. {
  1878. //this is a valid error.
  1879. //so based on the value of bAllowRdrTimeout and based
  1880. //on whether we have already failed in deleting something
  1881. //we will try repeatedly to delete the file for 10 seconds
  1882. //note: there is no point in repeatedly trying if the deletion
  1883. //failed because this was a directory which was not empty
  1884. if (bAllowRdrTimeout && ERROR_DIR_NOT_EMPTY != Status)
  1885. {
  1886. for (i = 0; i < 11; i++)
  1887. {
  1888. Sleep (1000); //wait for 1 second and try again
  1889. bStatus = CSCDelete (pwszPath);
  1890. if (bStatus)
  1891. {
  1892. Status = ERROR_SUCCESS;
  1893. break;
  1894. }
  1895. }
  1896. }
  1897. }
  1898. else
  1899. {
  1900. Status = ERROR_SUCCESS;
  1901. }
  1902. }
  1903. if (ERROR_SUCCESS != Status)
  1904. {
  1905. DebugMsg ((DM_VERBOSE, IDS_CSCDELETE_FAIL, pwszPath, Status));
  1906. }
  1907. return Status;
  1908. }
  1909. //+--------------------------------------------------------------------------
  1910. //
  1911. // Function: DisplayStatusMessage
  1912. //
  1913. // Synopsis: displays the status message in the UI when the verbose status
  1914. // is on.
  1915. //
  1916. // Arguments: resource id of the message to be displayed.
  1917. //
  1918. // Returns: nothing
  1919. //
  1920. // History: 2/25/1999 RahulTh created
  1921. //
  1922. // Notes: failures are ignored.
  1923. //
  1924. //---------------------------------------------------------------------------
  1925. void DisplayStatusMessage (UINT rid)
  1926. {
  1927. WCHAR pwszMessage [MAX_PATH];
  1928. if (!gpStatusCallback)
  1929. return;
  1930. if (!LoadString (ghDllInstance, rid, pwszMessage, MAX_PATH))
  1931. return;
  1932. gpStatusCallback (TRUE, pwszMessage);
  1933. }
  1934. //+--------------------------------------------------------------------------
  1935. //
  1936. // Function: DeleteCSCShareIfEmpty
  1937. //
  1938. // Synopsis: given a file name, this function deletes from the local cache
  1939. // the share to which the file belongs if the local cache for that
  1940. // share is empty
  1941. //
  1942. // Arguments: [in] pwszFileName : the full file name -- must be UNC
  1943. // [in] shStatus : the share status - online, offline, local etc.
  1944. //
  1945. // Returns: ERROR_SUCCESS : if successful
  1946. // a win32 error code if something goes wrong
  1947. //
  1948. // History: 4/22/1999 RahulTh created
  1949. //
  1950. // Notes: we do not have to explicitly check if the share is empty
  1951. // because if it is not, then the delete will fail anyway
  1952. //
  1953. //---------------------------------------------------------------------------
  1954. DWORD DeleteCSCShareIfEmpty (LPCTSTR pwszFileName, SHARESTATUS shStatus)
  1955. {
  1956. DWORD Status;
  1957. WCHAR * pwszFullPath = NULL;
  1958. WCHAR * pwszCurr = NULL;
  1959. int len;
  1960. WCHAR * pwszShrEnd;
  1961. if (PathLocal == shStatus || NoCSC == shStatus)
  1962. return ERROR_SUCCESS;
  1963. if (ShareOffline == shStatus)
  1964. return ERROR_CSCSHARE_OFFLINE;
  1965. len = wcslen (pwszFileName);
  1966. if (len <= 2)
  1967. return ERROR_BAD_PATHNAME;
  1968. if (pwszFileName[0] != L'\\' || pwszFileName[1] != L'\\')
  1969. return ERROR_BAD_PATHNAME;
  1970. __try {
  1971. pwszFullPath = (WCHAR *) alloca (sizeof (WCHAR) * (len + 1));
  1972. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  1973. _resetstkoflw();
  1974. return ERROR_OUTOFMEMORY;
  1975. }
  1976. if (NULL == _wfullpath(pwszFullPath, pwszFileName, len + 1))
  1977. return ERROR_BAD_PATHNAME; //canonicalization was unsuccessful.
  1978. // -- rarely happens
  1979. pwszShrEnd = wcschr (pwszFullPath + 2, L'\\');
  1980. if (NULL == pwszShrEnd)
  1981. return ERROR_BAD_PATHNAME; //the path does not have the share component
  1982. pwszShrEnd++;
  1983. pwszShrEnd = wcschr (pwszShrEnd, L'\\');
  1984. if (NULL == pwszShrEnd)
  1985. {
  1986. //we already have the path in \\server\share form, so just try to
  1987. //delete the share.
  1988. return DeleteCSCFile (pwszFullPath, TRUE);
  1989. }
  1990. //if we are here, then we have a path longer than just \\server\share.
  1991. //so try to delete all the way up to the share name. This is necessary
  1992. //because a user might be redirected to something like
  1993. // \\server\share\folder\%username% and we wouldn't want only \\server\share
  1994. // and \\server\share\folder to be cached.
  1995. Status = ERROR_SUCCESS;
  1996. do
  1997. {
  1998. pwszCurr = wcsrchr (pwszFullPath, L'\\');
  1999. if (NULL == pwszCurr)
  2000. break;
  2001. *pwszCurr = L'\0';
  2002. Status = DeleteCSCFile (pwszFullPath, TRUE);
  2003. //no point trying to delete the parent if deletion of this directory
  2004. //failed.
  2005. if (ERROR_SUCCESS != Status)
  2006. break;
  2007. } while ( pwszCurr > pwszShrEnd );
  2008. return Status;
  2009. }
  2010. //+--------------------------------------------------------------------------
  2011. //
  2012. // Function: MergePinInfo
  2013. //
  2014. // Synopsis: merges the pin info. from the source to destination
  2015. //
  2016. // Arguments: [in] pwszSource : the full path to the source
  2017. // [in] pwszDest : the full path to the destination
  2018. // [in] StatusFrom : CSC status of the source share
  2019. // [in] StatusTo : CSC status of the destination share
  2020. //
  2021. // Returns: ERROR_SUCCESS : if it was successful
  2022. // a Win32 error code otherwise
  2023. //
  2024. // History: 4/23/1999 RahulTh created
  2025. //
  2026. // Notes: the hint flags are a union of the source hint flags and
  2027. // destination hint flags. The pin count is the greater of the
  2028. // source and destination pin count
  2029. //
  2030. // Usually this function should only be called for folders. The
  2031. // CSC rename API handles files well. But this function will work
  2032. // for files as well.
  2033. //
  2034. //---------------------------------------------------------------------------
  2035. DWORD MergePinInfo (LPCTSTR pwszSource, LPCTSTR pwszDest,
  2036. SHARESTATUS StatusFrom, SHARESTATUS StatusTo)
  2037. {
  2038. BOOL bStatus;
  2039. DWORD dwSourceStat, dwDestStat;
  2040. DWORD dwSourcePinCount, dwDestPinCount;
  2041. DWORD dwSourceHints, dwDestHints;
  2042. DWORD Status = ERROR_SUCCESS;
  2043. DWORD i;
  2044. if (ShareOffline == StatusFrom || ShareOffline == StatusTo)
  2045. return ERROR_CSCSHARE_OFFLINE;
  2046. if (ShareOnline != StatusFrom || ShareOnline != StatusTo)
  2047. return ERROR_SUCCESS; //there is nothing to do if one of the shares
  2048. //is local.
  2049. if (!pwszSource || !pwszDest ||
  2050. 0 == wcslen(pwszSource) || 0 == wcslen(pwszDest))
  2051. return ERROR_BAD_PATHNAME;
  2052. bStatus = CSCQueryFileStatus (pwszSource, &dwSourceStat, &dwSourcePinCount,
  2053. &dwSourceHints);
  2054. if (!bStatus)
  2055. return GetLastError();
  2056. bStatus = CSCQueryFileStatus (pwszDest, &dwDestStat, &dwDestPinCount,
  2057. &dwDestHints);
  2058. if (!bStatus)
  2059. return GetLastError();
  2060. //first set the hint flags on the destination
  2061. if (dwDestHints != dwSourceHints)
  2062. {
  2063. bStatus = CSCPinFile (pwszDest, dwSourceHints, &dwDestStat,
  2064. &dwDestPinCount, &dwDestHints);
  2065. if (!bStatus)
  2066. Status = GetLastError(); //note: we do not bail out here. we try
  2067. //to at least merge the pin count before
  2068. //leaving
  2069. }
  2070. //now merge the pin count : there is nothing to be done if the destination
  2071. //pin count is greater than or equal to the source pin count
  2072. if (dwDestPinCount < dwSourcePinCount)
  2073. {
  2074. for (i = 0, bStatus = TRUE; i < (dwSourcePinCount - dwDestPinCount) &&
  2075. bStatus;
  2076. i++)
  2077. {
  2078. bStatus = CSCPinFile( pwszDest,
  2079. FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT,
  2080. NULL, NULL, NULL );
  2081. }
  2082. if (!bStatus && ERROR_SUCCESS == Status)
  2083. Status = GetLastError();
  2084. }
  2085. return Status;
  2086. }
  2087. //+--------------------------------------------------------------------------
  2088. //
  2089. // Function: PinIfNecessary
  2090. //
  2091. // Synopsis: this function pins a file if necessary.
  2092. //
  2093. // Arguments: [in] pwszPath : full path of the file/folder to be pinned
  2094. // [in] shStatus : CSC status of the share.
  2095. //
  2096. // Returns: ERROR_SUCCESS if it was successful. An error code otherwise.
  2097. //
  2098. // History: 5/27/1999 RahulTh created
  2099. //
  2100. // Notes:
  2101. //
  2102. //---------------------------------------------------------------------------
  2103. DWORD PinIfNecessary (const WCHAR * pwszPath, SHARESTATUS shStatus)
  2104. {
  2105. DWORD Status = ERROR_SUCCESS;
  2106. BOOL bStatus;
  2107. DWORD dwStatus;
  2108. DWORD dwPinCount;
  2109. DWORD dwHints;
  2110. if (!pwszPath || !pwszPath[0])
  2111. return ERROR_BAD_NETPATH;
  2112. if (! g_bCSCEnabled)
  2113. return ERROR_SUCCESS;
  2114. if (ShareOffline == shStatus)
  2115. return ERROR_CSCSHARE_OFFLINE;
  2116. else if (PathLocal == shStatus || NoCSC == shStatus)
  2117. return ERROR_SUCCESS;
  2118. bStatus = CSCQueryFileStatus (pwszPath, &dwStatus, &dwPinCount, &dwHints);
  2119. if (!bStatus || dwPinCount <= 0)
  2120. {
  2121. bStatus = CSCPinFile (pwszPath,
  2122. FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT,
  2123. NULL, NULL, NULL);
  2124. if (!bStatus)
  2125. Status = GetLastError();
  2126. }
  2127. return Status;
  2128. }
  2129. //+--------------------------------------------------------------------------
  2130. //
  2131. // Function: CacheDesktopIni
  2132. //
  2133. // Synopsis: some special folders use the desktop.ini file to present
  2134. // special views in explorer (e.g. My Pictures). If a folder is
  2135. // redirected to a network share and a user goes offline without
  2136. // ever looking at the folder in explorer, the desktop.ini file
  2137. // is not in the cache and therefore the special views are lost
  2138. //
  2139. // This function tries to cache the desktop.ini file as soon as
  2140. // the folder is pinned so that special views are not lost even
  2141. // if the user goes offline
  2142. //
  2143. // When the folder is unpinned, this function merely unpins the
  2144. // dekstop.ini file
  2145. //
  2146. // Arguments: [in] pwszPath : the path to the folder
  2147. // [in] shStatus : the CSC status of the share
  2148. // [in] uCommand : Pin/Unpin
  2149. //
  2150. // Returns: ERROR_SUCCESS if everything is successful.
  2151. // a win32 error code otherwise.
  2152. //
  2153. // History: 4/25/1999 RahulTh created
  2154. //
  2155. // Notes: this function should only be called after the folder has been
  2156. // successfully redirected.
  2157. //
  2158. // if desktop.ini is already pinned, this function does not try
  2159. // to pin it again.
  2160. //
  2161. //---------------------------------------------------------------------------
  2162. DWORD CacheDesktopIni (LPCTSTR pwszPath, SHARESTATUS shStatus, CSCPINCOMMAND uCommand)
  2163. {
  2164. int len;
  2165. WCHAR szDesktopIniName[] = L"desktop.ini";
  2166. WCHAR * pwszDesktopIni = NULL;
  2167. size_t desktopIniSize = 0;
  2168. BOOL bStatus;
  2169. DWORD Status = ERROR_SUCCESS;
  2170. DWORD dwStatus;
  2171. DWORD dwPinCount;
  2172. DWORD dwHints;
  2173. if (PathLocal == shStatus)
  2174. return ERROR_SUCCESS; //nothing to be done.
  2175. if (ShareOffline == shStatus)
  2176. return ERROR_CSCSHARE_OFFLINE;
  2177. len = wcslen (pwszPath);
  2178. if (!pwszPath || 0 == len)
  2179. return ERROR_BAD_PATHNAME;
  2180. desktopIniSize = len + wcslen(szDesktopIniName) + 2; //extra char for backslash
  2181. __try {
  2182. pwszDesktopIni = (WCHAR *) alloca (sizeof (WCHAR) * desktopIniSize);
  2183. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  2184. _resetstkoflw();
  2185. return ERROR_OUTOFMEMORY;
  2186. }
  2187. (void) StringCchCopy(pwszDesktopIni, desktopIniSize, pwszPath);
  2188. if (L'\\' != pwszDesktopIni[len - 1])
  2189. {
  2190. pwszDesktopIni[len] = L'\\';
  2191. pwszDesktopIni[len + 1] = L'\0';
  2192. }
  2193. (void) StringCchCat(pwszDesktopIni, desktopIniSize, szDesktopIniName);
  2194. if (PinFile == uCommand)
  2195. {
  2196. bStatus = CSCQueryFileStatus (pwszDesktopIni, &dwStatus, &dwPinCount,
  2197. &dwHints);
  2198. //pin only if it has not already been pinned.
  2199. if (!bStatus || dwPinCount <= 0)
  2200. {
  2201. if (bStatus = CSCPinFile(pwszDesktopIni,
  2202. FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT,
  2203. NULL, NULL, NULL)
  2204. )
  2205. {
  2206. bStatus = CSCFillSparseFiles(pwszDesktopIni, FALSE,
  2207. CSCCallbackProc, 0);
  2208. }
  2209. if (!bStatus)
  2210. Status = GetLastError();
  2211. }
  2212. }
  2213. else if (UnpinFile == uCommand)
  2214. {
  2215. if (! CSCUnpinFile (pwszDesktopIni, FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT,
  2216. NULL, NULL, NULL))
  2217. Status = GetLastError();
  2218. }
  2219. return Status;
  2220. }
  2221. //+--------------------------------------------------------------------------
  2222. //
  2223. // Function: CSCCallbackProc
  2224. //
  2225. // Synopsis: callback function for CSCFillSparseFiles
  2226. //
  2227. // Arguments: see docs
  2228. //
  2229. // Returns: see docs
  2230. //
  2231. // History: 4/26/1999 RahulTh created
  2232. //
  2233. // Notes: just returns a default value for all...
  2234. //
  2235. //---------------------------------------------------------------------------
  2236. DWORD WINAPI
  2237. CSCCallbackProc(LPCTSTR pszName,
  2238. DWORD dwStatus,
  2239. DWORD dwHintFlags,
  2240. DWORD dwPinCount,
  2241. LPWIN32_FIND_DATA pFind32,
  2242. DWORD dwReason,
  2243. DWORD dwParam1,
  2244. DWORD dwParam2,
  2245. DWORD_PTR dwContext)
  2246. {
  2247. return CSCPROC_RETURN_CONTINUE;
  2248. }
  2249. //+--------------------------------------------------------------------------
  2250. //
  2251. // Function: UpdateMyPicsShellLink
  2252. //
  2253. // Synopsis: there is no easy way to get to My Pictures if it is redirected
  2254. // independently of My Documents since the shell does not really
  2255. // address this issue. Therefore, this function is required to
  2256. // make sure that if My Pictures is not located directly under
  2257. // My Documents, there is at least a shell link pointing to the
  2258. // location of My Pictures. This function also makes sure that
  2259. // the shell link is cached in case My Documents is on a network
  2260. // share. This way, if the share were to go offline before the user
  2261. // had a chance to access My Documents, then the shell link would
  2262. // still be accessible.
  2263. //
  2264. // Also, if My Pictures is a direct descendant of My Documents,
  2265. // this function gets rid of the shell link to minimize user
  2266. // confusion.
  2267. //
  2268. // Arguments: [in] pwszMyPicsLocName : localized display name of My Pictures.
  2269. //
  2270. // Returns: S_OK : if everything went smoothly.
  2271. // an HRESULT derived from the error if there is one.
  2272. //
  2273. // History: 5/2/1999 RahulTh created
  2274. // 2/14/2001 RahulTh Fixed shortcut creation problem in free
  2275. // threaded COM by using SHCoCreateInstance
  2276. // to short-circuit COM.
  2277. //
  2278. // Notes: This function shall not be required if the shell comes up with
  2279. // an easy way to get to My Pictures even when it does not lie
  2280. // directly under My Documents.
  2281. //
  2282. //---------------------------------------------------------------------------
  2283. HRESULT UpdateMyPicsShellLinks (HANDLE hUserToken, const WCHAR * pwszMyPicsLocName)
  2284. {
  2285. WCHAR szMyDocsPath [TARGETPATHLIMIT];
  2286. WCHAR szMyPicsPath [TARGETPATHLIMIT];
  2287. WCHAR * pwszMyPicsLink = NULL;
  2288. size_t myPicsLinkSize = 0;
  2289. int MyDocsLen;
  2290. int MyPicsLen;
  2291. HRESULT hr = S_OK;
  2292. SHARESTATUS MyDocsCSCStatus;
  2293. BOOL fMyPicsFollows = TRUE;
  2294. BOOL bStatus;
  2295. DWORD Status = ERROR_SUCCESS;
  2296. IShellLink * psl = NULL;
  2297. IPersistFile * ppf = NULL;
  2298. DWORD dwPinCount;
  2299. DWORD dwHints;
  2300. DWORD dwCSCState;
  2301. BOOL bComInitialized = FALSE;
  2302. //make sure that we have the user token.
  2303. if (!hUserToken)
  2304. {
  2305. hr = E_FAIL;
  2306. goto UpdateMyPicsLink_End;
  2307. }
  2308. //now get the path to My Documents
  2309. hr = SHGetFolderPath(0, CSIDL_PERSONAL | CSIDL_FLAG_DONT_VERIFY,
  2310. hUserToken, 0, szMyDocsPath);
  2311. if (S_OK != hr)
  2312. goto UpdateMyPicsLink_End;
  2313. //now make sure that we can get enough memory to store the path to the
  2314. //shell link.
  2315. MyDocsLen = wcslen (szMyDocsPath);
  2316. if (0 == MyDocsLen)
  2317. {
  2318. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  2319. goto UpdateMyPicsLink_End;
  2320. }
  2321. myPicsLinkSize = MyDocsLen + wcslen(pwszMyPicsLocName) + 6;
  2322. __try {
  2323. pwszMyPicsLink = (WCHAR *) alloca (sizeof(WCHAR) * myPicsLinkSize);
  2324. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  2325. _resetstkoflw();
  2326. pwszMyPicsLink = NULL;
  2327. hr = E_OUTOFMEMORY;
  2328. goto UpdateMyPicsLink_End;
  2329. }
  2330. //now make sure that the share on which My Docs exists is not offline
  2331. MyDocsCSCStatus = GetCSCStatus (szMyDocsPath);
  2332. if (ShareOffline == MyDocsCSCStatus)
  2333. {
  2334. hr = HRESULT_FROM_WIN32 (ERROR_CSCSHARE_OFFLINE);
  2335. goto UpdateMyPicsLink_End;
  2336. }
  2337. //now construct the path to the shell link to MyPics in the MyDocs folder
  2338. (void) StringCchCopy(pwszMyPicsLink, myPicsLinkSize, szMyDocsPath);
  2339. if (L'\\' != szMyDocsPath[MyDocsLen - 1])
  2340. {
  2341. pwszMyPicsLink[MyDocsLen] = L'\\';
  2342. pwszMyPicsLink[MyDocsLen+1] = L'\0';
  2343. }
  2344. (void) StringCchCat(pwszMyPicsLink, myPicsLinkSize, pwszMyPicsLocName);
  2345. (void) StringCchCat(pwszMyPicsLink, myPicsLinkSize, L".lnk");
  2346. //now obtain the path to My Pictures
  2347. hr = SHGetFolderPath (0, CSIDL_MYPICTURES | CSIDL_FLAG_DONT_VERIFY,
  2348. hUserToken, 0, szMyPicsPath);
  2349. if (S_OK != hr)
  2350. goto UpdateMyPicsLink_End;
  2351. //now find out if MyPics is a descendant of My Docs.
  2352. MyPicsLen = wcslen (szMyPicsPath);
  2353. if (0 == MyPicsLen)
  2354. {
  2355. hr = HRESULT_FROM_WIN32 (ERROR_BAD_PATHNAME);
  2356. goto UpdateMyPicsLink_End;
  2357. }
  2358. if (MyPicsLen <= MyDocsLen ||
  2359. 0 != _wcsnicmp (szMyPicsPath, szMyDocsPath, MyDocsLen)
  2360. )
  2361. {
  2362. fMyPicsFollows = FALSE;
  2363. }
  2364. //delete the shell link if MyPics is supposed to follow MyDocs.
  2365. if (fMyPicsFollows)
  2366. {
  2367. Status = ERROR_SUCCESS;
  2368. if (!DeleteFile (pwszMyPicsLink))
  2369. {
  2370. Status = GetLastError();
  2371. if (ERROR_PATH_NOT_FOUND == Status ||
  2372. ERROR_FILE_NOT_FOUND == Status)
  2373. Status = ERROR_SUCCESS;
  2374. }
  2375. hr = HRESULT_FROM_WIN32 (Status);
  2376. goto UpdateMyPicsLink_End;
  2377. }
  2378. //if we are here, we need to create/update the shell link.
  2379. //
  2380. // Note: We need to use SHCoCreateInstance. This is because the GP engine
  2381. // is now freethreaded whereas the threading model for CLSID_ShellLink is
  2382. // still Apartment. This means that if we use CoCreateInstance, it will
  2383. // create the object on a COM thread in its own apartment. This means that
  2384. // it will run as LocalSystem and not impersonated as the logged on user.
  2385. // This is not what we want, especially when we are creating shortcuts on
  2386. // a network share and we need to go across the wire as the logged on user
  2387. // rather than the machine account in order to authenticate successfully.
  2388. // To get around this problem, we use SHCoCreateInstance() which is an
  2389. // internal shell API that completely short-circuits COM and calls into
  2390. // shell32's DllGetClassObject directly thus creating the objects on the
  2391. // same thread. This API is primarily for shell user *ONLY* to handle crufty
  2392. // compat stuff so use this *VERY* *VERY *SPARINGLY*. If you want to do
  2393. // anything remotely fancy with IShellLink, check with the shell team to
  2394. // make sure that it is still okay to use the SHCoCreateInstance API.
  2395. //
  2396. hr = CoInitialize (NULL);
  2397. if (SUCCEEDED(hr))
  2398. bComInitialized = TRUE;
  2399. if (SUCCEEDED (hr) || RPC_E_CHANGED_MODE == hr) // If we successfully initialized COM or if it was already initialized.
  2400. {
  2401. hr = SHCoCreateInstance(NULL,
  2402. &CLSID_ShellLink,
  2403. NULL,
  2404. IID_IShellLink,
  2405. (LPVOID*)&psl);
  2406. if (FAILED(hr))
  2407. psl = NULL; // For safety.
  2408. }
  2409. if (SUCCEEDED(hr))
  2410. hr = psl->SetPath (szMyPicsPath);
  2411. if (SUCCEEDED(hr))
  2412. hr = psl->SetDescription (pwszMyPicsLocName);
  2413. if (SUCCEEDED(hr))
  2414. hr = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
  2415. if (SUCCEEDED(hr))
  2416. {
  2417. hr = ppf->Save(pwszMyPicsLink, TRUE);
  2418. ppf->Release();
  2419. }
  2420. // Release IShellLink if necessary.
  2421. if (psl)
  2422. psl->Release();
  2423. // Uninitialize COM if necessary.
  2424. if (bComInitialized)
  2425. {
  2426. CoUninitialize();
  2427. bComInitialized = FALSE;
  2428. }
  2429. //also cache the shell link if MyDocs is redirected to a cacheable
  2430. //network share
  2431. if (SUCCEEDED(hr) && ShareOnline == MyDocsCSCStatus)
  2432. {
  2433. Status = ERROR_SUCCESS;
  2434. bStatus = TRUE;
  2435. bStatus = CSCQueryFileStatus (pwszMyPicsLink, &dwCSCState,
  2436. &dwPinCount, &dwHints);
  2437. if (bStatus)
  2438. {
  2439. if (0 == dwPinCount && (!(dwHints & FLAG_CSC_HINT_PIN_USER)))
  2440. {
  2441. if (bStatus = CSCPinFile(pwszMyPicsLink,
  2442. FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT,
  2443. NULL, NULL, NULL)
  2444. )
  2445. {
  2446. bStatus = CSCFillSparseFiles(pwszMyPicsLink, FALSE,
  2447. CSCCallbackProc, 0);
  2448. }
  2449. }
  2450. }
  2451. if (!bStatus)
  2452. {
  2453. Status = GetLastError();
  2454. }
  2455. hr = HRESULT_FROM_WIN32 (Status);
  2456. }
  2457. UpdateMyPicsLink_End:
  2458. if (FAILED(hr))
  2459. {
  2460. DebugMsg ((DM_VERBOSE, IDS_MYPICSLINK_FAILED, GetWin32ErrFromHResult(hr)));
  2461. }
  2462. else
  2463. {
  2464. DebugMsg ((DM_VERBOSE, IDS_MYPICSLINK_SUCCEEDED, szMyDocsPath));
  2465. }
  2466. return hr;
  2467. }
  2468. //+--------------------------------------------------------------------------
  2469. //
  2470. // Function: LoadLocalizedFolderNames
  2471. //
  2472. // Synopsis: loads the localized folder names into global objects of
  2473. // CRedirectInfo class.
  2474. //
  2475. // Arguments: none.
  2476. //
  2477. // Returns: ERROR_SUCCESS if everything was successful.
  2478. // an error code otherwise.
  2479. //
  2480. // History: 5/6/1999 RahulTh created
  2481. //
  2482. // Notes: the function bails out at the first failure. Also see notes
  2483. // on CRedirectInfo::LoadLocalizedNames.
  2484. //
  2485. //---------------------------------------------------------------------------
  2486. DWORD LoadLocalizedFolderNames (void)
  2487. {
  2488. DWORD i;
  2489. DWORD Status;
  2490. for (i = 0, Status = ERROR_SUCCESS; i < (DWORD)EndRedirectable; i++)
  2491. {
  2492. Status = gPolicyResultant[i].LoadLocalizedNames();
  2493. if (ERROR_SUCCESS == Status)
  2494. Status = gAddedPolicyResultant[i].LoadLocalizedNames();
  2495. if (ERROR_SUCCESS == Status)
  2496. Status = gDeletedPolicyResultant[i].LoadLocalizedNames();
  2497. if (ERROR_SUCCESS != Status)
  2498. break;
  2499. }
  2500. return Status;
  2501. }
  2502. //+--------------------------------------------------------------------------
  2503. //
  2504. // Function: DeleteCachedConfigFiles
  2505. //
  2506. // Synopsis: deletes the locally cached copies of fdeploy.ini for GPOs
  2507. // in a list of GPOs.
  2508. //
  2509. // Arguments: [in] pGPOList : the list of GPOs
  2510. // [in] pFileDB : a filedb object containing info. about the
  2511. // location of the files etc.
  2512. //
  2513. // Returns: ERROR_SUCCESS if all the files were successfully deleted.
  2514. // an error code otherwise.
  2515. //
  2516. // History: 5/27/1999 RahulTh created
  2517. //
  2518. // Notes: this function tries its best to delete as many files as
  2519. // possible
  2520. //
  2521. //---------------------------------------------------------------------------
  2522. DWORD DeleteCachedConfigFiles (const PGROUP_POLICY_OBJECT pGPOList,
  2523. CFileDB * pFileDB)
  2524. {
  2525. DWORD Status = ERROR_SUCCESS;
  2526. DWORD StrStatus = ERROR_SUCCESS;
  2527. WCHAR * pwszPath = NULL;
  2528. size_t pathSize = 0;
  2529. WCHAR * pwszEnd = NULL;
  2530. size_t endSize = 0;
  2531. int len;
  2532. PGROUP_POLICY_OBJECT pGPO;
  2533. len = wcslen (pFileDB->GetLocalStoragePath());
  2534. pathSize = len + MAX_PATH + 2;
  2535. __try {
  2536. pwszPath = (WCHAR *) alloca (sizeof (WCHAR) * pathSize);
  2537. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  2538. _resetstkoflw();
  2539. return ERROR_OUTOFMEMORY;
  2540. }
  2541. (void) StringCchCopy(pwszPath, pathSize, pFileDB->GetLocalStoragePath());
  2542. (void) StringCchCat(pwszPath, pathSize, L"\\");
  2543. pwszEnd = pwszPath + len + 1;
  2544. endSize = pathSize - len - 1;
  2545. for (pGPO = pGPOList; pGPO; pGPO = pGPO->pNext)
  2546. {
  2547. StrStatus = HRESULT_CODE(StringCchCopy(pwszEnd, endSize, pGPO->szGPOName));
  2548. if ( StrStatus == ERROR_SUCCESS )
  2549. {
  2550. StrStatus = HRESULT_CODE(StringCchCat(pwszEnd, endSize, L".ini"));
  2551. if ( StrStatus == ERROR_SUCCESS )
  2552. {
  2553. if (!DeleteFile (pwszPath) && ERROR_SUCCESS == Status)
  2554. {
  2555. Status = GetLastError();
  2556. if (ERROR_PATH_NOT_FOUND == Status ||
  2557. ERROR_FILE_NOT_FOUND == Status)
  2558. {
  2559. Status = ERROR_SUCCESS;
  2560. }
  2561. }
  2562. }
  2563. }
  2564. // If we could not build a specific file name, we are still going to go on,
  2565. // but will cache this error code to be returned by the function at the end.
  2566. if ( (StrStatus != ERROR_SUCCESS) && (Status == ERROR_SUCCESS) )
  2567. {
  2568. Status = StrStatus;
  2569. }
  2570. }
  2571. return Status;
  2572. }
  2573. //+--------------------------------------------------------------------------
  2574. //
  2575. // Function: SimplifyPath
  2576. //
  2577. // Synopsis: given a path, this function tries to simplify it by
  2578. // canonicalizing it and removing extra slashes by calling
  2579. // _wfullpath
  2580. //
  2581. // Arguments: [in/out] pwszPath : the given path
  2582. //
  2583. // Returns: nothing
  2584. //
  2585. // History: 5/27/1999 RahulTh created
  2586. //
  2587. // Notes: the function tries its best to simplify the path. If it fails,
  2588. // it simply returns the path supplied originally.
  2589. //
  2590. //---------------------------------------------------------------------------
  2591. void SimplifyPath (WCHAR * pwszPath)
  2592. {
  2593. size_t len;
  2594. WCHAR * pwszAbsPath;
  2595. if (!pwszPath || !pwszPath[0])
  2596. return;
  2597. len = wcslen (pwszPath);
  2598. __try {
  2599. pwszAbsPath = (WCHAR *) alloca (sizeof (WCHAR) * (len + 1));
  2600. } __except(GetExceptionCode() == STATUS_STACK_OVERFLOW) {
  2601. _resetstkoflw();
  2602. return;
  2603. }
  2604. if (NULL != _wfullpath (pwszAbsPath, pwszPath, len + 1) &&
  2605. wcslen (pwszAbsPath) <= len)
  2606. {
  2607. (void) StringCchCopy(pwszPath, len + 1, pwszAbsPath);
  2608. }
  2609. return;
  2610. }
  2611. //+--------------------------------------------------------------------------
  2612. //
  2613. // Function: PrecreateUnicodeIniFile
  2614. //
  2615. // Synopsis: The WritePrivateProfile* functions do not write in unicode
  2616. // unless the file already exists in unicode format. Therefore,
  2617. // this function is used to precreate a unicode file so that
  2618. // the WritePrivateProfile* functions can preserve the unicodeness.
  2619. //
  2620. // Arguments: [in] lpszFilePath : the full path of the ini file.
  2621. //
  2622. // Returns: ERROR_SUCCESS if successful.
  2623. // an error code otherwise.
  2624. //
  2625. // History: 7/9/1999 RahulTh created
  2626. //
  2627. // Notes:
  2628. //
  2629. //---------------------------------------------------------------------------
  2630. DWORD PrecreateUnicodeIniFile (LPCTSTR lpszFilePath)
  2631. {
  2632. HANDLE hFile;
  2633. WIN32_FILE_ATTRIBUTE_DATA fad;
  2634. DWORD Status = ERROR_ALREADY_EXISTS;
  2635. DWORD dwWritten;
  2636. if (!GetFileAttributesEx (lpszFilePath, GetFileExInfoStandard, &fad))
  2637. {
  2638. if (ERROR_FILE_NOT_FOUND == (Status = GetLastError()))
  2639. {
  2640. hFile = CreateFile(lpszFilePath, GENERIC_WRITE, 0, NULL,
  2641. CREATE_NEW, FILE_ATTRIBUTE_HIDDEN, NULL);
  2642. if (hFile != INVALID_HANDLE_VALUE)
  2643. {
  2644. BOOL bRet;
  2645. //add the unicode mask to the beginning of the file so that
  2646. //ReadPrivate* APIs do not accidentally think that this an ANSI
  2647. //file.
  2648. bRet = WriteFile(hFile, L"\xfeff\r\n", 3 * sizeof(WCHAR), &dwWritten, NULL);
  2649. //add a few unicode characters just to be safe.
  2650. if (bRet)
  2651. {
  2652. bRet = WriteFile(hFile, L" \r\n", 7 * sizeof(WCHAR),
  2653. &dwWritten, NULL);
  2654. }
  2655. if (bRet)
  2656. {
  2657. Status = ERROR_SUCCESS;
  2658. }
  2659. else
  2660. {
  2661. Status = GetLastError();
  2662. }
  2663. CloseHandle(hFile);
  2664. }
  2665. else
  2666. {
  2667. Status = GetLastError();
  2668. }
  2669. }
  2670. }
  2671. return Status;
  2672. }
  2673. //+--------------------------------------------------------------------------
  2674. //
  2675. // Function: IsPathLocal
  2676. //
  2677. // Synopsis: this function determines if a given path is a local path
  2678. // or a UNC path.
  2679. //
  2680. // Arguments: pwszPath : the full path of the file.
  2681. //
  2682. // Returns: FALSE : if it is a UNC path.
  2683. // TRUE : otherwise
  2684. //
  2685. // History: 7/30/1999 RahulTh created
  2686. //
  2687. // Notes: this function basically returns TRUE unless the first
  2688. // characters of the supplied path are '\'.
  2689. //
  2690. //---------------------------------------------------------------------------
  2691. BOOL IsPathLocal (LPCWSTR pwszPath)
  2692. {
  2693. if (NULL == pwszPath || 2 > wcslen (pwszPath))
  2694. return TRUE; //assume local
  2695. if (L'\\' == pwszPath[0] && L'\\' == pwszPath[1])
  2696. return FALSE;
  2697. return TRUE; //local in all other cases.
  2698. }
  2699. //+--------------------------------------------------------------------------
  2700. //
  2701. // Function: ExpandPathSpecial
  2702. //
  2703. // Synopsis: expands a path using a given user name.
  2704. //
  2705. // Arguments: [in] pFileDB : pointer to the CFileDB object
  2706. // [in] pwszPath : pointer to the path to be expanded.
  2707. // [in] pwszUserName : the user name to be used in expansion
  2708. // [out] wszExpandedPath : the expanded path.
  2709. // [in, out] pDesiredBufferSize (optional) : on input, the size
  2710. // of the wszExpandedPath buffer. On output, the size needed
  2711. // to expand the path to that buffer. This may be NULL, in which
  2712. // case the buffer is assumed to be TARGETPATHLIMIT size.
  2713. //
  2714. // Returns: ERROR_SUCCESS : if the expanded path was successfully obtained.
  2715. // STATUS_BUFFER_TOO_SMALL : if the expanded path was too large
  2716. // to fit in the supplied buffer.
  2717. // other win32 error codes based on what goes wrong.
  2718. //
  2719. // History: 9/20/1999 RahulTh created
  2720. //
  2721. // Notes: wszExpandedPath is assumed to be a buffer containing
  2722. // TARGETPATHLIMIT characters, unlesss pDesiredBufferSize
  2723. // is specified. It is the caller's responsibility
  2724. // to allocate/free this memory. This function does not try to
  2725. // validate the memory that wszExpandedPath points to.
  2726. //
  2727. // This function is also not equipped to handle multiple
  2728. // occurrences of the %username% substring in the last path.
  2729. //
  2730. //---------------------------------------------------------------------------
  2731. DWORD ExpandPathSpecial (
  2732. CFileDB * pFileDB,
  2733. const WCHAR * pwszPath,
  2734. const WCHAR * pwszUserName,
  2735. WCHAR * wszExpandedPath,
  2736. ULONG * pDesiredBufferSize
  2737. )
  2738. {
  2739. DWORD Status = ERROR_SUCCESS;
  2740. UNICODE_STRING Path;
  2741. UNICODE_STRING ExpandedPath;
  2742. WCHAR wszLastPath [TARGETPATHLIMIT];
  2743. WCHAR * wszTemp = NULL;
  2744. WCHAR wszSuffix [TARGETPATHLIMIT];
  2745. if (TARGETPATHLIMIT <= wcslen(pwszPath))
  2746. return STATUS_BUFFER_TOO_SMALL;
  2747. wszSuffix[0] = L'\0';
  2748. wszLastPath[0] = L'\0';
  2749. (void) StringCchCopy(wszLastPath, TARGETPATHLIMIT, pwszPath);
  2750. //convert it to lower case. useful while searching for other strings
  2751. //within the string.
  2752. _wcslwr (wszLastPath);
  2753. //
  2754. // If the caller does not specify a user name, just use environment
  2755. // variable substitution to take care of %username% instead of
  2756. // doing it ourselves
  2757. //
  2758. if ( pwszUserName )
  2759. {
  2760. //the username has changed since the last logon. must first substitute
  2761. //the occurence of %username% if any with the supplied user name
  2762. wszTemp = wcsstr (wszLastPath, L"%username%");
  2763. }
  2764. //if the %username% string is not present, nothing more needs to be
  2765. //done. we can just go ahead and expand what we've got.
  2766. if (NULL != wszTemp)
  2767. {
  2768. //the last path contains %username%
  2769. //get the parts that appear before and after the %username% string
  2770. //reuse wszLastPath for the prefix
  2771. *wszTemp = L'\0';
  2772. (void) StringCchCopy(wszSuffix, TARGETPATHLIMIT, wszTemp + 10); //go past %username%
  2773. //make sure that we are not running out of space.
  2774. if (TARGETPATHLIMIT <=
  2775. wcslen (wszLastPath) +
  2776. wcslen (pwszUserName) +
  2777. wcslen (wszSuffix))
  2778. {
  2779. return STATUS_BUFFER_TOO_SMALL;
  2780. }
  2781. (void) StringCchCat(wszLastPath, TARGETPATHLIMIT, pwszUserName);
  2782. (void) StringCchCat(wszLastPath, TARGETPATHLIMIT, wszSuffix);
  2783. }
  2784. USHORT ExpandedBufferMax = (USHORT)(pDesiredBufferSize ? *pDesiredBufferSize : TARGETPATHLIMIT);
  2785. //
  2786. // In planning mode, we are only interested in %username% --
  2787. // any other environment variables can remain unexpanded since
  2788. // they may depend on local state to which we do not have access
  2789. // in planning mode.
  2790. //
  2791. if ( pFileDB->GetRsopContext()->IsPlanningModeEnabled() )
  2792. {
  2793. if ( ERROR_SUCCESS == Status )
  2794. {
  2795. Status = HRESULT_CODE(StringCchCopy( wszExpandedPath, ExpandedBufferMax, wszLastPath ));
  2796. if ( (Status != ERROR_SUCCESS) && (pDesiredBufferSize != NULL) )
  2797. {
  2798. *pDesiredBufferSize = wcslen(wszLastPath) + 1;
  2799. }
  2800. }
  2801. return Status;
  2802. }
  2803. //now expand other variables in the path
  2804. Path.Length = (wcslen (wszLastPath) + 1) * sizeof (WCHAR);
  2805. Path.MaximumLength = sizeof (wszLastPath);
  2806. Path.Buffer = wszLastPath;
  2807. ExpandedPath.Length = 0;
  2808. ExpandedPath.MaximumLength = ExpandedBufferMax * sizeof (WCHAR);
  2809. ExpandedPath.Buffer = wszExpandedPath;
  2810. Status = RtlExpandEnvironmentStrings_U (
  2811. pFileDB->GetEnvBlock(),
  2812. &Path,
  2813. &ExpandedPath,
  2814. pDesiredBufferSize
  2815. );
  2816. return Status;
  2817. }
  2818. //+--------------------------------------------------------------------------
  2819. //
  2820. // Function: ExpandHomeDir
  2821. //
  2822. // Synopsis: Expands the HOMEDIR component in the path.
  2823. //
  2824. // Arguments: [in] rID : the id of the folder
  2825. // [in] pwszPath : the unexpanded path
  2826. // [in] bAllowMyPics : allow expansion of homedir for MyPics
  2827. // [out] ppwszExpandedPath : the expanded result
  2828. // [in, optional] pwszHomedir : the value of homedir to be used in substitution
  2829. //
  2830. // Returns: ERROR_SUCCESS : if successful.
  2831. // a Win32 error code otherwise.
  2832. //
  2833. // History: 3/10/2000 RahulTh created
  2834. //
  2835. // Notes: This function has the following additional restrictions:
  2836. // (a) It is a no-op for all folders except MyDocs and MyPics.
  2837. // (b) It only acts on paths that begin with
  2838. // %HOMESHARE%%HOMEPATH%
  2839. //
  2840. // Also, if the function does not return ERROR_SUCCESS, then
  2841. // *ppwszExpandedPath can be null.
  2842. //
  2843. // if the caller does not supply the homedir value for
  2844. // substitution, the value is obtained from the user object.
  2845. //
  2846. //---------------------------------------------------------------------------
  2847. DWORD ExpandHomeDir (IN REDIRECTABLE rID,
  2848. IN const WCHAR * pwszPath,
  2849. BOOL bAllowMyPics,
  2850. OUT WCHAR ** ppwszExpandedPath,
  2851. IN const WCHAR * pwszHomedir // = NULL
  2852. )
  2853. {
  2854. DWORD Status = ERROR_SUCCESS;
  2855. int len;
  2856. const WCHAR * wszSuffix;
  2857. int expandedLen;
  2858. int lenHomedir;
  2859. // First free the supplied buffer if necessary
  2860. if (*ppwszExpandedPath)
  2861. {
  2862. delete [] *ppwszExpandedPath;
  2863. *ppwszExpandedPath = NULL;
  2864. }
  2865. if (! pwszPath || L'\0' == *pwszPath)
  2866. {
  2867. Status = ERROR_BAD_PATHNAME;
  2868. goto ExpandHomeDir_End;
  2869. }
  2870. // Proceed only if the HOMEDIR component is in the right place.
  2871. if (! IsHomedirPath (rID, pwszPath, bAllowMyPics))
  2872. {
  2873. if (! HasHomeVariables (pwszPath))
  2874. {
  2875. goto ExpandHomeDir_ReturnSame;
  2876. }
  2877. else
  2878. {
  2879. // This is not a homedir path, so it is not supposed to have any home variables
  2880. Status = ERROR_BAD_PATHNAME;
  2881. goto ExpandHomeDir_End;
  2882. }
  2883. }
  2884. //
  2885. // If we are here, then we need to substitute the HOMEDIR part
  2886. // with the actual home directory. We use the current homedir value
  2887. // only if the caller hasn't already supplied us with one.
  2888. //
  2889. len = lstrlen (HOMEDIR_STR);
  2890. if (! pwszHomedir)
  2891. pwszHomedir = gUserInfo.GetHomeDir(Status);
  2892. //
  2893. // At this point, pwszHomeDir can be NULL even if Status is ERROR_SUCCESS
  2894. // This happens if the user object does not have a homedir set on it
  2895. // So we must fail even in that case.
  2896. //
  2897. if (! pwszHomedir || ERROR_SUCCESS != Status)
  2898. {
  2899. // Do not clobber status set by GetHomeDir if it had failed.
  2900. Status = (ERROR_SUCCESS == Status) ? ERROR_BAD_PATHNAME : Status;
  2901. goto ExpandHomeDir_End;
  2902. }
  2903. //
  2904. // Now we have everything we need to proceed.
  2905. // First allocate the required buffer.
  2906. //
  2907. lenHomedir = lstrlen (pwszHomedir);
  2908. expandedLen = lenHomedir + lstrlen (&pwszPath[len]);
  2909. *ppwszExpandedPath = new WCHAR [expandedLen + 1];
  2910. if (! *ppwszExpandedPath)
  2911. {
  2912. Status = ERROR_OUTOFMEMORY;
  2913. goto ExpandHomeDir_End;
  2914. }
  2915. // Generate the new path.
  2916. (void) StringCchCopy(*ppwszExpandedPath, expandedLen + 1, pwszHomedir);
  2917. // Append the suffix
  2918. wszSuffix = &pwszPath[len];
  2919. // Eliminate duplicate '\' characters
  2920. if (L'\\' == (*ppwszExpandedPath)[lenHomedir - 1] && L'\\' == pwszPath[len])
  2921. wszSuffix++;
  2922. (void) StringCchCat(*ppwszExpandedPath, expandedLen + 1, wszSuffix);
  2923. DebugMsg ((DM_VERBOSE, IDS_HOMEDIR_EXPANDED, pwszPath, *ppwszExpandedPath));
  2924. goto ExpandHomeDir_End;
  2925. //
  2926. // If we are here, then, there was no error, but no substitution was
  2927. // necessary either. So we just return the same path.
  2928. //
  2929. ExpandHomeDir_ReturnSame:
  2930. len = lstrlen (pwszPath);
  2931. *ppwszExpandedPath = new WCHAR [len + 1];
  2932. if (! *ppwszExpandedPath)
  2933. {
  2934. Status = ERROR_OUTOFMEMORY;
  2935. goto ExpandHomeDir_End;
  2936. }
  2937. // Copy the path.
  2938. (void) StringCchCopy(*ppwszExpandedPath, len + 1, pwszPath);
  2939. Status = ERROR_SUCCESS;
  2940. ExpandHomeDir_End:
  2941. if (ERROR_SUCCESS != Status)
  2942. {
  2943. DebugMsg ((DM_VERBOSE, IDS_HOMEDIR_EXPAND_FAIL, pwszPath, Status));
  2944. }
  2945. return Status;
  2946. }
  2947. //+--------------------------------------------------------------------------
  2948. //
  2949. // Function: ExpandHomeDirPolicyPath
  2950. //
  2951. // Synopsis: Expands the HOMEDIR component in the path as specified in the
  2952. // ini file (ones that begin with \\%HOMESHARE%%HOMEPATH%)
  2953. //
  2954. // Arguments: [in] rID : the id of the folder
  2955. // [in] pwszPath : the unexpanded path
  2956. // [in] bAllowMyPics : allow expansion of homedir for MyPics
  2957. // [out] ppwszExpandedPath : the expanded result
  2958. // [in, optional] pwszHomedir : the value of homedir to be used in substitution
  2959. //
  2960. // Returns: ERROR_SUCCESS : if successful.
  2961. // a Win32 error code otherwise.
  2962. //
  2963. // History: 3/10/2000 RahulTh created
  2964. //
  2965. // Notes: This function has the following additional restrictions:
  2966. // (a) It is a no-op for all folders except MyDocs and MyPics.
  2967. // (b) It only acts on paths that begin with
  2968. // \\%HOMESHARE%%HOMEPATH% or %HOMESHARE%%HOMEPATH%
  2969. //
  2970. // Also, if the function does not return ERROR_SUCCESS, then
  2971. // *ppwszExpandedPath can be null.
  2972. //
  2973. // if the caller does not supply the homedir value for
  2974. // substitution, the value is obtained from the user object.
  2975. //
  2976. //---------------------------------------------------------------------------
  2977. DWORD ExpandHomeDirPolicyPath (IN REDIRECTABLE rID,
  2978. IN const WCHAR * pwszPath,
  2979. IN BOOL bAllowMyPics,
  2980. OUT WCHAR ** ppwszExpandedPath,
  2981. IN const WCHAR * pwszHomedir // = NULL
  2982. )
  2983. {
  2984. if (IsHomedirPolicyPath(rID, pwszPath, bAllowMyPics))
  2985. {
  2986. return ExpandHomeDir (rID,
  2987. &pwszPath[2],
  2988. bAllowMyPics,
  2989. ppwszExpandedPath,
  2990. pwszHomedir
  2991. );
  2992. }
  2993. else
  2994. {
  2995. return ExpandHomeDir (rID,
  2996. pwszPath,
  2997. bAllowMyPics,
  2998. ppwszExpandedPath,
  2999. pwszHomedir
  3000. );
  3001. }
  3002. }
  3003. //+--------------------------------------------------------------------------
  3004. //
  3005. // Function: IsHomedirPath
  3006. //
  3007. // Synopsis: determines if a given redirection destination is in the homedir.
  3008. // This means all paths that begin with %HOMESHARE%%HOMEPATH
  3009. //
  3010. // Arguments: [in] rID : The folder identifier.
  3011. // [in] pwszPath : The path.
  3012. // [in] bAllowMyPics : allow homedir paths for MyPics folder
  3013. //
  3014. // Returns: TRUE : if it is a homedir path
  3015. // FALSE: otherwise.
  3016. //
  3017. // History: 3/12/2000 RahulTh created
  3018. //
  3019. // Notes: This function returns FALSE for all folders other than
  3020. // MyDocs because that is the only folder for which redirection
  3021. // to the home directory is permitted.
  3022. //
  3023. // See additional notes about bAllowMyPics in the comments above
  3024. // IsHomedirPolicyPath
  3025. //
  3026. //---------------------------------------------------------------------------
  3027. BOOL IsHomedirPath (IN REDIRECTABLE rID, IN LPCWSTR pwszPath, BOOL bAllowMyPics)
  3028. {
  3029. int len;
  3030. if ((MyDocs != rID && (MyPics != rID || !bAllowMyPics)) ||
  3031. ! pwszPath ||
  3032. L'\0' == *pwszPath)
  3033. {
  3034. return FALSE;
  3035. }
  3036. //
  3037. // Make sure that the length of the path is longer than that of
  3038. // %HOMESHARE%%HOMEPATH. If not, there is no way that this can be
  3039. // a homedir path
  3040. //
  3041. len = lstrlen (HOMEDIR_STR);
  3042. if (lstrlen (pwszPath) < len)
  3043. return FALSE;
  3044. // If we are here, we need to compare the two strings
  3045. if (0 == _wcsnicmp (HOMEDIR_STR, pwszPath, len) &&
  3046. (L'\0' == pwszPath[len] || L'\\' == pwszPath[len]) &&
  3047. NULL == wcschr (&pwszPath[len], L'%') // no other variable names allowed
  3048. )
  3049. {
  3050. // If all the conditions above are met, then this is indeed a homedir path
  3051. return TRUE;
  3052. }
  3053. // If we are here, this cannot be a homedir path.
  3054. return FALSE;
  3055. }
  3056. //+--------------------------------------------------------------------------
  3057. //
  3058. // Function: IsHomedirPolicyPath
  3059. //
  3060. // Synopsis: determines if a given redirection destination is in the homedir.
  3061. // This means all paths that begin with \\%HOMESHARE%%HOMEPATH
  3062. //
  3063. // Arguments: [in] rID : The folder identifier.
  3064. // [in] pwszPath : The path.
  3065. // [in] bAllowMyPics : Allow MyPics to have a homedir path
  3066. //
  3067. // Returns: TRUE : if it is a homedir path as specified in the ini file
  3068. // FALSE: otherwise.
  3069. //
  3070. // History: 3/12/2000 RahulTh created
  3071. //
  3072. // Notes: This function returns FALSE for all folders other than
  3073. // MyDocs & MyPics because that is the only folder for which
  3074. // redirection to the home directory is permitted.
  3075. //
  3076. // Note: redirection to HOMEDIR is permitted for MyPics only
  3077. // if it is set to follow MyDocs. Not otherwise. That is why
  3078. // we need the third parameter.
  3079. //
  3080. //---------------------------------------------------------------------------
  3081. BOOL IsHomedirPolicyPath (IN REDIRECTABLE rID,
  3082. IN LPCWSTR pwszPath,
  3083. IN BOOL bAllowMyPics)
  3084. {
  3085. //
  3086. // It is a homedir path as specified by the policy if it begins
  3087. // with \\%HOMESHARE%%HOMEPATH%
  3088. //
  3089. if ((MyDocs == rID || (MyPics == rID && bAllowMyPics)) &&
  3090. pwszPath &&
  3091. lstrlen (pwszPath) > 2 &&
  3092. L'\\' == pwszPath[0] &&
  3093. L'\\' == pwszPath[1] )
  3094. {
  3095. return IsHomedirPath (rID, &pwszPath[2], bAllowMyPics);
  3096. }
  3097. return FALSE;
  3098. }
  3099. //+--------------------------------------------------------------------------
  3100. //
  3101. // Function: HasHomeVariables
  3102. //
  3103. // Synopsis: finds if a given string has any of the home variables
  3104. // i.e. HOMESHARE, HOMEPATH or HOMEDRIVE
  3105. //
  3106. // Arguments: [in] pwszPath : the path string
  3107. //
  3108. // Returns: TRUE: if the path has home variables.
  3109. // FALSE: otherwise
  3110. //
  3111. // History: 3/22/2000 RahulTh created
  3112. //
  3113. // Notes: This function is required because we do not wish to allow
  3114. // these variables in the path except in highly restricted
  3115. // conditions, viz. My Docs redirection that begins with
  3116. // \\%HOMESHARE%%HOMEPATH%
  3117. //
  3118. // Therefore, we need to explicitly check for the existence of
  3119. // variables in paths that don't meet these requirements.
  3120. //
  3121. //---------------------------------------------------------------------------
  3122. BOOL HasHomeVariables (IN LPCWSTR pwszPath)
  3123. {
  3124. WCHAR * pszTmp;
  3125. pszTmp = wcschr (pwszPath, L'%');
  3126. while (pszTmp)
  3127. {
  3128. if (0 == _wcsnicmp (HOMESHARE_VARIABLE, pszTmp, HOMESHARE_VARLEN) ||
  3129. 0 == _wcsnicmp (HOMEDRIVE_VARIABLE, pszTmp, HOMEDRIVE_VARLEN) ||
  3130. 0 == _wcsnicmp (HOMEPATH_VARIABLE, pszTmp, HOMEPATH_VARLEN))
  3131. {
  3132. return TRUE;
  3133. }
  3134. pszTmp = wcschr (pszTmp + 1, L'%');
  3135. }
  3136. // If we are here, we did not find any home variables in the path.
  3137. return FALSE;
  3138. }
  3139. //+--------------------------------------------------------------------------
  3140. //
  3141. // Function: GetWin32ErrFromHResult
  3142. //
  3143. // Synopsis: given an HResult, this function tries to extract the
  3144. // corresponding Win 32 error.
  3145. //
  3146. // Arguments: [in] hr : the hresult value
  3147. //
  3148. // Returns: the Win 32 Error code.
  3149. //
  3150. // History: 3/13/2000 RahulTh created
  3151. //
  3152. // Notes: if hr is not S_OK, the return value will be something other
  3153. // than ERROR_SUCCESS;
  3154. //
  3155. //---------------------------------------------------------------------------
  3156. DWORD GetWin32ErrFromHResult (IN HRESULT hr)
  3157. {
  3158. DWORD Status = ERROR_SUCCESS;
  3159. if (S_OK != hr)
  3160. {
  3161. if (FACILITY_WIN32 == HRESULT_FACILITY(hr))
  3162. {
  3163. Status = HRESULT_CODE(hr);
  3164. }
  3165. else
  3166. {
  3167. Status = GetLastError();
  3168. if (ERROR_SUCCESS == Status)
  3169. {
  3170. //an error had occurred but nobody called SetLastError
  3171. //should not be mistaken as a success.
  3172. Status = (DWORD) hr;
  3173. }
  3174. }
  3175. }
  3176. return Status;
  3177. }
  3178. //+--------------------------------------------------------------------------
  3179. //
  3180. // Function: GetExpandedPath
  3181. //
  3182. // Synopsis: given a redirected path that may contain environment variables,
  3183. // evaluates the variables to produce a fully realized path
  3184. //
  3185. // Arguments: [in] pFileDB : object containing general context information
  3186. // [in] wszSourcePath : the unexpanded path
  3187. // [in] rID : The folder identifier.
  3188. // [in] bAllowMyPics : allow homedir paths for MyPics folder
  3189. // [out] ppwszExpandedPath -- on output, the address of the
  3190. // expanded path, must be freed by the caller
  3191. //
  3192. // Returns: the Win 32 Error code.
  3193. //
  3194. // History: 5/30/2000 AdamEd created
  3195. //
  3196. // Notes:
  3197. //
  3198. //---------------------------------------------------------------------------
  3199. DWORD
  3200. GetExpandedPath(
  3201. IN CFileDB* pFileDB,
  3202. IN WCHAR* wszSourcePath,
  3203. IN int rID,
  3204. IN BOOL bAllowMyPics,
  3205. OUT WCHAR** ppwszExpandedPath)
  3206. {
  3207. WCHAR* wszDestination;
  3208. WCHAR* wszHomeDir;
  3209. DWORD Status;
  3210. const WCHAR* wszUserName;
  3211. *ppwszExpandedPath = NULL;
  3212. wszDestination = NULL;
  3213. wszHomeDir = NULL;
  3214. wszUserName = NULL;
  3215. //
  3216. // In diagnostic mode, we will end up expanding %username% and
  3217. // the %homedir% and %homepath% vars, as well as any other variables that
  3218. // are defined, but in planning mode we will only
  3219. // handle the first 3, and we only do so if a specific user account
  3220. //
  3221. if ( pFileDB->GetRsopContext()->IsPlanningModeEnabled() )
  3222. {
  3223. //
  3224. // In planning mode, the thread context is not that
  3225. // of the desired user, so we must override it
  3226. //
  3227. wszUserName = gUserInfo.GetUserName( Status );
  3228. if ( ERROR_SUCCESS != Status )
  3229. {
  3230. goto GetExpandedPath_Exit;
  3231. }
  3232. if ( ! wszUserName )
  3233. {
  3234. //
  3235. // In planning mode, GetUserName can return success
  3236. // but return a NULL username -- this means that
  3237. // the planning mode target contained only a SOM, not a user
  3238. // name, so we can accept the blank user name and cease
  3239. // further expansion attempts -- we cannot expand %username%
  3240. // or the %home%* variables if we do not have a user account. Thus,
  3241. // we use the unexpanded path.
  3242. //
  3243. wszDestination = StringDuplicate( wszSourcePath );
  3244. if ( ! wszDestination )
  3245. {
  3246. Status = ERROR_NOT_ENOUGH_MEMORY;
  3247. }
  3248. goto GetExpandedPath_Exit;
  3249. }
  3250. }
  3251. Status = ExpandHomeDirPolicyPath(
  3252. (REDIRECTABLE) rID,
  3253. wszSourcePath,
  3254. bAllowMyPics,
  3255. &wszHomeDir);
  3256. if ( ERROR_SUCCESS != Status )
  3257. {
  3258. //
  3259. // In planning mode, we may not have access to read homedir,
  3260. // especially in cases where the user object resides in a different
  3261. // domain than the one with the dc running this code --
  3262. // in that case, we will just not expand it
  3263. //
  3264. if ( pFileDB->GetRsopContext()->IsPlanningModeEnabled() )
  3265. {
  3266. Status = ERROR_SUCCESS;
  3267. //
  3268. // Allocate an unexpanded version of the string with
  3269. // homedir in it
  3270. //
  3271. wszHomeDir = StringDuplicate( wszSourcePath );
  3272. if ( ! wszHomeDir )
  3273. {
  3274. Status = ERROR_NOT_ENOUGH_MEMORY;
  3275. }
  3276. }
  3277. if ( ERROR_SUCCESS != Status )
  3278. {
  3279. goto GetExpandedPath_Exit;
  3280. }
  3281. }
  3282. USHORT cchDestination;
  3283. ULONG ReturnedLength;
  3284. NTSTATUS NtStatus;
  3285. cchDestination = TARGETPATHLIMIT;
  3286. wszDestination = new WCHAR [ cchDestination + 1 ];
  3287. if ( ! wszDestination )
  3288. {
  3289. goto GetExpandedPath_Exit;
  3290. }
  3291. ReturnedLength = cchDestination * sizeof( *wszDestination );
  3292. NtStatus = ExpandPathSpecial(
  3293. pFileDB,
  3294. wszHomeDir,
  3295. wszUserName,
  3296. wszDestination,
  3297. &ReturnedLength);
  3298. if ( STATUS_BUFFER_TOO_SMALL == NtStatus )
  3299. {
  3300. delete [] wszDestination;
  3301. wszDestination = new WCHAR [ ReturnedLength / sizeof( *wszDestination ) + 1 ];
  3302. if ( ! wszDestination )
  3303. {
  3304. Status = ERROR_NOT_ENOUGH_MEMORY;
  3305. goto GetExpandedPath_Exit;
  3306. }
  3307. NtStatus = ExpandPathSpecial(
  3308. pFileDB,
  3309. wszHomeDir,
  3310. wszUserName,
  3311. wszDestination,
  3312. &ReturnedLength);
  3313. }
  3314. if ( STATUS_SUCCESS != NtStatus )
  3315. {
  3316. Status = RtlNtStatusToDosError( NtStatus );
  3317. }
  3318. GetExpandedPath_Exit:
  3319. delete [] wszHomeDir;
  3320. if ( ERROR_SUCCESS != Status )
  3321. {
  3322. delete [] wszDestination;
  3323. }
  3324. else
  3325. {
  3326. *ppwszExpandedPath = wszDestination;
  3327. }
  3328. return Status;
  3329. }