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.

646 lines
18 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1991-1993
  4. //
  5. // File: stream.c
  6. //
  7. // This file contains some of the stream support code that is used by
  8. // the shell. It also contains the shells implementation of a memory
  9. // stream that is used by the cabinet to allow views to be serialized.
  10. //
  11. // History:
  12. // 08-20-93 KurtE Added header block and memory stream.
  13. //
  14. //---------------------------------------------------------------------------
  15. #include "priv.h"
  16. #include <new.h>
  17. #include "nullstm.h"
  18. // This code was stolen from shell32. This is the BETTER_STRONGER_FASTER
  19. // version (smaller and half the allocs), added after Win95 shipped.
  20. #include "stream.h"
  21. EXTERN_C HKEY SHRegDuplicateHKey(HKEY hkey);
  22. // The Win95/NT4/IE4 code did not enforce the grfMode. Turn this on to enforce:
  23. //#define ENFORCE_GRFMODE // Note: I haven't tested compat issues with this turned on yet... [mikesh]
  24. STDMETHODIMP CMemStream::QueryInterface(REFIID riid, void **ppvObj)
  25. {
  26. if (IsEqualIID(riid, IID_IStream) || IsEqualIID(riid, IID_IUnknown))
  27. {
  28. *ppvObj=this;
  29. this->cRef++;
  30. return S_OK;
  31. }
  32. *ppvObj = NULL;
  33. return E_NOINTERFACE;
  34. }
  35. STDMETHODIMP_(ULONG) CMemStream::AddRef()
  36. {
  37. this->cRef++;
  38. return this->cRef;
  39. }
  40. BOOL CMemStream::WriteToReg()
  41. {
  42. if (this->cbData)
  43. {
  44. return ERROR_SUCCESS == RegSetValueEx(this->hkey,
  45. this->szValue[0] ? this->szValue : NULL, 0, REG_BINARY,
  46. this->cbData ? this->pBuf : (LPBYTE)"", this->cbData);
  47. }
  48. else
  49. {
  50. DWORD dwRet = SHDeleteValue(this->hkey, NULL, this->szValue);
  51. // If the Stream is being stored in the default key, then
  52. // we should clean up the key. Otherwise, the caller
  53. // passed us the key, and they need it. It would be rude for us
  54. // to delete it. Fixes a Start Menu bug (NT#361333) where we would delete the
  55. // programs key where start menu stores it's stuff on a load, so we
  56. // never persist anything. - lamadio (6.25.99)
  57. if (this->szValue[0] == TEXT('\0'))
  58. {
  59. SHDeleteEmptyKey(this->hkey, NULL);
  60. }
  61. return ERROR_SUCCESS == dwRet;
  62. }
  63. }
  64. STDMETHODIMP_(ULONG) CMemStream::Release()
  65. {
  66. this->cRef--;
  67. if (this->cRef > 0)
  68. return this->cRef;
  69. // If this is backed up by the registry serialize the data
  70. if (this->hkey)
  71. {
  72. // Backed by the registry.
  73. // Write and cleanup.
  74. WriteToReg();
  75. RegCloseKey(this->hkey);
  76. }
  77. // Free the data buffer that is allocated to the stream
  78. if (this->pBuf)
  79. LocalFree(this->pBuf);
  80. LocalFree((HLOCAL)this);
  81. return 0;
  82. }
  83. STDMETHODIMP CMemStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
  84. {
  85. #ifdef ENFORCE_GRFMODE
  86. if ((this->grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) == STGM_WRITE)
  87. {
  88. if (pcbRead != NULL)
  89. *pcbRead = 0;
  90. return STG_E_ACCESSDENIED;
  91. }
  92. #endif
  93. ASSERT(pv);
  94. // I guess a null read is ok.
  95. if (!cb)
  96. {
  97. if (pcbRead != NULL)
  98. *pcbRead = 0;
  99. return S_OK;
  100. }
  101. if (this->iSeek >= this->cbData)
  102. {
  103. if (pcbRead != NULL)
  104. *pcbRead = 0; // nothing read
  105. }
  106. else
  107. {
  108. if ((this->iSeek + cb) > this->cbData)
  109. cb = this->cbData - this->iSeek;
  110. // Now Copy the memory
  111. ASSERT(this->pBuf);
  112. CopyMemory(pv, this->pBuf + this->iSeek, cb);
  113. this->iSeek += (UINT)cb;
  114. if (pcbRead != NULL)
  115. *pcbRead = cb;
  116. }
  117. return S_OK;
  118. }
  119. LPBYTE CMemStream::GrowBuffer(ULONG cbNew)
  120. {
  121. if (this->pBuf == NULL)
  122. {
  123. this->pBuf = (LPBYTE)LocalAlloc(LPTR, cbNew);
  124. }
  125. else
  126. {
  127. LPBYTE pTemp = (LPBYTE)LocalReAlloc(this->pBuf, cbNew, LMEM_MOVEABLE | LMEM_ZEROINIT);
  128. if (pTemp)
  129. {
  130. this->pBuf = pTemp;
  131. }
  132. else
  133. {
  134. TraceMsg(TF_ERROR, "Stream buffer realloc failed");
  135. return NULL;
  136. }
  137. }
  138. if (this->pBuf)
  139. this->cbAlloc = cbNew;
  140. return this->pBuf;
  141. }
  142. #define SIZEINCR 0x1000
  143. STDMETHODIMP CMemStream::Write(void const *pv, ULONG cb, ULONG *pcbWritten)
  144. {
  145. #ifdef ENFORCE_GRFMODE
  146. if ((this->grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) == STGM_READ)
  147. {
  148. if (pcbWritten != NULL)
  149. *pcbWritten = 0;
  150. return STG_E_ACCESSDENIED;
  151. }
  152. #endif
  153. // I guess a null write is ok.
  154. if (!cb)
  155. {
  156. if (pcbWritten != NULL)
  157. *pcbWritten = 0;
  158. return S_OK;
  159. }
  160. // See if the data will fit into our current buffer
  161. if ((this->iSeek + cb) > this->cbAlloc)
  162. {
  163. // enlarge the buffer
  164. // Give it a little slop to avoid a lot of reallocs.
  165. if (GrowBuffer(this->iSeek + (UINT)cb + SIZEINCR) == NULL)
  166. return STG_E_INSUFFICIENTMEMORY;
  167. }
  168. ASSERT(this->pBuf);
  169. // See if we need to fill the area between the data size and
  170. // the seek position
  171. if (this->iSeek > this->cbData)
  172. {
  173. ZeroMemory(this->pBuf + this->cbData, this->iSeek - this->cbData);
  174. }
  175. CopyMemory(this->pBuf + this->iSeek, pv, cb); // buffer grown above
  176. this->iSeek += (UINT)cb;
  177. if (this->iSeek > this->cbData)
  178. this->cbData = this->iSeek;
  179. if (pcbWritten != NULL)
  180. *pcbWritten = cb;
  181. return S_OK;
  182. }
  183. STDMETHODIMP CMemStream::Seek(LARGE_INTEGER dlibMove,
  184. DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
  185. {
  186. LONG lNewSeek;
  187. // Note: curently not testing for error conditions for number wrap...
  188. switch (dwOrigin)
  189. {
  190. case STREAM_SEEK_SET:
  191. lNewSeek = (LONG)dlibMove.LowPart;
  192. break;
  193. case STREAM_SEEK_CUR:
  194. lNewSeek = (LONG)this->iSeek + (LONG)dlibMove.LowPart;
  195. break;
  196. case STREAM_SEEK_END:
  197. lNewSeek = (LONG)this->cbData + (LONG)dlibMove.LowPart;
  198. break;
  199. default:
  200. return STG_E_INVALIDPARAMETER;
  201. }
  202. if (lNewSeek < 0)
  203. return STG_E_INVALIDFUNCTION;
  204. this->iSeek = (UINT)lNewSeek;
  205. if (plibNewPosition != NULL)
  206. {
  207. plibNewPosition->LowPart = (DWORD)lNewSeek;
  208. plibNewPosition->HighPart = 0;
  209. }
  210. return S_OK;
  211. }
  212. STDMETHODIMP CMemStream::SetSize(ULARGE_INTEGER libNewSize)
  213. {
  214. #ifdef ENFORCE_GRFMODE
  215. if ((this->grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) == STGM_READ)
  216. {
  217. return STG_E_ACCESSDENIED;
  218. }
  219. #endif
  220. UINT cbNew = (UINT)libNewSize.LowPart;
  221. // See if the data will fit into our current buffer
  222. if (cbNew > this->cbData)
  223. {
  224. // See if we have to Enlarge the buffer.
  225. if (cbNew > this->cbAlloc)
  226. {
  227. // enlarge the buffer - Does not check wrap...
  228. // Give it a little slop to avoid a lot of reallocs.
  229. if (GrowBuffer(cbNew) == NULL)
  230. return STG_E_INSUFFICIENTMEMORY;
  231. }
  232. // Now fill some memory
  233. ZeroMemory(this->pBuf + this->cbData, cbNew - this->cbData);
  234. }
  235. // Save away the new size.
  236. this->cbData = cbNew;
  237. return S_OK;
  238. }
  239. STDMETHODIMP CMemStream::CopyTo(IStream *pstmTo,
  240. ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
  241. {
  242. #ifdef ENFORCE_GRFMODE
  243. if ((this->grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) == STGM_WRITE)
  244. {
  245. if (pcbRead != NULL)
  246. ZeroMemory(pcbRead, sizeof(*pcbRead));
  247. if (pcbWritten != NULL)
  248. ZeroMemory(pcbWritten, sizeof(*pcbWritten));
  249. return STG_E_ACCESSDENIED;
  250. }
  251. #endif
  252. HRESULT hres = S_OK;
  253. UINT cbRead = this->cbData - this->iSeek;
  254. ULONG cbWritten = 0;
  255. if (cb.HighPart == 0 && cb.LowPart < cbRead)
  256. {
  257. cbRead = cb.LowPart;
  258. }
  259. if (cbRead > 0)
  260. {
  261. hres = pstmTo->Write(this->pBuf + this->iSeek, cbRead, &cbWritten);
  262. this->iSeek += cbRead;
  263. }
  264. if (pcbRead)
  265. {
  266. pcbRead->LowPart = cbRead;
  267. pcbRead->HighPart = 0;
  268. }
  269. if (pcbWritten)
  270. {
  271. pcbWritten->LowPart = cbWritten;
  272. pcbWritten->HighPart = 0;
  273. }
  274. return hres;
  275. }
  276. STDMETHODIMP CMemStream::Commit(DWORD grfCommitFlags)
  277. {
  278. return E_NOTIMPL;
  279. }
  280. STDMETHODIMP CMemStream::Revert()
  281. {
  282. return E_NOTIMPL;
  283. }
  284. STDMETHODIMP CMemStream::LockRegion(ULARGE_INTEGER libOffset,
  285. ULARGE_INTEGER cb, DWORD dwLockType)
  286. {
  287. return E_NOTIMPL;
  288. }
  289. STDMETHODIMP CMemStream::UnlockRegion(ULARGE_INTEGER libOffset,
  290. ULARGE_INTEGER cb, DWORD dwLockType)
  291. {
  292. return E_NOTIMPL;
  293. }
  294. // Trident calls this to determine the size of the structure.
  295. // No reason to not support this one.
  296. STDMETHODIMP CMemStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
  297. {
  298. ZeroMemory(pstatstg, sizeof(*pstatstg));
  299. // we have no name
  300. pstatstg->type = STGTY_STREAM;
  301. pstatstg->cbSize.LowPart = this->cbData;
  302. // blow off modify, create, access times (we don't track anyway)
  303. pstatstg->grfMode = this->grfMode;
  304. // we're not transacting, so we have no lock modes
  305. // we're the null clsid already
  306. // we're not based on storage, so we have no state or storage bits
  307. return S_OK;
  308. }
  309. STDMETHODIMP CMemStream::Clone(IStream **ppstm)
  310. {
  311. *ppstm = NULL;
  312. return E_NOTIMPL;
  313. }
  314. CMemStream *
  315. CreateMemStreamEx(
  316. LPBYTE pInit,
  317. UINT cbInit,
  318. LPCTSTR pszValue) OPTIONAL
  319. {
  320. UINT cchValue = (pszValue ? lstrlen(pszValue) : 0);
  321. UINT l_cbAlloc = sizeof(CMemStream) + (cchValue * sizeof(TCHAR)); // null terminator for pszValue is taken care of by CMemStream.szValue[1]
  322. CMemStream *localthis = (CMemStream *)LocalAlloc(LPTR, l_cbAlloc);
  323. if (localthis)
  324. {
  325. new (localthis) CMemStream;
  326. localthis->cRef = 1;
  327. // See if there is some initial data we should map in here.
  328. if ((pInit != NULL) && (cbInit > 0))
  329. {
  330. if (localthis->GrowBuffer(cbInit) == NULL)
  331. {
  332. // Could not allocate buffer!
  333. LocalFree((HLOCAL)localthis);
  334. return NULL;
  335. }
  336. localthis->cbData = cbInit;
  337. CopyMemory(localthis->pBuf, pInit, cbInit);
  338. }
  339. if (pszValue)
  340. {
  341. StringCchCopy(localthis->szValue, cchValue + 1, pszValue);
  342. }
  343. // We have no other value to set this to
  344. localthis->grfMode = STGM_READWRITE;
  345. return localthis;
  346. }
  347. return NULL;
  348. }
  349. STDAPI_(IStream *)
  350. SHCreateMemStream(
  351. LPBYTE pInit,
  352. UINT cbInit)
  353. {
  354. CMemStream *localthis = CreateMemStreamEx(pInit, cbInit, NULL);
  355. if (localthis)
  356. return localthis;
  357. return NULL;
  358. }
  359. //----------------------------------------------------------------------------
  360. // Open a stream to the reg file given an open key.
  361. // NB pszValue can be NULL.
  362. //
  363. // Win9x exported OpenRegStream which *always* returned a stream, even for read,
  364. // even when there was no data there. IE4 shell32 delegated to shlwapi's SHOpenRegStream
  365. // which needs to support this sub-optimal behavior. See NT5 bug 190878 (shell32 fault).
  366. //
  367. STDAPI_(IStream *)
  368. SHOpenRegStreamW(
  369. HKEY hkey,
  370. LPCWSTR pszSubkey,
  371. LPCWSTR pszValue, OPTIONAL
  372. DWORD grfMode)
  373. {
  374. IStream * pstm = SHOpenRegStream2W(hkey, pszSubkey, pszValue, grfMode);
  375. #ifndef UNIX
  376. if (!pstm)
  377. pstm = SHConstNullStream();
  378. #endif
  379. return pstm;
  380. }
  381. STDAPI_(IStream *)
  382. SHOpenRegStreamA(
  383. HKEY hkey,
  384. LPCSTR pszSubkey,
  385. LPCSTR pszValue, OPTIONAL
  386. DWORD grfMode)
  387. {
  388. IStream * pstm = SHOpenRegStream2A(hkey, pszSubkey, pszValue, grfMode);
  389. #ifndef UNIX
  390. if (!pstm)
  391. pstm = SHConstNullStream();
  392. #endif
  393. return pstm;
  394. }
  395. // We should add STGM_CREATE support to the shlwapi streams. When saving out
  396. // streams, we currently create the stream with STGM_WRITE (but not STGM_CREATE)
  397. // so shlwapi goes to all the wasted trouble of reading the old stream data into
  398. // memory, only to throw it away when we write over it.
  399. //
  400. // STGM_CREATE means "I don't care about the old values because I'm going to
  401. // overwrite them anyway." (It really should be named STGM_TRUNCATEONOPEN.)
  402. //
  403. STDAPI_(IStream *)
  404. SHOpenRegStream2(
  405. HKEY hkey,
  406. LPCTSTR pszSubkey,
  407. LPCTSTR pszValue, OPTIONAL
  408. DWORD grfMode)
  409. {
  410. CMemStream *localthis; // In bed with class...
  411. RIPMSG(IS_VALID_HANDLE(hkey, KEY), "SHOpenRegStream2: Caller passed invalid hkey");
  412. RIPMSG(!pszSubkey || IS_VALID_STRING_PTR(pszSubkey, -1), "SHOpenRegStream2: Caller passed invalid pszSubkey");
  413. RIPMSG(!pszValue || IS_VALID_STRING_PTR(pszValue, -1), "SHOpenRegStream2: Caller passed invalid pszValue");
  414. // Null keys are illegal.
  415. if (!hkey)
  416. {
  417. return NULL;
  418. }
  419. localthis = CreateMemStreamEx(NULL, 0, pszValue);
  420. if (!localthis)
  421. return NULL; // Failed to allocate space
  422. localthis->grfMode = grfMode;
  423. // Get the hkey we're going to deal with
  424. //
  425. // Did the caller pass us a subkey, and does it contain a string?
  426. if (pszSubkey && *pszSubkey)
  427. {
  428. // Yes; Then try to bind to that key.
  429. // If this stream is one the user mentioned as wanting to write to
  430. // we need to save away the regkey and value.
  431. if ((grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_READ)
  432. {
  433. // Store away the key.
  434. // write access required.
  435. if (RegCreateKeyEx(hkey, pszSubkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &localthis->hkey, NULL) != ERROR_SUCCESS)
  436. {
  437. TraceMsg(TF_ERROR, "SHOpenRegStream: Unable to create key.");
  438. localthis->hkey = NULL; // be paranoid
  439. }
  440. }
  441. else if (RegOpenKeyEx(hkey, pszSubkey, 0, KEY_READ, &localthis->hkey) != ERROR_SUCCESS)
  442. {
  443. localthis->hkey = NULL; // be paranoid
  444. }
  445. }
  446. else
  447. {
  448. localthis->hkey = SHRegDuplicateHKey(hkey);
  449. }
  450. // we don't have an hkey, bail
  451. if (NULL == localthis->hkey)
  452. {
  453. localthis->Release();
  454. return NULL;
  455. }
  456. // Now see if we need to initialize the stream.
  457. if ((grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_WRITE)
  458. {
  459. DWORD dwType;
  460. DWORD cbData;
  461. if ((RegQueryValueEx(localthis->hkey, pszValue, NULL, &dwType, NULL, &cbData) == ERROR_SUCCESS) && cbData)
  462. {
  463. if (localthis->GrowBuffer(cbData) != NULL)
  464. {
  465. ASSERT(localthis->cbAlloc >= cbData);
  466. // Get the data.
  467. RegQueryValueEx(localthis->hkey, pszValue, NULL, &dwType, localthis->pBuf, &cbData);
  468. localthis->cbData = cbData;
  469. }
  470. else
  471. {
  472. TraceMsg(TF_ERROR, "OpenRegStream: Unable to initialize stream to registry.");
  473. localthis->Release();
  474. return NULL;
  475. }
  476. }
  477. }
  478. // If the stream was opened read-only, then close the key so
  479. // CMemStream::Release won't try to write the "updates" back out to the
  480. // registry.
  481. if ((grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) == STGM_READ)
  482. {
  483. RegCloseKey(localthis->hkey);
  484. localthis->hkey = NULL;
  485. }
  486. return localthis;
  487. }
  488. #ifdef UNICODE
  489. STDAPI_(IStream *)
  490. SHOpenRegStream2A(
  491. HKEY hkey,
  492. LPCSTR pszSubkey,
  493. LPCSTR pszValue, OPTIONAL
  494. DWORD grfMode)
  495. {
  496. IStream * pstm = NULL;
  497. RIPMSG(IS_VALID_HANDLE(hkey, KEY), "SHOpenRegStream2A: Caller passed invalid hkey");
  498. RIPMSG(!pszSubkey || IS_VALID_STRING_PTRA(pszSubkey, -1), "SHOpenRegStream2A: Caller passed invalid pszSubkey");
  499. RIPMSG(!pszValue || IS_VALID_STRING_PTRA(pszValue, -1), "SHOpenRegStream2A: Caller passed invalid pszValue");
  500. WCHAR wszSubkey[MAX_PATH];
  501. if (pszSubkey)
  502. {
  503. if (!MultiByteToWideChar(CP_ACP, 0, pszSubkey, -1, wszSubkey, SIZECHARS(wszSubkey)))
  504. return NULL;
  505. pszSubkey = (LPCSTR)wszSubkey;
  506. }
  507. WCHAR wszValue[MAX_PATH];
  508. if (pszValue)
  509. {
  510. if (!MultiByteToWideChar(CP_ACP, 0, pszValue, -1, wszValue, SIZECHARS(wszValue)))
  511. return NULL;
  512. pszValue = (LPCSTR)wszValue;
  513. }
  514. pstm = SHOpenRegStream2W(hkey, (LPCWSTR)pszSubkey, (LPCWSTR)pszValue, grfMode);
  515. return pstm;
  516. }
  517. #else
  518. STDAPI_(IStream *)
  519. SHOpenRegStream2W(
  520. HKEY hkey,
  521. LPCWSTR pszSubkey,
  522. LPCWSTR pszValue, OPTIONAL
  523. DWORD grfMode)
  524. {
  525. IStream * pstm = NULL;
  526. RIPMSG(IS_VALID_HANDLE(hkey, KEY), "SHOpenRegStream2W: Caller passed invalid hkey");
  527. RIPMSG(!pszSubkey || IS_VALID_STRING_PTRW(pszSubkey, -1), "SHOpenRegStream2W: Caller passed invalid pszSubkey");
  528. RIPMSG(!pszValue || IS_VALID_STRING_PTRW(pszValue, -1), "SHOpenRegStream2W: Caller passed invalid pszValue");
  529. CHAR szSubkey[MAX_PATH];
  530. if (pszSubkey)
  531. {
  532. if (!WideCharToMultiByte(CP_ACP, 0, pszSubkey, -1, szSubkey, SIZECHARS(szSubkey), NULL, NULL))
  533. return NULL;
  534. pszSubkey = (LPCWSTR)szSubkey;
  535. }
  536. CHAR szValue[MAX_PATH];
  537. if (pszValue)
  538. {
  539. if (!WideCharToMultiByte(CP_ACP, 0, pszValue, -1, szValue, SIZECHARS(szValue), NULL, NULL))
  540. return NULL;
  541. pszValue = (LPCWSTR)szValue;
  542. }
  543. pstm = SHOpenRegStream2A(hkey, (LPCSTR)pszSubkey, (LPCSTR)pszValue, grfMode);
  544. return pstm;
  545. }
  546. #endif // UNICODE