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.

1058 lines
31 KiB

  1. /***************************************************************************
  2. * FILERES.C
  3. *
  4. * File resource extraction routines.
  5. *
  6. ***************************************************************************/
  7. //
  8. // REARCHITECT - GetVerInfoSize plays tricks and tells the caller to allocate
  9. // some extra slop at the end of the buffer in case we need to thunk all
  10. // the strings to ANSI. The bug is that it only tells it to allocate
  11. // one extra ANSI char (== BYTE) for each Unicode char. This is not correct
  12. // in the DBCS case (since one Unicode char can equal a two byte DBCS char)
  13. //
  14. // We should change GetVerInfoSize return the Unicode size * 2 (instead
  15. // of (Unicode size * 1.5) and then change VerQueryInfoA to also use the
  16. // * 2 computation instead of * 1.5 (== x + x/2)
  17. //
  18. // 23-May-1996 JonPa
  19. //
  20. #include <nt.h>
  21. #include <ntrtl.h>
  22. #include <nturtl.h>
  23. #include "verpriv.h"
  24. #include <memory.h>
  25. #define DWORDUP(x) (((x)+3)&~03)
  26. typedef struct tagVERBLOCK {
  27. WORD wTotLen;
  28. WORD wValLen;
  29. WORD wType;
  30. WCHAR szKey[1];
  31. } VERBLOCK ;
  32. typedef struct tagVERHEAD {
  33. WORD wTotLen;
  34. WORD wValLen;
  35. WORD wType; /* always 0 */
  36. WCHAR szKey[(sizeof("VS_VERSION_INFO")+3)&~03];
  37. VS_FIXEDFILEINFO vsf;
  38. } VERHEAD ;
  39. typedef struct tagVERBLOCK16 {
  40. WORD wTotLen;
  41. WORD wValLen;
  42. CHAR szKey[1];
  43. } VERBLOCK16 ;
  44. typedef struct tagVERHEAD16 {
  45. WORD wTotLen;
  46. WORD wValLen;
  47. CHAR szKey[(sizeof("VS_VERSION_INFO")+3)&~03];
  48. VS_FIXEDFILEINFO vsf; // same as win31
  49. } VERHEAD16 ;
  50. DWORD VER2_SIG='X2EF';
  51. extern WCHAR szTrans[];
  52. // function resides in this file
  53. extern LPSTR WINAPI VerCharNextA(LPCSTR lpCurrentChar);
  54. /* ----- Functions ----- */
  55. DWORD
  56. MyExtractVersionResource16W (
  57. LPCWSTR lpwstrFilename,
  58. LPHANDLE hVerRes
  59. )
  60. {
  61. DWORD dwTemp = 0;
  62. DWORD (__stdcall *pExtractVersionResource16W)(LPCWSTR, LPHANDLE);
  63. HINSTANCE hShell32 = LoadLibraryW(L"shell32.dll");
  64. if (hShell32) {
  65. pExtractVersionResource16W = (DWORD(__stdcall *)(LPCWSTR, LPHANDLE))
  66. GetProcAddress(hShell32, "ExtractVersionResource16W");
  67. if (pExtractVersionResource16W) {
  68. dwTemp = pExtractVersionResource16W( lpwstrFilename, hVerRes );
  69. } else {
  70. dwTemp = 0;
  71. }
  72. FreeLibrary(hShell32);
  73. }
  74. return dwTemp;
  75. }
  76. /* GetFileVersionInfoSize
  77. * Gets the size of the version information; notice this is quick
  78. * and dirty, and the handle is just the offset
  79. *
  80. * Returns size of version info in bytes
  81. * lpwstrFilename is the name of the file to get version information from
  82. * lpdwHandle is outdated for the Win32 api and is set to zero.
  83. */
  84. DWORD
  85. APIENTRY
  86. GetFileVersionInfoSizeW(
  87. LPCWSTR lpwstrFilename,
  88. LPDWORD lpdwHandle
  89. )
  90. {
  91. DWORD dwTemp;
  92. VERHEAD *pVerHead;
  93. HANDLE hMod;
  94. HANDLE hVerRes;
  95. HANDLE h;
  96. DWORD dwError;
  97. if (lpdwHandle != NULL)
  98. *lpdwHandle = 0;
  99. dwTemp = SetErrorMode(SEM_FAILCRITICALERRORS);
  100. hMod = LoadLibraryEx(lpwstrFilename, NULL, LOAD_LIBRARY_AS_DATAFILE);
  101. SetErrorMode(dwTemp);
  102. if (!hMod && GetLastError() == ERROR_FILE_NOT_FOUND)
  103. return FALSE;
  104. pVerHead = NULL;
  105. if (!hMod) {
  106. hVerRes = NULL;
  107. __try
  108. {
  109. dwTemp = MyExtractVersionResource16W( lpwstrFilename, &hVerRes );
  110. if (!dwTemp) {
  111. dwError = ERROR_RESOURCE_DATA_NOT_FOUND;
  112. __leave;
  113. }
  114. if (!(pVerHead = GlobalLock(hVerRes)) || (pVerHead->wTotLen > dwTemp)) {
  115. dwError = ERROR_INVALID_DATA;
  116. dwTemp = 0;
  117. __leave;
  118. }
  119. dwError = ERROR_SUCCESS;
  120. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  121. dwError = ERROR_INVALID_DATA;
  122. dwTemp = 0 ;
  123. }
  124. if (pVerHead)
  125. GlobalUnlock(hVerRes);
  126. if (hVerRes)
  127. GlobalFree(hVerRes);
  128. SetLastError(dwError);
  129. return dwTemp ? dwTemp * 3 : 0; // 3x == 1x for ansi input, 2x for unicode convert space
  130. }
  131. __try {
  132. dwError = ERROR_SUCCESS;
  133. if ((hVerRes = FindResource(hMod, MAKEINTRESOURCE(VS_VERSION_INFO), VS_FILE_INFO)) == NULL) {
  134. dwError = ERROR_RESOURCE_TYPE_NOT_FOUND;
  135. dwTemp = 0;
  136. __leave;
  137. }
  138. if ((dwTemp=SizeofResource(hMod, hVerRes)) == 0) {
  139. dwError = ERROR_INVALID_DATA;
  140. dwTemp = 0;
  141. __leave;
  142. }
  143. if ((h = LoadResource(hMod, hVerRes)) == NULL) {
  144. dwError = ERROR_INVALID_DATA;
  145. dwTemp = 0;
  146. __leave;
  147. }
  148. if ((pVerHead = (VERHEAD*)LockResource(h)) == NULL) {
  149. dwError = ERROR_INVALID_DATA;
  150. dwTemp = 0;
  151. __leave;
  152. }
  153. if ((DWORD)pVerHead->wTotLen > dwTemp) {
  154. dwError = ERROR_INVALID_DATA;
  155. dwTemp = 0;
  156. __leave;
  157. }
  158. dwTemp = (DWORD)pVerHead->wTotLen;
  159. dwTemp = DWORDUP(dwTemp);
  160. if (pVerHead->vsf.dwSignature != VS_FFI_SIGNATURE) {
  161. dwError = ERROR_INVALID_DATA;
  162. dwTemp = 0;
  163. __leave;
  164. }
  165. } __except(EXCEPTION_EXECUTE_HANDLER) {
  166. dwError = ERROR_INVALID_DATA;
  167. dwTemp = 0;
  168. }
  169. if (pVerHead)
  170. UnlockResource(h);
  171. FreeLibrary(hMod);
  172. SetLastError(dwError);
  173. //
  174. // dwTemp should be evenly divisible by two since not single
  175. // byte components at all (also DWORDUP for safety above):
  176. // alloc space for ansi components
  177. //
  178. //
  179. // Keep space for DBCS chars.
  180. //
  181. return dwTemp ? (dwTemp * 2) + sizeof(VER2_SIG) : 0;
  182. }
  183. /* GetFileVersionInfo
  184. * Gets the version information; fills in the structure up to
  185. * the size specified by the dwLen parameter (since Control Panel
  186. * only cares about the version numbers, it won't even call
  187. * GetFileVersionInfoSize). Notice this is quick and dirty
  188. * version, and dwHandle is just the offset (or NULL).
  189. *
  190. * lpwstrFilename is the name of the file to get version information from.
  191. * dwHandle is the handle filled in from the GetFileVersionInfoSize call.
  192. * dwLen is the length of the buffer to fill.
  193. * lpData is the buffer to fill.
  194. */
  195. BOOL
  196. APIENTRY
  197. GetFileVersionInfoW(
  198. LPCWSTR lpwstrFilename,
  199. DWORD dwHandle,
  200. DWORD dwLen,
  201. LPVOID lpData
  202. )
  203. {
  204. VERHEAD *pVerHead;
  205. VERHEAD16 *pVerHead16;
  206. HANDLE hMod;
  207. HANDLE hVerRes;
  208. HANDLE h;
  209. UINT dwTemp;
  210. BOOL bTruncate, rc;
  211. DWORD dwError;
  212. UNREFERENCED_PARAMETER(dwHandle);
  213. // Check minimum size to prevent access violations
  214. // WORD for the VERHEAD wTotLen field
  215. if (dwLen < sizeof(WORD)) {
  216. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  217. return (FALSE);
  218. }
  219. dwTemp = SetErrorMode(SEM_FAILCRITICALERRORS);
  220. hMod = LoadLibraryEx(lpwstrFilename, NULL, LOAD_LIBRARY_AS_DATAFILE);
  221. SetErrorMode(dwTemp);
  222. if (!hMod && GetLastError() == ERROR_FILE_NOT_FOUND)
  223. return FALSE;
  224. if (hMod == NULL) {
  225. // Allow 16bit stuff
  226. __try {
  227. dwTemp = MyExtractVersionResource16W( lpwstrFilename, &hVerRes );
  228. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  229. dwTemp = 0 ;
  230. }
  231. if (!dwTemp) {
  232. SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
  233. return (FALSE);
  234. }
  235. if (!(pVerHead16 = GlobalLock(hVerRes))) {
  236. SetLastError(ERROR_INVALID_DATA);
  237. GlobalFree(hVerRes);
  238. return (FALSE);
  239. }
  240. __try {
  241. dwTemp = (DWORD)pVerHead16->wTotLen;
  242. if (dwTemp > dwLen / 3) {
  243. //
  244. // We are forced to truncate.
  245. //
  246. dwTemp = dwLen/3;
  247. bTruncate = TRUE;
  248. } else {
  249. bTruncate = FALSE;
  250. }
  251. // Now mem copy only the real size of the resource. (We alloced
  252. // extra space for unicode)
  253. memcpy((PVOID)lpData, (PVOID)pVerHead16, dwTemp);
  254. if (bTruncate) {
  255. // If we truncated above, then we must set the new
  256. // size of the block so that we don't overtraverse.
  257. ((VERHEAD16*)lpData)->wTotLen = (WORD)dwTemp;
  258. }
  259. rc = TRUE;
  260. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  261. rc = FALSE;
  262. }
  263. GlobalUnlock(hVerRes);
  264. GlobalFree(hVerRes);
  265. SetLastError(rc ? ERROR_INVALID_DATA : ERROR_SUCCESS);
  266. return rc;
  267. }
  268. rc = TRUE;
  269. dwError = ERROR_SUCCESS;
  270. if (((hVerRes = FindResource(hMod, MAKEINTRESOURCE(VS_VERSION_INFO), VS_FILE_INFO)) == NULL) ||
  271. ((pVerHead = LoadResource(hMod, hVerRes)) == NULL))
  272. {
  273. dwError = ERROR_RESOURCE_TYPE_NOT_FOUND;
  274. rc = FALSE;
  275. } else {
  276. __try {
  277. dwTemp = (DWORD)pVerHead->wTotLen;
  278. if (dwTemp > (dwLen - sizeof(VER2_SIG)) / 2) {
  279. // We are forced to truncate.
  280. //
  281. // dwLen = UnicodeBuffer + AnsiBuffer.
  282. //
  283. // if we try to "memcpy" with "(dwLen/3) * 2" size, pVerHead
  284. // might not have such a big data...
  285. //
  286. dwTemp = (dwLen - sizeof(VER2_SIG)) / 2;
  287. bTruncate = TRUE;
  288. } else {
  289. bTruncate = FALSE;
  290. }
  291. // Now mem copy only the real size of the resource. (We alloced
  292. // extra space for ansi)
  293. memcpy((PVOID)lpData, (PVOID)pVerHead, dwTemp);
  294. // Store a sig between the raw data and the ANSI translation area so we know
  295. // how much space we have available in VerQuery for ANSI translation.
  296. *((DWORD UNALIGNED *)((ULONG_PTR)lpData + dwTemp)) = VER2_SIG;
  297. if (bTruncate) {
  298. // If we truncated above, then we must set the new
  299. // size of the block so that we don't overtraverse.
  300. ((VERHEAD*)lpData)->wTotLen = (WORD)dwTemp;
  301. }
  302. rc = TRUE;
  303. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  304. dwError = ERROR_INVALID_DATA;
  305. rc = FALSE;
  306. }
  307. }
  308. FreeLibrary(hMod);
  309. SetLastError(dwError);
  310. return (rc);
  311. }
  312. BOOL
  313. VerpQueryValue16(
  314. const LPVOID pb,
  315. LPVOID lpSubBlockX,
  316. INT nIndex,
  317. LPVOID *lplpKey,
  318. LPVOID *lplpBuffer,
  319. PUINT puLen,
  320. BOOL bUnicodeNeeded
  321. )
  322. {
  323. ANSI_STRING AnsiString;
  324. UNICODE_STRING UnicodeString;
  325. LPSTR lpSubBlock;
  326. LPSTR lpSubBlockOrg;
  327. NTSTATUS Status;
  328. UINT uLen;
  329. VERBLOCK16 *pBlock = (VERBLOCK16*)pb;
  330. LPSTR lpStart, lpEndBlock, lpEndSubBlock;
  331. CHAR cTemp, cEndBlock;
  332. BOOL bLastSpec;
  333. DWORD dwHeadLen, dwTotBlockLen;
  334. INT nCmp;
  335. DWORD LastError = ERROR_SUCCESS;
  336. BOOL bThunkNeeded;
  337. /*
  338. * If needs unicode, then we must thunk the input parameter
  339. * to ansi. If it's ansi already, we make a copy so we can
  340. * modify it.
  341. */
  342. if (bUnicodeNeeded) {
  343. //
  344. // Thunk is not needed if lpSubBlockX == \VarFileInfo\Translation
  345. // or if lpSubBlockX == \
  346. //
  347. bThunkNeeded = (BOOL)((*(LPTSTR)lpSubBlockX != 0) &&
  348. (lstrcmp(lpSubBlockX, TEXT("\\")) != 0) &&
  349. (lstrcmpi(lpSubBlockX, szTrans) != 0));
  350. RtlInitUnicodeString(&UnicodeString, lpSubBlockX);
  351. Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
  352. if (!NT_SUCCESS(Status)) {
  353. SetLastError(Status);
  354. return FALSE;
  355. }
  356. lpSubBlock = AnsiString.Buffer;
  357. } else {
  358. lpSubBlockOrg = (LPSTR)LocalAlloc(LPTR,(lstrlenA(lpSubBlockX)+1)*sizeof(CHAR));
  359. if (lpSubBlockOrg == NULL ) {
  360. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  361. return FALSE;
  362. }
  363. lstrcpyA(lpSubBlockOrg,lpSubBlockX);
  364. lpSubBlock = lpSubBlockOrg;
  365. }
  366. if (!puLen)
  367. puLen = &uLen;
  368. *puLen = 0;
  369. /* Ensure that the total length is less than 32K but greater than the
  370. * size of a block header; we will assume that the size of pBlock is at
  371. * least the value of this first INT.
  372. */
  373. if ((INT)pBlock->wTotLen < sizeof(VERBLOCK16)) {
  374. LastError = ERROR_INVALID_DATA;
  375. goto Fail;
  376. }
  377. /*
  378. * Put a '\0' at the end of the block so that none of the lstrlen's will
  379. * go past then end of the block. We will replace it before returning.
  380. */
  381. lpEndBlock = ((LPSTR)pBlock) + pBlock->wTotLen - 1;
  382. cEndBlock = *lpEndBlock;
  383. *lpEndBlock = '\0';
  384. bLastSpec = FALSE;
  385. while ((*lpSubBlock || nIndex != -1)) {
  386. //
  387. // Ignore leading '\\'s
  388. //
  389. while (*lpSubBlock == '\\')
  390. ++lpSubBlock;
  391. if ((*lpSubBlock || nIndex != -1)) {
  392. /* Make sure we still have some of the block left to play with
  393. */
  394. dwTotBlockLen = (DWORD)(lpEndBlock - ((LPSTR)pBlock) + 1);
  395. if ((INT)dwTotBlockLen<sizeof(VERBLOCK16) ||
  396. pBlock->wTotLen>dwTotBlockLen)
  397. goto NotFound;
  398. /* Calculate the length of the "header" (the two length WORDs plus
  399. * the identifying string) and skip past the value
  400. */
  401. dwHeadLen = sizeof(WORD)*2 + DWORDUP(lstrlenA(pBlock->szKey)+1)
  402. + DWORDUP(pBlock->wValLen);
  403. if (dwHeadLen > pBlock->wTotLen)
  404. goto NotFound;
  405. lpEndSubBlock = ((LPSTR)pBlock) + pBlock->wTotLen;
  406. pBlock = (VERBLOCK16 FAR *)((LPSTR)pBlock+dwHeadLen);
  407. /* Look for the first sub-block name and terminate it
  408. */
  409. for (lpStart=lpSubBlock; *lpSubBlock && *lpSubBlock!='\\';
  410. lpSubBlock=VerCharNextA(lpSubBlock))
  411. /* find next '\\' */ ;
  412. cTemp = *lpSubBlock;
  413. *lpSubBlock = '\0';
  414. /* Continue while there are sub-blocks left
  415. * pBlock->wTotLen should always be a valid pointer here because
  416. * we have validated dwHeadLen above, and we validated the previous
  417. * value of pBlock->wTotLen before using it
  418. */
  419. nCmp = 1;
  420. while ((INT)pBlock->wTotLen>sizeof(VERBLOCK16) &&
  421. (INT)(lpEndSubBlock-((LPSTR)pBlock))>=(INT)pBlock->wTotLen) {
  422. //
  423. // Index functionality: if we are at the end of the path
  424. // (cTemp == 0 set below) and nIndex is NOT -1 (index search)
  425. // then break on nIndex zero. Else do normal wscicmp.
  426. //
  427. if (bLastSpec && nIndex != -1) {
  428. if (!nIndex) {
  429. if (lplpKey) {
  430. *lplpKey = pBlock->szKey;
  431. }
  432. nCmp=0;
  433. //
  434. // Index found, set nInde to -1
  435. // so that we exit this loop
  436. //
  437. nIndex = -1;
  438. break;
  439. }
  440. nIndex--;
  441. } else {
  442. //
  443. // Check if the sub-block name is what we are looking for
  444. //
  445. if (!(nCmp=lstrcmpiA(lpStart, pBlock->szKey)))
  446. break;
  447. }
  448. /* Skip to the next sub-block
  449. */
  450. pBlock=(VERBLOCK16 FAR *)((LPSTR)pBlock+DWORDUP(pBlock->wTotLen));
  451. }
  452. /* Restore the char NULLed above and return failure if the sub-block
  453. * was not found
  454. */
  455. *lpSubBlock = cTemp;
  456. if (nCmp)
  457. goto NotFound;
  458. }
  459. bLastSpec = !cTemp;
  460. }
  461. /* Fill in the appropriate buffers and return success
  462. */
  463. *puLen = pBlock->wValLen;
  464. *lplpBuffer = (LPSTR)pBlock + 4 + DWORDUP(lstrlenA(pBlock->szKey) + 1);
  465. //
  466. // Shouldn't need zero-length value check since win31 compatible.
  467. //
  468. *lpEndBlock = cEndBlock;
  469. /*
  470. * Must free string we allocated above
  471. */
  472. if (bUnicodeNeeded) {
  473. RtlFreeAnsiString(&AnsiString);
  474. } else {
  475. LocalFree(lpSubBlockOrg);
  476. }
  477. /*----------------------------------------------------------------------
  478. * thunk the results
  479. *
  480. * Must always thunk key, always ??? value
  481. *
  482. * We have no way of knowing if the resource info is binary or strings
  483. * Version stuff is usually string info, so thunk.
  484. *
  485. * The best we can do is assume that everything is a string UNLESS
  486. * we are looking at \VarFileInfo\Translation or at \.
  487. *
  488. * This is acceptable because the documenation of VerQueryValue
  489. * indicates that this is used only for strings (except these cases.)
  490. *----------------------------------------------------------------------*/
  491. if (bUnicodeNeeded) {
  492. //
  493. // Do thunk only if we aren't looking for \VarFileInfo\Translation or \
  494. //
  495. if (bThunkNeeded) {
  496. // subtract 1 since puLen includes null
  497. AnsiString.Length = AnsiString.MaximumLength = (SHORT)*puLen - 1;
  498. AnsiString.Buffer = *lplpBuffer;
  499. //
  500. // Do the string conversion in the second half of the buffer
  501. // Assumes wTotLen is first filed in VERHEAD
  502. //
  503. UnicodeString.Buffer = (LPWSTR)((PBYTE)pb + DWORDUP(*((WORD*)pb)) +
  504. (DWORD)((PBYTE)*lplpBuffer - (PBYTE)pb)*2);
  505. UnicodeString.MaximumLength = (SHORT)(*puLen * sizeof(WCHAR));
  506. RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE);
  507. *lplpBuffer = UnicodeString.Buffer;
  508. }
  509. if (lplpKey) {
  510. //
  511. // Thunk the key
  512. //
  513. dwHeadLen = lstrlenA(*lplpKey);
  514. AnsiString.Length = AnsiString.MaximumLength = (SHORT)dwHeadLen;
  515. AnsiString.Buffer = *lplpKey;
  516. UnicodeString.Buffer = (LPWSTR) ((PBYTE)pb + DWORDUP(*((WORD*)pb)) +
  517. (DWORD)((PBYTE)*lplpKey - (PBYTE)pb)*2);
  518. UnicodeString.MaximumLength = (SHORT)((dwHeadLen+1) * sizeof(WCHAR));
  519. RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE);
  520. *lplpKey = UnicodeString.Buffer;
  521. }
  522. }
  523. SetLastError(LastError);
  524. return (TRUE);
  525. NotFound:
  526. /* Restore the char we NULLed above
  527. */
  528. *lpEndBlock = cEndBlock;
  529. LastError = ERROR_RESOURCE_TYPE_NOT_FOUND;
  530. Fail:
  531. if (bUnicodeNeeded) {
  532. RtlFreeAnsiString(&AnsiString);
  533. } else {
  534. LocalFree(lpSubBlockOrg);
  535. }
  536. SetLastError(LastError);
  537. return (FALSE);
  538. }
  539. /* VerpQueryValue
  540. * Given a pointer to a branch of a version info tree and the name of a
  541. * sub-branch (as in "sub\subsub\subsubsub\..."), this fills in a pointer
  542. * to the specified value and a word for its length. Returns TRUE on success,
  543. * FALSE on failure.
  544. *
  545. * Note that a subblock name may start with a '\\', but it will be ignored.
  546. * To get the value of the current block, use lpSubBlock=""
  547. */
  548. BOOL
  549. APIENTRY
  550. VerpQueryValue(
  551. const LPVOID pb,
  552. LPVOID lpSubBlockX, // can be ansi or unicode
  553. INT nIndex,
  554. LPVOID *lplpKey,
  555. LPVOID *lplpBuffer,
  556. PUINT puLen,
  557. BOOL bUnicodeNeeded
  558. )
  559. {
  560. ANSI_STRING AnsiString;
  561. UNICODE_STRING UnicodeString;
  562. LPWSTR lpSubBlockOrg;
  563. LPWSTR lpSubBlock;
  564. NTSTATUS Status;
  565. VERBLOCK *pBlock = (PVOID)pb;
  566. LPWSTR lpStart, lpEndBlock, lpEndSubBlock;
  567. WCHAR cTemp, cEndBlock;
  568. DWORD dwHeadLen, dwTotBlockLen;
  569. BOOL bLastSpec;
  570. INT nCmp;
  571. BOOL bString;
  572. UINT uLen;
  573. DWORD LastError = ERROR_SUCCESS;
  574. if (!puLen) {
  575. puLen = &uLen;
  576. }
  577. *puLen = 0;
  578. /*
  579. * Major hack: wType is 0 for win32 versions, but holds 56 ('V')
  580. * for win16.
  581. */
  582. if (((VERHEAD*)pb)->wType)
  583. return VerpQueryValue16(pb,
  584. lpSubBlockX,
  585. nIndex,
  586. lplpKey,
  587. lplpBuffer,
  588. puLen,
  589. bUnicodeNeeded);
  590. /*
  591. * If doesnt need unicode, then we must thunk the input parameter
  592. * to unicode.
  593. */
  594. if (!bUnicodeNeeded) {
  595. RtlInitAnsiString(&AnsiString, (LPSTR)lpSubBlockX);
  596. Status = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE);
  597. if (!NT_SUCCESS(Status)) {
  598. SetLastError(Status);
  599. return FALSE;
  600. }
  601. lpSubBlock = UnicodeString.Buffer;
  602. } else {
  603. lpSubBlockOrg = (LPWSTR)LocalAlloc(LPTR,(lstrlen(lpSubBlockX)+1)*sizeof(WCHAR));
  604. if (lpSubBlockOrg == NULL ) {
  605. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  606. return FALSE;
  607. }
  608. lstrcpy(lpSubBlockOrg,lpSubBlockX);
  609. lpSubBlock = lpSubBlockOrg;
  610. }
  611. /* Ensure that the total length is less than 32K but greater than the
  612. * size of a block header; we will assume that the size of pBlock is at
  613. * least the value of this first int.
  614. * Put a '\0' at the end of the block so that none of the wcslen's will
  615. * go past then end of the block. We will replace it before returning.
  616. */
  617. if ((int)pBlock->wTotLen < sizeof(VERBLOCK)) {
  618. LastError = ERROR_INVALID_DATA;
  619. goto Fail;
  620. }
  621. lpEndBlock = (LPWSTR)((LPSTR)pBlock + pBlock->wTotLen - sizeof(WCHAR));
  622. cEndBlock = *lpEndBlock;
  623. *lpEndBlock = 0;
  624. bString = FALSE;
  625. bLastSpec = FALSE;
  626. while ((*lpSubBlock || nIndex != -1)) {
  627. //
  628. // Ignore leading '\\'s
  629. //
  630. while (*lpSubBlock == TEXT('\\'))
  631. ++lpSubBlock;
  632. if ((*lpSubBlock || nIndex != -1)) {
  633. /* Make sure we still have some of the block left to play with
  634. */
  635. dwTotBlockLen = (DWORD)((LPSTR)lpEndBlock - (LPSTR)pBlock + sizeof(WCHAR));
  636. if ((int)dwTotBlockLen < sizeof(VERBLOCK) ||
  637. pBlock->wTotLen > (WORD)dwTotBlockLen)
  638. goto NotFound;
  639. /* Calculate the length of the "header" (the two length WORDs plus
  640. * the data type flag plus the identifying string) and skip
  641. * past the value.
  642. */
  643. dwHeadLen = (DWORD)(DWORDUP(sizeof(VERBLOCK) - sizeof(WCHAR) +
  644. (wcslen(pBlock->szKey) + 1) * sizeof(WCHAR)) +
  645. DWORDUP(pBlock->wValLen));
  646. if (dwHeadLen > pBlock->wTotLen)
  647. goto NotFound;
  648. lpEndSubBlock = (LPWSTR)((LPSTR)pBlock + pBlock->wTotLen);
  649. pBlock = (VERBLOCK*)((LPSTR)pBlock+dwHeadLen);
  650. /* Look for the first sub-block name and terminate it
  651. */
  652. for (lpStart=lpSubBlock; *lpSubBlock && *lpSubBlock!=TEXT('\\');
  653. lpSubBlock++)
  654. /* find next '\\' */ ;
  655. cTemp = *lpSubBlock;
  656. *lpSubBlock = 0;
  657. /* Continue while there are sub-blocks left
  658. * pBlock->wTotLen should always be a valid pointer here because
  659. * we have validated dwHeadLen above, and we validated the previous
  660. * value of pBlock->wTotLen before using it
  661. */
  662. nCmp = 1;
  663. while ((int)pBlock->wTotLen > sizeof(VERBLOCK) &&
  664. (int)pBlock->wTotLen <= (LPSTR)lpEndSubBlock-(LPSTR)pBlock) {
  665. //
  666. // Index functionality: if we are at the end of the path
  667. // (cTemp == 0 set below) and nIndex is NOT -1 (index search)
  668. // then break on nIndex zero. Else do normal wscicmp.
  669. //
  670. if (bLastSpec && nIndex != -1) {
  671. if (!nIndex) {
  672. if (lplpKey) {
  673. *lplpKey = pBlock->szKey;
  674. }
  675. nCmp=0;
  676. //
  677. // Index found, set nInde to -1
  678. // so that we exit this loop
  679. //
  680. nIndex = -1;
  681. break;
  682. }
  683. nIndex--;
  684. } else {
  685. //
  686. // Check if the sub-block name is what we are looking for
  687. //
  688. if (!(nCmp=_wcsicmp(lpStart, pBlock->szKey)))
  689. break;
  690. }
  691. /* Skip to the next sub-block
  692. */
  693. pBlock=(VERBLOCK*)((LPSTR)pBlock+DWORDUP(pBlock->wTotLen));
  694. }
  695. /* Restore the char NULLed above and return failure if the sub-block
  696. * was not found
  697. */
  698. *lpSubBlock = cTemp;
  699. if (nCmp)
  700. goto NotFound;
  701. }
  702. bLastSpec = !cTemp;
  703. }
  704. /* Fill in the appropriate buffers and return success
  705. */
  706. *puLen = pBlock->wValLen;
  707. /* Add code to handle the case of a null value.
  708. *
  709. * If zero-len, then return the pointer to the null terminator
  710. * of the key. Remember that this is thunked in the ansi case.
  711. *
  712. * We can't just look at pBlock->wValLen. Check if it really is
  713. * zero-len by seeing if the end of the key string is the end of the
  714. * block (i.e., the val string is outside of the current block).
  715. */
  716. lpStart = (LPWSTR)((LPSTR)pBlock+DWORDUP((sizeof(VERBLOCK)-sizeof(WCHAR))+
  717. (wcslen(pBlock->szKey)+1)*sizeof(WCHAR)));
  718. *lplpBuffer = lpStart < (LPWSTR)((LPBYTE)pBlock+pBlock->wTotLen) ?
  719. lpStart :
  720. (LPWSTR)(pBlock->szKey+wcslen(pBlock->szKey));
  721. bString = pBlock->wType;
  722. *lpEndBlock = cEndBlock;
  723. /*
  724. * Must free string we allocated above
  725. */
  726. if (!bUnicodeNeeded) {
  727. RtlFreeUnicodeString(&UnicodeString);
  728. } else {
  729. LocalFree(lpSubBlockOrg);
  730. }
  731. /*----------------------------------------------------------------------
  732. * thunk the results
  733. *
  734. * Must always thunk key, sometimes (if bString true) value
  735. *----------------------------------------------------------------------*/
  736. if (!bUnicodeNeeded) {
  737. // See if we're looking at a V1 or a V2 input block so we know how much space we
  738. // have for decoding the strings.
  739. BOOL fV2 = *(PDWORD)((PBYTE)pb + DWORDUP(*((WORD*)pb))) == VER2_SIG ? TRUE : FALSE;
  740. DWORD cbAnsiTranslateBuffer;
  741. if (fV2) {
  742. cbAnsiTranslateBuffer = DWORDUP(*((WORD *)pb));
  743. } else {
  744. cbAnsiTranslateBuffer = DWORDUP(*((WORD *)pb)) / 2;
  745. }
  746. if (bString && *puLen != 0) {
  747. DWORD cb, cb2;
  748. //
  749. // Must multiply length by two (first subtract 1 since puLen includes the null terminator)
  750. //
  751. UnicodeString.Length = UnicodeString.MaximumLength = (SHORT)((*puLen - 1) * 2);
  752. UnicodeString.Buffer = *lplpBuffer;
  753. //
  754. // Do the string conversion in the second half of the buffer
  755. // Assumes wTotLen is first filed in VERHEAD
  756. //
  757. // cb = offset in buffer to beginning of string
  758. cb = (DWORD)((PBYTE)*lplpBuffer - (PBYTE)pb);
  759. // cb2 = offset in translation area for this string
  760. if (fV2) {
  761. cb2 = cb + sizeof(VER2_SIG);
  762. } else {
  763. cb2 = cb / 2;
  764. }
  765. AnsiString.Buffer = (PBYTE)pb + DWORDUP(*((WORD*)pb)) + cb2;
  766. AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(&UnicodeString);
  767. if ( AnsiString.MaximumLength > MAXUSHORT ) {
  768. LastError = ERROR_INVALID_DATA;
  769. goto Fail;
  770. }
  771. AnsiString.MaximumLength = (USHORT)(__min((DWORD)AnsiString.MaximumLength,
  772. (DWORD)(cbAnsiTranslateBuffer-cb2)));
  773. RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
  774. *lplpBuffer = AnsiString.Buffer;
  775. *puLen = AnsiString.Length + 1;
  776. }
  777. if (lplpKey) {
  778. DWORD cb, cb2;
  779. //
  780. // Thunk the key
  781. //
  782. dwHeadLen = wcslen(*lplpKey);
  783. UnicodeString.Length = UnicodeString.MaximumLength = (SHORT)(dwHeadLen * sizeof(WCHAR));
  784. UnicodeString.Buffer = *lplpKey;
  785. // cb2 = offset in translation area for this string
  786. cb = (DWORD)((PBYTE)*lplpKey - (PBYTE)pb);
  787. if (fV2) {
  788. cb2 = cb + sizeof(VER2_SIG);
  789. } else {
  790. cb2 = cb / 2;
  791. }
  792. AnsiString.Buffer = (PBYTE)pb + DWORDUP(*((WORD*)pb)) + cb2;
  793. AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(&UnicodeString);
  794. if ( AnsiString.MaximumLength > MAXUSHORT ) {
  795. LastError = ERROR_INVALID_DATA;
  796. goto Fail;
  797. }
  798. AnsiString.MaximumLength = (USHORT)(__min((DWORD)AnsiString.MaximumLength,
  799. (DWORD)(cbAnsiTranslateBuffer-cb2)));
  800. RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
  801. *lplpKey = AnsiString.Buffer;
  802. *puLen = AnsiString.Length+1;
  803. }
  804. }
  805. SetLastError(LastError);
  806. return (TRUE);
  807. NotFound:
  808. /* Restore the char we NULLed above
  809. */
  810. *lpEndBlock = cEndBlock;
  811. LastError = ERROR_RESOURCE_TYPE_NOT_FOUND;
  812. Fail:
  813. if (!bUnicodeNeeded) {
  814. RtlFreeUnicodeString(&UnicodeString);
  815. } else {
  816. LocalFree(lpSubBlockOrg);
  817. }
  818. SetLastError(LastError);
  819. return (FALSE);
  820. }
  821. //////////////////////////////////////////////////////////
  822. //
  823. // This is an EXACT replica of CharNextA api found in user
  824. // it is here so that we won't have to link to user32
  825. LPSTR WINAPI VerCharNextA(
  826. LPCSTR lpCurrentChar)
  827. {
  828. if ((!!NLS_MB_CODE_PAGE_TAG) && IsDBCSLeadByte(*lpCurrentChar)) {
  829. lpCurrentChar++;
  830. }
  831. /*
  832. * if we have only DBCS LeadingByte, we will point to string-terminator
  833. */
  834. if (*lpCurrentChar) {
  835. lpCurrentChar++;
  836. }
  837. return (LPSTR)lpCurrentChar;
  838. }