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.

3223 lines
99 KiB

  1. //*************************************************************
  2. //
  3. // Copyright (c) Microsoft Corporation 1998
  4. // All rights reserved
  5. //
  6. // filedb.cxx
  7. //
  8. //*************************************************************
  9. #include "fdeploy.hxx"
  10. #define SAVED_SETTINGS_FILE L"{25537BA6-77A8-11D2-9B6C-0000F8080861}.ini"
  11. HRESULT RsopSidsFromToken(PRSOPTOKEN pRsopToken,
  12. PTOKEN_GROUPS* ppGroups);
  13. FOLDERINFO gUserShellFolders[] =
  14. {
  15. {CSIDL_APPDATA, 17, L"Application Data\\"},
  16. // {CSIDL_COOKIES, 8, L"Cookies\\"},
  17. {CSIDL_DESKTOPDIRECTORY, 8, L"Desktop\\"},
  18. {CSIDL_FAVORITES, 10, L"Favorites\\"},
  19. // {CSIDL_HISTORY, 8, L"History\\"},
  20. // {0, 15, L"Local Settings\\"}, Has no reg key, no CSIDL
  21. {CSIDL_MYPICTURES, 25, L"My Documents\\My Pictures\\"},
  22. {CSIDL_PERSONAL, 13, L"My Documents\\"},
  23. {CSIDL_NETHOOD, 8, L"NetHood\\"},
  24. {CSIDL_PRINTHOOD, 10, L"PrintHood\\"},
  25. // {CSIDL_RECENT, 7, L"Recent\\"},
  26. {CSIDL_SENDTO, 7, L"SendTo\\"},
  27. {CSIDL_STARTUP, 28, L"Start Menu\\Programs\\Startup\\"},
  28. {CSIDL_PROGRAMS, 20, L"Start Menu\\Programs\\"},
  29. {CSIDL_STARTMENU, 11, L"Start Menu\\"},
  30. {CSIDL_TEMPLATES, 10, L"Templates\\"},
  31. // {CSIDL_INTERNET_CACHE, 25, L"Temporary Internet Files\\"},
  32. {0, 0, NULL }
  33. };
  34. FOLDERINFO gMachineShellFolders[] =
  35. {
  36. {CSIDL_COMMON_APPDATA, 17, L"Application Data\\"},
  37. {CSIDL_COMMON_DESKTOPDIRECTORY, 8, L"Desktop\\"},
  38. // {0, 10, L"Documents\\"}, No shell support
  39. {CSIDL_COMMON_STARTUP, 28, L"Start Menu\\Programs\\Startup\\"},
  40. {CSIDL_COMMON_PROGRAMS, 20, L"Start Menu\\Programs\\"},
  41. {CSIDL_COMMON_STARTMENU, 11, L"Start Menu\\"},
  42. {0, 0, NULL}
  43. };
  44. static DWORD gSchema = 1;
  45. CFileDB::CFileDB()
  46. {
  47. _hUserToken = 0;
  48. _hkRoot = 0;
  49. _pEnvBlock = 0;
  50. _pGroups = 0;
  51. _pwszGPTPath = 0;
  52. _GPTPathLen = 0;
  53. _pwszIniFilePath = 0;
  54. _IniFileLen = 0;
  55. _pwszGPOName = 0;
  56. _pwszGPOUniqueName = 0;
  57. _pwszGPOSOMPath = 0;
  58. _pRsopContext = 0;
  59. }
  60. CFileDB::~CFileDB()
  61. {
  62. if ( _pEnvBlock )
  63. DestroyEnvironmentBlock( _pEnvBlock );
  64. if (_pwszGPTPath)
  65. delete [] _pwszGPTPath;
  66. if (_pwszIniFilePath)
  67. delete [] _pwszIniFilePath;
  68. //
  69. // Note that _pGroups must be freed with LocalFree --
  70. // we are able to free it with delete because we
  71. // have redefined delete to be LocalFree
  72. //
  73. if (_pGroups)
  74. delete [] (BYTE*) _pGroups;
  75. // _pwszGPOName is not allocated
  76. // _pwszGPOUniqueName is not allocated
  77. // _pwszGPODSPath is not allocated
  78. }
  79. DWORD CFileDB::Initialize (
  80. HANDLE hUserToken,
  81. HKEY hkRoot,
  82. CRsopContext* pRsopContext)
  83. {
  84. BOOL bStatus;
  85. DWORD Status = ERROR_SUCCESS;
  86. ULONG Size;
  87. int CSidl;
  88. WCHAR * pwszSlash;
  89. HRESULT hr;
  90. HANDLE hFind;
  91. WIN32_FIND_DATA FindData;
  92. //set the token
  93. _hUserToken = hUserToken;
  94. //set the root key
  95. _hkRoot = hkRoot;
  96. // set the rsop logging context
  97. _pRsopContext = pRsopContext;
  98. //create an environment block for the user. we need this for expanding
  99. //variables.
  100. if (! _pEnvBlock)
  101. {
  102. if (!CreateEnvironmentBlock ( &_pEnvBlock, _hUserToken, FALSE))
  103. {
  104. Status = GetLastError();
  105. goto InitializeEnd;
  106. }
  107. }
  108. //get the list of group to which the user belongs
  109. _pGroups = 0;
  110. Size = 0;
  111. //
  112. // We may only use the Nt security subsystem api below
  113. // to retrieve groups when we are not in planning mode
  114. //
  115. for (; ! _pRsopContext->IsPlanningModeEnabled() ;)
  116. {
  117. Status = NtQueryInformationToken(
  118. _hUserToken,
  119. TokenGroups,
  120. _pGroups,
  121. Size,
  122. &Size );
  123. if ( (STATUS_BUFFER_TOO_SMALL == Status) || (ERROR_OUTOFMEMORY == Status) )
  124. {
  125. _pGroups = (PTOKEN_GROUPS) new BYTE [ Size ];
  126. if ( ! _pGroups )
  127. {
  128. Status = ERROR_OUTOFMEMORY;
  129. break;
  130. }
  131. continue;
  132. }
  133. if ( Status != STATUS_SUCCESS )
  134. {
  135. if (_pGroups)
  136. delete [] ((BYTE*) _pGroups);
  137. _pGroups = 0;
  138. }
  139. break;
  140. }
  141. //
  142. // In planning mode, we get our security groups from
  143. // the policy engine's simulated token, not from a real token
  144. //
  145. if ( _pRsopContext->IsPlanningModeEnabled() &&
  146. ! _pRsopContext->IsReportingModeEnabled() )
  147. {
  148. DWORD cbSize;
  149. PRSOP_TARGET pRsopTarget;
  150. pRsopTarget = _pRsopContext->_pRsopTarget;
  151. //
  152. // The call below uses RSoP's planning mode "simulated"
  153. // security subsystem to retrieve the sids from the simulated
  154. // token. The function allocates memory in _pGroups that
  155. // must be freed with LocalFree.
  156. //
  157. hr = RsopSidsFromToken(pRsopTarget->pRsopToken, &_pGroups);
  158. Status = HRESULT_CODE(hr);
  159. }
  160. if (ERROR_SUCCESS != Status)
  161. goto InitializeEnd;
  162. //
  163. // Retrieve the local path -- note that we do not need this in planning mode
  164. //
  165. if ( ! _pRsopContext->IsPlanningModeEnabled() )
  166. {
  167. //get the path to our directory under Local Settings.
  168. CSidl = CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE;
  169. hr = SHGetFolderPath( NULL, CSidl, _hUserToken, 0, _pwszLocalPath );
  170. if ( hr != S_OK )
  171. {
  172. //try to get the last error.
  173. if (FACILITY_WIN32 == HRESULT_FACILITY (hr))
  174. {
  175. Status = HRESULT_CODE(hr);
  176. }
  177. else
  178. {
  179. Status = GetLastError();
  180. if (ERROR_SUCCESS == Status)
  181. {
  182. //an error had occurred but nobody called SetLastError
  183. //should not be mistaken as a success.
  184. Status = (DWORD) hr;
  185. }
  186. }
  187. DebugMsg((DM_WARNING, IDS_NO_LOCALAPPDATA, Status));
  188. goto InitializeEnd;
  189. }
  190. pwszSlash = _pwszLocalPath + wcslen( _pwszLocalPath );
  191. hr = StringCchCat( _pwszLocalPath, MAX_PATH, L"\\Microsoft\\Windows\\File Deployment" );
  192. if ( FAILED(hr) )
  193. {
  194. Status = HRESULT_CODE(hr);
  195. goto InitializeEnd;
  196. }
  197. Status = ERROR_SUCCESS;
  198. //now create directories as necessary
  199. // Quick check to see if we have necessary local dirs.
  200. hFind = FindFirstFile( _pwszLocalPath, &FindData );
  201. if ( INVALID_HANDLE_VALUE == hFind )
  202. {
  203. do
  204. {
  205. pwszSlash = wcschr( &pwszSlash[1], L'\\' );
  206. if ( pwszSlash )
  207. *pwszSlash = 0;
  208. bStatus = CreateDirectory( _pwszLocalPath, NULL );
  209. if ( ! bStatus && (GetLastError() != ERROR_ALREADY_EXISTS) )
  210. {
  211. Status = GetLastError();
  212. break;
  213. }
  214. if ( pwszSlash )
  215. *pwszSlash = L'\\';
  216. } while ( pwszSlash );
  217. }
  218. else
  219. {
  220. FindClose( hFind );
  221. }
  222. }
  223. InitializeEnd:
  224. return Status;
  225. }
  226. DWORD
  227. CFileDB::Process(
  228. PGROUP_POLICY_OBJECT pGPO,
  229. BOOL bRemove
  230. )
  231. {
  232. WCHAR * pwszGPTIniFilePath = NULL;
  233. DWORD Length = 0;
  234. DWORD Status;
  235. DWORD ProcessStatus;
  236. BOOL bStatus;
  237. BOOL bPolicyApplied;
  238. HANDLE hFind;
  239. DWORD dwAttr = INVALID_FILE_ATTRIBUTES;
  240. WIN32_FIND_DATA FindData;
  241. HRESULT hr = S_OK;
  242. if ( bRemove && _pRsopContext->IsPlanningModeEnabled() )
  243. {
  244. return ERROR_INVALID_PARAMETER;
  245. }
  246. bPolicyApplied = FALSE;
  247. Status = ERROR_SUCCESS;
  248. //first initialize the variables that vary with policies
  249. _bRemove = bRemove;
  250. if ( ! _bRemove )
  251. {
  252. Length = wcslen(pGPO->lpFileSysPath) + wcslen(GPT_SUBDIR) + 1;
  253. if (Length > _GPTPathLen)
  254. {
  255. //we need more memory than has been allocated.
  256. //so get that before proceeding
  257. if (_pwszGPTPath)
  258. delete _pwszGPTPath;
  259. _GPTPathLen = 0; //make sure that this always reflects the correct value
  260. _pwszGPTPath = new WCHAR [Length];
  261. if ( ! _pwszGPTPath )
  262. {
  263. Status = ERROR_OUTOFMEMORY;
  264. goto ProcessEnd;
  265. }
  266. _GPTPathLen = Length; //make sure that this always reflects the correct value
  267. }
  268. hr = StringCchCopy( _pwszGPTPath, _GPTPathLen, pGPO->lpFileSysPath );
  269. if ( FAILED(hr) )
  270. {
  271. Status = HRESULT_CODE(hr);
  272. goto ProcessEnd;
  273. }
  274. hr = StringCchCat( _pwszGPTPath, _GPTPathLen, GPT_SUBDIR );
  275. if ( FAILED(hr) )
  276. {
  277. Status = HRESULT_CODE(hr);
  278. goto ProcessEnd;
  279. }
  280. Length += wcslen( INIFILE_NAME );
  281. pwszGPTIniFilePath = new WCHAR[ Length ];
  282. if ( ! pwszGPTIniFilePath )
  283. {
  284. Status = ERROR_OUTOFMEMORY;
  285. goto ProcessEnd;
  286. }
  287. hr = StringCchCopy( pwszGPTIniFilePath, Length, _pwszGPTPath );
  288. if ( FAILED(hr) )
  289. {
  290. Status = HRESULT_CODE(hr);
  291. goto ProcessEnd;
  292. }
  293. hr = StringCchCat( pwszGPTIniFilePath, Length, INIFILE_NAME );
  294. if ( FAILED(hr) )
  295. {
  296. Status = HRESULT_CODE(hr);
  297. goto ProcessEnd;
  298. }
  299. //
  300. // Do a quick check to see if we have any file deployment
  301. // for this policy.
  302. //
  303. hFind = FindFirstFile( pwszGPTIniFilePath, &FindData );
  304. if ( INVALID_HANDLE_VALUE == hFind )
  305. {
  306. Status = GetLastError();
  307. goto ProcessEnd;
  308. }
  309. else
  310. {
  311. bPolicyApplied = TRUE;
  312. DebugMsg((DM_VERBOSE, IDS_HASADD_POLICY, pGPO->lpDisplayName));
  313. FindClose( hFind );
  314. }
  315. }
  316. Status = ERROR_SUCCESS;
  317. if ( _pRsopContext->IsPlanningModeEnabled() )
  318. {
  319. Length = wcslen( pwszGPTIniFilePath ) + 1;
  320. }
  321. else
  322. {
  323. Length = wcslen( _pwszLocalPath ) + wcslen( pGPO->szGPOName ) + 6;
  324. }
  325. if (Length > _IniFileLen)
  326. {
  327. //we need more memory than has been allocated
  328. if (_pwszIniFilePath)
  329. delete [] _pwszIniFilePath;
  330. _IniFileLen = 0; //make sure that this always reflects the current value
  331. _pwszIniFilePath = new WCHAR[Length];
  332. if ( ! _pwszIniFilePath )
  333. {
  334. Status = ERROR_OUTOFMEMORY;
  335. goto ProcessEnd;
  336. }
  337. _IniFileLen = Length; //make sure that this always reflects the current value
  338. }
  339. if ( _pRsopContext->IsPlanningModeEnabled() )
  340. {
  341. hr = StringCchCopy( _pwszIniFilePath, _IniFileLen, pwszGPTIniFilePath );
  342. if ( FAILED(hr) )
  343. {
  344. Status = HRESULT_CODE(hr);
  345. goto ProcessEnd;
  346. }
  347. }
  348. else
  349. {
  350. hr = StringCchCopy( _pwszIniFilePath, _IniFileLen, _pwszLocalPath );
  351. if ( FAILED(hr) )
  352. {
  353. Status = HRESULT_CODE(hr);
  354. goto ProcessEnd;
  355. }
  356. hr = StringCchCat( _pwszIniFilePath, _IniFileLen, L"\\" );
  357. if ( FAILED(hr) )
  358. {
  359. Status = HRESULT_CODE(hr);
  360. goto ProcessEnd;
  361. }
  362. hr = StringCchCat( _pwszIniFilePath, _IniFileLen, pGPO->szGPOName );
  363. if ( FAILED(hr) )
  364. {
  365. Status = HRESULT_CODE(hr);
  366. goto ProcessEnd;
  367. }
  368. hr = StringCchCat( _pwszIniFilePath, _IniFileLen, L".ini" );
  369. if ( FAILED(hr) )
  370. {
  371. Status = HRESULT_CODE(hr);
  372. goto ProcessEnd;
  373. }
  374. }
  375. if ( _bRemove )
  376. {
  377. hFind = FindFirstFile( _pwszIniFilePath, &FindData );
  378. if ( INVALID_HANDLE_VALUE == hFind )
  379. {
  380. //this error should be ignored since there is nothing we can do.
  381. //the policy has been deleted and the local settings are missing
  382. //so we have no choice but to treat these as if the settings were
  383. //to orphan the folder upon policy removal.
  384. goto ProcessEnd;
  385. }
  386. else
  387. {
  388. bPolicyApplied = TRUE;
  389. DebugMsg((DM_VERBOSE, IDS_HASREMOVE_POLICY, pGPO->lpDisplayName));
  390. FindClose( hFind );
  391. }
  392. }
  393. else if ( ! _pRsopContext->IsPlanningModeEnabled() )
  394. {
  395. //
  396. // Cache the ini file locally. Note that if a local copy of the GPO already
  397. // exists and it has hidden/read-only/system attributes, then the CopyFile
  398. // API will fail with ERROR_ACCESS_DENIED unless the source (on the sysvol)
  399. // has the same attributes.
  400. // Therefore we first slap on normal attributes on the local copy if any
  401. // and restore the original attributes if we fail in the CopyFile.
  402. //
  403. dwAttr = GetFileAttributes (_pwszIniFilePath);
  404. //
  405. // Don't set the file attributes to normal unless GetFileAttributes
  406. // succeeds
  407. //
  408. if (INVALID_FILE_ATTRIBUTES != dwAttr)
  409. SetFileAttributes (_pwszIniFilePath, FILE_ATTRIBUTE_NORMAL);
  410. bStatus = CopyFile( pwszGPTIniFilePath, _pwszIniFilePath, FALSE );
  411. if ( ! bStatus )
  412. {
  413. Status = GetLastError();
  414. //
  415. // Restore the local cache's attributes (if we had changed them)
  416. // since our copy operation has failed.
  417. //
  418. if (INVALID_FILE_ATTRIBUTES != dwAttr)
  419. SetFileAttributes (_pwszIniFilePath, dwAttr);
  420. }
  421. }
  422. if ( Status != ERROR_SUCCESS )
  423. goto ProcessEnd;
  424. _pwszGPOName = (WCHAR *) pGPO->lpDisplayName;
  425. _pwszGPOUniqueName = (WCHAR *) pGPO->szGPOName;
  426. _pwszGPOSOMPath = ( WCHAR *) pGPO->lpLink;
  427. _pwszGPODSPath = ( WCHAR *) pGPO->lpDSPath;
  428. ProcessStatus = ProcessRedirects();
  429. if ( (ProcessStatus != ERROR_SUCCESS) && (ERROR_SUCCESS == Status) )
  430. Status = ProcessStatus;
  431. ProcessEnd:
  432. if ( Status != ERROR_SUCCESS )
  433. {
  434. gpEvents->Report (
  435. EVENT_FDEPLOY_POLICYPROCESS_FAIL,
  436. 2,
  437. pGPO->lpDisplayName,
  438. StatusToString (Status)
  439. );
  440. }
  441. else if ( bPolicyApplied )
  442. {
  443. DebugMsg((DM_VERBOSE, IDS_PROCESS_GATHER_OK, pGPO->lpDisplayName));
  444. }
  445. delete [] pwszGPTIniFilePath;
  446. return Status;
  447. }
  448. DWORD
  449. CFileDB::ProcessRedirects(void)
  450. {
  451. WCHAR * pwszString = 0;
  452. WCHAR * pwszSectionStrings = 0;
  453. WCHAR * pwszRedirection = 0;
  454. WCHAR wszFolderName[80];
  455. UNICODE_STRING String;
  456. DWORD Flags;
  457. DWORD RedirectStatus;
  458. DWORD Status;
  459. BOOL bStatus;
  460. REDIRECTABLE index;
  461. DWORD RedirStatus;
  462. CRedirectInfo riPolicy [(int) EndRedirectable]; //the redirection info. for this policy
  463. DWORD i;
  464. //first load the localized folder names
  465. for (i = 0, Status = ERROR_SUCCESS; i < (DWORD)EndRedirectable; i++)
  466. {
  467. Status = riPolicy[i].LoadLocalizedNames();
  468. if (ERROR_SUCCESS != Status)
  469. return Status; //bail out if the resource names cannot be loaded.
  470. }
  471. pwszSectionStrings = 0;
  472. bStatus = ReadIniSection( L"FolderStatus", &pwszSectionStrings );
  473. if ( ! bStatus )
  474. {
  475. Status = ERROR_OUTOFMEMORY;
  476. goto ProcessRedirectsEnd;
  477. }
  478. Status = ERROR_SUCCESS;
  479. for ( pwszString = pwszSectionStrings;
  480. *pwszString != 0;
  481. pwszString += lstrlen(pwszString) + 1 )
  482. {
  483. //
  484. // The syntax for each line is :
  485. // foldername=FLAGS
  486. //
  487. //
  488. // First extract the foldername.
  489. //
  490. pwszRedirection = wcschr( pwszString, L'=' );
  491. if (!pwszRedirection)
  492. {
  493. Status = ERROR_FILE_CORRUPT;
  494. goto ProcessRedirectsEnd;
  495. }
  496. *pwszRedirection = 0;
  497. Status = HRESULT_CODE(StringCbCopy( wszFolderName, sizeof(wszFolderName), pwszString ));
  498. if ( Status != ERROR_SUCCESS )
  499. {
  500. goto ProcessRedirectsEnd;
  501. }
  502. *pwszRedirection++ = L'=';
  503. //
  504. // Now grab the hex FLAGS.
  505. //
  506. String.Buffer = pwszRedirection;
  507. pwszRedirection = wcschr( pwszRedirection, L' ' );
  508. if ( pwszRedirection )
  509. *pwszRedirection = 0;
  510. String.Length = wcslen( String.Buffer ) * sizeof(WCHAR);
  511. String.MaximumLength = String.Length + sizeof(WCHAR);
  512. RtlUnicodeStringToInteger( &String, 16, &Flags );
  513. //just gather the information here.
  514. //actual redirections are performed in ProcessGPO after all the policies
  515. //have been processed.
  516. if (EndRedirectable ==
  517. (index = CRedirectInfo::GetFolderIndex (wszFolderName)))
  518. {
  519. //redirection of this folder is not supported
  520. DebugMsg ((DM_VERBOSE, IDS_REDIR_NOTSUPPORTED, wszFolderName));
  521. }
  522. else
  523. {
  524. //if this is a policy that has been removed and it was decided to
  525. //orphan the contents, we don't even look at it
  526. if (!_bRemove || (Flags & (REDIR_RELOCATEONREMOVE | REDIR_FOLLOW_PARENT)))
  527. {
  528. //if there is a problem in gathering redirection info. for a folder
  529. //quit immediately, or we might end up computing an incorrect
  530. //resultant policy
  531. if (ERROR_SUCCESS != (Status = riPolicy [(int) index].GatherRedirectionInfo (this, Flags, _bRemove)))
  532. goto ProcessRedirectsEnd;
  533. }
  534. }
  535. }
  536. //now update the data stored in the descendant objects
  537. //this is required because if the descendants follow the parent
  538. //then the settings need to be obtained from the parent
  539. //note that we do not call UpdateDescendant for MyPics here, but in fdeploy.cxx
  540. //for details on why we do this, look at comments in operator= in redir.cxx
  541. Status = riPolicy[(int) Programs].UpdateDescendant();
  542. if ( ERROR_SUCCESS == Status )
  543. {
  544. Status = riPolicy[(int) Startup].UpdateDescendant(); //this call must be made after Programs has been updated
  545. }
  546. if ( ERROR_SUCCESS != Status )
  547. {
  548. goto ProcessRedirectsEnd;
  549. }
  550. //merge info into the global redirection store
  551. for (i = 0; i < (DWORD) EndRedirectable; i++)
  552. {
  553. if (_bRemove)
  554. gDeletedPolicyResultant[i] = riPolicy[i];
  555. else
  556. gAddedPolicyResultant[i] = riPolicy[i];
  557. }
  558. ProcessRedirectsEnd:
  559. delete pwszSectionStrings;
  560. if ( ERROR_SUCCESS != Status )
  561. {
  562. DebugMsg((DM_VERBOSE, IDS_PROCESSREDIRECTS, Status));
  563. }
  564. else
  565. {
  566. if ( ! _bRemove && _pRsopContext->IsRsopEnabled() )
  567. {
  568. (void) AddRedirectionPolicies(
  569. this,
  570. riPolicy);
  571. }
  572. }
  573. return Status;
  574. }
  575. BOOL
  576. CFileDB::ReadIniSection(
  577. WCHAR * pwszSectionName,
  578. WCHAR ** ppwszStrings,
  579. DWORD * pcchLen
  580. )
  581. {
  582. DWORD Length;
  583. DWORD ReturnLength;
  584. *ppwszStrings = 0;
  585. Length = 256;
  586. for (;;)
  587. {
  588. // prevent a corrupt Ini file from eating up all available memory.
  589. if (MaxIniSectionSize < Length)
  590. return FALSE;
  591. delete *ppwszStrings;
  592. *ppwszStrings = new WCHAR[Length];
  593. if ( ! *ppwszStrings )
  594. return FALSE;
  595. ReturnLength = GetPrivateProfileSection(
  596. pwszSectionName,
  597. *ppwszStrings,
  598. Length,
  599. _pwszIniFilePath );
  600. if ( ReturnLength != (Length - 2) )
  601. {
  602. if (pcchLen)
  603. {
  604. *pcchLen = ReturnLength;
  605. }
  606. return TRUE;
  607. }
  608. Length *= 2;
  609. }
  610. }
  611. DWORD
  612. CFileDB::GetLocalFilePath(
  613. WCHAR * pwszFolderPath,
  614. WCHAR * wszFullPath
  615. )
  616. {
  617. int CSidl;
  618. DWORD Status;
  619. HRESULT hr;
  620. WCHAR * pwszFolderName;
  621. CSidl = CSIDL_FLAG_MASK; //use a value that is not a valid CSIDL value for any folder.
  622. for (DWORD n = 0; gUserShellFolders[n].FolderName; n++)
  623. {
  624. if (0 == _wcsicmp (pwszFolderPath, gUserShellFolders[n].FolderName))
  625. {
  626. pwszFolderName = gUserShellFolders[n].FolderName;
  627. CSidl = gUserShellFolders[n].CSidl;
  628. break;
  629. }
  630. }
  631. if ( CSIDL_FLAG_MASK != CSidl )
  632. {
  633. hr = SHGetFolderPath( 0, CSidl | CSIDL_FLAG_DONT_VERIFY,
  634. _hUserToken, 0, wszFullPath );
  635. Status = GetWin32ErrFromHResult (hr);
  636. if ( ERROR_SUCCESS != Status )
  637. {
  638. DebugMsg((DM_WARNING, IDS_FOLDERPATH_FAIL, pwszFolderName, Status));
  639. return Status;
  640. }
  641. }
  642. else
  643. return ERROR_INVALID_NAME;
  644. return ERROR_SUCCESS;
  645. }
  646. DWORD
  647. CFileDB::GetPathFromFolderName(
  648. WCHAR * pwszFolderName,
  649. WCHAR * wszFullPath
  650. )
  651. {
  652. int CSidl;
  653. DWORD Status;
  654. HRESULT hr;
  655. CSidl = CSIDL_FLAG_MASK; //use a csidl value that is not valid for any folder
  656. for (DWORD n = 0; gUserShellFolders[n].FolderName; n++)
  657. {
  658. //we subtract 1 from the length because one of the paths is \ terminated
  659. //and the other is not.
  660. if ( _wcsnicmp( pwszFolderName, gUserShellFolders[n].FolderName, gUserShellFolders[n].FolderNameLength - 1 ) == 0 )
  661. {
  662. CSidl = gUserShellFolders[n].CSidl;
  663. break;
  664. }
  665. }
  666. if ( CSIDL_FLAG_MASK != CSidl )
  667. {
  668. hr = SHGetFolderPath( 0, CSidl | CSIDL_FLAG_DONT_VERIFY,
  669. _hUserToken, 0, wszFullPath );
  670. if ( S_OK != hr )
  671. {
  672. DebugMsg((DM_WARNING, IDS_FOLDERPATH_FAIL, pwszFolderName, hr));
  673. return (DWORD) hr;
  674. }
  675. }
  676. else
  677. return ERROR_INVALID_NAME;
  678. return ERROR_SUCCESS;
  679. }
  680. const FOLDERINFO*
  681. CFileDB::FolderInfoFromFolderName(
  682. WCHAR * pwszFolderName
  683. )
  684. {
  685. //
  686. // This method returns the index into global array.
  687. //
  688. if ( _hUserToken )
  689. {
  690. for ( DWORD n = 0; gUserShellFolders[n].FolderName; n++ )
  691. {
  692. if ( _wcsnicmp( pwszFolderName, gUserShellFolders[n].FolderName, gUserShellFolders[n].FolderNameLength - 1 ) == 0 )
  693. return &gUserShellFolders[n];
  694. }
  695. }
  696. else
  697. {
  698. for ( DWORD n = 0; gMachineShellFolders[n].FolderName; n++ )
  699. {
  700. if ( _wcsnicmp( pwszFolderName, gMachineShellFolders[n].FolderName, gMachineShellFolders[n].FolderNameLength - 1 ) == 0 )
  701. return &gMachineShellFolders[n];
  702. }
  703. }
  704. return NULL;
  705. }
  706. int
  707. CFileDB::RegValueCSIDLFromFolderName(
  708. WCHAR * pwszFolderName
  709. )
  710. {
  711. const FOLDERINFO *pFI;
  712. pFI = FolderInfoFromFolderName(pwszFolderName);
  713. if (pFI != NULL)
  714. return pFI->CSidl;
  715. else
  716. return -1; // invalid CSIDL
  717. }
  718. const WCHAR *
  719. CFileDB::GetLocalStoragePath ()
  720. {
  721. return (LPCWSTR) _pwszLocalPath;
  722. }
  723. DWORD
  724. CFileDB::CopyFileTree(
  725. WCHAR * pwszExistingPath,
  726. WCHAR * pwszNewPath,
  727. WCHAR * pwszIgnoredSubdir,
  728. SHARESTATUS StatusFrom,
  729. SHARESTATUS StatusTo,
  730. BOOL bAllowRdrTimeout,
  731. CCopyFailData * pCopyFailure
  732. )
  733. {
  734. HANDLE hFind = INVALID_HANDLE_VALUE;
  735. WIN32_FIND_DATA FindData;
  736. WIN32_FILE_ATTRIBUTE_DATA SourceAttr;
  737. WIN32_FILE_ATTRIBUTE_DATA DestAttr;
  738. WCHAR * wszSource = NULL;
  739. WCHAR * pwszSourceEnd = 0;
  740. WCHAR * wszDest = NULL;
  741. WCHAR * pwszDestEnd = 0;
  742. WCHAR * pwszTempFilename = 0;
  743. DWORD FileAttributes;
  744. DWORD Status;
  745. BOOL bStatus;
  746. BOOL bReuseTempName = FALSE;
  747. int lenSource;
  748. int lenDest;
  749. DWORD StatusCSCDel = ERROR_SUCCESS;
  750. DWORD dwAttr = INVALID_FILE_ATTRIBUTES;
  751. size_t sourceRemaining;
  752. size_t destRemaining;
  753. if (! pwszExistingPath || ! pwszNewPath)
  754. return ERROR_PATH_NOT_FOUND;
  755. lenSource = wcslen (pwszExistingPath);
  756. lenDest = wcslen (pwszNewPath);
  757. if (! lenSource || ! lenDest)
  758. return ERROR_PATH_NOT_FOUND;
  759. // Create source string
  760. sourceRemaining = lenSource + MAX_PATH + 2;
  761. wszSource = new WCHAR[ sourceRemaining ];
  762. if (NULL == wszSource)
  763. {
  764. return ERROR_OUTOFMEMORY;
  765. }
  766. Status = HRESULT_CODE(StringCchCopyEx( wszSource, sourceRemaining, pwszExistingPath, &pwszSourceEnd, &sourceRemaining, 0 ));
  767. if ( Status != ERROR_SUCCESS )
  768. {
  769. goto CopyFileTree_End;
  770. }
  771. if (L'\\' != pwszSourceEnd[-1])
  772. {
  773. Status = HRESULT_CODE(StringCchCopyEx( pwszSourceEnd, sourceRemaining, L"\\", &pwszSourceEnd, &sourceRemaining, 0 ));
  774. if ( Status != ERROR_SUCCESS )
  775. {
  776. goto CopyFileTree_End;
  777. }
  778. }
  779. Status = HRESULT_CODE(StringCchCopy( pwszSourceEnd, sourceRemaining, L"*" ));
  780. if ( Status != ERROR_SUCCESS )
  781. {
  782. goto CopyFileTree_End;
  783. }
  784. // Create destination string
  785. destRemaining = lenDest + MAX_PATH + 2;
  786. wszDest = new WCHAR[ destRemaining ];
  787. if (NULL == wszDest)
  788. {
  789. Status = ERROR_OUTOFMEMORY;
  790. goto CopyFileTree_End;
  791. }
  792. Status = HRESULT_CODE(StringCchCopyEx( wszDest, destRemaining, pwszNewPath, &pwszDestEnd, &destRemaining, 0 ));
  793. if ( Status != ERROR_SUCCESS )
  794. {
  795. goto CopyFileTree_End;
  796. }
  797. if (L'\\' != pwszDestEnd[-1])
  798. {
  799. Status = HRESULT_CODE(StringCchCopyEx( pwszDestEnd, destRemaining, L"\\", &pwszDestEnd, &destRemaining, 0 ));
  800. if ( Status != ERROR_SUCCESS )
  801. {
  802. goto CopyFileTree_End;
  803. }
  804. }
  805. // Now find the relevant file(s)
  806. hFind = FindFirstFile( wszSource, &FindData );
  807. Status = ERROR_SUCCESS;
  808. if ( INVALID_HANDLE_VALUE == hFind )
  809. {
  810. goto CopyFileTree_End;
  811. }
  812. do
  813. {
  814. Status = HRESULT_CODE(StringCchCopy( pwszSourceEnd, sourceRemaining, FindData.cFileName ));
  815. if ( Status != ERROR_SUCCESS )
  816. {
  817. break;
  818. }
  819. Status = HRESULT_CODE(StringCchCopy( pwszDestEnd, destRemaining, FindData.cFileName ));
  820. if ( Status != ERROR_SUCCESS )
  821. {
  822. break;
  823. }
  824. if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  825. {
  826. if ( lstrcmp( FindData.cFileName, L"." ) == 0 ||
  827. lstrcmp( FindData.cFileName, L".." ) == 0 ||
  828. (pwszIgnoredSubdir && lstrcmpi( FindData.cFileName, pwszIgnoredSubdir ) == 0) )
  829. continue;
  830. Status = FullDirCopyW (wszSource, wszDest, FALSE);
  831. if ( ERROR_SUCCESS == Status )
  832. {
  833. if (ERROR_SUCCESS == StatusCSCDel)
  834. {
  835. Status = CopyFileTree( wszSource, wszDest, NULL, StatusFrom, StatusTo, bAllowRdrTimeout, pCopyFailure );
  836. }
  837. else
  838. {
  839. //no point delaying CSCDeletes anymore since we have already failed once.
  840. Status = CopyFileTree (wszSource, wszDest, NULL, StatusFrom, StatusTo, FALSE, pCopyFailure);
  841. }
  842. //copy over the pin info. too
  843. if (ERROR_SUCCESS == Status)
  844. MergePinInfo (wszSource, wszDest, StatusFrom, StatusTo);
  845. }
  846. else
  847. {
  848. pCopyFailure->RegisterFailure (wszSource, wszDest);
  849. DebugMsg((DM_VERBOSE, IDS_DIRCREATE_FAIL, wszDest, Status));
  850. }
  851. }
  852. else
  853. {
  854. Status = ERROR_SUCCESS;
  855. // First check if it is necessary to copy the file over.
  856. bStatus = GetFileAttributesEx( wszSource, GetFileExInfoStandard, &SourceAttr );
  857. if ( bStatus )
  858. {
  859. bStatus = GetFileAttributesEx( wszDest, GetFileExInfoStandard, &DestAttr );
  860. if (bStatus)
  861. {
  862. if (CompareFileTime( &SourceAttr.ftLastWriteTime, &DestAttr.ftLastWriteTime ) <= 0)
  863. {
  864. // The destination is newer or at least as old as the source.
  865. // There is no need to copy. However, we should delete
  866. // the locally cached copy of the file if any since the
  867. // destination is newer.
  868. DebugMsg((DM_VERBOSE, IDS_SKIP_FILE, wszDest));
  869. if (ERROR_SUCCESS == StatusCSCDel)
  870. StatusCSCDel = DeleteCSCFile ( wszSource, bAllowRdrTimeout);
  871. else
  872. DeleteCSCFile ( wszSource, FALSE);
  873. continue;
  874. }
  875. }
  876. else
  877. {
  878. Status = GetLastError();
  879. if (ERROR_PATH_NOT_FOUND == Status ||
  880. ERROR_FILE_NOT_FOUND == Status)
  881. {
  882. // The destination was not found. So we must proceed with the copy.
  883. bStatus = TRUE;
  884. Status = ERROR_SUCCESS;
  885. }
  886. }
  887. }
  888. else
  889. {
  890. // We failed to get the attributes of the source file.
  891. Status = GetLastError();
  892. }
  893. if (ERROR_SUCCESS == Status)
  894. {
  895. //
  896. // If we are here, we need to copy the file over.
  897. // In order to avoid loss of data, we must first copy the file
  898. // over to a temporary file at the destination and then rename
  899. // the file at the destination. This is because if the network
  900. // connection gets dropped during the filecopy operation, then
  901. // we are left with an incomplete file at the destination.
  902. // If we use the real name on the destination directly, then
  903. // the file will be skipped at the next redirection attempt
  904. // because of our last writer wins algorithm. As a result, when
  905. // the redirection succeeds subsequently, we end up with a loss
  906. // of user data. Copying to a temp name and then renaming the
  907. // file prevents this problem from happening because the rename
  908. // operation is atomic.
  909. //
  910. // First check if we need to generate a new temporary filename
  911. // Note: We try to minimize the number of calls to GetTempFilename
  912. // because it can be a very expensive call as it can result in
  913. // multiple CreateFile calls over the network which can be
  914. // especially slow for EFS shares.
  915. //
  916. bReuseTempName = FALSE;
  917. if (NULL != pwszTempFilename && L'\0' != *pwszTempFilename)
  918. {
  919. dwAttr = GetFileAttributes (pwszTempFilename);
  920. if (INVALID_FILE_ATTRIBUTES == dwAttr)
  921. {
  922. Status = GetLastError();
  923. if (ERROR_PATH_NOT_FOUND == Status ||
  924. ERROR_FILE_NOT_FOUND == Status)
  925. {
  926. Status = ERROR_SUCCESS;
  927. bReuseTempName = TRUE;
  928. }
  929. }
  930. }
  931. if (ERROR_SUCCESS == Status && FALSE == bReuseTempName)
  932. {
  933. // We need to generate a new temporary filename.
  934. if (NULL == pwszTempFilename)
  935. {
  936. pwszTempFilename = new WCHAR [MAX_PATH + 1];
  937. if (NULL == pwszTempFilename)
  938. Status = ERROR_OUTOFMEMORY;
  939. }
  940. if (ERROR_SUCCESS == Status)
  941. {
  942. *pwszTempFilename = 0;
  943. *pwszDestEnd = 0;
  944. bStatus = GetTempFileName(wszDest, TEXT("frd"), 0, pwszTempFilename);
  945. *pwszDestEnd = FindData.cFileName[0];
  946. if (!bStatus)
  947. {
  948. Status = GetLastError();
  949. DebugMsg((DM_VERBOSE, IDS_NO_TEMPNAME, wszDest, Status));
  950. }
  951. }
  952. }
  953. if (ERROR_SUCCESS == Status)
  954. {
  955. // Now we have a temp. filename and we are ready to copy.
  956. Status = FullFileCopyW (wszSource, pwszTempFilename, FALSE);
  957. if (ERROR_SUCCESS == Status)
  958. {
  959. // Now we rename the file at the destination in one atomic
  960. // step. Note however that if the destination file exists
  961. // and has readonly/hidden/system attributes, the MoveFileEx
  962. // API will fail with ERROR_ACCESS_DENIED. So we slap on normal
  963. // attributes on the file before doing the Move. If we fail,
  964. // we restore the attributes.
  965. dwAttr = GetFileAttributes (wszDest);
  966. // Change attributes only if we managed to figure out the
  967. // actual attributes.
  968. if (INVALID_FILE_ATTRIBUTES != dwAttr)
  969. SetFileAttributes (wszDest, FILE_ATTRIBUTE_NORMAL);
  970. if (!MoveFileEx(pwszTempFilename, wszDest, MOVEFILE_REPLACE_EXISTING))
  971. {
  972. Status = GetLastError();
  973. DebugMsg((DM_VERBOSE, IDS_RENAME_FAILED, pwszTempFilename, wszDest, Status));
  974. // Restore the attributes of the destination file.
  975. // Provided we changed those in the first place.
  976. if (INVALID_FILE_ATTRIBUTES != dwAttr)
  977. SetFileAttributes (wszDest, dwAttr);
  978. // Also try to delete the temp file because we might still
  979. // be able to do it. But ignore any failures. We are just
  980. // trying to be nice by removing turds.
  981. DeleteFile(pwszTempFilename);
  982. }
  983. }
  984. else
  985. {
  986. //
  987. // ignore failure here because we may no longer have network connectivity and
  988. // the delete operation may fail here.
  989. //
  990. DeleteFile(pwszTempFilename);
  991. }
  992. }
  993. }
  994. if ( Status != ERROR_SUCCESS )
  995. {
  996. pCopyFailure->RegisterFailure (wszSource, wszDest);
  997. switch (Status)
  998. {
  999. case ERROR_INVALID_SECURITY_DESCR:
  1000. DebugMsg((DM_VERBOSE, IDS_SETSECURITY_FAIL, wszSource, wszDest));
  1001. break;
  1002. default:
  1003. DebugMsg((DM_VERBOSE, IDS_FILECOPY_FAIL, wszSource, wszDest, Status));
  1004. break;
  1005. }
  1006. }
  1007. }
  1008. if ( Status != ERROR_SUCCESS )
  1009. break;
  1010. } while ( FindNextFile( hFind, &FindData ) );
  1011. CopyFileTree_End:
  1012. // Some final cleanup before we return.
  1013. delete [] wszSource;
  1014. delete [] wszDest;
  1015. delete [] pwszTempFilename;
  1016. if (INVALID_HANDLE_VALUE != hFind)
  1017. FindClose( hFind );
  1018. return Status;
  1019. }
  1020. DWORD
  1021. CFileDB::DeleteFileTree(
  1022. WCHAR * pwszPath,
  1023. WCHAR * pwszIgnoredSubdir
  1024. )
  1025. {
  1026. HANDLE hFind = INVALID_HANDLE_VALUE;
  1027. WIN32_FIND_DATA FindData;
  1028. WCHAR * wszSource = NULL;
  1029. WCHAR * pwszSourceEnd = 0;
  1030. DWORD Status;
  1031. BOOL bStatus;
  1032. int len;
  1033. size_t sourceRemaining;
  1034. if (!pwszPath)
  1035. return ERROR_PATH_NOT_FOUND;
  1036. len = wcslen (pwszPath);
  1037. if (!len)
  1038. return ERROR_PATH_NOT_FOUND;
  1039. // Create source string
  1040. sourceRemaining = len + MAX_PATH + 2;
  1041. wszSource = new WCHAR[ sourceRemaining ];
  1042. if (NULL == wszSource)
  1043. {
  1044. return ERROR_OUTOFMEMORY;
  1045. }
  1046. Status = HRESULT_CODE(StringCchCopyEx( wszSource, sourceRemaining, pwszPath, &pwszSourceEnd, &sourceRemaining, 0 ));
  1047. if ( Status != ERROR_SUCCESS )
  1048. {
  1049. goto DeleteFileTree_End;
  1050. }
  1051. if (L'\\' != pwszSourceEnd[-1])
  1052. {
  1053. Status = HRESULT_CODE(StringCchCopyEx( pwszSourceEnd, sourceRemaining, L"\\", &pwszSourceEnd, &sourceRemaining, 0 ));
  1054. if ( Status != ERROR_SUCCESS )
  1055. {
  1056. goto DeleteFileTree_End;
  1057. }
  1058. }
  1059. Status = HRESULT_CODE(StringCchCopy( pwszSourceEnd, sourceRemaining, L"*" ));
  1060. if ( Status != ERROR_SUCCESS )
  1061. {
  1062. goto DeleteFileTree_End;
  1063. }
  1064. // Now find the file(s)
  1065. hFind = FindFirstFile( wszSource, &FindData );
  1066. Status = ERROR_SUCCESS;
  1067. if ( INVALID_HANDLE_VALUE == hFind )
  1068. {
  1069. goto DeleteFileTree_End;
  1070. }
  1071. for (;;)
  1072. {
  1073. Status = HRESULT_CODE(StringCchCopy( pwszSourceEnd, sourceRemaining, FindData.cFileName ));
  1074. if ( Status != ERROR_SUCCESS )
  1075. {
  1076. break;
  1077. }
  1078. if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  1079. {
  1080. if ( lstrcmp( FindData.cFileName, L"." ) != 0 &&
  1081. lstrcmp( FindData.cFileName, L".." ) != 0 &&
  1082. (! pwszIgnoredSubdir || lstrcmpi( FindData.cFileName, pwszIgnoredSubdir ) != 0) )
  1083. {
  1084. SetFileAttributes( wszSource, FILE_ATTRIBUTE_NORMAL );
  1085. Status = DeleteFileTree( wszSource, NULL);
  1086. if ( ERROR_SUCCESS == Status )
  1087. {
  1088. if ( ! RemoveDirectory( wszSource ) )
  1089. {
  1090. Status = GetLastError();
  1091. DebugMsg((DM_VERBOSE, IDS_DIRDEL_FAIL, wszSource, Status));
  1092. }
  1093. }
  1094. }
  1095. }
  1096. else
  1097. {
  1098. SetFileAttributes( wszSource, FILE_ATTRIBUTE_NORMAL );
  1099. bStatus = DeleteFile( wszSource );
  1100. if ( ! bStatus )
  1101. {
  1102. Status = GetLastError();
  1103. DebugMsg((DM_VERBOSE, IDS_FILEDEL_FAIL, wszSource, Status));
  1104. }
  1105. }
  1106. if ( Status != ERROR_SUCCESS )
  1107. break;
  1108. bStatus = FindNextFile( hFind, &FindData );
  1109. if ( ! bStatus )
  1110. {
  1111. Status = GetLastError();
  1112. if ( ERROR_NO_MORE_FILES == Status )
  1113. Status = ERROR_SUCCESS;
  1114. break;
  1115. }
  1116. }
  1117. DeleteFileTree_End:
  1118. if (INVALID_HANDLE_VALUE != hFind)
  1119. {
  1120. FindClose( hFind );
  1121. }
  1122. delete [] wszSource;
  1123. return Status;
  1124. }
  1125. //note: the bCheckOwner flag: if set to true, then if the directory in question
  1126. // already exists, the function Fails with an ERROR_INVALID_OWNER if the
  1127. // the owner of the existing directory is not the user.
  1128. // bSourceValid indicates if there is a valid path in pwszSource
  1129. DWORD
  1130. CFileDB::CreateRedirectedFolderPath(
  1131. const WCHAR * pwszSource,
  1132. const WCHAR * pwszDest,
  1133. BOOL bSourceValid,
  1134. BOOL bCheckOwner,
  1135. BOOL bMoveContents
  1136. )
  1137. {
  1138. WCHAR * pwszSlash = 0;
  1139. WCHAR * pwszPath = NULL;
  1140. DWORD Status = ERROR_SUCCESS;
  1141. int len;
  1142. WCHAR * pwszSuccess = NULL;
  1143. DWORD dwAttributes;
  1144. //first make a local copy of the destination path to work with.
  1145. //while copying, we actually convert it into an absolute path so
  1146. //that we eliminate any problems with weird paths like
  1147. //\\server\share\..\hello\..\there
  1148. len = wcslen (pwszDest) + 1;
  1149. pwszPath = new WCHAR[len];
  1150. if ( !pwszPath )
  1151. {
  1152. return ERROR_OUTOFMEMORY;
  1153. }
  1154. pwszSuccess = _wfullpath (pwszPath, pwszDest, len);
  1155. if (!pwszSuccess)
  1156. {
  1157. Status = ERROR_BAD_PATHNAME; //actually _wfullpath rarely fails
  1158. goto CreateRedirectedFolderPath_Cleanup;
  1159. }
  1160. //
  1161. // Will only accept drive based or UNC based paths.
  1162. //
  1163. // A redirect path of just <drive>:\ or \\server\share will be accepted
  1164. // even though this would be a strange choice for redirection.
  1165. //
  1166. // IMPORTANT: also see notes at the beginning of the function
  1167. if ( L':' == pwszPath[1] && L'\\' == pwszPath[2] )
  1168. {
  1169. pwszSlash = &pwszPath[2];
  1170. }
  1171. else if ( L'\\' == pwszPath[0] && L'\\' == pwszPath[1] )
  1172. {
  1173. pwszSlash = wcschr( &pwszPath[2], L'\\' );
  1174. if ( pwszSlash )
  1175. {
  1176. //watch out for '\' terminated paths
  1177. if (L'\0' == pwszSlash[1])
  1178. {
  1179. pwszSlash = 0;
  1180. }
  1181. else //it is at least of the form \\server\share
  1182. {
  1183. pwszSlash = wcschr( &pwszSlash[1], L'\\' );
  1184. //if it is of the form \\server\share, then we allow this path
  1185. //based depending on the ownership checks if any
  1186. if (!pwszSlash)
  1187. {
  1188. Status = bCheckOwner ? IsUserOwner(pwszPath) : ERROR_SUCCESS;
  1189. goto CreateRedirectedFolderPath_Cleanup;
  1190. }
  1191. //note: we do not have to watch out for the '\' terminated
  1192. //paths here (e.g. \\server\share\) because that will be
  1193. //taken care of below : in -> if (!pwszSlash[1])
  1194. }
  1195. }
  1196. }
  1197. else
  1198. {
  1199. pwszSlash = 0;
  1200. }
  1201. if ( ! pwszSlash )
  1202. {
  1203. Status = ERROR_BAD_PATHNAME;
  1204. goto CreateRedirectedFolderPath_Cleanup;
  1205. }
  1206. //if it is the root directory of a drive or root of a UNC share
  1207. //we succeed based on ownership checks, if any...
  1208. //but before that, we also need to make sure that the specified path
  1209. //exists or we may end up redirecting to a non-existent location.
  1210. if ( !pwszSlash[1])
  1211. {
  1212. if (0xFFFFFFFF != (dwAttributes = GetFileAttributes(pwszPath)))
  1213. {
  1214. //it exists
  1215. if (! (FILE_ATTRIBUTE_DIRECTORY & dwAttributes))
  1216. {
  1217. Status = ERROR_DIRECTORY;
  1218. }
  1219. else
  1220. {
  1221. //it exists and is a directory
  1222. Status = bCheckOwner ? IsUserOwner (pwszPath) : ERROR_SUCCESS;
  1223. }
  1224. }
  1225. else
  1226. {
  1227. Status = GetLastError();
  1228. }
  1229. goto CreateRedirectedFolderPath_Cleanup;
  1230. }
  1231. //if we are here, it is not the root of a drive or a share.
  1232. //so we might have to do create the destination.
  1233. //First do a quick check to see if the path exists already. this
  1234. //is not only an optimization, but is also necessary for cases
  1235. //where an admin. may want to lock down access to certain folders
  1236. //by pre-creating the folders and putting highly resitrictive ACLs on them
  1237. //in that case, CreateDirectory (later in the code) would fail with
  1238. //ACCESS_DENIED rather than ERROR_ALREADY_EXISTS and the redirection code
  1239. //will bail out even though it is not necessary to.
  1240. if (0xFFFFFFFF != (dwAttributes = GetFileAttributes(pwszPath)))
  1241. {
  1242. //it exists
  1243. if (! (FILE_ATTRIBUTE_DIRECTORY & dwAttributes))
  1244. {
  1245. Status = ERROR_DIRECTORY;
  1246. }
  1247. else
  1248. {
  1249. //it exists and is a directory
  1250. Status = bCheckOwner ? IsUserOwner (pwszPath) : ERROR_SUCCESS;
  1251. }
  1252. goto CreateRedirectedFolderPath_Cleanup;
  1253. }
  1254. //the destination has not been pre-created, so we need to do that
  1255. //ourselves
  1256. do
  1257. {
  1258. pwszSlash = wcschr( &pwszSlash[1], L'\\' );
  1259. // Watch out for '\' terminated paths.
  1260. if ( pwszSlash && (L'\0' == pwszSlash[1]) )
  1261. pwszSlash = 0;
  1262. if ( pwszSlash )
  1263. {
  1264. *pwszSlash = 0;
  1265. CreateDirectory( pwszPath, NULL );
  1266. *pwszSlash = L'\\';
  1267. //ignore all errors in the intermediate folders not just
  1268. //ERROR_ALREADY_EXISTS because of folders like
  1269. //\\server\share\dir1\dir2\%username% where the user may not
  1270. //have write access in dir1 but might have so in dir2
  1271. //if the path is invalid we will either discover it at the last
  1272. //dir in the chain or while trying to redirect to the destination
  1273. //retaining the code here just in case...
  1274. //
  1275. /*if ( ! bStatus && (GetLastError() != ERROR_ALREADY_EXISTS) )
  1276. return GetLastError();*/
  1277. }
  1278. else
  1279. {
  1280. //
  1281. // Last dir in the chain. We set security on the last dir in
  1282. // the path to only allow the user & system access if
  1283. // the directory did not already exist.
  1284. //
  1285. if (bCheckOwner)
  1286. {
  1287. Status = CreateFolderWithUserFileSecurity( pwszPath );
  1288. }
  1289. else
  1290. {
  1291. Status = ERROR_SUCCESS;
  1292. if (!CreateDirectory( pwszPath, NULL ))
  1293. Status = GetLastError();
  1294. }
  1295. if ( ERROR_SUCCESS == Status )
  1296. {
  1297. //the extension created the directory, so try to set the user as
  1298. //the owner explicitly because if a member of the local administrators
  1299. //group creates a directory/file, the Administrators group becomes
  1300. //the owner by default. This can cause problems with quota accounting
  1301. //and also if the settings on the redirection policy are changed
  1302. //at a later date. However, since it is not necessary that the
  1303. //we will succeed in setting the owner here, we ignore any failures
  1304. SetUserAsOwner (pwszPath);
  1305. //
  1306. // We want to skip the DACL if we want to apply exclusive ACLs
  1307. // i.e., bCheckOwner is true. Otherwise, we should just copy
  1308. // over all the metadata.
  1309. //
  1310. if (bSourceValid && bMoveContents)
  1311. FullDirCopyW (pwszSource, pwszPath, bCheckOwner);
  1312. goto CreateRedirectedFolderPath_Cleanup;
  1313. }
  1314. else if ( ERROR_ALREADY_EXISTS != Status)
  1315. {
  1316. goto CreateRedirectedFolderPath_Cleanup;
  1317. }
  1318. else
  1319. {
  1320. //the directory already exists
  1321. //start anti-spoofing agent
  1322. //do the ownership check only on the last dir in chain and only
  1323. //if the flags in the ini file tell you to.
  1324. if (bCheckOwner &&
  1325. (ERROR_SUCCESS != (Status = IsUserOwner(pwszPath))))
  1326. {
  1327. DebugMsg ((DM_VERBOSE, IDS_ACL_MISMATCH, pwszPath, Status));
  1328. goto CreateRedirectedFolderPath_Cleanup;
  1329. }
  1330. else
  1331. {
  1332. Status = ERROR_SUCCESS;
  1333. goto CreateRedirectedFolderPath_Cleanup;
  1334. }
  1335. }
  1336. }
  1337. } while ( pwszSlash );
  1338. Status = ERROR_SUCCESS;
  1339. CreateRedirectedFolderPath_Cleanup:
  1340. delete [] pwszPath;
  1341. return Status;
  1342. }
  1343. DWORD
  1344. CFileDB::SetUserAsOwner(
  1345. WCHAR * pwszPath
  1346. )
  1347. {
  1348. SECURITY_DESCRIPTOR SecDesc;
  1349. PSID pSidUser = 0;
  1350. PTOKEN_USER pTokenUser = 0;
  1351. DWORD Size = 0;
  1352. DWORD Status = ERROR_SUCCESS;
  1353. BOOL bStatus;
  1354. if ( ! _hUserToken )
  1355. return ERROR_SUCCESS;
  1356. Status = NtQueryInformationToken(
  1357. _hUserToken,
  1358. TokenUser,
  1359. pTokenUser,
  1360. Size,
  1361. &Size );
  1362. if ( STATUS_BUFFER_TOO_SMALL == Status )
  1363. {
  1364. pTokenUser = (PTOKEN_USER) new BYTE[ Size ];
  1365. if ( ! pTokenUser )
  1366. {
  1367. return ERROR_OUTOFMEMORY;
  1368. }
  1369. Status = NtQueryInformationToken(
  1370. _hUserToken,
  1371. TokenUser,
  1372. pTokenUser,
  1373. Size,
  1374. &Size );
  1375. }
  1376. if ( Status != ERROR_SUCCESS )
  1377. {
  1378. goto SetUserAsOwner_End;
  1379. }
  1380. Size = RtlLengthSid( pTokenUser->User.Sid );
  1381. pSidUser = (PSID) new BYTE[ Size ];
  1382. if ( pSidUser == NULL )
  1383. {
  1384. Status = ERROR_OUTOFMEMORY;
  1385. goto SetUserAsOwner_End;
  1386. }
  1387. Status = RtlCopySid( Size, pSidUser, pTokenUser->User.Sid );
  1388. if ( Status != ERROR_SUCCESS )
  1389. {
  1390. goto SetUserAsOwner_End;
  1391. }
  1392. bStatus = InitializeSecurityDescriptor( &SecDesc, SECURITY_DESCRIPTOR_REVISION );
  1393. if ( bStatus )
  1394. {
  1395. bStatus = SetSecurityDescriptorOwner (&SecDesc, pSidUser, 0);
  1396. }
  1397. if (bStatus)
  1398. {
  1399. bStatus = SetFileSecurity( pwszPath, OWNER_SECURITY_INFORMATION, &SecDesc);
  1400. }
  1401. if ( ! bStatus )
  1402. {
  1403. Status = GetLastError();
  1404. }
  1405. SetUserAsOwner_End:
  1406. delete [] (BYTE*) pTokenUser;
  1407. delete [] (BYTE*) pSidUser;
  1408. return Status;
  1409. }
  1410. DWORD
  1411. CFileDB::CreateFolderWithUserFileSecurity(
  1412. WCHAR * pwszPath
  1413. )
  1414. {
  1415. SECURITY_DESCRIPTOR SecDesc;
  1416. SID_IDENTIFIER_AUTHORITY AuthorityNT = SECURITY_NT_AUTHORITY;
  1417. SID_IDENTIFIER_AUTHORITY AuthWorld = SECURITY_WORLD_SID_AUTHORITY;
  1418. PSID pSidUser = 0;
  1419. PSID pSidSystem = 0;
  1420. PACL pAcl = 0;
  1421. ACE_HEADER * pAceHeader;
  1422. PTOKEN_USER pTokenUser = 0;
  1423. PSID pSid = 0;
  1424. DWORD AclSize;
  1425. DWORD AceIndex;
  1426. DWORD Size;
  1427. DWORD Status;
  1428. BOOL bStatus;
  1429. if ( ! _hUserToken )
  1430. return ERROR_SUCCESS;
  1431. pSidSystem = 0;
  1432. pTokenUser = 0;
  1433. Size = 0;
  1434. Status = NtQueryInformationToken(
  1435. _hUserToken,
  1436. TokenUser,
  1437. pTokenUser,
  1438. Size,
  1439. &Size );
  1440. if ( STATUS_BUFFER_TOO_SMALL == Status )
  1441. {
  1442. pTokenUser = (PTOKEN_USER) new BYTE[ Size ];
  1443. if ( ! pTokenUser )
  1444. {
  1445. return ERROR_OUTOFMEMORY;
  1446. }
  1447. Status = NtQueryInformationToken(
  1448. _hUserToken,
  1449. TokenUser,
  1450. pTokenUser,
  1451. Size,
  1452. &Size );
  1453. }
  1454. if ( Status != ERROR_SUCCESS )
  1455. {
  1456. goto SetUserFileSecurityEnd;
  1457. }
  1458. Size = RtlLengthSid( pTokenUser->User.Sid );
  1459. pSidUser = (PSID) new BYTE[ Size ];
  1460. if ( pSidUser == NULL )
  1461. {
  1462. Status = ERROR_OUTOFMEMORY;
  1463. goto SetUserFileSecurityEnd;
  1464. }
  1465. Status = RtlCopySid( Size, pSidUser, pTokenUser->User.Sid );
  1466. if ( Status != ERROR_SUCCESS )
  1467. {
  1468. goto SetUserFileSecurityEnd;
  1469. }
  1470. bStatus = AllocateAndInitializeSid( &AuthorityNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSidSystem);
  1471. if ( ! bStatus )
  1472. {
  1473. Status = GetLastError();
  1474. goto SetUserFileSecurityEnd;
  1475. }
  1476. //
  1477. // Allocate space for the ACL
  1478. //
  1479. AclSize = (GetLengthSid(pSidUser)) +
  1480. (GetLengthSid(pSidSystem)) +
  1481. sizeof(ACL) + (2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)));
  1482. pAcl = (PACL) new BYTE[ AclSize ];
  1483. if ( pAcl )
  1484. {
  1485. bStatus = InitializeAcl( pAcl, AclSize, ACL_REVISION );
  1486. if ( ! bStatus )
  1487. Status = GetLastError();
  1488. }
  1489. else
  1490. {
  1491. Status = ERROR_OUTOFMEMORY;
  1492. }
  1493. if ( Status != ERROR_SUCCESS )
  1494. {
  1495. goto SetUserFileSecurityEnd;
  1496. }
  1497. //
  1498. // Add Aces for User, System, and Admin. Non-inheritable ACEs first
  1499. //
  1500. AceIndex = 0;
  1501. bStatus = AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, pSidUser);
  1502. if ( bStatus )
  1503. {
  1504. bStatus = GetAce(pAcl, AceIndex, (void **) &pAceHeader);
  1505. if ( bStatus )
  1506. pAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
  1507. }
  1508. if ( bStatus )
  1509. {
  1510. AceIndex++;
  1511. bStatus = AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, pSidSystem);
  1512. if ( bStatus )
  1513. {
  1514. bStatus = GetAce(pAcl, AceIndex, (void **) &pAceHeader);
  1515. if ( bStatus )
  1516. pAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
  1517. }
  1518. }
  1519. if ( ! bStatus )
  1520. {
  1521. Status = GetLastError();
  1522. goto SetUserFileSecurityEnd;
  1523. }
  1524. bStatus = InitializeSecurityDescriptor( &SecDesc, SECURITY_DESCRIPTOR_REVISION );
  1525. if (bStatus)
  1526. {
  1527. SetSecurityDescriptorControl (&SecDesc, SE_DACL_PROTECTED,
  1528. SE_DACL_PROTECTED);
  1529. //SE_DACL_PROTECTED is supported by NTFS5 but not by NTFS4, therefore
  1530. //we ignore any failures in SetSecurityDesciptorControl
  1531. }
  1532. if ( bStatus )
  1533. {
  1534. bStatus = SetSecurityDescriptorDacl( &SecDesc, TRUE, pAcl, FALSE );
  1535. }
  1536. //set the owner explicitly. This is required because if the user is an
  1537. //admin, then when the directory is created, the owner is set to the group
  1538. //administrators, rather than the user
  1539. if ( bStatus )
  1540. {
  1541. bStatus = SetSecurityDescriptorOwner (&SecDesc, pSidUser, 0);
  1542. }
  1543. if ( bStatus )
  1544. {
  1545. SECURITY_ATTRIBUTES sa;
  1546. sa.bInheritHandle = FALSE;
  1547. sa.lpSecurityDescriptor = &SecDesc;
  1548. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  1549. bStatus = CreateDirectory(pwszPath, &sa);
  1550. }
  1551. if ( ! bStatus )
  1552. {
  1553. Status = GetLastError();
  1554. }
  1555. SetUserFileSecurityEnd:
  1556. delete [] (BYTE*) pAcl;
  1557. delete [] (BYTE*) pTokenUser;
  1558. delete [] (BYTE*) pSidUser;
  1559. if (pSidSystem)
  1560. {
  1561. FreeSid( pSidSystem );
  1562. }
  1563. return Status;
  1564. }
  1565. //+--------------------------------------------------------------------------
  1566. //
  1567. // Member: CFileDB::IsUserOwner
  1568. //
  1569. // Synopsis: given a path. this function determines if the user is the
  1570. // the owner of the file/folder
  1571. //
  1572. // Arguments: [in] pwszPath
  1573. //
  1574. // Returns: ERROR_SUCCESS if user is owner
  1575. // otherwise an error code
  1576. //
  1577. // History: 10/6/1998 RahulTh created
  1578. //
  1579. // Notes:
  1580. //
  1581. //---------------------------------------------------------------------------
  1582. DWORD CFileDB::IsUserOwner (const WCHAR * pwszPath)
  1583. {
  1584. BOOL bStatus;
  1585. DWORD Status;
  1586. BOOL bIsMember;
  1587. DWORD dwLengthNeeded;
  1588. PSECURITY_DESCRIPTOR pSecDesc = 0;
  1589. PSID pOwnerSid = 0;
  1590. BOOL bDaclDefaulted;
  1591. //first check if we are on FAT. if we are on FAT, then we simply let
  1592. //succeed since FAT cannot have any ACLs anyway
  1593. Status = IsOnNTFS (pwszPath);
  1594. if (ERROR_NO_SECURITY_ON_OBJECT == Status)
  1595. {
  1596. Status = ERROR_SUCCESS; //we are on FAT
  1597. goto UserOwnerEnd;
  1598. }
  1599. else if (ERROR_SUCCESS != Status)
  1600. goto UserOwnerEnd; //there was some other error
  1601. //else we are on NTFS and ready to rumble!
  1602. //get the owner sid from the folder.
  1603. dwLengthNeeded = 0;
  1604. if ( !GetFileSecurity (pwszPath, OWNER_SECURITY_INFORMATION,
  1605. pSecDesc, dwLengthNeeded,
  1606. &dwLengthNeeded) )
  1607. {
  1608. //GetFileSecurity failed if we are here
  1609. Status = GetLastError();
  1610. if (ERROR_INSUFFICIENT_BUFFER != Status)
  1611. goto UserOwnerEnd;
  1612. //GetFileSecurity failed due to insufficient memory if we are here.
  1613. pSecDesc = (PSECURITY_DESCRIPTOR) new BYTE[dwLengthNeeded];
  1614. if (NULL == pSecDesc)
  1615. {
  1616. Status = ERROR_OUTOFMEMORY;
  1617. goto UserOwnerEnd;
  1618. }
  1619. if ( !GetFileSecurity (pwszPath, OWNER_SECURITY_INFORMATION,
  1620. pSecDesc, dwLengthNeeded,
  1621. &dwLengthNeeded) )
  1622. {
  1623. Status = GetLastError();
  1624. goto UserOwnerEnd;
  1625. }
  1626. }
  1627. //now get the owner sid
  1628. bStatus = GetSecurityDescriptorOwner (
  1629. pSecDesc,
  1630. &pOwnerSid,
  1631. &bDaclDefaulted);
  1632. if (!bStatus)
  1633. {
  1634. Status = GetLastError();
  1635. }
  1636. else
  1637. {
  1638. if (!pOwnerSid)
  1639. {
  1640. Status = ERROR_INVALID_OWNER;
  1641. }
  1642. else
  1643. {
  1644. bStatus = CheckTokenMembership (_hUserToken, pOwnerSid, &bIsMember);
  1645. if (!bStatus)
  1646. Status = GetLastError();
  1647. else
  1648. {
  1649. if (bIsMember)
  1650. Status = ERROR_SUCCESS;
  1651. else
  1652. Status = ERROR_INVALID_OWNER;
  1653. }
  1654. }
  1655. }
  1656. UserOwnerEnd:
  1657. delete [] (BYTE*) pSecDesc;
  1658. return Status;
  1659. }
  1660. //+--------------------------------------------------------------------------
  1661. //
  1662. // Member: CFileDB::GetEnvBlock
  1663. //
  1664. // Synopsis: gets a pointer to the user's environment block
  1665. //
  1666. // Arguments: none.
  1667. //
  1668. // Returns: pointer to the user's environment block.
  1669. // NULL if it is not created yet
  1670. //
  1671. // History: 9/20/1999 RahulTh created
  1672. //
  1673. // Notes:
  1674. //
  1675. //---------------------------------------------------------------------------
  1676. PVOID CFileDB::GetEnvBlock (void)
  1677. {
  1678. return _pEnvBlock;
  1679. }
  1680. //+--------------------------------------------------------------------------
  1681. //
  1682. // Member: CFileDB::GetRsopContext
  1683. //
  1684. // Synopsis: gets a pointer to the rsop logging context
  1685. //
  1686. // Arguments: none.
  1687. //
  1688. // Returns: pointer to the rsop logging context -- never null
  1689. //
  1690. //
  1691. // History: 12/8/1999 adamed created
  1692. //
  1693. // Notes:
  1694. //
  1695. //---------------------------------------------------------------------------
  1696. CRsopContext*
  1697. CFileDB::GetRsopContext()
  1698. {
  1699. return _pRsopContext;
  1700. }
  1701. //member functions and data for class CSavedSettings
  1702. //initialize the class's static variables.
  1703. int CSavedSettings::m_idConstructor = 0;
  1704. CFileDB * CSavedSettings::m_pFileDB = NULL;
  1705. WCHAR * CSavedSettings::m_szSavedSettingsPath = NULL;
  1706. //+--------------------------------------------------------------------------
  1707. //
  1708. // Member: CSavedSettings::ResetStaticMembers
  1709. //
  1710. // Synopsis: resets the static members to their default values.
  1711. //
  1712. // Arguments: none
  1713. //
  1714. // Returns: nothing
  1715. //
  1716. // History: 12/17/2000 RahulTh created
  1717. //
  1718. // Notes: static members of a class, like other global variables are
  1719. // initialized only when the dll is loaded. So if this dll
  1720. // stays loaded across logons, then the fact that the globals
  1721. // have been initialized based on a previous logon can cause
  1722. // problems. Therefore, this function is used to reinitialize
  1723. // the globals.
  1724. //
  1725. //---------------------------------------------------------------------------
  1726. void CSavedSettings::ResetStaticMembers(void)
  1727. {
  1728. //
  1729. // No need to delete it. This usually points to a local variable in
  1730. // ProcessGroupPolicyInternal. So the actual object gets deleted when
  1731. // after each processing. We just need to make sure that it is reflected
  1732. // here.
  1733. //
  1734. m_pFileDB = NULL;
  1735. if (m_szSavedSettingsPath)
  1736. {
  1737. delete [] m_szSavedSettingsPath;
  1738. m_szSavedSettingsPath = NULL;
  1739. }
  1740. // No need to do anything about m_idConstructor
  1741. }
  1742. //+--------------------------------------------------------------------------
  1743. //
  1744. // Member: CSavedSettings
  1745. //
  1746. // Synopsis: default constructor for the class.
  1747. //
  1748. // Arguments:
  1749. //
  1750. // Returns:
  1751. //
  1752. // History: 11/18/1998 RahulTh created
  1753. //
  1754. // Notes:
  1755. //
  1756. //---------------------------------------------------------------------------
  1757. CSavedSettings::CSavedSettings ()
  1758. {
  1759. m_rID = (REDIRECTABLE) m_idConstructor;
  1760. m_idConstructor = (m_idConstructor + 1) % ((int) EndRedirectable);
  1761. m_szLastRedirectedPath = NULL;
  1762. m_szCurrentPath = NULL;
  1763. m_szLastUserName = NULL;
  1764. m_szLastHomedir = NULL;
  1765. m_bIsHomedirRedir = FALSE;
  1766. m_bHomedirChanged = FALSE;
  1767. m_dwFlags = REDIR_DONT_CARE; //this is always a safe default
  1768. m_psid = 0;
  1769. m_bValidGPO = FALSE;
  1770. m_bUserNameChanged = FALSE;
  1771. m_szGPOName[0] = L'\0';
  1772. }
  1773. //+--------------------------------------------------------------------------
  1774. //
  1775. // Member: CSavedSettings::ResetMembers
  1776. //
  1777. // Synopsis: Resets the member variables.
  1778. //
  1779. // Arguments: none.
  1780. //
  1781. // Returns: nothing.
  1782. //
  1783. // History: 12/17/2000 RahulTh created
  1784. //
  1785. // Notes: see ResetStaticMembers.
  1786. //
  1787. //---------------------------------------------------------------------------
  1788. void CSavedSettings::ResetMembers(void)
  1789. {
  1790. if (m_szLastRedirectedPath)
  1791. {
  1792. delete [] m_szLastRedirectedPath;
  1793. m_szLastRedirectedPath = NULL;
  1794. }
  1795. if (m_szCurrentPath)
  1796. {
  1797. delete [] m_szCurrentPath;
  1798. m_szCurrentPath = NULL;
  1799. }
  1800. if (m_szLastUserName)
  1801. {
  1802. delete [] m_szLastUserName;
  1803. m_szLastUserName = NULL;
  1804. }
  1805. if (m_szLastHomedir)
  1806. {
  1807. delete [] m_szLastHomedir;
  1808. m_szLastHomedir = NULL;
  1809. }
  1810. if (m_psid)
  1811. {
  1812. RtlFreeSid (m_psid);
  1813. m_psid = NULL;
  1814. }
  1815. m_bHomedirChanged = FALSE;
  1816. m_dwFlags = REDIR_DONT_CARE; //this is always a safe default
  1817. m_bValidGPO = FALSE;
  1818. m_bUserNameChanged = FALSE;
  1819. m_szGPOName[0] = L'\0';
  1820. // No need to do anything about m_rID.
  1821. }
  1822. //+--------------------------------------------------------------------------
  1823. //
  1824. // Member: ~CSavedSettings
  1825. //
  1826. // Synopsis: default destructor
  1827. //
  1828. // Arguments:
  1829. //
  1830. // Returns:
  1831. //
  1832. // History: 11/18/1998 RahulTh created
  1833. //
  1834. // Notes:
  1835. //
  1836. //---------------------------------------------------------------------------
  1837. CSavedSettings::~CSavedSettings ()
  1838. {
  1839. //free static members if necessary
  1840. if (m_szSavedSettingsPath)
  1841. {
  1842. delete [] m_szSavedSettingsPath;
  1843. m_szSavedSettingsPath = NULL; //this is necessary so that the
  1844. //destructor of the next object does
  1845. //not try to free it again
  1846. }
  1847. //
  1848. // Free other memory allocated for members of this object
  1849. // and reset them.
  1850. //
  1851. ResetMembers();
  1852. }
  1853. //+--------------------------------------------------------------------------
  1854. //
  1855. // Member: Load
  1856. //
  1857. // Synopsis: loads the saved settings into the object for its corresponding
  1858. // folder
  1859. //
  1860. // Arguments: none
  1861. //
  1862. // Returns: ERROR_SUCCESS if successful. otherwise an error code
  1863. //
  1864. // History: 11/18/1998 RahulTh created
  1865. //
  1866. // Notes:
  1867. //
  1868. //---------------------------------------------------------------------------
  1869. DWORD CSavedSettings::Load (CFileDB * pFileDB)
  1870. {
  1871. DWORD Status;
  1872. BOOL bStatus;
  1873. DWORD len;
  1874. //first set the FileDB object if it has not already been set
  1875. if (!m_pFileDB)
  1876. m_pFileDB = pFileDB;
  1877. ASSERT (m_pFileDB);
  1878. //get the name of the file where the last settings have been saved
  1879. //if we haven't already done so.
  1880. if (!m_szSavedSettingsPath)
  1881. {
  1882. len = wcslen (pFileDB->_pwszLocalPath) +
  1883. wcslen (SAVED_SETTINGS_FILE) + 2;
  1884. m_szSavedSettingsPath = (WCHAR *) new WCHAR [len];
  1885. if (!m_szSavedSettingsPath)
  1886. {
  1887. Status = ERROR_OUTOFMEMORY;
  1888. goto LoadEnd;
  1889. }
  1890. Status = HRESULT_CODE(StringCchCopy(m_szSavedSettingsPath, len, pFileDB->_pwszLocalPath));
  1891. if ( Status != ERROR_SUCCESS )
  1892. {
  1893. goto LoadEnd;
  1894. }
  1895. Status = HRESULT_CODE(StringCchCat(m_szSavedSettingsPath, len, L"\\"));
  1896. if ( Status != ERROR_SUCCESS )
  1897. {
  1898. goto LoadEnd;
  1899. }
  1900. Status = HRESULT_CODE(StringCchCat(m_szSavedSettingsPath, len, SAVED_SETTINGS_FILE));
  1901. if ( Status != ERROR_SUCCESS )
  1902. {
  1903. goto LoadEnd;
  1904. }
  1905. }
  1906. //do a quick check to see if the file exists
  1907. if (0xFFFFFFFF == GetFileAttributes(m_szSavedSettingsPath))
  1908. Status = LoadDefaultLocal ();
  1909. else
  1910. Status = LoadFromIniFile ();
  1911. LoadEnd:
  1912. return Status;
  1913. }
  1914. //+--------------------------------------------------------------------------
  1915. //
  1916. // Member: GetCurrentPath
  1917. //
  1918. // Synopsis: gets the current path of the folder.
  1919. //
  1920. // Arguments: none
  1921. //
  1922. // Returns: ERROR_SUCCESS if successful
  1923. //
  1924. // History: 11/18/1998 RahulTh created
  1925. //
  1926. // Notes:
  1927. //
  1928. //---------------------------------------------------------------------------
  1929. DWORD CSavedSettings::GetCurrentPath (void)
  1930. {
  1931. DWORD Status = ERROR_SUCCESS;
  1932. WCHAR * pwszValueName = 0;
  1933. DWORD Size;
  1934. WCHAR * pwszProcessedPath = NULL;
  1935. if (m_szCurrentPath)
  1936. {
  1937. delete [] m_szCurrentPath;
  1938. m_szCurrentPath = NULL;
  1939. }
  1940. m_szCurrentPath = new WCHAR [MAX_PATH];
  1941. if (!m_szCurrentPath)
  1942. {
  1943. Status = ERROR_OUTOFMEMORY;
  1944. goto GetCurrentPathEnd;
  1945. }
  1946. m_szCurrentPath[0] = L'\0';
  1947. Status = m_pFileDB->GetPathFromFolderName (
  1948. g_szRelativePathNames[(int)m_rID],
  1949. m_szCurrentPath
  1950. );
  1951. if (((DWORD) S_OK != Status) && //if SHGetFolderPath failed, use the local userprofile path
  1952. ERROR_INVALID_NAME != Status) //the only error from GetPathFromFolderName that is not generated by SHGetFolderPath
  1953. {
  1954. Status = HRESULT_CODE(StringCchCopy(m_szCurrentPath, MAX_PATH, L"%USERPROFILE%\\"));
  1955. if ( Status != ERROR_SUCCESS )
  1956. {
  1957. goto GetCurrentPathEnd;
  1958. }
  1959. Status = HRESULT_CODE(StringCchCat(m_szCurrentPath, MAX_PATH, g_szRelativePathNames[(int)m_rID]));
  1960. if ( Status != ERROR_SUCCESS )
  1961. {
  1962. goto GetCurrentPathEnd;
  1963. }
  1964. }
  1965. else
  1966. {
  1967. // expand the homedir path if applicable.
  1968. if (IsHomedirPath (m_rID, m_szCurrentPath, TRUE))
  1969. {
  1970. Status = ExpandHomeDir (m_rID, m_szCurrentPath, TRUE, &pwszProcessedPath);
  1971. delete [] m_szCurrentPath;
  1972. m_szCurrentPath = NULL;
  1973. if (ERROR_SUCCESS == Status)
  1974. {
  1975. m_szCurrentPath = pwszProcessedPath;
  1976. pwszProcessedPath = NULL;
  1977. }
  1978. }
  1979. }
  1980. if (m_szCurrentPath)
  1981. RemoveEndingSlash( m_szCurrentPath );
  1982. GetCurrentPathEnd:
  1983. return Status;
  1984. }
  1985. //+--------------------------------------------------------------------------
  1986. //
  1987. // Member: ResetLastUserName
  1988. //
  1989. // Synopsis: sets the last user name to the current username
  1990. //
  1991. // Arguments: none.
  1992. //
  1993. // Returns: ERROR_SUCCESS : if there is no error.
  1994. // an error code otherwise.
  1995. //
  1996. // History: 9/15/1999 RahulTh created
  1997. //
  1998. // Notes: the most likely cause of failure for this function -- if at all
  1999. // it happens, will be an out of memory condition.
  2000. //
  2001. //---------------------------------------------------------------------------
  2002. DWORD CSavedSettings::ResetLastUserName (void)
  2003. {
  2004. DWORD Status = ERROR_SUCCESS;
  2005. size_t lastUserNameLength = 0;
  2006. if (m_szLastUserName)
  2007. {
  2008. delete [] m_szLastUserName;
  2009. m_szLastUserName = NULL;
  2010. }
  2011. lastUserNameLength = wcslen(gwszUserName);
  2012. m_szLastUserName = new WCHAR [lastUserNameLength + 1];
  2013. if (!m_szLastUserName)
  2014. {
  2015. return ERROR_OUTOFMEMORY;
  2016. }
  2017. Status = HRESULT_CODE(StringCchCopy(m_szLastUserName, lastUserNameLength+1, gwszUserName));
  2018. if ( Status == ERROR_SUCCESS )
  2019. {
  2020. m_bUserNameChanged = FALSE;
  2021. }
  2022. return Status;
  2023. }
  2024. //+--------------------------------------------------------------------------
  2025. //
  2026. // Member: LoadDefaultLocal
  2027. //
  2028. // Synopsis: loads the default local userprofile path and default
  2029. // flags into the object
  2030. //
  2031. // Arguments: none
  2032. //
  2033. // Returns: ERROR_SUCCESS if successful an error code otherwise
  2034. //
  2035. // History: 11/18/1998 RahulTh created
  2036. //
  2037. // Notes: the security group is set to Everyone as you can never
  2038. // cease being a member of the group
  2039. //
  2040. // this function is usually invoked when one can't find
  2041. // the ini file that contains the last saved settings or
  2042. // if the ini file does not contain the corresponding section
  2043. //
  2044. //---------------------------------------------------------------------------
  2045. DWORD CSavedSettings::LoadDefaultLocal (void)
  2046. {
  2047. DWORD Status = ERROR_SUCCESS;
  2048. DWORD len;
  2049. // The default case cannot be homedir redirection.
  2050. // Just make sure that our bool has been properly set.
  2051. m_bIsHomedirRedir = FALSE;
  2052. //set the default flags
  2053. m_dwFlags = REDIR_DONT_CARE;
  2054. //to be on the safe side -- set the default values for all members.
  2055. m_bValidGPO = FALSE;
  2056. m_szGPOName[0] = L'\0';
  2057. //set the last username to be the same as the current username
  2058. //since we load the defaults only when we do not have any data about the
  2059. //last logon (i.e., no per user per machine FR cache
  2060. Status = ResetLastUserName ();
  2061. if (ERROR_SUCCESS != Status)
  2062. goto LoadDefaultsEnd;
  2063. //allocate the sid
  2064. //to be on the safe side, free the sid if it has already been allocated
  2065. if (m_psid)
  2066. {
  2067. RtlFreeSid (m_psid);
  2068. m_psid = NULL;
  2069. }
  2070. Status = AllocateAndInitSidFromString (L"S-1-1-0", &m_psid);
  2071. if (ERROR_SUCCESS != Status)
  2072. {
  2073. m_psid = 0;
  2074. goto LoadDefaultsEnd;
  2075. }
  2076. //get the last redirected path
  2077. //again, to be on the safe side, free any used memory first
  2078. if (m_szLastRedirectedPath)
  2079. {
  2080. delete [] m_szLastRedirectedPath;
  2081. m_szLastRedirectedPath = NULL;
  2082. }
  2083. len = wcslen (L"%USERPROFILE%\\") +
  2084. wcslen (g_szRelativePathNames[(int)m_rID]) + 1;
  2085. m_szLastRedirectedPath = new WCHAR [len];
  2086. if (!m_szLastRedirectedPath)
  2087. {
  2088. Status = ERROR_OUTOFMEMORY;
  2089. goto LoadDefaultsEnd;
  2090. }
  2091. Status = HRESULT_CODE(StringCchCopy(m_szLastRedirectedPath, len, L"%USERPROFILE%\\"));
  2092. if ( Status != ERROR_SUCCESS )
  2093. {
  2094. goto LoadDefaultsEnd;
  2095. }
  2096. Status = HRESULT_CODE(StringCchCat(m_szLastRedirectedPath, len, g_szRelativePathNames[(int)m_rID]));
  2097. if ( Status != ERROR_SUCCESS )
  2098. {
  2099. goto LoadDefaultsEnd;
  2100. }
  2101. //get the current path
  2102. Status = GetCurrentPath();
  2103. LoadDefaultsEnd:
  2104. return Status;
  2105. }
  2106. //+--------------------------------------------------------------------------
  2107. //
  2108. // Member: LoadFromIniFile
  2109. //
  2110. // Synopsis: this function loads redirection info. from the ini file
  2111. // that contains the last saved settings.
  2112. // file.
  2113. //
  2114. // Arguments: none
  2115. //
  2116. // Returns: ERROR_SUCCESS if everything is successful. an error code otherwise
  2117. //
  2118. // History: 11/18/1998 RahulTh created
  2119. //
  2120. // Notes: if this function cannot find the corresponding section in the
  2121. // ini file, it loads the default local path
  2122. //
  2123. //---------------------------------------------------------------------------
  2124. DWORD CSavedSettings::LoadFromIniFile (void)
  2125. {
  2126. DWORD Status = ERROR_SUCCESS;
  2127. WCHAR pwszDefault[] = L"*";
  2128. WCHAR * pwszReturnedString = NULL;
  2129. WCHAR * pwszProcessedPath = NULL;
  2130. DWORD retVal;
  2131. DWORD Size;
  2132. UNICODE_STRING StringW;
  2133. GUID GPOGuid;
  2134. //
  2135. // If this object contains the MyDocs settings, get the last value
  2136. // of homedir
  2137. //
  2138. if (MyDocs == m_rID || MyPics == m_rID)
  2139. {
  2140. Status = SafeGetPrivateProfileStringW (
  2141. g_szDisplayNames [(int) m_rID],
  2142. L"Homedir",
  2143. pwszDefault,
  2144. &m_szLastHomedir,
  2145. &Size,
  2146. m_szSavedSettingsPath);
  2147. if (ERROR_SUCCESS != Status)
  2148. goto LoadIniEnd;
  2149. if (L'*' == *m_szLastHomedir)
  2150. {
  2151. // The value of homedir at the last redirection was not found.
  2152. delete [] m_szLastHomedir;
  2153. m_szLastHomedir = NULL;
  2154. }
  2155. }
  2156. //first try to get the user name when the user had last logged on.
  2157. Status = SafeGetPrivateProfileStringW (
  2158. g_szDisplayNames [(int) m_rID],
  2159. L"Username",
  2160. pwszDefault,
  2161. &m_szLastUserName,
  2162. &Size,
  2163. m_szSavedSettingsPath);
  2164. if (ERROR_SUCCESS != Status)
  2165. goto LoadIniEnd;
  2166. if (L'*' == *m_szLastUserName)
  2167. {
  2168. //the username field was not found in the ini file. must be an older
  2169. //cache. since we do not have the information, we just use the defaults,
  2170. //i.e. set the current user name as the last user name
  2171. Status = ResetLastUserName();
  2172. if (ERROR_SUCCESS != Status)
  2173. goto LoadIniEnd;
  2174. }
  2175. else
  2176. {
  2177. //we found a user name in the local cache.
  2178. //update the member variable which indicates whether the user name
  2179. //has changed since the last logon.
  2180. if (0 == _wcsicmp (m_szLastUserName, gwszUserName))
  2181. m_bUserNameChanged = FALSE;
  2182. else
  2183. m_bUserNameChanged = TRUE;
  2184. }
  2185. //then try to get the path
  2186. Status = SafeGetPrivateProfileStringW (
  2187. g_szDisplayNames [(int) m_rID],
  2188. L"Path",
  2189. pwszDefault,
  2190. &m_szLastRedirectedPath,
  2191. &Size,
  2192. m_szSavedSettingsPath);
  2193. if (ERROR_SUCCESS != Status)
  2194. goto LoadIniEnd;
  2195. if (L'*' == *m_szLastRedirectedPath) //we could not find the required data.
  2196. {
  2197. //so we go with the defaults
  2198. Status = LoadDefaultLocal();
  2199. goto LoadIniEnd;
  2200. }
  2201. else if (IsHomedirPath (m_rID, m_szLastRedirectedPath, TRUE))
  2202. {
  2203. Status = ExpandHomeDir (m_rID,
  2204. m_szLastRedirectedPath,
  2205. TRUE,
  2206. &pwszProcessedPath,
  2207. m_szLastHomedir
  2208. );
  2209. delete [] m_szLastRedirectedPath;
  2210. if (ERROR_SUCCESS != Status)
  2211. {
  2212. m_szLastRedirectedPath = NULL;
  2213. goto LoadIniEnd;
  2214. }
  2215. else
  2216. {
  2217. m_bIsHomedirRedir = TRUE;
  2218. m_szLastRedirectedPath = pwszProcessedPath;
  2219. pwszProcessedPath = NULL;
  2220. }
  2221. }
  2222. //next try to get the security group
  2223. Status = SafeGetPrivateProfileStringW (
  2224. g_szDisplayNames[(int)m_rID],
  2225. L"Group",
  2226. pwszDefault,
  2227. &pwszReturnedString,
  2228. &Size,
  2229. m_szSavedSettingsPath
  2230. );
  2231. if (ERROR_SUCCESS != Status)
  2232. goto LoadIniEnd;
  2233. if (L'*' == *pwszReturnedString)
  2234. {
  2235. //data was missing, so go with the defaults
  2236. Status = LoadDefaultLocal();
  2237. goto LoadIniEnd;
  2238. }
  2239. if (m_psid)
  2240. {
  2241. RtlFreeSid (m_psid);
  2242. m_psid = NULL;
  2243. }
  2244. Status = AllocateAndInitSidFromString (pwszReturnedString, &m_psid);
  2245. if (ERROR_SUCCESS != Status)
  2246. {
  2247. m_psid = 0;
  2248. goto LoadIniEnd;
  2249. }
  2250. //now get the flags
  2251. Status = SafeGetPrivateProfileStringW (
  2252. g_szDisplayNames[(int)m_rID],
  2253. L"Flags",
  2254. pwszDefault,
  2255. &pwszReturnedString,
  2256. &Size,
  2257. m_szSavedSettingsPath
  2258. );
  2259. if (ERROR_SUCCESS != Status)
  2260. goto LoadIniEnd;
  2261. if (L'*' == *pwszReturnedString)
  2262. {
  2263. Status = LoadDefaultLocal();
  2264. goto LoadIniEnd;
  2265. }
  2266. //now grab the hex flags
  2267. StringW.Buffer = pwszReturnedString;
  2268. StringW.Length = wcslen (pwszReturnedString) * sizeof (WCHAR);
  2269. StringW.MaximumLength = StringW.Length + sizeof(WCHAR);
  2270. RtlUnicodeStringToInteger( &StringW, 16, &m_dwFlags );
  2271. //now get the unique name of the GPO (if any) that was used to redirect the folder
  2272. Status = SafeGetPrivateProfileStringW (
  2273. g_szDisplayNames[(int) m_rID],
  2274. L"GPO",
  2275. pwszDefault,
  2276. &pwszReturnedString,
  2277. &Size,
  2278. m_szSavedSettingsPath
  2279. );
  2280. if (ERROR_SUCCESS != Status)
  2281. goto LoadIniEnd;
  2282. StringW.Length = sizeof (WCHAR) * wcslen (pwszReturnedString);
  2283. StringW.MaximumLength = (USHORT)(sizeof(WCHAR) * Size);
  2284. StringW.Buffer = pwszReturnedString;
  2285. if ((L'*' == *pwszReturnedString) || //there is no valid GPO info., or
  2286. (sizeof(m_szGPOName) <= wcslen(pwszReturnedString)) || //the file has been corrupted. the length of a valid unique name cannot exceed the size of m_szGPO, or
  2287. (STATUS_INVALID_PARAMETER == RtlGUIDFromString (&StringW, &GPOGuid)) //it is not a valid GUID
  2288. )
  2289. {
  2290. //there is no valid GPO info.
  2291. m_bValidGPO = FALSE;
  2292. m_szGPOName[0] = L'\0';
  2293. }
  2294. else
  2295. {
  2296. m_bValidGPO = TRUE;
  2297. (void) StringCbCopy(m_szGPOName, sizeof(m_szGPOName), pwszReturnedString);
  2298. }
  2299. //get the current path.
  2300. Status = GetCurrentPath ();
  2301. LoadIniEnd:
  2302. if (pwszReturnedString)
  2303. delete [] pwszReturnedString;
  2304. if (pwszProcessedPath)
  2305. delete [] pwszProcessedPath;
  2306. return Status;
  2307. }
  2308. //+--------------------------------------------------------------------------
  2309. //
  2310. // Member: NeedsProcessing
  2311. //
  2312. // Synopsis: once the saved settings and the registry values have been
  2313. // loaded, this function determines if we need to look at all the
  2314. // policies. Note that this assumes that the GPO_NOCHANGES flag
  2315. // has already been set by the policy engine
  2316. //
  2317. // Arguments: none
  2318. //
  2319. // Returns: TRUE / FALSE
  2320. //
  2321. // History: 11/18/1998 RahulTh created
  2322. //
  2323. // Notes:
  2324. //
  2325. //---------------------------------------------------------------------------
  2326. BOOL CSavedSettings::NeedsProcessing (void)
  2327. {
  2328. BOOL bStatus;
  2329. DWORD i;
  2330. DWORD Status;
  2331. PTOKEN_GROUPS pGroups;
  2332. WCHAR wszExpandedPath [TARGETPATHLIMIT + 1];
  2333. UNICODE_STRING Path;
  2334. UNICODE_STRING ExpandedPath;
  2335. const WCHAR * pwszCurrentHomedir = NULL;
  2336. WCHAR wszProcessedSource [TARGETPATHLIMIT + 1];
  2337. WCHAR wszProcessedDest [TARGETPATHLIMIT + 1];
  2338. int len;
  2339. //if policy didn't care about the location of the folder at last logon
  2340. //then even if the last redirected path is not the same as the current
  2341. //path, it should not mean that that we need to reprocess the policy
  2342. //when the GPO_NOCHANGES flag has been provided
  2343. if (m_dwFlags & REDIR_DONT_CARE)
  2344. return FALSE;
  2345. //if we are here, policy specified the location of the folder at the
  2346. //the last logon. make sure that the user name has not changed since
  2347. //last logon. If so, we need to process the policies again and move
  2348. //the user's folders accordingly.
  2349. if (m_bUserNameChanged)
  2350. return TRUE;
  2351. //
  2352. // If we are here, the username had not changed, but if the homedir
  2353. // changed, that can affect the path.
  2354. // Note: Here, we cannot use the IsHomedirPath function to determine
  2355. // if this is a homedir redirection since we would have already expanded
  2356. // the path in LoadFromIniFile. Therefore, we must use m_bIsHomedirRedir
  2357. //
  2358. if (m_bIsHomedirRedir)
  2359. {
  2360. //
  2361. // Note: GetHomeDir is an expensive call since it can result
  2362. // in a call to the DS to get the user's home directory. So we try to
  2363. // be as lazy as possible about executing it.
  2364. //
  2365. pwszCurrentHomedir = gUserInfo.GetHomeDir (Status);
  2366. if (ERROR_SUCCESS != Status ||
  2367. ! pwszCurrentHomedir ||
  2368. ! m_szLastHomedir ||
  2369. 0 != lstrcmpi (m_szLastHomedir, pwszCurrentHomedir))
  2370. {
  2371. m_bHomedirChanged = TRUE;
  2372. return TRUE;
  2373. }
  2374. }
  2375. //check if the last redirected path and current path are identical
  2376. if (0 != _wcsicmp(m_szLastRedirectedPath, m_szCurrentPath))
  2377. {
  2378. //the paths are different. we need to do processing
  2379. //even if the policy engine thinks otherwise
  2380. //but sometimes we may have an expanded path in m_szCurrentPath
  2381. //e.g. if some User Shell Folder values are missing. So expand
  2382. //the last redirected path and compare it with the current path
  2383. Path.Length = (wcslen (m_szLastRedirectedPath) + 1) * sizeof (WCHAR);
  2384. Path.MaximumLength = sizeof (m_szLastRedirectedPath);
  2385. Path.Buffer = m_szLastRedirectedPath;
  2386. ExpandedPath.Length = 0;
  2387. ExpandedPath.MaximumLength = sizeof (wszExpandedPath);
  2388. ExpandedPath.Buffer = wszExpandedPath;
  2389. Status = RtlExpandEnvironmentStrings_U (
  2390. m_pFileDB->_pEnvBlock,
  2391. &Path,
  2392. &ExpandedPath,
  2393. NULL
  2394. );
  2395. if (ERROR_SUCCESS != Status)
  2396. return TRUE; //that's our best bet in case of failure
  2397. //
  2398. // Now process the paths so that they do not contain any redundant
  2399. // slashes etc.
  2400. //
  2401. if (NULL == _wfullpath (wszProcessedSource, m_szCurrentPath, MAX_PATH))
  2402. {
  2403. return TRUE;
  2404. }
  2405. else
  2406. {
  2407. //
  2408. // Eliminate any trailing slashes. Note: after going through
  2409. // _wfullpath, there can be at the most one trailing slash
  2410. //
  2411. len = lstrlen (wszProcessedSource);
  2412. if (L'\\' == wszProcessedSource[len-1])
  2413. wszProcessedSource[len - 1] = L'\0';
  2414. }
  2415. if (NULL == _wfullpath (wszProcessedDest, wszExpandedPath, MAX_PATH))
  2416. {
  2417. return TRUE;
  2418. }
  2419. else
  2420. {
  2421. //
  2422. // Eliminate any trailing slashes. Note: after going through
  2423. // _wfullpath, there can be at the most one trailing slash
  2424. //
  2425. len = lstrlen (wszProcessedDest);
  2426. if (L'\\' == wszProcessedDest[len-1])
  2427. wszProcessedDest[len - 1] = L'\0';
  2428. }
  2429. // Now that we have the nice compact paths, we compare them.
  2430. if (0 != _wcsicmp (wszProcessedSource, wszProcessedDest))
  2431. return TRUE;
  2432. }
  2433. return FALSE;
  2434. }
  2435. //+--------------------------------------------------------------------------
  2436. //
  2437. // Member: Save
  2438. //
  2439. // Synopsis: saves settings back to the local settings
  2440. //
  2441. // Arguments: [in] pwszPath : the path to which the folder was redirected
  2442. // [in] dwFlags : the flags that were used for redirection
  2443. // [in] pSid : the Sid of the group that was used for
  2444. // redirection. If this is NULL, then we
  2445. // default to the Sid for everyone : S-1-1-0
  2446. //
  2447. // Returns: ERROR_SUCCESS if everything was successful or an error code.
  2448. //
  2449. // History: 11/20/1998 RahulTh created
  2450. //
  2451. // Notes:
  2452. //
  2453. //---------------------------------------------------------------------------
  2454. DWORD CSavedSettings::Save (const WCHAR * pwszPath, DWORD dwFlags, PSID pSid, const WCHAR * pszGPOName)
  2455. {
  2456. WCHAR * pwszSection = NULL;
  2457. UNICODE_STRING StringW;
  2458. WCHAR pwszFlags [ MAX_PATH ];
  2459. size_t len;
  2460. int homedirInfoLen = 0;
  2461. WCHAR * pszCurr;
  2462. WCHAR pwszSid [ MaxSidString ];
  2463. DWORD Status;
  2464. BOOL bStatus;
  2465. ULONG ulUserNameInfoLen;
  2466. const WCHAR * wszHomedir = NULL;
  2467. BOOL bSaveHomedir = FALSE;
  2468. //
  2469. // First determine if the homedir value needs to be saved.
  2470. // We need to do this first so that we can add the necessary value to the
  2471. // buffer.
  2472. if (IsHomedirPath (m_rID, pwszPath, TRUE) ||
  2473. IsHomedirPolicyPath (m_rID, pwszPath, TRUE))
  2474. {
  2475. wszHomedir = gUserInfo.GetHomeDir (Status);
  2476. if (ERROR_SUCCESS != Status ||
  2477. ! wszHomedir ||
  2478. L'\0' == *wszHomedir)
  2479. {
  2480. Status = (ERROR_SUCCESS == Status) ? ERROR_BAD_PATHNAME : Status;
  2481. goto SaveSettings_End;
  2482. }
  2483. // If we are here, then we need to save the homedir value
  2484. bSaveHomedir = TRUE;
  2485. homedirInfoLen = wcslen (wszHomedir) + 9; // 8 chars for Homedir=
  2486. // + 1 for the terminating NULL
  2487. }
  2488. //calculate the # of characters required to store UserName=<username>
  2489. //including the terminating NULL character.
  2490. ulUserNameInfoLen = wcslen (gwszUserName) + 10; //9 chars for Username=
  2491. //+ 1 terminating NULL
  2492. //don't need to calculate the exact length, this will be more than enough
  2493. // no double null termination required since new is 0 initing it.
  2494. len = wcslen(pwszPath) + homedirInfoLen + 2 * MAX_PATH + ulUserNameInfoLen + 50;
  2495. pwszSection = new WCHAR[sizeof(WCHAR) * len];
  2496. if (NULL == pwszSection)
  2497. {
  2498. Status = ERROR_OUTOFMEMORY;
  2499. goto SaveSettings_End;
  2500. }
  2501. pwszFlags[0] = L'\0';
  2502. StringW.Length = 0;
  2503. StringW.MaximumLength = sizeof (pwszFlags);
  2504. StringW.Buffer = pwszFlags;
  2505. RtlIntegerToUnicodeString (dwFlags, 16, &StringW);
  2506. Status = HRESULT_CODE(StringCchCopy(pwszSection, len, L"Username="));
  2507. if ( Status != ERROR_SUCCESS )
  2508. {
  2509. goto SaveSettings_End;
  2510. }
  2511. Status = HRESULT_CODE(StringCchCatEx(pwszSection, len, gwszUserName, &pszCurr, &len, 0));
  2512. if ( Status != ERROR_SUCCESS )
  2513. {
  2514. goto SaveSettings_End;
  2515. }
  2516. pszCurr++;
  2517. // Save the homedir value only if the redirection destination is the homedir.
  2518. if (bSaveHomedir)
  2519. {
  2520. Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"Homedir="));
  2521. if ( Status != ERROR_SUCCESS )
  2522. {
  2523. goto SaveSettings_End;
  2524. }
  2525. Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, wszHomedir, &pszCurr, &len, 0));
  2526. if ( Status != ERROR_SUCCESS )
  2527. {
  2528. goto SaveSettings_End;
  2529. }
  2530. pszCurr++;
  2531. }
  2532. Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"Path="));
  2533. if ( Status != ERROR_SUCCESS )
  2534. {
  2535. goto SaveSettings_End;
  2536. }
  2537. if (IsHomedirPolicyPath (m_rID, pwszPath, TRUE))
  2538. {
  2539. Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, &pwszPath[2], &pszCurr, &len, 0));
  2540. if ( Status != ERROR_SUCCESS )
  2541. {
  2542. goto SaveSettings_End;
  2543. }
  2544. }
  2545. else
  2546. {
  2547. Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, pwszPath, &pszCurr, &len, 0));
  2548. if ( Status != ERROR_SUCCESS )
  2549. {
  2550. goto SaveSettings_End;
  2551. }
  2552. }
  2553. pszCurr++;
  2554. Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"Flags="));
  2555. if ( Status != ERROR_SUCCESS )
  2556. {
  2557. goto SaveSettings_End;
  2558. }
  2559. Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, pwszFlags, &pszCurr, &len, 0));
  2560. if ( Status != ERROR_SUCCESS )
  2561. {
  2562. goto SaveSettings_End;
  2563. }
  2564. pszCurr++;
  2565. Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"Group="));
  2566. if ( Status != ERROR_SUCCESS )
  2567. {
  2568. goto SaveSettings_End;
  2569. }
  2570. //now we set the sid to everyone (that is the safest) if no sid has been
  2571. //specified or if we are unable to convert the supplied sid into a string.
  2572. Status = ERROR_INVALID_SID; //just some error code
  2573. if (pSid)
  2574. {
  2575. pwszSid [0] = L'\0';
  2576. StringW.Length = 0;
  2577. StringW.MaximumLength = sizeof (pwszSid);
  2578. StringW.Buffer = pwszSid;
  2579. Status = RtlConvertSidToUnicodeString (&StringW, pSid, FALSE);
  2580. }
  2581. if (ERROR_SUCCESS != Status)
  2582. {
  2583. (void) StringCbCopy(pwszSid, sizeof(pwszSid), L"S-1-1-0"); //use the sid for everyone if we can't find anything else
  2584. }
  2585. Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, pwszSid, &pszCurr, &len, 0));
  2586. if ( Status != ERROR_SUCCESS )
  2587. {
  2588. goto SaveSettings_End;
  2589. }
  2590. pszCurr++;
  2591. //add the GPO if there is a valid one.
  2592. if (pszGPOName)
  2593. {
  2594. Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"GPO="));
  2595. if ( Status != ERROR_SUCCESS )
  2596. {
  2597. goto SaveSettings_End;
  2598. }
  2599. Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, pszGPOName, &pszCurr, &len, 0));
  2600. if ( Status != ERROR_SUCCESS )
  2601. {
  2602. goto SaveSettings_End;
  2603. }
  2604. }
  2605. //before writing to the ini file, we must pre-create it in Unicode,
  2606. //otherwise, the WritePrivate* APIs will write the file in ANSI, which
  2607. //will break folder redirection in international/ML builds.
  2608. Status = PrecreateUnicodeIniFile (m_szSavedSettingsPath);
  2609. if ((Status != ERROR_SUCCESS) && (Status != ERROR_ALREADY_EXISTS))
  2610. {
  2611. goto SaveSettings_End;
  2612. }
  2613. Status = ERROR_SUCCESS;
  2614. //now we can go ahead and save the section
  2615. //first empty the section
  2616. bStatus = WritePrivateProfileSection (
  2617. g_szDisplayNames[(int) m_rID],
  2618. NULL,
  2619. m_szSavedSettingsPath
  2620. );
  2621. //now write the actual section
  2622. if (bStatus)
  2623. bStatus = WritePrivateProfileSection (
  2624. g_szDisplayNames[(int) m_rID],
  2625. pwszSection,
  2626. m_szSavedSettingsPath
  2627. );
  2628. if (!bStatus)
  2629. Status = GetLastError();
  2630. SaveSettings_End:
  2631. if (pwszSection)
  2632. delete [] pwszSection;
  2633. return Status;
  2634. }
  2635. //+--------------------------------------------------------------------------
  2636. //
  2637. // Member: CSavedSettings::HandleUserNameChange
  2638. //
  2639. // Synopsis: this function handles any changes in the user name that
  2640. // have occurred since the last logon. in case the username has
  2641. // changed since the last logon, this function renames any
  2642. // folders redirected earlier using the %username% variable so
  2643. // that the path that the redirected folder points to is continues
  2644. // to be valid.
  2645. //
  2646. // Arguments: [in] pFileDB : point to the CFileDB object.
  2647. //
  2648. // Returns: ERROR_SUCCESS if everything worked properly
  2649. // an error code otherwise.
  2650. //
  2651. // History: 9/20/1999 RahulTh created
  2652. //
  2653. // Notes: a failed rename is not considered a failure. in this case,
  2654. // an event is logged and the code ensures that redirection
  2655. // is not attempted if there is a simultaneous change in the
  2656. // redirection policies.
  2657. //
  2658. // in this way a failed rename for one folder does not hold up
  2659. // the redirection of other folders which might be independent of
  2660. // this particular folder.
  2661. //---------------------------------------------------------------------------
  2662. DWORD CSavedSettings::HandleUserNameChange (CFileDB * pFileDB,
  2663. CRedirectInfo * pRedir)
  2664. {
  2665. BOOL bStatus;
  2666. DWORD Status = ERROR_SUCCESS;
  2667. DWORD RedirStatus = ERROR_SUCCESS;
  2668. WCHAR wszLastPath [TARGETPATHLIMIT];
  2669. WCHAR wszExpandedSource [TARGETPATHLIMIT];
  2670. WCHAR wszExpandedDest [TARGETPATHLIMIT];
  2671. WCHAR wszRenamePart [TARGETPATHLIMIT];
  2672. WCHAR wszExpandedRenameSource [TARGETPATHLIMIT];
  2673. WCHAR wszExpandedRenameDest [TARGETPATHLIMIT];
  2674. WCHAR * wszTemp = NULL;
  2675. WCHAR * wszEnd = NULL;
  2676. SHARESTATUS SourceStatus;
  2677. SHARESTATUS DestStatus;
  2678. BOOL bRenamePerformed = FALSE;
  2679. if ((! m_bUserNameChanged) ||
  2680. pRedir->WasRedirectionAttempted() ||
  2681. (REDIR_DONT_CARE & m_dwFlags))
  2682. {
  2683. goto HandleUPNChangeEnd;//nothing to do if the username has not changed
  2684. //since the last logon or if the rename has
  2685. //already been attempted. Even if the attempted
  2686. //redirection had failed, it is not fatal.
  2687. //similarly, if policy didn't set the location
  2688. //of the folder last time, we don't care.
  2689. }
  2690. if (Programs == m_rID || //since programs and startup always follow the
  2691. Startup == m_rID //Start Menu, rename of Start Menu handles these
  2692. //folders automatically
  2693. )
  2694. {
  2695. goto HandleUPNChangeEnd;
  2696. }
  2697. //show additional status messages if the verbose status is on.
  2698. DisplayStatusMessage (IDS_REDIR_CALLBACK);
  2699. DebugMsg ((DM_VERBOSE, IDS_UPN_CHANGE, pRedir->GetLocalizedName(), m_szLastUserName, gwszUserName));
  2700. //okay, so there is a change in the user name and policy did care about the
  2701. //the location. check if last path contained the username. if not, we have
  2702. //nothing more to do.
  2703. if (TARGETPATHLIMIT <= wcslen (m_szLastRedirectedPath))
  2704. {
  2705. gpEvents->Report (
  2706. EVENT_FDEPLOY_DESTPATH_TOO_LONG,
  2707. 3,
  2708. pRedir->GetLocalizedName(),
  2709. m_szLastRedirectedPath,
  2710. NumberToString ( TARGETPATHLIMIT )
  2711. );
  2712. pRedir->PreventRedirection (STATUS_BUFFER_TOO_SMALL);
  2713. goto HandleUPNChangeEnd;
  2714. }
  2715. (void) StringCchCopy(wszLastPath, TARGETPATHLIMIT, m_szLastRedirectedPath);
  2716. _wcslwr (wszLastPath);
  2717. wszTemp = wcsstr (wszLastPath, L"%username%");
  2718. if (NULL == wszTemp)
  2719. goto HandleUPNChangeEnd; //there is no %username% string, we are
  2720. //done. no rename is required.
  2721. //get the part that needs to be renamed.
  2722. (void) StringCchCopy(wszRenamePart, TARGETPATHLIMIT, wszLastPath);
  2723. wszEnd = wcschr (wszRenamePart + (wszTemp - wszLastPath), L'\\');
  2724. if (wszEnd)
  2725. *wszEnd = L'\0';
  2726. //get expanded versions of the paths -- using the new username and the old
  2727. //username
  2728. wszTemp = wszLastPath;
  2729. RedirStatus = ExpandPathSpecial (pFileDB, wszLastPath, m_szLastUserName, wszExpandedSource);
  2730. if (ERROR_SUCCESS == RedirStatus)
  2731. {
  2732. RedirStatus = ExpandPathSpecial (pFileDB, wszLastPath, gwszUserName, wszExpandedDest);
  2733. }
  2734. if (ERROR_SUCCESS == RedirStatus)
  2735. {
  2736. wszTemp = wszRenamePart;
  2737. RedirStatus = ExpandPathSpecial (pFileDB, wszRenamePart, m_szLastUserName, wszExpandedRenameSource);
  2738. }
  2739. if (ERROR_SUCCESS == RedirStatus)
  2740. {
  2741. RedirStatus = ExpandPathSpecial (pFileDB, wszRenamePart, gwszUserName, wszExpandedRenameDest);
  2742. }
  2743. if (STATUS_BUFFER_TOO_SMALL == RedirStatus)
  2744. {
  2745. gpEvents->Report (
  2746. EVENT_FDEPLOY_DESTPATH_TOO_LONG,
  2747. 3,
  2748. pRedir->GetLocalizedName(),
  2749. wszTemp,
  2750. NumberToString ( TARGETPATHLIMIT )
  2751. );
  2752. pRedir->PreventRedirection (STATUS_BUFFER_TOO_SMALL);
  2753. goto HandleUPNChangeEnd;
  2754. }
  2755. else if (ERROR_SUCCESS != RedirStatus)
  2756. {
  2757. gpEvents->Report (
  2758. EVENT_FDEPLOY_FOLDER_EXPAND_FAIL,
  2759. 2,
  2760. pRedir->GetLocalizedName(),
  2761. StatusToString ( RedirStatus )
  2762. );
  2763. pRedir->PreventRedirection (RedirStatus);
  2764. goto HandleUPNChangeEnd;
  2765. }
  2766. //get the online/offline status of the shares.
  2767. SourceStatus = GetCSCStatus (wszExpandedRenameSource);
  2768. DestStatus = GetCSCStatus (wszExpandedRenameDest);
  2769. if (ShareOffline == SourceStatus || ShareOffline == DestStatus)
  2770. {
  2771. gpEvents->Report (
  2772. EVENT_FDEPLOY_FOLDER_OFFLINE,
  2773. 3,
  2774. pRedir->GetLocalizedName(),
  2775. wszExpandedSource,
  2776. wszExpandedDest
  2777. );
  2778. pRedir->PreventRedirection (ERROR_CSCSHARE_OFFLINE);
  2779. goto HandleUPNChangeEnd;
  2780. }
  2781. //we are finally ready to rename. first make sure that the source exists.
  2782. //sometimes an earlier rename operation might have already renamed this
  2783. //folder
  2784. RedirStatus = ERROR_SUCCESS;
  2785. if (0xFFFFFFFF == GetFileAttributes(wszExpandedRenameSource))
  2786. {
  2787. RedirStatus = GetLastError();
  2788. }
  2789. if (ERROR_FILE_NOT_FOUND != RedirStatus)
  2790. {
  2791. bStatus = MoveFile (wszExpandedRenameSource, wszExpandedRenameDest);
  2792. if (!bStatus)
  2793. {
  2794. RedirStatus = GetLastError();
  2795. gpEvents->Report (
  2796. EVENT_FDEPLOY_REDIRECT_FAIL,
  2797. 4,
  2798. pRedir->GetLocalizedName(),
  2799. StatusToString (RedirStatus),
  2800. wszExpandedSource,
  2801. wszExpandedDest
  2802. );
  2803. pRedir->PreventRedirection (RedirStatus);
  2804. goto HandleUPNChangeEnd;
  2805. }
  2806. }
  2807. //the rename was successful. now rename the CSC cache.
  2808. if (ShareOnline == SourceStatus)
  2809. {
  2810. MoveDirInCSC (wszExpandedSource, wszExpandedDest, NULL, SourceStatus, DestStatus, TRUE, TRUE);
  2811. DeleteCSCShareIfEmpty (wszExpandedSource, SourceStatus);
  2812. }
  2813. bRenamePerformed = TRUE;
  2814. HandleUPNChangeEnd:
  2815. if (m_bUserNameChanged && ERROR_SUCCESS == pRedir->GetRedirStatus())
  2816. {
  2817. UpdateUserNameInCache();
  2818. if (bRenamePerformed)
  2819. {
  2820. gpEvents->Report(
  2821. EVENT_FDEPLOY_FOLDER_REDIRECT,
  2822. 1,
  2823. pRedir->GetLocalizedName());
  2824. }
  2825. }
  2826. return RedirStatus;
  2827. }
  2828. //+--------------------------------------------------------------------------
  2829. //
  2830. // Member: CSavedSettings::UpdateUserNameInCache
  2831. //
  2832. // Synopsis: updates the user name in the cache with the new username
  2833. //
  2834. // Arguments: none.
  2835. //
  2836. // Returns: ERROR_SUCCESS : if successful.
  2837. // an error code otherwise.
  2838. //
  2839. // History: 9/20/1999 RahulTh created
  2840. //
  2841. // Notes:
  2842. //
  2843. //---------------------------------------------------------------------------
  2844. DWORD CSavedSettings::UpdateUserNameInCache (void)
  2845. {
  2846. BOOL bStatus;
  2847. bStatus = WritePrivateProfileString (
  2848. g_szDisplayNames[(int) m_rID],
  2849. L"UserName",
  2850. gwszUserName,
  2851. m_szSavedSettingsPath
  2852. );
  2853. if (!bStatus)
  2854. return GetLastError();
  2855. return ERROR_SUCCESS;
  2856. }