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.

653 lines
18 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: enum.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include <shlwapip.h> // QITAB, QISearch
  13. #include <shsemip.h> // ILFree(), etc
  14. #include "folder.h"
  15. #include "security.h"
  16. //
  17. // Create a single entry in the server status cache.
  18. //
  19. CServerStatusCache::CEntry::CEntry(
  20. LPCTSTR pszServer,
  21. DWORD dwStatus
  22. ) : m_pszServer(StrDup(pszServer)),
  23. m_dwStatus(dwStatus)
  24. {
  25. }
  26. //
  27. // Destroy a single entry in the server status cache.
  28. //
  29. CServerStatusCache::CEntry::~CEntry(
  30. void
  31. )
  32. {
  33. if (NULL != m_pszServer)
  34. {
  35. LocalFree(m_pszServer);
  36. }
  37. }
  38. //
  39. // Destroy the server status cache.
  40. //
  41. CServerStatusCache::~CServerStatusCache(
  42. void
  43. )
  44. {
  45. if (NULL != m_hdpa)
  46. {
  47. //
  48. // Delete each entry in the DPA then destroy the
  49. // DPA itself.
  50. //
  51. int cEntries = DPA_GetPtrCount(m_hdpa);
  52. for (int i = 0; i < cEntries; i++)
  53. {
  54. delete (CEntry *)DPA_GetPtr(m_hdpa, i);
  55. }
  56. DPA_Destroy(m_hdpa);
  57. }
  58. }
  59. //
  60. // Add a share's status to the cache. We strip the UNC path to it's
  61. // bare server name then add the status to the cache. If there's
  62. // no existing entry we just add it. If there is an existing entry,
  63. // we bitwise OR the status bits in with the existing entry. This way
  64. // the status of the server is the summation of the status of all
  65. // it's shares.
  66. //
  67. bool
  68. CServerStatusCache::AddShareStatus(
  69. LPCTSTR pszShare,
  70. DWORD dwShareStatus
  71. )
  72. {
  73. bool bResult = true;
  74. TCHAR szServer[MAX_PATH];
  75. CEntry *pEntry = FindEntry(ServerFromUNC(pszShare, szServer, ARRAYSIZE(szServer)));
  76. if (NULL != pEntry)
  77. {
  78. //
  79. // Found existing server entry for this share. Merge in the
  80. // status bits for this share.
  81. //
  82. pEntry->AddStatus(dwShareStatus);
  83. }
  84. else
  85. {
  86. //
  87. // No existing entry for this share's server.
  88. //
  89. if (NULL == m_hdpa)
  90. {
  91. //
  92. // No DPA exists yet. Create one.
  93. // We delay creation of the DPA until we really need one.
  94. //
  95. m_hdpa = DPA_Create(8);
  96. }
  97. if (NULL != m_hdpa)
  98. {
  99. //
  100. // We have a DPA. Create a new entry for this share's server
  101. // and add it to the DPA.
  102. //
  103. pEntry = new CEntry(szServer, dwShareStatus);
  104. if (NULL != pEntry)
  105. {
  106. if (!pEntry->IsValid() || -1 == DPA_AppendPtr(m_hdpa, pEntry))
  107. {
  108. //
  109. // One of the following happened:
  110. // 1. Failure allocating server name in CEntry obj.
  111. // 2. Failure adding CEntry obj ptr to DPA.
  112. //
  113. delete pEntry;
  114. bResult = false;
  115. }
  116. }
  117. }
  118. else
  119. {
  120. bResult = false; // DPA creation failed.
  121. }
  122. }
  123. return bResult;
  124. }
  125. //
  126. // Obtain the CSC status bits for a given server.
  127. // This function assumes the pszUNC arg is a valid UNC path.
  128. //
  129. DWORD
  130. CServerStatusCache::GetServerStatus(
  131. LPCTSTR pszUNC
  132. )
  133. {
  134. TCHAR szServer[MAX_PATH];
  135. CEntry *pEntry = FindEntry(ServerFromUNC(pszUNC, szServer, ARRAYSIZE(szServer)));
  136. if (NULL == pEntry)
  137. {
  138. //
  139. // No entry for this server. Scan the CSC cache and pick up any new
  140. // servers added. Since the lifetime of this server cache is only for a single
  141. // enumeration, we should have to do this only once. However, if for some
  142. // reason, something gets added to the CSC cache while we're opening the viewer,
  143. // this code path will pick up the new server entry.
  144. //
  145. WIN32_FIND_DATA fd;
  146. DWORD dwStatus = 0;
  147. CCscFindHandle hFind = CacheFindFirst(NULL, &fd, &dwStatus, NULL, NULL, NULL);
  148. if (hFind.IsValid())
  149. {
  150. do
  151. {
  152. AddShareStatus(fd.cFileName, dwStatus);
  153. }
  154. while(CacheFindNext(hFind, &fd, &dwStatus, NULL, NULL, NULL));
  155. }
  156. //
  157. // Now that we have rescanned the CSC cache, try it again.
  158. //
  159. pEntry = FindEntry(szServer);
  160. }
  161. return pEntry ? pEntry->GetStatus() : 0;
  162. }
  163. //
  164. // Find a single entry in the server cache.
  165. // Assumes pszServer is a raw server name (not UNC).
  166. // Returns NULL if no match found.
  167. //
  168. CServerStatusCache::CEntry *
  169. CServerStatusCache::FindEntry(
  170. LPCTSTR pszServer
  171. )
  172. {
  173. CEntry *pEntry = NULL;
  174. if (NULL != m_hdpa)
  175. {
  176. int cEntries = DPA_GetPtrCount(m_hdpa);
  177. for (int i = 0; i < cEntries; i++)
  178. {
  179. CEntry *pe = (CEntry *)DPA_GetPtr(m_hdpa, i);
  180. if (NULL != pe && 0 == lstrcmpi(pe->GetServer(), pszServer))
  181. {
  182. pEntry = pe;
  183. break;
  184. }
  185. }
  186. }
  187. return pEntry;
  188. }
  189. LPTSTR
  190. CServerStatusCache::ServerFromUNC(
  191. LPCTSTR pszShare,
  192. LPTSTR pszServer,
  193. UINT cchServer
  194. )
  195. {
  196. LPTSTR pszReturn = pszServer; // Remember for return.
  197. cchServer--; // Leave room for terminating nul.
  198. while(*pszShare && TEXT('\\') == *pszShare)
  199. pszShare++;
  200. while(*pszShare && TEXT('\\') != *pszShare && cchServer--)
  201. *pszServer++ = *pszShare++;
  202. *pszServer = TEXT('\0');
  203. return pszReturn;
  204. }
  205. STDMETHODIMP COfflineFilesEnum::QueryInterface(REFIID riid, void **ppv)
  206. {
  207. static const QITAB qit[] = {
  208. QITABENT(COfflineFilesEnum, IEnumIDList),
  209. { 0 },
  210. };
  211. return QISearch(this, qit, riid, ppv);
  212. }
  213. STDMETHODIMP_ (ULONG) COfflineFilesEnum::AddRef()
  214. {
  215. return InterlockedIncrement(&_cRef);
  216. }
  217. STDMETHODIMP_ (ULONG) COfflineFilesEnum::Release()
  218. {
  219. ASSERT( 0 != _cRef );
  220. ULONG cRef = InterlockedDecrement(&_cRef);
  221. if ( 0 == cRef )
  222. {
  223. delete this;
  224. }
  225. return cRef;
  226. }
  227. COfflineFilesEnum::COfflineFilesEnum(DWORD grfFlags, COfflineFilesFolder *pfolder)
  228. {
  229. _cRef = 1;
  230. //
  231. // The minimum size of the buffer must be MAX_PATH.
  232. // The enumeration code is designed to grow it as needed.
  233. //
  234. _cchPathBuf = MAX_PATH;
  235. _pszPath = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * _cchPathBuf);
  236. if (NULL != _pszPath)
  237. *_pszPath = TEXT('\0');
  238. else
  239. _cchPathBuf = 0;
  240. _grfFlags = grfFlags,
  241. _pfolder = pfolder;
  242. _pfolder->AddRef();
  243. _dwServerStatus = 0;
  244. _hdsaFolderPathInfo = DSA_Create(sizeof(FolderPathInfo), 10);
  245. //
  246. // Determine if we should be showing system and/or hidden files.
  247. //
  248. _bShowHiddenFiles = boolify(ShowHidden());
  249. _bShowSuperHiddenFiles = boolify(ShowSuperHidden());
  250. _bUserIsAdmin = boolify(IsCurrentUserAnAdminMember());
  251. DllAddRef();
  252. }
  253. COfflineFilesEnum::~COfflineFilesEnum()
  254. {
  255. if (_pfolder)
  256. _pfolder->Release();
  257. Reset();
  258. if (_hdsaFolderPathInfo)
  259. {
  260. int cPaths = DSA_GetItemCount(_hdsaFolderPathInfo);
  261. FolderPathInfo fpi;
  262. for (int i = 0; i < cPaths; i++)
  263. {
  264. if (DSA_GetItem(_hdsaFolderPathInfo, i, &fpi) && NULL != fpi.pszPath)
  265. LocalFree(fpi.pszPath);
  266. }
  267. DSA_Destroy(_hdsaFolderPathInfo);
  268. }
  269. if (NULL != _pszPath)
  270. LocalFree(_pszPath);
  271. DllRelease();
  272. }
  273. //
  274. // Since we're not throwing exceptions, clients must call this after ctor
  275. // to verify allocations succeeded.
  276. //
  277. bool
  278. COfflineFilesEnum::IsValid(
  279. void
  280. ) const
  281. {
  282. return (NULL != _hdsaFolderPathInfo) && (NULL != _pszPath);
  283. }
  284. bool
  285. COfflineFilesEnum::PopFolderPathInfo(
  286. FolderPathInfo *pfpi
  287. )
  288. {
  289. bool bResult = false;
  290. TraceAssert(NULL != _hdsaFolderPathInfo);
  291. int iItem = DSA_GetItemCount(_hdsaFolderPathInfo) - 1;
  292. if ((0 <= iItem) && DSA_GetItem(_hdsaFolderPathInfo, iItem, pfpi))
  293. {
  294. DSA_DeleteItem(_hdsaFolderPathInfo, iItem);
  295. bResult = true;
  296. }
  297. return bResult;
  298. }
  299. //
  300. // Build complete path to folder in a heap allocation and push it onto
  301. // stack of saved folder paths.
  302. // Returns false if memory can't be allocated for path.
  303. //
  304. bool
  305. COfflineFilesEnum::SaveFolderPath(
  306. LPCTSTR pszRoot,
  307. LPCTSTR pszFolder
  308. )
  309. {
  310. bool bResult = false;
  311. FolderPathInfo fpi;
  312. //
  313. // Length is "root" + '\' + "folder" + <nul>
  314. //
  315. fpi.cchPath = lstrlen(pszRoot) + lstrlen(pszFolder) + 2;
  316. fpi.pszPath = (LPTSTR)LocalAlloc(LPTR, MAX(fpi.cchPath, DWORD(MAX_PATH)) * sizeof(TCHAR));
  317. if (NULL != fpi.pszPath)
  318. {
  319. if (PathCombine(fpi.pszPath, pszRoot, pszFolder) && PushFolderPathInfo(fpi))
  320. bResult = true;
  321. else
  322. LocalFree(fpi.pszPath);
  323. }
  324. return bResult;
  325. }
  326. //
  327. // Increases the size of the _pszPath buffer by a specified amount.
  328. // Original contents of buffer ARE NOT preserved.
  329. // Returns:
  330. // S_FALSE - _pszPath buffer was large enough. Not modified.
  331. // S_OK - _pszPath points to new bigger buffer.
  332. // E_OUTOFMEMORY - _pszPath points to original unmodified buffer.
  333. //
  334. HRESULT
  335. COfflineFilesEnum::GrowPathBuffer(
  336. INT cchRequired,
  337. INT cchExtra
  338. )
  339. {
  340. HRESULT hres = S_FALSE;
  341. if (_cchPathBuf <= cchRequired)
  342. {
  343. LPTSTR pszNewBuf = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cchRequired + cchExtra));
  344. if (NULL != pszNewBuf)
  345. {
  346. if (NULL != _pszPath)
  347. LocalFree(_pszPath);
  348. _pszPath = pszNewBuf;
  349. _cchPathBuf = cchRequired + cchExtra;
  350. hres = S_OK;
  351. }
  352. else
  353. {
  354. hres = E_OUTOFMEMORY; // Failure. Orig buffer is left intact.
  355. }
  356. }
  357. return hres;
  358. }
  359. //
  360. // Determine if user has access to view this file.
  361. //
  362. bool
  363. COfflineFilesEnum::UserHasAccess(
  364. const CscFindData& cscfd
  365. )
  366. {
  367. return _bUserIsAdmin ||
  368. CscAccessUser(cscfd.dwStatus) ||
  369. CscAccessGuest(cscfd.dwStatus);
  370. }
  371. //
  372. // Centralize any item-exclusion logic in a single function.
  373. //
  374. bool
  375. COfflineFilesEnum::Exclude(
  376. const CscFindData& cscfd
  377. )
  378. {
  379. return ((FILE_ATTRIBUTE_DIRECTORY & cscfd.fd.dwFileAttributes) ||
  380. (FLAG_CSC_COPY_STATUS_LOCALLY_DELETED & cscfd.dwStatus) ||
  381. ((FILE_ATTRIBUTE_HIDDEN & cscfd.fd.dwFileAttributes) && !_bShowHiddenFiles) ||
  382. (IsHiddenSystem(cscfd.fd.dwFileAttributes) && !_bShowSuperHiddenFiles) ||
  383. !UserHasAccess(cscfd));
  384. }
  385. //
  386. // If a folder is hidden and the current shell setting says to not show hidden files,
  387. // don't enumerate any children of a folder. Likewise for super hidden files and the
  388. // "show super hidden files" setting.
  389. //
  390. bool
  391. COfflineFilesEnum::OkToEnumFolder(
  392. const CscFindData& cscfd
  393. )
  394. {
  395. return (_bShowHiddenFiles || (0 == (FILE_ATTRIBUTE_HIDDEN & cscfd.fd.dwFileAttributes))) &&
  396. (_bShowSuperHiddenFiles || !IsHiddenSystem(cscfd.fd.dwFileAttributes));
  397. }
  398. HRESULT COfflineFilesEnum::Next(ULONG celt, LPITEMIDLIST *rgelt,
  399. ULONG *pceltFetched)
  400. {
  401. HRESULT hres;
  402. CscFindData cscfd;
  403. ULONG celtEnumed;
  404. //
  405. // If you've hit one of these asserts, you didn't call IsValid()
  406. // before using the enumerator.
  407. //
  408. TraceAssert(NULL != _pszPath);
  409. TraceAssert(NULL != _hdsaFolderPathInfo);
  410. //
  411. // This label is used to restart the enum if an item is excluded.
  412. //
  413. enum_start:
  414. hres = S_FALSE;
  415. celtEnumed = 0;
  416. ZeroMemory(&cscfd, sizeof(cscfd));
  417. if (!_hEnumShares.IsValid())
  418. {
  419. //
  420. // First time through.
  421. // Enumerate shares and files until we find a folder or file.
  422. //
  423. _hEnumShares = CacheFindFirst(NULL, &cscfd);
  424. if (_hEnumShares.IsValid())
  425. {
  426. _dwServerStatus = _ServerStatusCache.GetServerStatus(cscfd.fd.cFileName);
  427. do
  428. {
  429. //
  430. // Buffer attached to _pszPath is guaranteed to be at least
  431. // MAX_PATH so it's safe to copy cFileName[].
  432. //
  433. StringCchCopy(_pszPath, _cchPathBuf, cscfd.fd.cFileName);
  434. _hEnum = CacheFindFirst(_pszPath, &cscfd);
  435. if (_hEnum.IsValid())
  436. {
  437. celtEnumed = 1;
  438. }
  439. }
  440. while(0 == celtEnumed && CacheFindNext(_hEnumShares, &cscfd));
  441. }
  442. }
  443. else
  444. {
  445. if (_hEnum.IsValid())
  446. {
  447. if (CacheFindNext(_hEnum, &cscfd))
  448. {
  449. //
  450. // Most common case. Got next file in current folder.
  451. //
  452. celtEnumed = 1;
  453. }
  454. else
  455. {
  456. //
  457. // Enumeration exhausted for this folder. If we have folder paths
  458. // saved on the stack, keep popping them until we find one containing
  459. // at least one file or folder.
  460. //
  461. FolderPathInfo fpi;
  462. while(SUCCEEDED(hres) && 0 == celtEnumed && PopFolderPathInfo(&fpi) && NULL != fpi.pszPath)
  463. {
  464. _hEnum = CacheFindFirst(fpi.pszPath, &cscfd);
  465. if (_hEnum.IsValid())
  466. {
  467. //
  468. // The popped folder path is the only opportunity we have
  469. // where a string could overflow the temp _pszPath buffer.
  470. // If necesary, grow the buffer to hold the path. Add
  471. // room for an extra 100 chars to minimize re-growth.
  472. // Buffer is not altered if required path length is
  473. // less than _cchPathBuf.
  474. //
  475. if (FAILED(GrowPathBuffer(fpi.cchPath, 100)))
  476. hres = E_OUTOFMEMORY;
  477. if (SUCCEEDED(hres))
  478. {
  479. StringCchCopy(_pszPath, _cchPathBuf, fpi.pszPath);
  480. celtEnumed = 1;
  481. }
  482. }
  483. LocalFree(fpi.pszPath);
  484. }
  485. if (SUCCEEDED(hres))
  486. {
  487. while(0 == celtEnumed && CacheFindNext(_hEnumShares, &cscfd))
  488. {
  489. //
  490. // No more saved folder paths. This share is exhausted.
  491. // Enumerate next share. If next is empty, keep enumerating
  492. // shares until we find one with content. The buffer
  493. // attached to _pszPath is guaranteed to be at least MAX_PATH
  494. // so it's always safe to copy cFileName[].
  495. //
  496. _dwServerStatus = _ServerStatusCache.GetServerStatus(cscfd.fd.cFileName);
  497. StringCchCopy(_pszPath, _cchPathBuf, cscfd.fd.cFileName);
  498. _hEnum = CacheFindFirst(_pszPath, &cscfd);
  499. if (_hEnum.IsValid())
  500. {
  501. celtEnumed = 1;
  502. }
  503. }
  504. }
  505. }
  506. }
  507. }
  508. if (celtEnumed)
  509. {
  510. if (FILE_ATTRIBUTE_DIRECTORY & cscfd.fd.dwFileAttributes)
  511. {
  512. if (OkToEnumFolder(cscfd))
  513. {
  514. //
  515. // Save the folder path on a stack. This is how we enumerate
  516. // the cache item hierarcy as a flat list. We'll pop these off
  517. // the stack on future calls to Next() when all children of the
  518. // current folder have been enumerated.
  519. //
  520. if (!SaveFolderPath(_pszPath, cscfd.fd.cFileName))
  521. {
  522. //
  523. // Path not saved. Insufficient heap memory.
  524. // Abort the enumeration.
  525. //
  526. hres = E_OUTOFMEMORY;
  527. }
  528. }
  529. }
  530. if (SUCCEEDED(hres))
  531. {
  532. if (!Exclude(cscfd))
  533. {
  534. //
  535. // An IDList is composed of a fixed-length part and a variable-length
  536. // path+name buffer.
  537. // The path+name variable-length buffer is formatted as follows:
  538. //
  539. // dir1\dir2\dir3<nul>name<nul>
  540. //
  541. TCHAR szUNC[MAX_PATH];
  542. if (PathCombine(szUNC, _pszPath, cscfd.fd.cFileName))
  543. {
  544. hres = COfflineFilesFolder::OLID_CreateFromUNCPath(szUNC,
  545. &cscfd.fd,
  546. cscfd.dwStatus,
  547. cscfd.dwPinCount,
  548. cscfd.dwHintFlags,
  549. _dwServerStatus,
  550. (LPOLID *)&rgelt[0]);
  551. }
  552. }
  553. else
  554. {
  555. //
  556. // This item is excluded from the enumeration. Restart.
  557. // I normally don't like goto's but doing this with a loop
  558. // is just plain harder to understand. The goto is quite
  559. // appropriate in this circumstance.
  560. //
  561. goto enum_start;
  562. }
  563. }
  564. }
  565. if (pceltFetched)
  566. *pceltFetched = celtEnumed;
  567. return hres;
  568. }
  569. HRESULT COfflineFilesEnum::Skip(ULONG celt)
  570. {
  571. return E_NOTIMPL;
  572. }
  573. HRESULT COfflineFilesEnum::Reset()
  574. {
  575. _hEnum.Close();
  576. _hEnumShares.Close();
  577. return S_OK;
  578. }
  579. HRESULT COfflineFilesEnum::Clone(IEnumIDList **ppenum)
  580. {
  581. return E_NOTIMPL;
  582. }