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.

943 lines
26 KiB

  1. /*++
  2. Copyright (c) 1997, 1998 Microsoft Corporation
  3. Module Name:
  4. index.c
  5. Abstract:
  6. Support for SIS indices.
  7. Authors:
  8. Bill Bolosky, Summer, 1997
  9. Environment:
  10. Kernel mode
  11. Revision History:
  12. --*/
  13. #include "sip.h"
  14. BOOLEAN
  15. SipIndicesFromReparseBuffer(
  16. IN PREPARSE_DATA_BUFFER reparseBuffer,
  17. OUT PCSID CSid,
  18. OUT PLINK_INDEX LinkIndex,
  19. OUT PLARGE_INTEGER CSFileNtfsId,
  20. OUT PLARGE_INTEGER LinkFileNtfsId,
  21. OUT PLONGLONG CSFileChecksum OPTIONAL,
  22. OUT PBOOLEAN EligibleForPartialFinalCopy OPTIONAL,
  23. OUT PBOOLEAN ReparseBufferCorrupt OPTIONAL)
  24. /*++
  25. Routine Description:
  26. Take a SIS reparse buffer, check it for internal consistency and
  27. decode it to its constituient parts.
  28. Arguments:
  29. reparseBuffer - the buffer to decode
  30. CSid
  31. LinkIndex
  32. CSFileNtfsId
  33. LinkFileNtfsId
  34. CSFileChecksum - the values from the reparse point
  35. EligibleForPartialFinalCopy - can we do a partial final copy on this file (ie., is the
  36. reparse format version > 4?)
  37. ReparseBufferCorrupt - are we convinced that the buffer is corrupt (rather than just
  38. being too new a version) Meaningful only if
  39. the return value of the function is FALSE
  40. Return Value:
  41. TRUE if the buffer was decoded successfully, FALSE otherwise.
  42. --*/
  43. {
  44. PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER)reparseBuffer->GenericReparseBuffer.DataBuffer;
  45. LONGLONG Checksum = 0;
  46. //
  47. // First check to be sure that we understand this reparse point format version and
  48. // that it has the correct size.
  49. //
  50. if (reparseBuffer->ReparseDataLength < sizeof(ULONG)) {
  51. //
  52. // The reparse buffer is to small to include a version number. We guarantee that
  53. // no SIS version will ever produce such a reparse point, so it is corrupt.
  54. //
  55. if (NULL != ReparseBufferCorrupt) {
  56. *ReparseBufferCorrupt = TRUE;
  57. }
  58. return FALSE;
  59. }
  60. if (sisReparseBuffer->ReparsePointFormatVersion < 4) {
  61. //
  62. // It's too old to be supported. Treat it as corrupt.
  63. //
  64. if (NULL != ReparseBufferCorrupt) {
  65. *ReparseBufferCorrupt = TRUE;
  66. }
  67. return FALSE;
  68. }
  69. if (sisReparseBuffer->ReparsePointFormatVersion > SIS_REPARSE_BUFFER_FORMAT_VERSION) {
  70. //
  71. // This buffer is from a newer version of SIS than the filter. It is non-corrupt,
  72. // but we don't understand it.
  73. //
  74. if (NULL != ReparseBufferCorrupt) {
  75. *ReparseBufferCorrupt = FALSE;
  76. }
  77. return FALSE;
  78. }
  79. //
  80. // Now check the checksum.
  81. //
  82. SipComputeChecksum(
  83. sisReparseBuffer,
  84. sizeof(SI_REPARSE_BUFFER) - sizeof sisReparseBuffer->Checksum,
  85. &Checksum);
  86. if (Checksum != sisReparseBuffer->Checksum.QuadPart) {
  87. if (NULL != ReparseBufferCorrupt) {
  88. *ReparseBufferCorrupt = TRUE;
  89. }
  90. return FALSE;
  91. }
  92. //
  93. // Fill in the return values from the reparse point.
  94. //
  95. *CSid = sisReparseBuffer->CSid;
  96. *LinkIndex = sisReparseBuffer->LinkIndex;
  97. *LinkFileNtfsId = sisReparseBuffer->LinkFileNtfsId;
  98. *CSFileNtfsId = sisReparseBuffer->CSFileNtfsId;
  99. if (NULL != CSFileChecksum) {
  100. *CSFileChecksum = sisReparseBuffer->CSChecksum;
  101. }
  102. if (NULL != EligibleForPartialFinalCopy) {
  103. *EligibleForPartialFinalCopy = (sisReparseBuffer->ReparsePointFormatVersion > 4);
  104. }
  105. if (NULL != ReparseBufferCorrupt) {
  106. *ReparseBufferCorrupt = FALSE;
  107. }
  108. return TRUE;
  109. }
  110. BOOLEAN
  111. SipIndicesIntoReparseBuffer(
  112. OUT PREPARSE_DATA_BUFFER reparseBuffer,
  113. IN PCSID CSid,
  114. IN PLINK_INDEX LinkIndex,
  115. IN PLARGE_INTEGER CSFileNtfsId,
  116. IN PLARGE_INTEGER LinkFileNtfsId,
  117. IN PLONGLONG CSFileChecksum,
  118. IN BOOLEAN EligibleForPartialFinalCopy)
  119. /*++
  120. Routine Description:
  121. Given the information that goes into a SIS reparse buffer, construct the
  122. buffer. The caller must provide a sufficiently large buffer, and is
  123. responsible for filling in the ReparseDataLength field of the buffer
  124. with a size that corresponds to the size of the buffer (note that this is
  125. not EQUAL to the size of the buffer, because the meaning of this field
  126. is that it gives the length of the buffer beyond the mandatory header
  127. portion).
  128. Arguments:
  129. reparseBuffer - the buffer into which to write the reparse data
  130. CSid
  131. LinkIndex
  132. CSFileNtfsId
  133. LinkFileNtfsId
  134. CSFileChecksum - the values to go into the reparse point
  135. Return Value:
  136. TRUE if the buffer was encoded successfully, FALSE otherwise.
  137. --*/
  138. {
  139. PSI_REPARSE_BUFFER sisReparseBuffer = (PSI_REPARSE_BUFFER)reparseBuffer->GenericReparseBuffer.DataBuffer;
  140. //
  141. // Check that we've got enough space.
  142. //
  143. if (reparseBuffer->ReparseDataLength < sizeof(SI_REPARSE_BUFFER)) {
  144. return FALSE;
  145. }
  146. //
  147. // Fill in the NTFS part of the reparse buffer.
  148. //
  149. reparseBuffer->ReparseTag = IO_REPARSE_TAG_SIS;
  150. reparseBuffer->Reserved = 0xcaf; //???
  151. //
  152. // Fill in SIS's part of the buffer.
  153. //
  154. if (EligibleForPartialFinalCopy) {
  155. sisReparseBuffer->ReparsePointFormatVersion = SIS_REPARSE_BUFFER_FORMAT_VERSION;
  156. } else {
  157. //
  158. // When we go to version 6 of the reparse buffer, EligibleForPartialFinalCopy should be
  159. // built into the reparse point. For now, we'll just use a version 4 reparse point.
  160. //
  161. sisReparseBuffer->ReparsePointFormatVersion = 4;
  162. }
  163. sisReparseBuffer->Reserved = 0xb111b010;
  164. sisReparseBuffer->CSid = *CSid;
  165. sisReparseBuffer->LinkIndex = *LinkIndex;
  166. sisReparseBuffer->LinkFileNtfsId = *LinkFileNtfsId;
  167. sisReparseBuffer->CSFileNtfsId = *CSFileNtfsId;
  168. sisReparseBuffer->CSChecksum = *CSFileChecksum;
  169. //
  170. // Compute the checksum.
  171. //
  172. sisReparseBuffer->Checksum.QuadPart = 0;
  173. SipComputeChecksum(
  174. sisReparseBuffer,
  175. sizeof(SI_REPARSE_BUFFER) - sizeof sisReparseBuffer->Checksum,
  176. &sisReparseBuffer->Checksum.QuadPart);
  177. //
  178. // Indicate the size.
  179. //
  180. reparseBuffer->ReparseDataLength = sizeof(SI_REPARSE_BUFFER);
  181. return TRUE;
  182. }
  183. NTSTATUS
  184. SipIntegerToBase36UnicodeString(
  185. ULONG Value,
  186. PUNICODE_STRING String)
  187. /*++
  188. Routine Description:
  189. This does what RtlIntegerToUnicodeString(Value,36,String) would do if it
  190. handled base 36. We use the same rules for digits as are normally used
  191. in Hex: 0-9, followed by a-z. Note that we're intentionally using Arabic
  192. numerals and English letters here rather than something localized because
  193. this is intended to generate filenames that are never seen by users, and
  194. are constant regardless of the language used on the machine.
  195. Arguments:
  196. Value - The ULONG to be converted into a base36 string
  197. String - A pointer to a UNICODE string to receive the result
  198. Return Value:
  199. success or buffer overflow
  200. --*/
  201. {
  202. ULONG numChars;
  203. ULONG ValueCopy = Value;
  204. ULONG currentCharacter;
  205. // First, figure out the length by seeing how many times we can divide 36 into the value
  206. for (numChars = 0; ValueCopy != 0; ValueCopy /= 36, numChars++) {
  207. // No loop body
  208. }
  209. // Special case the value 0.
  210. if (numChars == 0) {
  211. ASSERT(Value == 0);
  212. if (String->MaximumLength < sizeof(WCHAR))
  213. return STATUS_BUFFER_OVERFLOW;
  214. String->Buffer[0] = '0';
  215. String->Length = sizeof(WCHAR);
  216. return STATUS_SUCCESS;
  217. }
  218. // If the string is too short, quit now.
  219. if (numChars * sizeof(WCHAR) > String->MaximumLength) {
  220. return STATUS_BUFFER_OVERFLOW;
  221. }
  222. // Convert the string character-by-character starting at the lowest order (and so rightmost) "digit"
  223. ValueCopy = Value;
  224. for (currentCharacter = 0 ; currentCharacter < numChars; currentCharacter++) {
  225. ULONG digit = ValueCopy % 36;
  226. ASSERT(ValueCopy != 0);
  227. if (digit < 10) {
  228. String->Buffer[numChars - (currentCharacter + 1)] = (WCHAR)('0' + (ValueCopy % 36));
  229. } else {
  230. String->Buffer[numChars - (currentCharacter + 1)] = (WCHAR)('a' + ((ValueCopy % 36) - 10));
  231. }
  232. ValueCopy /= 36;
  233. }
  234. ASSERT(ValueCopy == 0);
  235. // Fill in the string length, and we're done
  236. String->Length = (USHORT)(numChars * sizeof(WCHAR));
  237. return STATUS_SUCCESS;
  238. }
  239. NTSTATUS
  240. SipIndexToFileName(
  241. IN PDEVICE_EXTENSION deviceExtension,
  242. IN PCSID CSid,
  243. IN ULONG appendBytes,
  244. IN BOOLEAN mayAllocate,
  245. OUT PUNICODE_STRING fileName
  246. )
  247. /*++
  248. Routine Description:
  249. Given an index, returns the corresponding fully qualified file name.
  250. Arguments:
  251. deviceExtension - device extension
  252. CSid - The id to convert
  253. appendBytes - A number of bytes that must be left unused at the end of fileName
  254. mayAllocate - May we allocate a new string, or do we have to live with what we have?
  255. fileName - A pointer to a UNICODE string to receive the result
  256. Return Value:
  257. success or buffer overflow
  258. --*/
  259. {
  260. NTSTATUS status;
  261. USHORT stringMaxLength;
  262. UNICODE_STRING GUIDString[1];
  263. BOOLEAN allocatedBufferSpace = FALSE;
  264. //
  265. // We generate the filename as <common store path>\<guid>.sis, where <guid> is
  266. // the standard striung representation of the GUID for the common store file (ie.,
  267. // its CSid).
  268. //
  269. stringMaxLength = (USHORT)(deviceExtension->CommonStorePathname.Length +
  270. INDEX_MAX_NUMERIC_STRING_LENGTH +
  271. appendBytes);
  272. if (mayAllocate && stringMaxLength > fileName->MaximumLength) {
  273. fileName->Buffer = ExAllocatePoolWithTag(PagedPool, stringMaxLength, ' siS');
  274. if (!fileName->Buffer) {
  275. return STATUS_INSUFFICIENT_RESOURCES;
  276. }
  277. allocatedBufferSpace = TRUE;
  278. fileName->MaximumLength = stringMaxLength;
  279. } else if (fileName->MaximumLength < stringMaxLength) {
  280. return STATUS_BUFFER_OVERFLOW;
  281. }
  282. RtlCopyUnicodeString(fileName,&deviceExtension->CommonStorePathname);
  283. ASSERT(fileName->Length < fileName->MaximumLength);
  284. ASSERT(fileName->Length == deviceExtension->CommonStorePathname.Length);
  285. status = RtlStringFromGUID(CSid,GUIDString);
  286. if (!NT_SUCCESS(status)) {
  287. SIS_MARK_POINT_ULONG(status);
  288. goto Error;
  289. }
  290. //
  291. // Get rid of the leading and trailing curly braces in the GUID name.
  292. //
  293. ASSERT(GUIDString->Buffer[0] == '{' && GUIDString->Buffer[(GUIDString->Length/sizeof(WCHAR)) - 1] == '}');
  294. GUIDString->Buffer++;
  295. GUIDString->Length -= 2 * sizeof(WCHAR);
  296. status = RtlAppendUnicodeStringToString(
  297. fileName,
  298. GUIDString);
  299. //
  300. // Just for safety, undo the hacking that we did on the GUID string before freeing it.
  301. //
  302. GUIDString->Buffer--;
  303. GUIDString->Length += 2 * sizeof(WCHAR);
  304. RtlFreeUnicodeString(GUIDString);
  305. if (!NT_SUCCESS(status)) {
  306. SIS_MARK_POINT_ULONG(status);
  307. goto Error;
  308. }
  309. status = RtlAppendUnicodeToString(fileName,L".sis");
  310. if (!NT_SUCCESS(status)) {
  311. SIS_MARK_POINT_ULONG(status);
  312. goto Error;
  313. }
  314. return STATUS_SUCCESS;
  315. Error:
  316. if (allocatedBufferSpace) {
  317. ExFreePool(fileName->Buffer);
  318. fileName->Buffer = NULL;
  319. }
  320. return status;
  321. }
  322. BOOLEAN
  323. SipFileNameToIndex(
  324. IN PUNICODE_STRING fileName,
  325. OUT PCSID CSid)
  326. /*++
  327. Routine Description:
  328. Given a common store file name, returns the corresponding index. The
  329. file name must be in the format generated by SipIndexToFileName().
  330. Arguments:
  331. fileName - A pointer to a UNICODE string containing the file name.
  332. CSid - A pointer to a CSID to receive the result.
  333. Return Value:
  334. TRUE if successful, else FALSE
  335. --*/
  336. {
  337. UNICODE_STRING substring[1];
  338. NTSTATUS status;
  339. #define BUFSIZE 42
  340. WCHAR buffer[BUFSIZE];
  341. //
  342. // Format: "<guid>.sis", where <guid> is the standard string representation of the
  343. // csid guid with the curly braces stripped off.
  344. //
  345. if (fileName->Length <= 4 * sizeof(WCHAR)) {
  346. //
  347. // It doesn't end in .sis, ignore it.
  348. //
  349. return FALSE;
  350. }
  351. substring->Buffer = buffer;
  352. substring->Buffer[0] = L'{';
  353. substring->Length = sizeof(WCHAR);
  354. substring->MaximumLength = BUFSIZE * sizeof(WCHAR);
  355. status = RtlAppendUnicodeStringToString(substring, fileName);
  356. if (!NT_SUCCESS(status)) {
  357. SIS_MARK_POINT_ULONG(status);
  358. return FALSE;
  359. }
  360. substring->Length = substring->Length - 3 * sizeof(WCHAR);
  361. substring->Buffer[(substring->Length - 1) / sizeof(WCHAR)] = L'}';
  362. status = RtlGUIDFromString(substring, CSid);
  363. if (!NT_SUCCESS(status)) {
  364. SIS_MARK_POINT_ULONG(status);
  365. return FALSE;
  366. }
  367. return TRUE;
  368. }
  369. NTSTATUS
  370. SipOpenMaxIndexFile(
  371. IN OUT PDEVICE_EXTENSION deviceExtension,
  372. IN BOOLEAN create
  373. )
  374. /*++
  375. Routine Description:
  376. Open the MaxIndex file for a given volume. Must be called in the
  377. PsInitialSystemProcess context.
  378. Arguments:
  379. deviceExtension - For the volume on which to open the MaxIndex file
  380. create - May we create the file, or must it already exist?
  381. Return Value:
  382. status of the open
  383. --*/
  384. {
  385. OBJECT_ATTRIBUTES Obja;
  386. UNICODE_STRING fileName;
  387. NTSTATUS status;
  388. IO_STATUS_BLOCK Iosb;
  389. ASSERT(deviceExtension->MaxAllocatedIndex.QuadPart == 0 || create);
  390. fileName.Buffer = ExAllocatePoolWithTag(
  391. NonPagedPool,
  392. deviceExtension->CommonStorePathname.Length + 8 * sizeof(WCHAR),
  393. ' siS');
  394. if (!fileName.Buffer) {
  395. status = STATUS_INSUFFICIENT_RESOURCES;
  396. goto done;
  397. }
  398. fileName.MaximumLength = deviceExtension->CommonStorePathname.Length + 8 * sizeof(WCHAR);
  399. fileName.Length = 0;
  400. RtlCopyUnicodeString(&fileName,&deviceExtension->CommonStorePathname);
  401. ASSERT(fileName.Length == deviceExtension->CommonStorePathname.Length);
  402. status = RtlAppendUnicodeToString(&fileName,L"MaxIndex");
  403. if (!NT_SUCCESS(status)) {
  404. goto done;
  405. }
  406. InitializeObjectAttributes(
  407. &Obja,
  408. &fileName,
  409. OBJ_CASE_INSENSITIVE,
  410. NULL,
  411. NULL);
  412. status = NtCreateFile(
  413. &deviceExtension->IndexHandle,
  414. GENERIC_READ|GENERIC_WRITE,
  415. &Obja,
  416. &Iosb,
  417. NULL, // Allocation Size
  418. 0, // File Attributes
  419. FILE_SHARE_READ,
  420. create ? FILE_OVERWRITE_IF : FILE_OPEN,
  421. FILE_WRITE_THROUGH,
  422. NULL, // EA Buffer
  423. 0); // EA Length
  424. done:
  425. if (NULL != fileName.Buffer) {
  426. ExFreePool(fileName.Buffer);
  427. }
  428. return status;
  429. }
  430. VOID
  431. SipAllocateIndices(
  432. IN PVOID Parameter)
  433. /*++
  434. Routine Description:
  435. This is a worker thread routine that allocates a new chunk of indices
  436. from the index file. Essentially, opens the index file and reads
  437. the old value if necessary. Then, it adds the chunk size onto the
  438. max allocated index, and writes the new value back into the file
  439. write through. When the write completes, set the event and exit.
  440. Arguments:
  441. parameter - a PSI_ALLOCATE_INDICES.
  442. Return Value:
  443. void
  444. --*/
  445. {
  446. PSI_ALLOCATE_INDICES allocateRequest = Parameter;
  447. KIRQL OldIrql;
  448. PDEVICE_EXTENSION deviceExtension = allocateRequest->deviceExtension;
  449. NTSTATUS status;
  450. IO_STATUS_BLOCK Iosb;
  451. LARGE_INTEGER ByteOffset;
  452. #if DBG
  453. // Just to ensure that we don't have more then one allocator running at once, we check
  454. // that the allocation request is really == TRUE (rather than just != 0), and then set
  455. // it to 2.
  456. KeAcquireSpinLock(deviceExtension->IndexSpinLock, &OldIrql);
  457. ASSERT(deviceExtension->IndexAllocationInProgress == TRUE);
  458. deviceExtension->IndexAllocationInProgress = 2;
  459. KeReleaseSpinLock(deviceExtension->IndexSpinLock, OldIrql);
  460. #endif // DBG
  461. if (deviceExtension->IndexHandle == NULL) {
  462. status = SipCreateEvent(
  463. SynchronizationEvent,
  464. &deviceExtension->IndexFileEventHandle,
  465. &deviceExtension->IndexFileEvent);
  466. if (!NT_SUCCESS(status)) {
  467. SIS_MARK_POINT_ULONG(status);
  468. goto done;
  469. }
  470. status = SipOpenMaxIndexFile(
  471. deviceExtension,
  472. (BOOLEAN) (deviceExtension->MaxAllocatedIndex.QuadPart != 0));
  473. if (!NT_SUCCESS(status)) {
  474. //
  475. // We can't open the MaxIndex file. It was probably deleted
  476. // or something. Kick off a volume check to rebuild it.
  477. //
  478. SIS_MARK_POINT_ULONG(status);
  479. KeAcquireSpinLock(deviceExtension->FlagsLock, &OldIrql);
  480. deviceExtension->Flags |= SIP_EXTENSION_FLAG_CORRUPT_MAXINDEX;
  481. KeReleaseSpinLock(deviceExtension->FlagsLock, OldIrql);
  482. status = STATUS_CORRUPT_SYSTEM_FILE;
  483. //
  484. // Do a volume check only if this is the first attempt.
  485. //
  486. if (deviceExtension->MaxAllocatedIndex.QuadPart == 0) {
  487. SipCheckVolume(deviceExtension);
  488. }
  489. goto done;
  490. }
  491. }
  492. if (deviceExtension->MaxAllocatedIndex.QuadPart == 0) {
  493. ByteOffset.QuadPart = 0;
  494. status = ZwReadFile(
  495. deviceExtension->IndexHandle,
  496. deviceExtension->IndexFileEventHandle,
  497. NULL, // APC routine
  498. NULL, // APC Context
  499. &Iosb,
  500. &deviceExtension->MaxAllocatedIndex,
  501. sizeof(deviceExtension->MaxAllocatedIndex),
  502. &ByteOffset,
  503. NULL); // Key
  504. if (status == STATUS_PENDING) {
  505. status = KeWaitForSingleObject(deviceExtension->IndexFileEvent,Executive,KernelMode,FALSE,NULL);
  506. ASSERT(status == STATUS_SUCCESS);
  507. status = Iosb.Status;
  508. }
  509. if (!NT_SUCCESS(status) || Iosb.Information != sizeof(LONGLONG) || deviceExtension->MaxAllocatedIndex.Check) {
  510. #if DBG
  511. DbgPrint(
  512. "SIS: SipAllocateIndices: ZwReadFile of MaxIndex file failed, wrong length or invalid value, 0x%x, %d\n",
  513. status,Iosb.Information);
  514. #endif // DBG
  515. ZwClose(deviceExtension->IndexHandle);
  516. deviceExtension->IndexHandle = NULL;
  517. KeAcquireSpinLock(deviceExtension->FlagsLock, &OldIrql);
  518. deviceExtension->Flags |= SIP_EXTENSION_FLAG_CORRUPT_MAXINDEX;
  519. KeReleaseSpinLock(deviceExtension->FlagsLock, OldIrql);
  520. status = STATUS_CORRUPT_SYSTEM_FILE;
  521. SipCheckVolume(deviceExtension);
  522. goto done;
  523. }
  524. deviceExtension->MaxUsedIndex = deviceExtension->MaxAllocatedIndex;
  525. }
  526. deviceExtension->MaxAllocatedIndex.QuadPart += 1000; // 1000 is pretty arbitrary. We can do better.
  527. ByteOffset.QuadPart = 0;
  528. status = ZwWriteFile(
  529. deviceExtension->IndexHandle,
  530. deviceExtension->IndexFileEventHandle,
  531. NULL, // APC routine
  532. NULL, // APC context
  533. &Iosb,
  534. &deviceExtension->MaxAllocatedIndex,
  535. sizeof(deviceExtension->MaxAllocatedIndex),
  536. &ByteOffset,
  537. NULL); // key
  538. if (status == STATUS_PENDING) {
  539. status = KeWaitForSingleObject(deviceExtension->IndexFileEvent,Executive,KernelMode,FALSE,NULL);
  540. ASSERT(status == STATUS_SUCCESS);
  541. status = Iosb.Status;
  542. }
  543. if (!NT_SUCCESS(status)) {
  544. // The write failed. Back out the allocation.
  545. deviceExtension->MaxAllocatedIndex.QuadPart -= 1000;
  546. #if DBG
  547. DbgPrint("SIS: SipAllocateIndices: writing MaxIndex file failed, 0x%x\n",status);
  548. #endif // DBG
  549. }
  550. done:
  551. KeAcquireSpinLock(deviceExtension->IndexSpinLock, &OldIrql);
  552. deviceExtension->IndexStatus = status;
  553. deviceExtension->IndexAllocationInProgress = FALSE;
  554. KeSetEvent(deviceExtension->IndexEvent, 0, FALSE); // we may no longer touch allocationRequest after this set
  555. KeReleaseSpinLock(deviceExtension->IndexSpinLock, OldIrql);
  556. return;
  557. }
  558. NTSTATUS
  559. SipAllocateIndex(
  560. IN PDEVICE_EXTENSION DeviceExtension,
  561. OUT PLINK_INDEX Index)
  562. /*++
  563. Routine Description:
  564. Allocate a new LINK_INDEX. If there are indices that have been reserved from the
  565. file but not yet allocated, we can just grab one and return it. Otherwise, we
  566. need to wait for a new index allocation. If one is not already in progress, we
  567. start it and wait for it to complete.
  568. Arguments:
  569. deviceExtension - for the volume on which we're to allocate the index.
  570. Index - returns the new index
  571. Return Value:
  572. status of the allocation.
  573. --*/
  574. {
  575. KIRQL OldIrql;
  576. BOOLEAN StartAllocator;
  577. SI_ALLOCATE_INDICES AllocateRequest[1];
  578. NTSTATUS status;
  579. if (DeviceExtension->Flags & SIP_EXTENSION_FLAG_CORRUPT_MAXINDEX) {
  580. return STATUS_CORRUPT_SYSTEM_FILE;
  581. }
  582. KeAcquireSpinLock(DeviceExtension->IndexSpinLock, &OldIrql);
  583. while (TRUE) {
  584. if (DeviceExtension->MaxAllocatedIndex.QuadPart > DeviceExtension->MaxUsedIndex.QuadPart) {
  585. DeviceExtension->MaxUsedIndex.QuadPart++;
  586. *Index = DeviceExtension->MaxUsedIndex;
  587. KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
  588. return STATUS_SUCCESS;
  589. }
  590. // There are no free indices left, we have to block.
  591. if (!DeviceExtension->IndexAllocationInProgress) {
  592. StartAllocator = TRUE;
  593. DeviceExtension->IndexAllocationInProgress = TRUE;
  594. // Stop anyone from passing the barrier until the allocator runs.
  595. KeClearEvent(DeviceExtension->IndexEvent);
  596. } else {
  597. StartAllocator = FALSE;
  598. }
  599. KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
  600. if (StartAllocator) {
  601. ExInitializeWorkItem(AllocateRequest->workQueueItem, SipAllocateIndices, AllocateRequest);
  602. AllocateRequest->deviceExtension = DeviceExtension;
  603. ExQueueWorkItem(AllocateRequest->workQueueItem, CriticalWorkQueue);
  604. }
  605. status = KeWaitForSingleObject(DeviceExtension->IndexEvent, Executive, KernelMode, FALSE, NULL);
  606. ASSERT(status == STATUS_SUCCESS);
  607. if ((status != STATUS_SUCCESS) && !StartAllocator) {
  608. // The reason that we check StartAllocator here is because the allocation request is
  609. // on our stack, and we really can't return until the work item is completed. (Of course,
  610. // the KeWaitForSingleObject should never fail in the first place...)
  611. return status;
  612. }
  613. KeAcquireSpinLock(DeviceExtension->IndexSpinLock, &OldIrql);
  614. if (!NT_SUCCESS(DeviceExtension->IndexStatus)) {
  615. status = DeviceExtension->IndexStatus;
  616. KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
  617. return status;
  618. }
  619. }
  620. }
  621. NTSTATUS
  622. SipGetMaxUsedIndex(
  623. IN PDEVICE_EXTENSION DeviceExtension,
  624. OUT PLINK_INDEX Index)
  625. /*++
  626. Routine Description:
  627. Return a number that's at least as big as the largest LINK_INDEX ever allocated
  628. on this volume. Note that if it looks like we don't have any indices available
  629. we'll kick the index allocator, because otherwise we can't be sure that the
  630. index values are valid (they may have never been read for this volume).
  631. Arguments:
  632. deviceExtension - for the volume we're considering
  633. Index - returns new index
  634. Return Value:
  635. status of the check. *Index is meaningful iff NT_SUCCESS(return value).
  636. --*/
  637. {
  638. KIRQL OldIrql;
  639. BOOLEAN StartAllocator;
  640. SI_ALLOCATE_INDICES AllocateRequest[1];
  641. NTSTATUS status;
  642. KeAcquireSpinLock(DeviceExtension->IndexSpinLock, &OldIrql);
  643. while (TRUE) {
  644. if (DeviceExtension->MaxAllocatedIndex.QuadPart > DeviceExtension->MaxUsedIndex.QuadPart) {
  645. *Index = DeviceExtension->MaxUsedIndex;
  646. KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
  647. return STATUS_SUCCESS;
  648. }
  649. // There are no free indices left, we have to block.
  650. if (!DeviceExtension->IndexAllocationInProgress) {
  651. StartAllocator = TRUE;
  652. DeviceExtension->IndexAllocationInProgress = TRUE;
  653. // Stop anyone from passing the barrier until the allocator runs.
  654. KeClearEvent(DeviceExtension->IndexEvent);
  655. } else {
  656. StartAllocator = FALSE;
  657. }
  658. KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
  659. if (StartAllocator) {
  660. ExInitializeWorkItem(AllocateRequest->workQueueItem, SipAllocateIndices, AllocateRequest);
  661. AllocateRequest->deviceExtension = DeviceExtension;
  662. ExQueueWorkItem(AllocateRequest->workQueueItem, CriticalWorkQueue);
  663. }
  664. status = KeWaitForSingleObject(DeviceExtension->IndexEvent, Executive, KernelMode, FALSE, NULL);
  665. ASSERT(status == STATUS_SUCCESS);
  666. if ((status != STATUS_SUCCESS) && !StartAllocator) {
  667. // The reason that we check StartAllocator here is because the allocation request is
  668. // on our stack, and we really can't return until the work item is completed. (Of course,
  669. // the KeWaitForSingleObject should never fail in the first place...)
  670. return status;
  671. }
  672. KeAcquireSpinLock(DeviceExtension->IndexSpinLock, &OldIrql);
  673. if (!NT_SUCCESS(DeviceExtension->IndexStatus)) {
  674. status = DeviceExtension->IndexStatus;
  675. KeReleaseSpinLock(DeviceExtension->IndexSpinLock, OldIrql);
  676. return status;
  677. }
  678. }
  679. }
  680. NTSTATUS
  681. SipAssureMaxIndexFileOpen(
  682. IN PDEVICE_EXTENSION deviceExtension)
  683. {
  684. NTSTATUS status;
  685. KIRQL OldIrql;
  686. LINK_INDEX uselessIndex;
  687. //
  688. // Make sure that the MaxIndex file is already opened. We need to
  689. // do this here to avoid a deadlock if someone
  690. // tries to do a copyfile with MaxIndex as the source, which would
  691. // otherwise deadlock. If things are messed up, this might kick off
  692. // a volume check, but we should still fail the open.
  693. //
  694. if (deviceExtension->IndexHandle != NULL) {
  695. //
  696. // The file's already open, no need to do any work.
  697. //
  698. return STATUS_SUCCESS;
  699. }
  700. //
  701. // The index file isn't open. Rather than trying to open it directly,
  702. // we avoid races by just calling the index allocator. We'll throw away
  703. // the index we get back, but they're plentiful so it's not much of a
  704. // problem.
  705. //
  706. status = SipAllocateIndex(deviceExtension, &uselessIndex);
  707. if (!NT_SUCCESS(status)) {
  708. BOOLEAN volumeCheckPending;
  709. SIS_MARK_POINT_ULONG(status);
  710. //
  711. // If we're in a volume check, transmute the error to STATUS_RETRY on the
  712. // theory that the volume check will rebuild the MaxIndex file. If not,
  713. // then just leave it alone.
  714. //
  715. KeAcquireSpinLock(deviceExtension->FlagsLock, &OldIrql);
  716. volumeCheckPending = (deviceExtension->Flags & SIP_EXTENSION_FLAG_VCHECK_PENDING) ? TRUE : FALSE;
  717. KeReleaseSpinLock(deviceExtension->FlagsLock, OldIrql);
  718. if (volumeCheckPending) {
  719. SIS_MARK_POINT();
  720. status = STATUS_RETRY;
  721. }
  722. return status;
  723. }
  724. return status;
  725. }