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.

746 lines
22 KiB

  1. /*++
  2. Copyright (c) 1991-92 Microsoft Corporation
  3. Module Name:
  4. packstr.c
  5. Abstract:
  6. Contains utilities for allocating and/or packing buffers that contain
  7. a fixed section and a variable (string) section. The following
  8. functions are available:
  9. NetpPackString
  10. NetpCopyStringToBuffer
  11. NetpCopyDataToBuffer
  12. NetpAllocateEnumBuffer
  13. Author:
  14. various
  15. Environment:
  16. User Mode -Win32
  17. Revision History:
  18. 30-Apr-1991 danl
  19. NetpAllocateEnumBuffer: Removed use of NetApiBufferFree. It has
  20. been changed to use MIDL_user_allocate and MIDL_user_free.
  21. Also added NT-style headers where needed.
  22. 16-Apr-1991 JohnRo
  23. Clarify UNICODE handling of pack and copy routines. Got rid of tabs
  24. in source file.
  25. 21-Nov-1991 JohnRo
  26. Removed NT dependencies to reduce recompiles.
  27. 15-Apr-1992 JohnRo
  28. FORMAT_POINTER is obsolete.
  29. --*/
  30. // These must be included first:
  31. #include <windef.h> // IN, OUT, etc.
  32. #include <lmcons.h> // Needed by <netlib.h>.
  33. #include <lmerr.h> // NERR_*
  34. #include <rpcutil.h> // MIDL_user_allocate & MIDL_user_free
  35. // These may be included in any order:
  36. #include <align.h> // ROUND_UP_COUNT().
  37. #include <debuglib.h> // IF_DEBUG().
  38. #include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
  39. #include <netlib.h> // My prototype.
  40. #include <tstring.h> // STRCPY(), STRLEN(), STRNCPY().
  41. DWORD
  42. NetpPackString(
  43. IN OUT LPTSTR * string, // pointer by reference: string to be copied.
  44. IN LPBYTE dataend, // pointer to end of fixed size data.
  45. IN OUT LPTSTR * laststring // pointer by reference: top of string data.
  46. )
  47. /*++
  48. Routine Description:
  49. NetPackString is used to stuff variable-length data, which
  50. is pointed to by (surpise!) a pointer. The data is assumed
  51. to be a nul-terminated string (ASCIIZ). Repeated calls to
  52. this function are used to pack data from an entire structure.
  53. Upon first call, the laststring pointer should point to just
  54. past the end of the buffer. Data will be copied into the buffer from
  55. the end, working towards the beginning. If a data item cannot
  56. fit, the pointer will be set to NULL, else the pointer will be
  57. set to the new data location.
  58. Pointers which are passed in as NULL will be set to be pointer
  59. to and empty string, as the NULL-pointer is reserved for
  60. data which could not fit as opposed to data not available.
  61. See the test case for sample usage. (tst/packtest.c)
  62. Arguments:
  63. string - pointer by reference: string to be copied.
  64. dataend - pointer to end of fixed size data.
  65. laststring - pointer by reference: top of string data.
  66. Return Value:
  67. 0 - if it could not fit data into the buffer. Or...
  68. sizeOfData - the size of data stuffed (guaranteed non-zero)
  69. --*/
  70. {
  71. DWORD size;
  72. IF_DEBUG(PACKSTR) {
  73. NetpKdPrint(("NetpPackString:\n"));
  74. NetpKdPrint((" string=" FORMAT_LPVOID
  75. ", *string=" FORMAT_LPVOID
  76. ", **string='" FORMAT_LPSTR "'\n",
  77. (LPVOID) string, (LPVOID) *string, *string));
  78. NetpKdPrint((" end=" FORMAT_LPVOID "\n", (LPVOID) dataend));
  79. NetpKdPrint((" last=" FORMAT_LPVOID
  80. ", *last=" FORMAT_LPVOID
  81. ", **last='" FORMAT_LPSTR "'\n",
  82. (LPVOID) laststring, (LPVOID) *laststring, *laststring));
  83. }
  84. //
  85. // convert NULL ptr to pointer to NULL string
  86. //
  87. if (*string == NULL) {
  88. // BUG 20.1160 - replaced (dataend +1) with dataend
  89. // to allow for a NULL ptr to be packed
  90. // (as a NULL string) with one byte left in the
  91. // buffer. - ERICPE
  92. //
  93. if ( *laststring > (LPTSTR)dataend ) {
  94. *(--(*laststring)) = 0;
  95. *string = *laststring;
  96. return 1;
  97. } else {
  98. return 0;
  99. }
  100. }
  101. //
  102. // is there room for the string?
  103. //
  104. size = STRLEN(*string) + 1;
  105. if ( ((DWORD)(*laststring - (LPTSTR)dataend)) < size) {
  106. *string = NULL;
  107. return(0);
  108. } else {
  109. *laststring -= size;
  110. STRCPY(*laststring, *string);
  111. *string = *laststring;
  112. return(size);
  113. }
  114. } // NetpPackString
  115. BOOL
  116. NetpCopyStringToBuffer (
  117. IN LPTSTR String OPTIONAL,
  118. IN DWORD CharacterCount,
  119. IN LPBYTE FixedDataEnd,
  120. IN OUT LPTSTR *EndOfVariableData,
  121. OUT LPTSTR *VariableDataPointer
  122. )
  123. /*++
  124. Routine Description:
  125. This routine puts a single variable-length string into an output buffer.
  126. The string is not written if it would overwrite the last fixed structure
  127. in the buffer.
  128. The code is swiped from svcsupp.c written by DavidTr.
  129. Sample usage:
  130. LPBYTE FixedDataEnd = OutputBuffer + sizeof(WKSTA_INFO_202);
  131. LPTSTR EndOfVariableData = OutputBuffer + OutputBufferSize;
  132. //
  133. // Copy user name
  134. //
  135. NetpCopyStringToBuffer(
  136. UserInfo->UserName.Buffer;
  137. UserInfo->UserName.Length;
  138. FixedDataEnd,
  139. &EndOfVariableData,
  140. &WkstaInfo->wki202_username
  141. );
  142. Arguments:
  143. String - Supplies a pointer to the source string to copy into the
  144. output buffer. If String is null then a pointer to a zero terminator
  145. is inserted into output buffer.
  146. CharacterCount - Supplies the length of String, not including zero
  147. terminator.
  148. FixedDataEnd - Supplies a pointer to just after the end of the last
  149. fixed structure in the buffer.
  150. EndOfVariableData - Supplies an address to a pointer to just after the
  151. last position in the output buffer that variable data can occupy.
  152. Returns a pointer to the string written in the output buffer.
  153. VariableDataPointer - Supplies a pointer to the place in the fixed
  154. portion of the output buffer where a pointer to the variable data
  155. should be written.
  156. Return Value:
  157. Returns TRUE if string fits into output buffer, FALSE otherwise.
  158. --*/
  159. {
  160. DWORD BytesNeeded = (CharacterCount + 1) * sizeof(TCHAR);
  161. IF_DEBUG(PACKSTR) {
  162. NetpKdPrint(("NetpStringToBuffer: String at " FORMAT_LPVOID
  163. ", CharacterCount=" FORMAT_DWORD
  164. ",\n FixedDataEnd at " FORMAT_LPVOID
  165. ", *EndOfVariableData at " FORMAT_LPVOID
  166. ",\n VariableDataPointer at " FORMAT_LPVOID
  167. ", BytesNeeded=" FORMAT_DWORD ".\n",
  168. (LPVOID) String, CharacterCount, FixedDataEnd,
  169. (LPVOID) *EndOfVariableData,
  170. (LPVOID) VariableDataPointer, BytesNeeded));
  171. }
  172. //
  173. // Determine if string will fit, allowing for a zero terminator. If no,
  174. // just set the pointer to NULL.
  175. //
  176. if ((*EndOfVariableData - (CharacterCount+1)) >= (LPTSTR)FixedDataEnd) {
  177. //
  178. // It fits. Move EndOfVariableData pointer up to the location where
  179. // we will write the string.
  180. //
  181. *EndOfVariableData -= (CharacterCount+1);
  182. //
  183. // Copy the string to the buffer if it is not null.
  184. //
  185. if (CharacterCount > 0 && String != NULL) {
  186. STRNCPY(*EndOfVariableData, String, CharacterCount);
  187. }
  188. //
  189. // Set the zero terminator.
  190. //
  191. *(*EndOfVariableData + CharacterCount) = TCHAR_EOS;
  192. //
  193. // Set up the pointer in the fixed data portion to point to where the
  194. // string is written.
  195. //
  196. *VariableDataPointer = *EndOfVariableData;
  197. return TRUE;
  198. }
  199. else {
  200. //
  201. // It doesn't fit. Set the offset to NULL.
  202. //
  203. *VariableDataPointer = NULL;
  204. return FALSE;
  205. }
  206. } // NetpCopyStringToBuffer
  207. BOOL
  208. NetpCopyDataToBuffer (
  209. IN LPBYTE Data,
  210. IN DWORD ByteCount,
  211. IN LPBYTE FixedDataEnd,
  212. IN OUT LPBYTE *EndOfVariableData,
  213. OUT LPBYTE *VariableDataPointer,
  214. IN DWORD Alignment
  215. )
  216. /*++
  217. Routine Description:
  218. This routine puts the specified data into an output buffer.
  219. The data is not written if it would overwrite the last fixed structure
  220. in the buffer.
  221. The output buffer is aligned as requested.
  222. Sample usage:
  223. LPBYTE FixedDataEnd = OutputBuffer + sizeof(WKSTA_INFO_202);
  224. LPBYTE EndOfVariableData = OutputBuffer + OutputBufferSize;
  225. //
  226. // Copy Logon hours
  227. //
  228. NetpCopyDataToBuffer(
  229. StructurePointer,
  230. sizeof( STRUCTURE_TYPE),
  231. FixedDataEnd,
  232. &EndOfVariableData,
  233. &UserInfo->usri2->LogonHours,
  234. sizeof(ULONG)
  235. );
  236. Arguments:
  237. Data - Supplies a pointer to the source data to copy into the
  238. output buffer. If Data is null then a null pointer is returned in
  239. VariableDataPointer.
  240. ByteCount - Supplies the length of Data.
  241. FixedDataEnd - Supplies a pointer to just after the end of the last
  242. fixed structure in the buffer.
  243. EndOfVariableData - Supplies an address to a pointer to just after the
  244. last position in the output buffer that variable data can occupy.
  245. Returns a pointer to the data written in the output buffer.
  246. VariableDataPointer - Supplies a pointer to the place in the fixed
  247. portion of the output buffer where a pointer to the variable data
  248. should be written.
  249. Alignment - Supplies the required alignment of the data expressed
  250. as the number of bytes in the primitive datatype (e.g., 1 for byte,
  251. 2 for short, 4 for long, and 8 for quad).
  252. Return Value:
  253. Returns TRUE if data fits into output buffer, FALSE otherwise.
  254. --*/
  255. {
  256. LPBYTE NewEndOfVariableData;
  257. //
  258. // If there is no data to copy, just return success.
  259. //
  260. if ( Data == NULL ) {
  261. *VariableDataPointer = NULL;
  262. return TRUE;
  263. }
  264. //
  265. // Compute where the data will be copied to (taking alignment into
  266. // consideration).
  267. //
  268. // We may end up with a few unused byte after the copied data.
  269. //
  270. NetpAssert((Alignment == 1) || (Alignment == 2) ||
  271. (Alignment == 4) || (Alignment == 8));
  272. NewEndOfVariableData = (LPBYTE)
  273. (((DWORD_PTR)(*EndOfVariableData - ByteCount)) & ~((LONG)Alignment - 1));
  274. //
  275. // If the data doesn't fit into the buffer, error out
  276. //
  277. if ( NewEndOfVariableData < FixedDataEnd) {
  278. *VariableDataPointer = NULL;
  279. return FALSE;
  280. }
  281. //
  282. // Copy the data to the buffer
  283. //
  284. if (ByteCount > 0) {
  285. NetpMoveMemory(NewEndOfVariableData, Data, ByteCount);
  286. }
  287. //
  288. // Return the pointer to the new data and update the pointer to
  289. // how much of the buffer we've used so far.
  290. //
  291. *VariableDataPointer = NewEndOfVariableData;
  292. *EndOfVariableData = NewEndOfVariableData;
  293. return TRUE;
  294. } // NetpCopyDataToBuffer
  295. NET_API_STATUS
  296. NetpAllocateEnumBuffer(
  297. IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
  298. IN BOOL IsGet,
  299. IN DWORD PrefMaxSize,
  300. IN DWORD NeededSize,
  301. IN VOID (*RelocationRoutine)( IN DWORD RelocationParameter,
  302. IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
  303. IN PTRDIFF_T Offset ),
  304. IN DWORD RelocationParameter
  305. )
  306. /*++
  307. Routine Description:
  308. Ensures a buffer is allocated which contains the needed size.
  309. Arguments:
  310. BufferDescriptor - Points to a structure which describes the allocated
  311. buffer. On the first call, pass in BufferDescriptor->Buffer set
  312. to NULL. On subsequent calls (in the 'enum' case), pass in the
  313. structure just as it was passed back on a previous call.
  314. The caller must deallocate the BufferDescriptor->Buffer using
  315. MIDL_user_free if it is non-null.
  316. IsGet - TRUE iff this is a 'get' rather than an 'enum' operation.
  317. PrefMaxSize - Callers prefered maximum size
  318. NeededSize - the number of bytes which must be available in the allocated
  319. buffer.
  320. RelocationRoutine - Supplies a pointer to a routine that will be called
  321. when the buffer needs to be relocated. The routine is called with
  322. both the fixed portion and the strings already copied. Merely,
  323. the pointers to the strings need to be adjusted.
  324. The 'Offset' parameter to the relocation routine merely needs to
  325. be added to the each pointer within the allocated buffer which points
  326. to a place within the allocated buffer. It is a byte-offset. This
  327. design depends on a 'flat' address space where the addresses of two
  328. unrelated pointers can simply be subtracted.
  329. RelocationParameter - Supplies a parameter which will (in turn) be passed
  330. to the relocation routine.
  331. Return Value:
  332. Error code for the operation.
  333. If this is an Enum call, the status can be ERROR_MORE_DATA implying that
  334. the Buffer has grown to PrefMaxSize and that this much data should
  335. be returned to the caller.
  336. --*/
  337. {
  338. return NetpAllocateEnumBufferEx(
  339. BufferDescriptor,
  340. IsGet,
  341. PrefMaxSize,
  342. NeededSize,
  343. RelocationRoutine,
  344. RelocationParameter,
  345. NETP_ENUM_GUESS );
  346. }
  347. NET_API_STATUS
  348. NetpAllocateEnumBufferEx(
  349. IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
  350. IN BOOL IsGet,
  351. IN DWORD PrefMaxSize,
  352. IN DWORD NeededSize,
  353. IN VOID (*RelocationRoutine)( IN DWORD RelocationParameter,
  354. IN OUT PBUFFER_DESCRIPTOR BufferDescriptor,
  355. IN PTRDIFF_T Offset ),
  356. IN DWORD RelocationParameter,
  357. IN DWORD IncrementalSize
  358. )
  359. /*++
  360. Routine Description:
  361. Ensures a buffer is allocated which contains the needed size.
  362. Arguments:
  363. BufferDescriptor - Points to a structure which describes the allocated
  364. buffer. On the first call, pass in BufferDescriptor->Buffer set
  365. to NULL. On subsequent calls (in the 'enum' case), pass in the
  366. structure just as it was passed back on a previous call.
  367. The caller must deallocate the BufferDescriptor->Buffer using
  368. MIDL_user_free if it is non-null.
  369. IsGet - TRUE iff this is a 'get' rather than an 'enum' operation.
  370. PrefMaxSize - Callers prefered maximum size
  371. NeededSize - the number of bytes which must be available in the allocated
  372. buffer.
  373. RelocationRoutine - Supplies a pointer to a routine that will be called
  374. when the buffer needs to be relocated. The routine is called with
  375. both the fixed portion and the strings already copied. Merely,
  376. the pointers to the strings need to be adjusted.
  377. The 'Offset' parameter to the relocation routine merely needs to
  378. be added to the each pointer within the allocated buffer which points
  379. to a place within the allocated buffer. It is a byte-offset. This
  380. design depends on a 'flat' address space where the addresses of two
  381. unrelated pointers can simply be subtracted.
  382. RelocationParameter - Supplies a parameter which will (in turn) be passed
  383. to the relocation routine.
  384. IncrementalSize - Mimimum number of bytes to extend the buffer by when it
  385. needs extending.
  386. Return Value:
  387. Error code for the operation.
  388. If this is an Enum call, the status can be ERROR_MORE_DATA implying that
  389. the Buffer has grown to PrefMaxSize and that this much data should
  390. be returned to the caller.
  391. --*/
  392. {
  393. NET_API_STATUS NetStatus;
  394. PBUFFER_DESCRIPTOR Desc = BufferDescriptor;
  395. IF_DEBUG(PACKSTR) {
  396. NetpKdPrint((
  397. "NetpAllocateEnumBuffer: Isget: " FORMAT_DWORD " PrefMaxSize: "
  398. FORMAT_HEX_DWORD " NeededSize: " FORMAT_HEX_DWORD "\n",
  399. IsGet, PrefMaxSize, NeededSize ));
  400. NetpKdPrint((
  401. "+BufferDescriptor: Buffer: " FORMAT_HEX_DWORD " AllocSize: "
  402. FORMAT_HEX_DWORD " AllocIncr: " FORMAT_HEX_DWORD "\n",
  403. Desc->Buffer, Desc->AllocSize, Desc->AllocIncrement ));
  404. NetpKdPrint(( " variable: " FORMAT_HEX_DWORD " Fixed:"
  405. FORMAT_HEX_DWORD "\n",
  406. Desc->EndOfVariableData, Desc->FixedDataEnd ));
  407. }
  408. //
  409. // If this is not a resume, initialize a buffer descriptor.
  410. //
  411. if ( Desc->Buffer == NULL ) {
  412. //
  413. // Allocate the return buffer.
  414. //
  415. // If this is a Getinfo call, allocate the buffer the correct size.
  416. //
  417. // If the is an Enum call, this is an initial allocation which
  418. // might be reallocated later if this size isn't big enough.
  419. // The initial allocation is the user's prefered maximum
  420. // length unless that length is deemed to be very large.
  421. // In that case we allocate a good sized buffer and will
  422. // reallocate later as needed.
  423. //
  424. if ( IsGet ) {
  425. Desc->AllocSize = NeededSize;
  426. } else {
  427. if ( PrefMaxSize < NeededSize ) {
  428. NetStatus = NERR_BufTooSmall;
  429. goto Cleanup;
  430. }
  431. Desc->AllocSize = min(PrefMaxSize, IncrementalSize);
  432. Desc->AllocSize = max(NeededSize, Desc->AllocSize );
  433. }
  434. // Some callers pack data on that top end of this buffer so
  435. // ensure the buffer size allows for proper alignment.
  436. Desc->AllocSize = ROUND_UP_COUNT( Desc->AllocSize, ALIGN_WORST );
  437. Desc->AllocIncrement = Desc->AllocSize;
  438. IF_DEBUG(PACKSTR) {
  439. NetpKdPrint((" Allocated size : " FORMAT_HEX_DWORD "\n",
  440. Desc->AllocSize ));
  441. }
  442. Desc->Buffer = MIDL_user_allocate(Desc->AllocSize);
  443. if (Desc->Buffer == NULL) {
  444. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  445. goto Cleanup;
  446. }
  447. IF_DEBUG(PACKSTR) {
  448. NetpKdPrint((" Allocated: " FORMAT_HEX_DWORD "\n", Desc->Buffer ));
  449. }
  450. Desc->FixedDataEnd = Desc->Buffer;
  451. Desc->EndOfVariableData = Desc->Buffer + Desc->AllocSize;
  452. //
  453. // If this is a resume, get the buffer descriptor that the caller passed in.
  454. //
  455. } else {
  456. //
  457. // If the entry doesn't fit, reallocate a larger return buffer
  458. //
  459. if ((DWORD)(Desc->EndOfVariableData - Desc->FixedDataEnd) < NeededSize){
  460. BUFFER_DESCRIPTOR OldDesc;
  461. DWORD FixedSize; // Total size of the fixed data
  462. DWORD StringSize; // Total size of the string data
  463. //
  464. // If the buffer is as big as is allowed,
  465. // we're all done for now.
  466. //
  467. if ( Desc->AllocSize >= PrefMaxSize ) {
  468. NetStatus = ERROR_MORE_DATA;
  469. goto Cleanup;
  470. }
  471. //
  472. // Allocate a larger return buffer.
  473. //
  474. OldDesc = *Desc;
  475. Desc->AllocSize += max( NeededSize, Desc->AllocIncrement );
  476. Desc->AllocSize = min( Desc->AllocSize, PrefMaxSize );
  477. Desc->AllocSize = ROUND_UP_COUNT( Desc->AllocSize, ALIGN_WORST );
  478. IF_DEBUG(PACKSTR) {
  479. NetpKdPrint(("Re-Allocated size : " FORMAT_HEX_DWORD "\n",
  480. Desc->AllocSize ));
  481. }
  482. Desc->Buffer = MIDL_user_allocate( Desc->AllocSize );
  483. if ( Desc->Buffer == NULL ) {
  484. MIDL_user_free( OldDesc.Buffer );
  485. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  486. goto Cleanup;
  487. }
  488. IF_DEBUG(PACKSTR) {
  489. NetpKdPrint(("ReAllocated: " FORMAT_HEX_DWORD "\n",
  490. Desc->Buffer ));
  491. }
  492. //
  493. // Copy the fixed length portion of the data to the new buffer
  494. //
  495. FixedSize = (DWORD)(OldDesc.FixedDataEnd - OldDesc.Buffer);
  496. RtlCopyMemory( Desc->Buffer,
  497. OldDesc.Buffer,
  498. FixedSize );
  499. Desc->FixedDataEnd = Desc->Buffer + FixedSize ;
  500. //
  501. // Copy the string portion of the data to the new buffer
  502. //
  503. StringSize = OldDesc.AllocSize -
  504. (DWORD)(OldDesc.EndOfVariableData - OldDesc.Buffer);
  505. Desc->EndOfVariableData = Desc->Buffer + Desc->AllocSize - StringSize;
  506. RtlCopyMemory( Desc->EndOfVariableData, OldDesc.EndOfVariableData, StringSize );
  507. //
  508. // Callback to allow the pointers into the string data to be
  509. // relocated.
  510. //
  511. // The callback routine merely needs to add the value I pass it
  512. // to all of the pointers from the fixed area to the string area.
  513. //
  514. (*RelocationRoutine)(
  515. RelocationParameter,
  516. Desc,
  517. (Desc->EndOfVariableData - OldDesc.EndOfVariableData) );
  518. //
  519. // Free the old buffer.
  520. //
  521. MIDL_user_free( OldDesc.Buffer );
  522. }
  523. }
  524. NetStatus = NERR_Success;
  525. //
  526. // Clean up
  527. //
  528. Cleanup:
  529. //
  530. //
  531. //
  532. if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA &&
  533. Desc->Buffer != NULL ) {
  534. MIDL_user_free (Desc->Buffer );
  535. Desc->Buffer = NULL;
  536. }
  537. IF_DEBUG(PACKSTR) {
  538. NetpKdPrint((
  539. "BufferDescriptor: Buffer: " FORMAT_HEX_DWORD " AllocSize: "
  540. FORMAT_HEX_DWORD " AllocIncr: " FORMAT_HEX_DWORD "\n",
  541. Desc->Buffer, Desc->AllocSize, Desc->AllocIncrement ));
  542. NetpKdPrint(( " variable: " FORMAT_HEX_DWORD " Fixed:"
  543. FORMAT_HEX_DWORD "\n",
  544. Desc->EndOfVariableData, Desc->FixedDataEnd ));
  545. }
  546. return NetStatus;
  547. } // NetpAllocateEnumBuffer