Source code of Windows XP (NT5)
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.

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