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.

797 lines
22 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1990 - 1998
  3. Module Name:
  4. prop.c
  5. Abstract:
  6. This is the NT SCSI port driver. This module contains code relating to
  7. property queries
  8. Authors:
  9. Peter Wieland
  10. Environment:
  11. kernel mode only
  12. Notes:
  13. Revision History:
  14. --*/
  15. #include "port.h"
  16. NTSTATUS
  17. SpBuildDeviceDescriptor(
  18. IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
  19. IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor,
  20. IN OUT PULONG DescriptorLength
  21. );
  22. NTSTATUS
  23. SpBuildDeviceIdDescriptor(
  24. IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
  25. IN PSTORAGE_DEVICE_ID_DESCRIPTOR Descriptor,
  26. IN OUT PULONG DescriptorLength
  27. );
  28. NTSTATUS
  29. SpBuildAdapterDescriptor(
  30. IN PADAPTER_EXTENSION Adapter,
  31. IN PSTORAGE_ADAPTER_DESCRIPTOR Descriptor,
  32. IN OUT PULONG DescriptorLength
  33. );
  34. #ifdef ALLOC_PRAGMA
  35. #pragma alloc_text(PAGE, SpBuildDeviceDescriptor)
  36. #pragma alloc_text(PAGE, SpBuildDeviceIdDescriptor)
  37. #pragma alloc_text(PAGE, SpBuildAdapterDescriptor)
  38. #pragma alloc_text(PAGE, ScsiPortQueryProperty)
  39. #pragma alloc_text(PAGE, ScsiPortQueryPropertyPdo)
  40. #pragma alloc_text(PAGE, SpQueryDeviceText)
  41. #endif
  42. NTSTATUS
  43. ScsiPortQueryPropertyPdo(
  44. IN PDEVICE_OBJECT DeviceObject,
  45. IN PIRP QueryIrp
  46. )
  47. /*++
  48. Routine Description:
  49. This routine will handle a property query request. It will build the
  50. descriptor on it's own if possible, or it may forward the request down
  51. to lower level drivers.
  52. Since this routine may forward the request downwards the caller should
  53. not complete the irp
  54. This routine is asynchronous.
  55. This routine must be called at <= IRQL_DISPATCH
  56. This routine must be called with the remove lock held
  57. Arguments:
  58. DeviceObject - a pointer to the device object being queried
  59. QueryIrp - a pointer to the irp for the query
  60. Return Value:
  61. STATUS_PENDING if the request cannot be completed yet
  62. STATUS_SUCCESS if the query was successful
  63. STATUS_INVALID_PARAMETER_1 if the property id does not exist
  64. STATUS_INVALID_PARAMETER_2 if the query type is invalid
  65. STATUS_INVALID_PARAMETER_3 if an invalid optional parameter was passed
  66. STATUS_INVALID_DEVICE_REQUEST if this request cannot be handled by this
  67. device
  68. other error values as applicable
  69. --*/
  70. {
  71. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(QueryIrp);
  72. PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension;
  73. PSTORAGE_PROPERTY_QUERY query = QueryIrp->AssociatedIrp.SystemBuffer;
  74. ULONG queryLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
  75. BOOLEAN callDown = FALSE;
  76. NTSTATUS status;
  77. PAGED_CODE();
  78. //
  79. // Make sure the caller has zeroed the Information field and that this is
  80. // a request to a target device.
  81. //
  82. ASSERT(QueryIrp->IoStatus.Information == 0);
  83. ASSERT(commonExtension->IsPdo);
  84. switch (query->PropertyId) {
  85. case StorageDeviceProperty: {
  86. if (query->QueryType == PropertyExistsQuery) {
  87. status = STATUS_SUCCESS;
  88. } else if (query->QueryType == PropertyStandardQuery) {
  89. status = SpBuildDeviceDescriptor(
  90. (PLOGICAL_UNIT_EXTENSION) commonExtension,
  91. QueryIrp->AssociatedIrp.SystemBuffer,
  92. &queryLength);
  93. QueryIrp->IoStatus.Information = queryLength;
  94. } else {
  95. status = STATUS_INVALID_PARAMETER_1;
  96. }
  97. break;
  98. }
  99. case StorageAdapterProperty: {
  100. //
  101. // Forward it down to the underlying device object. This lets
  102. // filters do their magic.
  103. //
  104. callDown = TRUE;
  105. break;
  106. }
  107. case StorageDeviceIdProperty: {
  108. PLOGICAL_UNIT_EXTENSION logicalUnit;
  109. logicalUnit = DeviceObject->DeviceExtension;
  110. //
  111. // Check to see if we have a device identifier page. If not then
  112. // fail any calls for this type of identifier.
  113. //
  114. if (logicalUnit->DeviceIdentifierPage != NULL) {
  115. if (query->QueryType == PropertyExistsQuery) {
  116. status = STATUS_SUCCESS;
  117. } else if (query->QueryType == PropertyStandardQuery) {
  118. status = SpBuildDeviceIdDescriptor(
  119. logicalUnit,
  120. QueryIrp->AssociatedIrp.SystemBuffer,
  121. &queryLength);
  122. QueryIrp->IoStatus.Information = queryLength;
  123. } else {
  124. status = STATUS_INVALID_PARAMETER_1;
  125. }
  126. } else {
  127. status = STATUS_NOT_SUPPORTED;
  128. }
  129. break;
  130. }
  131. default: {
  132. //
  133. // Some filter beneath us may handle this property.
  134. //
  135. callDown = TRUE;
  136. break;
  137. }
  138. }
  139. if (callDown == TRUE) {
  140. IoSkipCurrentIrpStackLocation(QueryIrp);
  141. SpReleaseRemoveLock(DeviceObject, QueryIrp);
  142. status = IoCallDriver(commonExtension->LowerDeviceObject, QueryIrp);
  143. } else {
  144. if(status != STATUS_PENDING) {
  145. QueryIrp->IoStatus.Status = status;
  146. SpReleaseRemoveLock(DeviceObject, QueryIrp);
  147. SpCompleteRequest(DeviceObject, QueryIrp, NULL, IO_DISK_INCREMENT);
  148. }
  149. }
  150. return status;
  151. }
  152. NTSTATUS
  153. ScsiPortQueryProperty(
  154. IN PDEVICE_OBJECT DeviceObject,
  155. IN PIRP QueryIrp
  156. )
  157. /*++
  158. Routine Description:
  159. This routine will handle a property query request. It will build the
  160. descriptor on it's own if possible.
  161. This routine is synchronous.
  162. This routine must be called at <= IRQL_DISPATCH
  163. This routine must be called with the remove lock held
  164. Arguments:
  165. DeviceObject - a pointer to the device object being queried
  166. QueryIrp - a pointer to the irp for the query
  167. Return Value:
  168. STATUS_SUCCESS if the query was successful
  169. STATUS_INVALID_PARAMETER_1 if the property id does not exist
  170. STATUS_INVALID_PARAMETER_2 if the query type is invalid
  171. STATUS_INVALID_PARAMETER_3 if an invalid optional parameter was passed
  172. STATUS_INVALID_DEVICE_REQUEST if this request cannot be handled by this
  173. device
  174. other error values as applicable
  175. --*/
  176. {
  177. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(QueryIrp);
  178. PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension;
  179. PSTORAGE_PROPERTY_QUERY query = QueryIrp->AssociatedIrp.SystemBuffer;
  180. ULONG queryLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
  181. NTSTATUS status;
  182. PAGED_CODE();
  183. //
  184. // Make sure the caller has zeroed the Information field.
  185. //
  186. ASSERT(!commonExtension->IsPdo);
  187. ASSERT(QueryIrp->IoStatus.Information == 0);
  188. //
  189. // Validate the QueryType. We don't actually support mask queries.
  190. //
  191. if (query->QueryType >= PropertyMaskQuery) {
  192. status = STATUS_INVALID_PARAMETER_1;
  193. return status;
  194. }
  195. switch (query->PropertyId) {
  196. case StorageDeviceProperty: {
  197. status = STATUS_INVALID_DEVICE_REQUEST;
  198. break;
  199. }
  200. case StorageAdapterProperty: {
  201. if (query->QueryType == PropertyExistsQuery) {
  202. status = STATUS_SUCCESS;
  203. } else {
  204. status = SpBuildAdapterDescriptor(
  205. (PADAPTER_EXTENSION) commonExtension,
  206. QueryIrp->AssociatedIrp.SystemBuffer,
  207. &queryLength);
  208. QueryIrp->IoStatus.Information = queryLength;
  209. }
  210. break;
  211. }
  212. case StorageDeviceIdProperty: {
  213. status = STATUS_INVALID_DEVICE_REQUEST;
  214. break;
  215. }
  216. default: {
  217. status = STATUS_INVALID_PARAMETER_1;
  218. break;
  219. }
  220. }
  221. return status;
  222. }
  223. NTSTATUS
  224. SpBuildDeviceDescriptor(
  225. IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
  226. IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor,
  227. IN OUT PULONG DescriptorLength
  228. )
  229. /*++
  230. Routine Description:
  231. This routine will create a device descriptor based on the information in
  232. it's device extension. It will copy as much data as possible into
  233. the Descriptor and will update the DescriptorLength to indicate the
  234. number of bytes copied
  235. Arguments:
  236. DeviceObject - a pointer to the PDO we are building a descriptor for
  237. Descriptor - a buffer to store the descriptor in
  238. DescriptorLength - the length of the buffer and the number of bytes
  239. returned
  240. QueryIrp - unused
  241. Return Value:
  242. status
  243. --*/
  244. {
  245. PSCSIPORT_DRIVER_EXTENSION driverExtension =
  246. IoGetDriverObjectExtension(LogicalUnit->DeviceObject->DriverObject,
  247. ScsiPortInitialize);
  248. LONG maxLength = *DescriptorLength;
  249. LONG bytesRemaining = maxLength;
  250. LONG realLength = sizeof(STORAGE_DEVICE_DESCRIPTOR);
  251. LONG serialNumberLength;
  252. PUCHAR currentOffset = (PUCHAR) Descriptor;
  253. LONG inquiryLength;
  254. PINQUIRYDATA inquiryData = &(LogicalUnit->InquiryData);
  255. STORAGE_DEVICE_DESCRIPTOR tmp;
  256. PAGED_CODE();
  257. ASSERT_PDO(LogicalUnit->DeviceObject);
  258. ASSERT(Descriptor != NULL);
  259. serialNumberLength = LogicalUnit->SerialNumber.Length + 1;
  260. //
  261. // Figure out what the total size of this structure is going to be.
  262. //
  263. inquiryLength = 4 + inquiryData->AdditionalLength;
  264. if(inquiryLength > INQUIRYDATABUFFERSIZE) {
  265. inquiryLength = INQUIRYDATABUFFERSIZE;
  266. }
  267. realLength += inquiryLength + 31; // 31 = length of the 3 id strings +
  268. // 3 nuls
  269. //
  270. // Add the length of the serial number.
  271. //
  272. realLength += serialNumberLength;
  273. //
  274. // Zero the buffer provided using the length provided by the caller.
  275. //
  276. RtlZeroMemory(Descriptor, *DescriptorLength);
  277. //
  278. // Build the device descriptor structure on the stack then copy as much as
  279. // can be copied over
  280. //
  281. RtlZeroMemory(&tmp, sizeof(STORAGE_DEVICE_DESCRIPTOR));
  282. tmp.Version = sizeof(STORAGE_DEVICE_DESCRIPTOR);
  283. tmp.Size = realLength;
  284. tmp.DeviceType = inquiryData->DeviceType;
  285. tmp.DeviceTypeModifier = inquiryData->DeviceTypeModifier;
  286. tmp.RemovableMedia = inquiryData->RemovableMedia;
  287. tmp.CommandQueueing = inquiryData->CommandQueue;
  288. tmp.SerialNumberOffset = 0xffffffff;
  289. tmp.BusType = driverExtension->BusType;
  290. RtlCopyMemory(currentOffset,
  291. &tmp,
  292. min(sizeof(STORAGE_DEVICE_DESCRIPTOR), bytesRemaining));
  293. bytesRemaining -= sizeof(STORAGE_DEVICE_DESCRIPTOR);
  294. if(bytesRemaining <= 0) {
  295. *DescriptorLength = maxLength;
  296. return STATUS_SUCCESS;
  297. }
  298. currentOffset = ((PUCHAR) Descriptor) + (maxLength - bytesRemaining);
  299. //
  300. // Copy over as much inquiry data as we can and update the raw byte count
  301. //
  302. RtlCopyMemory(currentOffset,
  303. inquiryData,
  304. min(inquiryLength, bytesRemaining));
  305. bytesRemaining -= inquiryLength;
  306. if(bytesRemaining <= 0) {
  307. *DescriptorLength = maxLength;
  308. Descriptor->RawPropertiesLength =
  309. maxLength - sizeof(STORAGE_DEVICE_DESCRIPTOR);
  310. return STATUS_SUCCESS;
  311. }
  312. Descriptor->RawPropertiesLength = inquiryLength;
  313. currentOffset = ((PUCHAR) Descriptor) + (maxLength - bytesRemaining);
  314. //
  315. // Now we need to start copying inquiry strings
  316. //
  317. //
  318. // first the vendor id
  319. //
  320. RtlCopyMemory(currentOffset,
  321. inquiryData->VendorId,
  322. min(bytesRemaining, sizeof(UCHAR) * 8));
  323. bytesRemaining -= sizeof(UCHAR) * 9; // include trailing null
  324. if(bytesRemaining >= 0) {
  325. Descriptor->VendorIdOffset = (ULONG)((ULONG_PTR) currentOffset -
  326. (ULONG_PTR) Descriptor);
  327. }
  328. if(bytesRemaining <= 0) {
  329. *DescriptorLength = maxLength;
  330. return STATUS_SUCCESS;
  331. }
  332. currentOffset = ((PUCHAR) Descriptor) + (maxLength - bytesRemaining);
  333. //
  334. // now the product id
  335. //
  336. RtlCopyMemory(currentOffset,
  337. inquiryData->ProductId,
  338. min(bytesRemaining, 16));
  339. bytesRemaining -= 17; // include trailing null
  340. if(bytesRemaining >= 0) {
  341. Descriptor->ProductIdOffset = (ULONG)((ULONG_PTR) currentOffset -
  342. (ULONG_PTR) Descriptor);
  343. }
  344. if(bytesRemaining <= 0) {
  345. *DescriptorLength = maxLength;
  346. return STATUS_SUCCESS;
  347. }
  348. currentOffset = ((PUCHAR) Descriptor) + (maxLength - bytesRemaining);
  349. //
  350. // And the product revision
  351. //
  352. RtlCopyMemory(currentOffset,
  353. inquiryData->ProductRevisionLevel,
  354. min(bytesRemaining, 4));
  355. bytesRemaining -= 5;
  356. if(bytesRemaining >= 0) {
  357. Descriptor->ProductRevisionOffset = (ULONG)((ULONG_PTR) currentOffset -
  358. (ULONG_PTR) Descriptor);
  359. }
  360. if(bytesRemaining <= 0) {
  361. *DescriptorLength = maxLength;
  362. return STATUS_SUCCESS;
  363. }
  364. currentOffset = ((PUCHAR) Descriptor) + (maxLength - bytesRemaining);
  365. //
  366. // If the device provides a SCSI serial number (vital product data page 80)
  367. // the report it.
  368. //
  369. if(LogicalUnit->SerialNumber.Length != 0) {
  370. //
  371. // And the product revision
  372. //
  373. RtlCopyMemory(currentOffset,
  374. LogicalUnit->SerialNumber.Buffer,
  375. min(bytesRemaining, serialNumberLength));
  376. bytesRemaining -= serialNumberLength;
  377. if(bytesRemaining >= 0) {
  378. Descriptor->SerialNumberOffset = (ULONG)((ULONG_PTR) currentOffset -
  379. (ULONG_PTR) Descriptor);
  380. }
  381. if(bytesRemaining <= 0) {
  382. *DescriptorLength = maxLength;
  383. return STATUS_SUCCESS;
  384. }
  385. }
  386. *DescriptorLength = maxLength - bytesRemaining;
  387. return STATUS_SUCCESS;
  388. }
  389. NTSTATUS
  390. SpBuildAdapterDescriptor(
  391. IN PADAPTER_EXTENSION Adapter,
  392. IN PSTORAGE_ADAPTER_DESCRIPTOR Descriptor,
  393. IN OUT PULONG DescriptorLength
  394. )
  395. {
  396. STORAGE_ADAPTER_DESCRIPTOR tmp;
  397. PIO_SCSI_CAPABILITIES capabilities = &(Adapter->Capabilities);
  398. PSCSIPORT_DRIVER_EXTENSION driverExtension;
  399. PAGED_CODE();
  400. ASSERT_FDO(Adapter->DeviceObject);
  401. driverExtension = IoGetDriverObjectExtension(
  402. Adapter->DeviceObject->DriverObject,
  403. ScsiPortInitialize);
  404. ASSERT(driverExtension != NULL);
  405. tmp.Version = sizeof(STORAGE_ADAPTER_DESCRIPTOR);
  406. tmp.Size = sizeof(STORAGE_ADAPTER_DESCRIPTOR);
  407. tmp.MaximumTransferLength = capabilities->MaximumTransferLength;
  408. tmp.MaximumPhysicalPages = capabilities->MaximumPhysicalPages;
  409. tmp.AlignmentMask = capabilities->AlignmentMask;
  410. tmp.AdapterUsesPio = capabilities->AdapterUsesPio;
  411. tmp.AdapterScansDown = capabilities->AdapterScansDown;
  412. tmp.CommandQueueing = capabilities->TaggedQueuing;
  413. tmp.AcceleratedTransfer = TRUE;
  414. tmp.BusType = (UCHAR) driverExtension->BusType;
  415. tmp.BusMajorVersion = 2;
  416. tmp.BusMinorVersion = 0;
  417. RtlCopyMemory(Descriptor,
  418. &tmp,
  419. min(*DescriptorLength, sizeof(STORAGE_ADAPTER_DESCRIPTOR)));
  420. *DescriptorLength = min(*DescriptorLength,
  421. sizeof(STORAGE_ADAPTER_DESCRIPTOR));
  422. return STATUS_SUCCESS;
  423. }
  424. NTSTATUS
  425. SpQueryDeviceText(
  426. IN PDEVICE_OBJECT LogicalUnit,
  427. IN DEVICE_TEXT_TYPE TextType,
  428. IN LCID LocaleId,
  429. IN OUT PWSTR *DeviceText
  430. )
  431. {
  432. PLOGICAL_UNIT_EXTENSION luExtension = LogicalUnit->DeviceExtension;
  433. UCHAR ansiBuffer[256];
  434. ANSI_STRING ansiText;
  435. UNICODE_STRING unicodeText;
  436. NTSTATUS status;
  437. PAGED_CODE();
  438. RtlInitUnicodeString(&unicodeText, NULL);
  439. if(TextType == DeviceTextDescription) {
  440. PSCSIPORT_DEVICE_TYPE deviceInfo =
  441. SpGetDeviceTypeInfo(luExtension->InquiryData.DeviceType);
  442. PUCHAR c;
  443. LONG i;
  444. RtlZeroMemory(ansiBuffer, sizeof(ansiBuffer));
  445. RtlCopyMemory(ansiBuffer,
  446. luExtension->InquiryData.VendorId,
  447. sizeof(luExtension->InquiryData.VendorId));
  448. c = ansiBuffer;
  449. for(i = sizeof(luExtension->InquiryData.VendorId); i >= 0; i--) {
  450. if((c[i] != '\0') &&
  451. (c[i] != ' ')) {
  452. break;
  453. }
  454. c[i] = '\0';
  455. }
  456. c = &(c[i + 1]);
  457. sprintf(c, " ");
  458. c++;
  459. RtlCopyMemory(c,
  460. luExtension->InquiryData.ProductId,
  461. sizeof(luExtension->InquiryData.ProductId));
  462. for(i = sizeof(luExtension->InquiryData.ProductId); i >= 0; i--) {
  463. if((c[i] != '\0') &&
  464. (c[i] != ' ')) {
  465. break;
  466. }
  467. c[i] = '\0';
  468. }
  469. c = &(c[i + 1]);
  470. sprintf(c, " SCSI %s Device", deviceInfo->DeviceTypeString);
  471. } else if (TextType == DeviceTextLocationInformation) {
  472. sprintf(ansiBuffer, "Bus Number %d, Target ID %d, LUN %d",
  473. luExtension->PathId,
  474. luExtension->TargetId,
  475. luExtension->Lun);
  476. } else {
  477. return STATUS_NOT_SUPPORTED;
  478. }
  479. RtlInitAnsiString(&ansiText, ansiBuffer);
  480. status = RtlAnsiStringToUnicodeString(&unicodeText,
  481. &ansiText,
  482. TRUE);
  483. *DeviceText = unicodeText.Buffer;
  484. return status;
  485. }
  486. NTSTATUS
  487. SpBuildDeviceIdDescriptor(
  488. IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
  489. IN PSTORAGE_DEVICE_ID_DESCRIPTOR Descriptor,
  490. IN OUT PULONG DescriptorLength
  491. )
  492. /*++
  493. Routine Description:
  494. This routine will create a device id descriptor based on the device
  495. identifier page retrieved during discovery. It is an error to call this
  496. routine if no device identifier page exists.
  497. This routine will copy as much data as possible into the Descriptor and
  498. will update the DescriptorLength to indicate the number of bytes copied.
  499. Arguments:
  500. DeviceObject - a pointer to the PDO we are building a descriptor for
  501. Descriptor - a buffer to store the descriptor in
  502. DescriptorLength - the length of the buffer and the number of bytes
  503. returned
  504. QueryIrp - unused
  505. Return Value:
  506. status
  507. --*/
  508. {
  509. PVPD_IDENTIFICATION_PAGE idPage = LogicalUnit->DeviceIdentifierPage;
  510. ULONG idOffset;
  511. ULONG maxLength = *DescriptorLength;
  512. PUCHAR destOffset;
  513. LONG identifierLength;
  514. ULONG identifierCount = 0;
  515. PAGED_CODE();
  516. ASSERT_PDO(LogicalUnit->DeviceObject);
  517. ASSERT(Descriptor != NULL);
  518. ASSERT(LogicalUnit->DeviceIdentifierPage != NULL);
  519. if(maxLength < sizeof(STORAGE_DESCRIPTOR_HEADER)) {
  520. return STATUS_INVALID_PARAMETER;
  521. }
  522. //
  523. // Initialize the header of the descriptor.
  524. //
  525. RtlZeroMemory(Descriptor, *DescriptorLength);
  526. Descriptor->Version = sizeof(STORAGE_DEVICE_ID_DESCRIPTOR);
  527. Descriptor->Size = FIELD_OFFSET(STORAGE_DEVICE_ID_DESCRIPTOR, Identifiers);
  528. //
  529. // Prepare to copy the identifiers directly into the buffer.
  530. //
  531. destOffset = Descriptor->Identifiers;
  532. //
  533. // Walk through the id page. Count the number of descriptors and
  534. // calculate the size of the descriptor page.
  535. //
  536. for(idOffset = 0; idOffset < idPage->PageLength;) {
  537. PVPD_IDENTIFICATION_DESCRIPTOR src;
  538. USHORT identifierSize;
  539. src = (PVPD_IDENTIFICATION_DESCRIPTOR) &(idPage->Descriptors[idOffset]);
  540. identifierSize = FIELD_OFFSET(STORAGE_IDENTIFIER, Identifier);
  541. identifierSize += src->IdentifierLength;
  542. //
  543. // Align the identifier size to 32-bits.
  544. //
  545. identifierSize += sizeof(ULONG);
  546. identifierSize &= ~(sizeof(ULONG) - 1);
  547. identifierCount += 1;
  548. Descriptor->Size += identifierSize;
  549. if(Descriptor->Size <= maxLength) {
  550. PSTORAGE_IDENTIFIER dest;
  551. dest = (PSTORAGE_IDENTIFIER) destOffset;
  552. dest->CodeSet = src->CodeSet;
  553. dest->Type = src->IdentifierType;
  554. dest->Association = src->Association;
  555. dest->IdentifierSize = src->IdentifierLength;
  556. dest->NextOffset = identifierSize;
  557. RtlCopyMemory(dest->Identifier,
  558. src->Identifier,
  559. src->IdentifierLength);
  560. destOffset += dest->NextOffset;
  561. }
  562. idOffset += sizeof(PVPD_IDENTIFICATION_DESCRIPTOR);
  563. idOffset += src->IdentifierLength;
  564. }
  565. if(*DescriptorLength >= FIELD_OFFSET(STORAGE_DEVICE_ID_DESCRIPTOR,
  566. Identifiers)) {
  567. Descriptor->NumberOfIdentifiers = identifierCount;
  568. }
  569. *DescriptorLength = min(Descriptor->Size, *DescriptorLength);
  570. return STATUS_SUCCESS;
  571. }