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.

1620 lines
39 KiB

  1. /*++
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name:
  4. notify.c
  5. Abstract:
  6. This module contians the notification logic for sr
  7. Author:
  8. Paul McDaniel (paulmcd) 23-Jan-2000
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. //
  13. // Private constants.
  14. //
  15. //
  16. // Private types.
  17. //
  18. //
  19. // Private prototypes.
  20. //
  21. PIRP
  22. SrDequeueIrp (
  23. IN PSR_CONTROL_OBJECT pControlObject
  24. );
  25. PSR_NOTIFICATION_RECORD
  26. SrDequeueNotifyRecord (
  27. IN PSR_CONTROL_OBJECT pControlObject
  28. );
  29. VOID
  30. SrCopyRecordToIrp (
  31. IN PIRP pIrp,
  32. IN SR_NOTIFICATION_TYPE NotificationType,
  33. IN PUNICODE_STRING pVolumeName,
  34. IN ULONG Context
  35. );
  36. VOID
  37. SrCancelWaitForNotification (
  38. IN PDEVICE_OBJECT pDeviceObject,
  39. IN PIRP pIrp
  40. );
  41. VOID
  42. SrCancelWaitForNotificationWorker (
  43. IN PVOID pContext
  44. );
  45. NTSTATUS
  46. SrLogError (
  47. IN PSR_DEVICE_EXTENSION pExtension,
  48. IN PUNICODE_STRING pFileName,
  49. IN NTSTATUS ErrorStatus,
  50. IN SR_EVENT_TYPE EventType
  51. );
  52. UCHAR
  53. SrIrpCodeFromEventType (
  54. IN SR_EVENT_TYPE EventType
  55. );
  56. //
  57. // linker commands
  58. //
  59. #ifdef ALLOC_PRAGMA
  60. #pragma alloc_text( PAGE, SrWaitForNotificationIoctl )
  61. #pragma alloc_text( PAGE, SrCancelWaitForNotificationWorker )
  62. #pragma alloc_text( PAGE, SrCopyRecordToIrp )
  63. #pragma alloc_text( PAGE, SrDequeueIrp )
  64. #pragma alloc_text( PAGE, SrDequeueNotifyRecord )
  65. #pragma alloc_text( PAGE, SrFireNotification )
  66. #pragma alloc_text( PAGE, SrUpdateBytesWritten )
  67. #pragma alloc_text( PAGE, SrNotifyVolumeError )
  68. #pragma alloc_text( PAGE, SrLogError )
  69. #pragma alloc_text( PAGE, SrIrpCodeFromEventType )
  70. #endif // ALLOW_UNLOAD
  71. #if 0
  72. NOT PAGEABLE -- SrCancelWaitForNotification
  73. #endif // 0
  74. //
  75. // Private globals.
  76. //
  77. //
  78. // Public globals.
  79. //
  80. //
  81. // Public functions.
  82. //
  83. /***************************************************************************++
  84. Routine Description:
  85. This routine enables sending notifications to user mode
  86. Note: This is a METHOD_OUT_DIRECT IOCTL.
  87. Arguments:
  88. pIrp - Supplies a pointer to the IO request packet.
  89. pIrpSp - Supplies a pointer to the IO stack location to use for this
  90. request.
  91. Return Value:
  92. NTSTATUS - Completion status.
  93. --***************************************************************************/
  94. NTSTATUS
  95. SrWaitForNotificationIoctl(
  96. IN PIRP pIrp,
  97. IN PIO_STACK_LOCATION pIrpSp
  98. )
  99. {
  100. NTSTATUS Status;
  101. PSR_CONTROL_OBJECT pControlObject;
  102. PSR_NOTIFICATION_RECORD pRecord;
  103. //
  104. // Sanity check.
  105. //
  106. PAGED_CODE();
  107. SrTrace(FUNC_ENTRY, ("SR!SrWaitForNotificationIoctl\n"));
  108. //
  109. // grab the control object
  110. //
  111. pControlObject = (PSR_CONTROL_OBJECT)pIrpSp->FileObject->FsContext;
  112. //
  113. // make sure we really have one
  114. //
  115. if (pIrpSp->FileObject->FsContext2 != SR_CONTROL_OBJECT_CONTEXT ||
  116. IS_VALID_CONTROL_OBJECT(pControlObject) == FALSE ||
  117. pIrp->MdlAddress == NULL)
  118. {
  119. Status = STATUS_INVALID_DEVICE_REQUEST;
  120. goto end;
  121. }
  122. //
  123. // make sure the buffer is at least minimum size.
  124. //
  125. if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
  126. sizeof(SR_NOTIFICATION_RECORD))
  127. {
  128. Status = STATUS_BUFFER_TOO_SMALL;
  129. goto end;
  130. }
  131. //
  132. // Grab the lock
  133. //
  134. SrAcquireGlobalLockExclusive();
  135. //
  136. // Do we have a queue'd record ?
  137. //
  138. pRecord = SrDequeueNotifyRecord(pControlObject);
  139. if (pRecord == NULL)
  140. {
  141. SrTrace(NOTIFY, ("SR!SrWaitForNotificationIoctl - queue'ing IRP(%p)\n", pIrp));
  142. //
  143. // Nope, queue the irp up.
  144. //
  145. IoMarkIrpPending(pIrp);
  146. //
  147. // give the irp a pointer to the control object (add a refcount
  148. // as the cancel routine runs queued, and needs to access the
  149. // control object - even if it's later deleted).
  150. //
  151. pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pControlObject;
  152. SrReferenceControlObject(pControlObject);
  153. //
  154. // set to these to null just in case the cancel routine runs
  155. //
  156. pIrp->Tail.Overlay.ListEntry.Flink = NULL;
  157. pIrp->Tail.Overlay.ListEntry.Blink = NULL;
  158. IoSetCancelRoutine(pIrp, &SrCancelWaitForNotification);
  159. //
  160. // cancelled?
  161. //
  162. if (pIrp->Cancel)
  163. {
  164. //
  165. // darn it, need to make sure the irp get's completed
  166. //
  167. if (IoSetCancelRoutine( pIrp, NULL ) != NULL)
  168. {
  169. //
  170. // we are in charge of completion, IoCancelIrp didn't
  171. // see our cancel routine (and won't). ioctl wrapper
  172. // will complete it
  173. //
  174. SrReleaseGlobalLock();
  175. pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  176. SrDereferenceControlObject(pControlObject);
  177. pIrp->IoStatus.Information = 0;
  178. SrUnmarkIrpPending( pIrp );
  179. Status = STATUS_CANCELLED;
  180. goto end;
  181. }
  182. //
  183. // our cancel routine will run and complete the irp,
  184. // don't touch it
  185. //
  186. SrReleaseGlobalLock();
  187. //
  188. // STATUS_PENDING will cause the ioctl wrapper to
  189. // not complete (or touch in any way) the irp
  190. //
  191. Status = STATUS_PENDING;
  192. goto end;
  193. }
  194. //
  195. // now we are safe to queue it
  196. //
  197. InsertTailList(
  198. &pControlObject->IrpListHead,
  199. &pIrp->Tail.Overlay.ListEntry
  200. );
  201. SrReleaseGlobalLock();
  202. Status = STATUS_PENDING;
  203. goto end;
  204. }
  205. else // if (pRecord == NULL)
  206. {
  207. //
  208. // Have a queue'd record, serve it up!
  209. //
  210. //
  211. // all done with the control object
  212. //
  213. SrReleaseGlobalLock();
  214. SrTrace( NOTIFY, ( "SR!SrWaitForNotificationIoctl - completing IRP(%p) NotifyRecord(%d, %wZ)\n",
  215. pIrp,
  216. pRecord->NotificationType,
  217. &pRecord->VolumeName ));
  218. //
  219. // Copy it to the irp, the routine will take ownership
  220. // of pRecord if it is not able to copy it to the irp.
  221. //
  222. // it will also complete the irp so don't touch it later.
  223. //
  224. IoMarkIrpPending(pIrp);
  225. //
  226. // Copy the data and complete the irp
  227. //
  228. (VOID) SrCopyRecordToIrp( pIrp,
  229. pRecord->NotificationType,
  230. &pRecord->VolumeName,
  231. pRecord->Context );
  232. //
  233. // don't touch pIrp, SrCopyRecordToIrp ALWAYS completes it.
  234. //
  235. pIrp = NULL;
  236. //
  237. // and free the record
  238. //
  239. SR_FREE_POOL_WITH_SIG(pRecord, SR_NOTIFICATION_RECORD_TAG);
  240. Status = STATUS_PENDING;
  241. goto end;
  242. }
  243. end:
  244. //
  245. // At this point if Status != PENDING, the ioctl wrapper will
  246. // complete pIrp
  247. //
  248. RETURN(Status);
  249. } // SrWaitForNotificationIoctl
  250. /***************************************************************************++
  251. Routine Description:
  252. cancels the pending user mode irp which was to receive the http request.
  253. this routine ALWAYS results in the irp being completed.
  254. note: we queue off to cancel in order to process the cancellation at lower
  255. irql.
  256. Arguments:
  257. pDeviceObject - the device object
  258. pIrp - the irp to cancel
  259. --***************************************************************************/
  260. VOID
  261. SrCancelWaitForNotification(
  262. IN PDEVICE_OBJECT pDeviceObject,
  263. IN PIRP pIrp
  264. )
  265. {
  266. C_ASSERT(sizeof(WORK_QUEUE_ITEM) <= sizeof(pIrp->Tail.Overlay.DriverContext));
  267. UNREFERENCED_PARAMETER( pDeviceObject );
  268. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  269. ASSERT(pIrp != NULL);
  270. //
  271. // release the cancel spinlock. This means the cancel routine
  272. // must be the one completing the irp (to avoid the race of
  273. // completion + reuse prior to the cancel routine running).
  274. //
  275. IoReleaseCancelSpinLock(pIrp->CancelIrql);
  276. //
  277. // queue the cancel to a worker to ensure passive irql.
  278. //
  279. ExInitializeWorkItem( (PWORK_QUEUE_ITEM)&pIrp->Tail.Overlay.DriverContext[0],
  280. &SrCancelWaitForNotificationWorker,
  281. pIrp );
  282. ExQueueWorkItem( (PWORK_QUEUE_ITEM)&pIrp->Tail.Overlay.DriverContext[0],
  283. DelayedWorkQueue );
  284. } // SrCancelWaitForNotification
  285. /***************************************************************************++
  286. Routine Description:
  287. Actually performs the cancel for the irp.
  288. Arguments:
  289. pWorkItem - the work item to process.
  290. --***************************************************************************/
  291. VOID
  292. SrCancelWaitForNotificationWorker(
  293. IN PVOID pContext
  294. )
  295. {
  296. PSR_CONTROL_OBJECT pControlObject;
  297. PIRP pIrp;
  298. //
  299. // Sanity check.
  300. //
  301. PAGED_CODE();
  302. ASSERT(pContext != NULL);
  303. //
  304. // grab the irp off the context
  305. //
  306. pIrp = (PIRP)pContext;
  307. ASSERT(IS_VALID_IRP(pIrp));
  308. SrTrace(CANCEL, ("SR!SrCancelWaitForNotificationWorker irp=%p\n", pIrp));
  309. //
  310. // grab the control object off the irp
  311. //
  312. pControlObject = (PSR_CONTROL_OBJECT)(
  313. IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.Type3InputBuffer
  314. );
  315. ASSERT(IS_VALID_CONTROL_OBJECT(pControlObject));
  316. //
  317. // grab the lock protecting the queue
  318. //
  319. SrAcquireGlobalLockExclusive();
  320. //
  321. // does it need to be de-queue'd ?
  322. //
  323. if (pIrp->Tail.Overlay.ListEntry.Flink != NULL)
  324. {
  325. //
  326. // remove it
  327. //
  328. RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
  329. pIrp->Tail.Overlay.ListEntry.Flink = NULL;
  330. pIrp->Tail.Overlay.ListEntry.Blink = NULL;
  331. }
  332. //
  333. // let the lock go
  334. //
  335. SrReleaseGlobalLock();
  336. //
  337. // let our reference go
  338. //
  339. IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  340. SrDereferenceControlObject(pControlObject);
  341. //
  342. // complete the irp
  343. //
  344. pIrp->IoStatus.Status = STATUS_CANCELLED;
  345. pIrp->IoStatus.Information = 0;
  346. IoCompleteRequest( pIrp, IO_NO_INCREMENT );
  347. } // SrCancelWaitForNotificationWorker
  348. /******************************************************************************
  349. Routine Description:
  350. this copies a record into a free irp. this routine completes the IRP!
  351. Arguments:
  352. pRecord - the record to copy
  353. pIrp - the irp to copy pRecord to. this routine completes this IRP !
  354. Return Value:
  355. VOID - it always works.
  356. ******************************************************************************/
  357. VOID
  358. SrCopyRecordToIrp(
  359. IN PIRP pIrp,
  360. IN SR_NOTIFICATION_TYPE NotificationType,
  361. IN PUNICODE_STRING pVolumeName,
  362. IN ULONG Context
  363. )
  364. {
  365. NTSTATUS Status;
  366. PIO_STACK_LOCATION pIrpSp;
  367. PVOID pBuffer;
  368. PSR_NOTIFICATION_RECORD pUserNotifyRecord;
  369. //
  370. // Sanity check.
  371. //
  372. PAGED_CODE();
  373. ASSERT(NotificationType < SrNotificationMaximum);
  374. ASSERT(NotificationType > SrNotificationInvalid);
  375. ASSERT(pVolumeName != NULL);
  376. SrTrace(FUNC_ENTRY, ("SR!SrCopyRecordToIrp\n"));
  377. ASSERT(global->pControlObject != NULL);
  378. //
  379. // assume SUCCESS!
  380. //
  381. Status = STATUS_SUCCESS;
  382. //
  383. // Make sure this is big enough to handle the request, and
  384. // if so copy it in.
  385. //
  386. pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
  387. //
  388. // do we have enough space?
  389. //
  390. if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength <
  391. sizeof(SR_NOTIFICATION_RECORD) + pVolumeName->Length
  392. + sizeof(WCHAR) )
  393. {
  394. Status = STATUS_BUFFER_TOO_SMALL;
  395. goto complete;
  396. }
  397. //
  398. // get the system address for the buffer
  399. //
  400. pBuffer = MmGetSystemAddressForMdlSafe( pIrp->MdlAddress,
  401. NormalPagePriority );
  402. if (pBuffer == NULL)
  403. {
  404. Status = STATUS_INSUFFICIENT_RESOURCES;
  405. goto complete;
  406. }
  407. //
  408. // wipe it clean
  409. //
  410. RtlZeroMemory( pBuffer,
  411. pIrpSp->Parameters.DeviceIoControl.OutputBufferLength );
  412. //
  413. // Fill in the user space
  414. //
  415. pUserNotifyRecord = (PSR_NOTIFICATION_RECORD) pBuffer;
  416. pUserNotifyRecord->Signature = SR_NOTIFICATION_RECORD_TAG;
  417. pUserNotifyRecord->NotificationType = NotificationType;
  418. pUserNotifyRecord->VolumeName.Length = pVolumeName->Length;
  419. pUserNotifyRecord->VolumeName.MaximumLength = pVolumeName->Length;
  420. //
  421. // put the virtual pointer in for the use by the user mode service
  422. //
  423. pUserNotifyRecord->VolumeName.Buffer =
  424. (PWSTR)((PUCHAR)(MmGetMdlVirtualAddress(pIrp->MdlAddress))
  425. + sizeof(SR_NOTIFICATION_RECORD));
  426. pUserNotifyRecord->Context = Context;
  427. //
  428. // and copy the string using the system address
  429. //
  430. RtlCopyMemory( pUserNotifyRecord+1,
  431. pVolumeName->Buffer,
  432. pVolumeName->Length );
  433. //
  434. // null terminate it
  435. //
  436. ((PWSTR)(pUserNotifyRecord+1))[pVolumeName->Length/sizeof(WCHAR)]
  437. = UNICODE_NULL;
  438. //
  439. // Tell everyone how much we copied
  440. //
  441. pIrp->IoStatus.Information = sizeof(SR_NOTIFICATION_RECORD)
  442. + pVolumeName->Length + sizeof(WCHAR);
  443. //
  444. // complete the irp
  445. //
  446. complete:
  447. pIrp->IoStatus.Status = Status;
  448. IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  449. //
  450. // success. we completed the irp
  451. //
  452. } // SrCopyRecordToIrp
  453. /******************************************************************************
  454. Routine Description:
  455. this will grab a free queue'd irp off the list and return it.
  456. Arguments:
  457. pControlObject - the control object to grab irp's from
  458. Return Value:
  459. PIRP - the free irp found (can be NULL)
  460. ******************************************************************************/
  461. PIRP
  462. SrDequeueIrp(
  463. IN PSR_CONTROL_OBJECT pControlObject
  464. )
  465. {
  466. PIRP pIrp = NULL;
  467. PSR_CONTROL_OBJECT pIrpControlObject;
  468. PAGED_CODE();
  469. ASSERT(IS_VALID_CONTROL_OBJECT(pControlObject));
  470. //
  471. // we are modifying the lists, better own the lock
  472. //
  473. ASSERT(IS_GLOBAL_LOCK_ACQUIRED());
  474. SrTrace(FUNC_ENTRY, ("SR!SrDequeueIrp\n"));
  475. //
  476. // check our list
  477. //
  478. while (!IsListEmpty(&(pControlObject->IrpListHead)))
  479. {
  480. PLIST_ENTRY pEntry;
  481. //
  482. // Found a free irp !
  483. //
  484. pEntry = RemoveHeadList(&pControlObject->IrpListHead);
  485. pEntry->Blink = pEntry->Flink = NULL;
  486. pIrp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
  487. //
  488. // pop the cancel routine
  489. //
  490. if (IoSetCancelRoutine(pIrp, NULL) == NULL)
  491. {
  492. //
  493. // IoCancelIrp pop'd it first
  494. //
  495. // ok to just ignore this irp, it's been pop'd off the queue
  496. // and will be completed in the cancel routine.
  497. //
  498. // keep looking for a irp to use
  499. //
  500. pIrp = NULL;
  501. }
  502. else if (pIrp->Cancel)
  503. {
  504. //
  505. // we pop'd it first. but the irp is being cancelled
  506. // and our cancel routine will never run. lets be
  507. // nice and complete the irp now (vs. using it
  508. // then completing it - which would also be legal).
  509. //
  510. pIrpControlObject = (PSR_CONTROL_OBJECT)(
  511. IoGetCurrentIrpStackLocation(pIrp)->
  512. Parameters.DeviceIoControl.Type3InputBuffer
  513. );
  514. ASSERT(pIrpControlObject == pControlObject);
  515. SrDereferenceControlObject(pControlObject);
  516. IoGetCurrentIrpStackLocation(pIrp)->
  517. Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  518. pIrp->IoStatus.Status = STATUS_CANCELLED;
  519. pIrp->IoStatus.Information = 0;
  520. IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  521. pIrp = NULL;
  522. }
  523. else
  524. {
  525. //
  526. // we are free to use this irp !
  527. //
  528. pIrpControlObject = (PSR_CONTROL_OBJECT)(
  529. IoGetCurrentIrpStackLocation(pIrp)->
  530. Parameters.DeviceIoControl.Type3InputBuffer
  531. );
  532. ASSERT(pIrpControlObject == pControlObject);
  533. SrDereferenceControlObject(pControlObject);
  534. IoGetCurrentIrpStackLocation(pIrp)->
  535. Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  536. break;
  537. }
  538. }
  539. return pIrp;
  540. } // SrDequeueIrp
  541. /******************************************************************************
  542. Routine Description:
  543. this will grab a notify record if one has been queue'd for completion.
  544. Arguments:
  545. pControlObject - the control object to grab records from
  546. Return Value:
  547. PSR_NOTIFICATION_RECORD - the record found (can be NULL)
  548. ******************************************************************************/
  549. PSR_NOTIFICATION_RECORD
  550. SrDequeueNotifyRecord(
  551. IN PSR_CONTROL_OBJECT pControlObject
  552. )
  553. {
  554. PSR_NOTIFICATION_RECORD pRecord = NULL;
  555. PAGED_CODE();
  556. ASSERT(IS_VALID_CONTROL_OBJECT(pControlObject));
  557. //
  558. // we are modifying the lists, better own the lock
  559. //
  560. ASSERT(IS_GLOBAL_LOCK_ACQUIRED());
  561. SrTrace(FUNC_ENTRY, ("SR!SrDequeueNotifyRecord\n"));
  562. //
  563. // check our list
  564. //
  565. if (IsListEmpty(&pControlObject->NotifyRecordListHead) == FALSE)
  566. {
  567. PLIST_ENTRY pEntry;
  568. //
  569. // Found a free record !
  570. //
  571. pEntry = RemoveHeadList(&pControlObject->NotifyRecordListHead);
  572. pEntry->Blink = pEntry->Flink = NULL;
  573. pRecord = CONTAINING_RECORD( pEntry,
  574. SR_NOTIFICATION_RECORD,
  575. ListEntry );
  576. ASSERT(IS_VALID_NOTIFICATION_RECORD(pRecord));
  577. //
  578. // give the record to the caller
  579. //
  580. }
  581. return pRecord;
  582. } // SrDequeueNotifyRecord
  583. /******************************************************************************
  584. Routine Description:
  585. this will fire a notify up to a listening usermode process.
  586. it does nothing if nobody is listening.
  587. it will queue the record if there are no free irp's.
  588. Arguments:
  589. NotificationType - the type of notification
  590. pExtension - the volume being notified about
  591. Return Value:
  592. NTSTATUS - completion code
  593. ******************************************************************************/
  594. NTSTATUS
  595. SrFireNotification(
  596. IN SR_NOTIFICATION_TYPE NotificationType,
  597. IN PSR_DEVICE_EXTENSION pExtension,
  598. IN ULONG Context OPTIONAL
  599. )
  600. {
  601. NTSTATUS Status;
  602. PIRP pIrp;
  603. BOOLEAN bReleaseLock = FALSE;
  604. PAGED_CODE();
  605. ASSERT(NotificationType < SrNotificationMaximum);
  606. ASSERT(NotificationType > SrNotificationInvalid);
  607. ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
  608. SrTrace(FUNC_ENTRY, ("SR!SrFireNotification\n"));
  609. Status = STATUS_SUCCESS;
  610. try {
  611. //
  612. // grab the lock EXCLUSIVE
  613. //
  614. SrAcquireGlobalLockExclusive();
  615. bReleaseLock = TRUE;
  616. //
  617. // do we still have a control object ? the agent could have just
  618. // crashed or he was never there... that's ok .
  619. //
  620. if (global->pControlObject == NULL)
  621. {
  622. Status = STATUS_SUCCESS;
  623. leave;
  624. }
  625. //
  626. // find a free irp to use
  627. //
  628. pIrp = SrDequeueIrp(global->pControlObject);
  629. if (pIrp != NULL)
  630. {
  631. //
  632. // Found one, release the lock
  633. //
  634. SrReleaseGlobalLock();
  635. bReleaseLock = FALSE;
  636. SrTrace( NOTIFY, ("SR!SrFireNotification(%d, %wZ, %X) - completing IRP(%p)\n",
  637. NotificationType,
  638. &pExtension->VolumeGuid,
  639. Context,
  640. pIrp ));
  641. //
  642. // Copy the data and complete the irp
  643. //
  644. (VOID) SrCopyRecordToIrp( pIrp,
  645. NotificationType,
  646. &pExtension->VolumeGuid,
  647. Context );
  648. //
  649. // don't touch pIrp, SrCopyRecordToIrp ALWAYS completes it.
  650. //
  651. NULLPTR( pIrp );
  652. }
  653. else
  654. {
  655. PSR_NOTIFICATION_RECORD pRecord = NULL;
  656. SrTrace(NOTIFY, ("SR!SrFireNotification(%d, %wZ) - no IRPs; queue'ing a NOTIFY_RECORD\n",
  657. NotificationType,
  658. &pExtension->VolumeGuid ));
  659. //
  660. // need to queue a NOTIFY_RECORD and wait for a free IRP to come down
  661. //
  662. //
  663. // allocate a notify record
  664. //
  665. pRecord = SR_ALLOCATE_STRUCT_WITH_SPACE( PagedPool,
  666. SR_NOTIFICATION_RECORD,
  667. pExtension->VolumeGuid.Length + sizeof(WCHAR),
  668. SR_NOTIFICATION_RECORD_TAG );
  669. if (NULL == pRecord)
  670. {
  671. Status = STATUS_INSUFFICIENT_RESOURCES;
  672. leave;
  673. }
  674. RtlZeroMemory(pRecord, sizeof(SR_NOTIFICATION_RECORD));
  675. pRecord->Signature = SR_NOTIFICATION_RECORD_TAG;
  676. pRecord->NotificationType = NotificationType;
  677. pRecord->VolumeName.Length = pExtension->VolumeGuid.Length;
  678. pRecord->VolumeName.MaximumLength = pExtension->VolumeGuid.Length;
  679. pRecord->VolumeName.Buffer = (PWSTR)(pRecord + 1);
  680. RtlCopyMemory( pRecord->VolumeName.Buffer,
  681. pExtension->VolumeGuid.Buffer,
  682. pExtension->VolumeGuid.Length );
  683. pRecord->VolumeName.Buffer
  684. [pRecord->VolumeName.Length/sizeof(WCHAR)] = UNICODE_NULL;
  685. pRecord->Context = Context;
  686. //
  687. // insert it into the list
  688. //
  689. InsertTailList( &global->pControlObject->NotifyRecordListHead,
  690. &pRecord->ListEntry );
  691. NULLPTR( pRecord );
  692. }
  693. } finally {
  694. //
  695. // release any locks we held during an error
  696. //
  697. if (bReleaseLock)
  698. {
  699. SrReleaseGlobalLock();
  700. }
  701. }
  702. RETURN(Status);
  703. } // SrFireNotification
  704. /******************************************************************************
  705. Routine Description:
  706. this update the bytes written count for the volume, and potentially
  707. fire a notification to user mode (for every 25mb).
  708. Arguments:
  709. pExtension - the volume being updated
  710. BytesWritten - how much was just written
  711. Return Value:
  712. NTSTATUS - completion code
  713. ******************************************************************************/
  714. NTSTATUS
  715. SrUpdateBytesWritten(
  716. IN PSR_DEVICE_EXTENSION pExtension,
  717. IN ULONGLONG BytesWritten
  718. )
  719. {
  720. NTSTATUS Status;
  721. PAGED_CODE();
  722. try {
  723. SrAcquireLogLockExclusive( pExtension );
  724. //
  725. // update the count
  726. //
  727. pExtension->BytesWritten += BytesWritten;
  728. SrTrace( BYTES_WRITTEN, ( "SR!SrUpdateBytesWritten: (%wZ) Wrote 0x%016I64x bytes; total bytes written 0x%016I64x\n",
  729. pExtension->pNtVolumeName,
  730. BytesWritten,
  731. pExtension->BytesWritten ) );
  732. while (pExtension->BytesWritten >= SR_NOTIFY_BYTE_COUNT)
  733. {
  734. SrTrace( BYTES_WRITTEN, ( "SR!SrUpdateBytesWritten: (%wZ) Reached threshold -- notifying service; Total bytes written 0x%016I64x\n",
  735. pExtension->pNtVolumeName,
  736. pExtension->BytesWritten ) );
  737. Status = SrFireNotification( SrNotificationVolume25MbWritten,
  738. pExtension,
  739. global->FileConfig.CurrentRestoreNumber );
  740. if (NT_SUCCESS(Status) == FALSE)
  741. leave;
  742. pExtension->BytesWritten -= SR_NOTIFY_BYTE_COUNT;
  743. }
  744. //
  745. // all done
  746. //
  747. Status = STATUS_SUCCESS;
  748. } finally {
  749. Status = FinallyUnwind(SrUpdateBytesWritten, Status);
  750. SrReleaseLogLock( pExtension );
  751. }
  752. RETURN(Status);
  753. } // SrUpdateBytesWritten
  754. NTSTATUS
  755. SrNotifyVolumeError(
  756. IN PSR_DEVICE_EXTENSION pExtension,
  757. IN PUNICODE_STRING pFileName OPTIONAL,
  758. IN NTSTATUS ErrorStatus,
  759. IN SR_EVENT_TYPE EventType OPTIONAL
  760. )
  761. {
  762. NTSTATUS Status = STATUS_SUCCESS;
  763. PAGED_CODE();
  764. if (!pExtension->Disabled)
  765. {
  766. //
  767. // trigger the failure notification to the service
  768. //
  769. Status = SrFireNotification( SrNotificationVolumeError,
  770. pExtension,
  771. RtlNtStatusToDosErrorNoTeb(ErrorStatus));
  772. CHECK_STATUS(Status);
  773. //
  774. // log the failure in our change log
  775. //
  776. if (pFileName != NULL &&
  777. pExtension->pNtVolumeName != NULL &&
  778. (pFileName->Length > pExtension->pNtVolumeName->Length))
  779. {
  780. Status = SrLogEvent( pExtension,
  781. SrEventVolumeError,
  782. NULL,
  783. pFileName,
  784. 0,
  785. NULL,
  786. NULL,
  787. 0,
  788. NULL );
  789. CHECK_STATUS(Status);
  790. }
  791. //
  792. // log the failure with nt
  793. //
  794. Status = SrLogError( pExtension,
  795. pFileName ? pFileName : pExtension->pNtVolumeName,
  796. ErrorStatus,
  797. EventType );
  798. CHECK_STATUS(Status);
  799. //
  800. // and temporarily disable the volume
  801. //
  802. SrTrace( VERBOSE_ERRORS,
  803. ("sr!SrNotifyVolumeError(%X): disabling \"%wZ\", error %X!\n",
  804. EventType,
  805. pExtension->pNtVolumeName,
  806. ErrorStatus) );
  807. pExtension->Disabled = TRUE;
  808. }
  809. RETURN(Status);
  810. }
  811. /******************************************************************************
  812. Routine Description:
  813. This routine clears out all the outstanding notification record in the
  814. queue.
  815. Arguments:
  816. Return Value:
  817. ******************************************************************************/
  818. VOID
  819. SrClearOutstandingNotifications (
  820. )
  821. {
  822. PSR_NOTIFICATION_RECORD pRecord;
  823. ASSERT( !IS_GLOBAL_LOCK_ACQUIRED() );
  824. try {
  825. SrAcquireGlobalLockExclusive();
  826. while (pRecord = SrDequeueNotifyRecord( _globals.pControlObject ))
  827. {
  828. //
  829. // We don't care about this notification, so just free the memory.
  830. //
  831. SR_FREE_POOL_WITH_SIG(pRecord, SR_NOTIFICATION_RECORD_TAG);
  832. }
  833. } finally {
  834. SrReleaseGlobalLock();
  835. }
  836. }
  837. /******************************************************************************
  838. Routine Description:
  839. This routine writes an eventlog entry to the eventlog. It is way more
  840. complicated then you would hope, as it needs to squeeze everything into
  841. less than 104 bytes (52 characters).
  842. Arguments:
  843. Return Value:
  844. NTSTATUS - completion code
  845. ******************************************************************************/
  846. NTSTATUS
  847. SrLogError(
  848. IN PSR_DEVICE_EXTENSION pExtension,
  849. IN PUNICODE_STRING pFileName,
  850. IN NTSTATUS ErrorStatus,
  851. IN SR_EVENT_TYPE EventType
  852. )
  853. {
  854. C_ASSERT(sizeof(NTSTATUS) == sizeof(ULONG));
  855. UCHAR ErrorPacketLength;
  856. UCHAR BasePacketLength;
  857. ULONG StringLength, ReservedLength;
  858. PIO_ERROR_LOG_PACKET ErrorLogEntry = NULL;
  859. PWCHAR String;
  860. PWCHAR pToken, pFileToken, pVolumeToken;
  861. ULONG TokenLength, FileTokenLength, VolumeTokenLength;
  862. ULONG Count;
  863. WCHAR ErrorString[10+1];
  864. NTSTATUS Status;
  865. ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
  866. ASSERT(pExtension->pNtVolumeName != NULL);
  867. ASSERT(pFileName != NULL);
  868. PAGED_CODE();
  869. //
  870. // get the name of just the file part
  871. //
  872. Status = SrFindCharReverse( pFileName->Buffer,
  873. pFileName->Length,
  874. L'\\',
  875. &pFileToken,
  876. &FileTokenLength );
  877. if (!NT_SUCCESS_NO_DBGBREAK(Status)) {
  878. FileTokenLength = 0;
  879. pFileToken = NULL;
  880. } else {
  881. //
  882. // skip the prefix slash
  883. //
  884. pFileToken += 1;
  885. FileTokenLength -= sizeof(WCHAR);
  886. }
  887. //
  888. // get the name of just the volume
  889. //
  890. Status = SrFindCharReverse( pExtension->pNtVolumeName->Buffer,
  891. pExtension->pNtVolumeName->Length,
  892. L'\\',
  893. &pVolumeToken,
  894. &VolumeTokenLength );
  895. if (!NT_SUCCESS_NO_DBGBREAK(Status))
  896. {
  897. VolumeTokenLength = 0;
  898. pVolumeToken = NULL;
  899. }
  900. else
  901. {
  902. //
  903. // skip the prefix slash
  904. //
  905. pVolumeToken += 1;
  906. VolumeTokenLength -= sizeof(WCHAR);
  907. }
  908. //
  909. // Get our error packet, holding the string and status code.
  910. //
  911. BasePacketLength = sizeof(IO_ERROR_LOG_PACKET) ;
  912. if ((BasePacketLength + sizeof(ErrorString) + VolumeTokenLength + sizeof(WCHAR) + FileTokenLength + sizeof(WCHAR)) <= ERROR_LOG_MAXIMUM_SIZE) {
  913. ErrorPacketLength = (UCHAR)(BasePacketLength
  914. + sizeof(ErrorString)
  915. + VolumeTokenLength + sizeof(WCHAR)
  916. + FileTokenLength + sizeof(WCHAR) );
  917. } else {
  918. ErrorPacketLength = ERROR_LOG_MAXIMUM_SIZE;
  919. }
  920. ErrorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry( pExtension->pDeviceObject,
  921. ErrorPacketLength );
  922. if (ErrorLogEntry == NULL)
  923. {
  924. RETURN(STATUS_INSUFFICIENT_RESOURCES);
  925. }
  926. //
  927. // Fill in the nonzero members of the packet.
  928. //
  929. ErrorLogEntry->MajorFunctionCode = SrIrpCodeFromEventType(EventType);
  930. ErrorLogEntry->ErrorCode = EVMSG_DISABLEDVOLUME;
  931. //
  932. // init the insertion strings
  933. //
  934. ErrorLogEntry->NumberOfStrings = 3;
  935. ErrorLogEntry->StringOffset = BasePacketLength;
  936. StringLength = ErrorPacketLength - BasePacketLength;
  937. ASSERT(!(StringLength % sizeof(WCHAR)));
  938. String = (PWCHAR) ((PUCHAR)ErrorLogEntry + BasePacketLength);
  939. RtlZeroMemory(String, StringLength);
  940. ASSERT(StringLength > 3 * sizeof(WCHAR));
  941. //
  942. // put the error code string in first
  943. //
  944. if (StringLength >= ((10+1)*sizeof(WCHAR)) ) {
  945. Count = swprintf(String, L"0x%08X", ErrorStatus);
  946. ASSERT(Count == 10);
  947. String += (10+1);
  948. StringLength -= (10+1) * sizeof(WCHAR);
  949. } else {
  950. String[0] = UNICODE_NULL;
  951. String += 1;
  952. StringLength -= sizeof(WCHAR);
  953. }
  954. //
  955. // now put the filename token in there
  956. //
  957. TokenLength = FileTokenLength;
  958. pToken = pFileToken;
  959. //
  960. // reserve space for the volume token
  961. //
  962. if (ErrorPacketLength == ERROR_LOG_MAXIMUM_SIZE) {
  963. if (StringLength > (VolumeTokenLength + 10)) {
  964. StringLength -= VolumeTokenLength;
  965. ReservedLength = VolumeTokenLength;
  966. } else {
  967. StringLength /= 2;
  968. ReservedLength = StringLength;
  969. if (StringLength % 2) {
  970. StringLength -=1;
  971. ReservedLength += 1;
  972. }
  973. }
  974. } else {
  975. ReservedLength = 0;
  976. }
  977. if (TokenLength > 0)
  978. {
  979. //
  980. // The filename string is appended to the end of the error log entry. We
  981. // may have to smash the middle to fit it in the limited space.
  982. //
  983. //
  984. // If the name does not fit in the packet, divide the name equally to the
  985. // prefix and suffix, with an ellipsis " .. " (4 wide characters) to indicate
  986. // the loss.
  987. //
  988. if (StringLength <= TokenLength) {
  989. ULONG BytesToCopy, ChunkLength;
  990. //
  991. // take the ending NULL off the top
  992. //
  993. StringLength -= sizeof(WCHAR);
  994. //
  995. // use half the chunk, minus the 4 " .. " characters
  996. // for the first and last half
  997. //
  998. ChunkLength = StringLength - 4*sizeof(WCHAR);
  999. ChunkLength /= 2;
  1000. //
  1001. // make sure it stays even
  1002. //
  1003. if (ChunkLength % 2)
  1004. ChunkLength -= 1;
  1005. BytesToCopy = ChunkLength;
  1006. RtlCopyMemory( String,
  1007. pToken,
  1008. BytesToCopy );
  1009. String += BytesToCopy/sizeof(WCHAR);
  1010. StringLength -= BytesToCopy;
  1011. BytesToCopy = 4*sizeof(WCHAR);
  1012. RtlCopyMemory( String,
  1013. L" .. ",
  1014. BytesToCopy );
  1015. String += BytesToCopy/sizeof(WCHAR);
  1016. StringLength -= BytesToCopy;
  1017. BytesToCopy = ChunkLength;
  1018. RtlCopyMemory( String,
  1019. ((PUCHAR)pToken)
  1020. + TokenLength
  1021. - BytesToCopy,
  1022. BytesToCopy );
  1023. String += BytesToCopy/sizeof(WCHAR);
  1024. StringLength -= BytesToCopy;
  1025. String[0] = UNICODE_NULL;
  1026. String += 1;
  1027. //
  1028. // already subtracted the NULL from the top (see above)
  1029. //
  1030. } else {
  1031. RtlCopyMemory( String,
  1032. pToken,
  1033. TokenLength );
  1034. String += TokenLength/sizeof(WCHAR);
  1035. StringLength -= TokenLength;
  1036. String[0] = UNICODE_NULL;
  1037. String += 1;
  1038. StringLength -= sizeof(WCHAR);
  1039. }
  1040. }
  1041. else
  1042. {
  1043. String[0] = UNICODE_NULL;
  1044. String += 1;
  1045. StringLength -= sizeof(WCHAR);
  1046. }
  1047. //
  1048. // put back any reserved length we kept
  1049. //
  1050. StringLength += ReservedLength;
  1051. //
  1052. // and put the volume name in there
  1053. //
  1054. TokenLength = VolumeTokenLength;
  1055. pToken = pVolumeToken;
  1056. if (TokenLength > 0)
  1057. {
  1058. //
  1059. // The filename string is appended to the end of the error log entry. We
  1060. // may have to smash the middle to fit it in the limited space.
  1061. //
  1062. //
  1063. // If the name does not fit in the packet, divide the name equally to the
  1064. // prefix and suffix, with an ellipsis " .. " (4 wide characters) to indicate
  1065. // the loss.
  1066. //
  1067. if (StringLength <= TokenLength) {
  1068. ULONG BytesToCopy, ChunkLength;
  1069. //
  1070. // take the ending NULL off the top
  1071. //
  1072. StringLength -= sizeof(WCHAR);
  1073. //
  1074. // use half the chunk, minus the 4 " .. " characters
  1075. // for the first and last half
  1076. //
  1077. ChunkLength = StringLength - 4*sizeof(WCHAR);
  1078. ChunkLength /= 2;
  1079. //
  1080. // make sure it stays even
  1081. //
  1082. if (ChunkLength % 2)
  1083. ChunkLength -= 1;
  1084. BytesToCopy = ChunkLength;
  1085. RtlCopyMemory( String,
  1086. pToken,
  1087. BytesToCopy );
  1088. String += BytesToCopy/sizeof(WCHAR);
  1089. StringLength -= BytesToCopy;
  1090. BytesToCopy = 4*sizeof(WCHAR);
  1091. RtlCopyMemory( String,
  1092. L" .. ",
  1093. BytesToCopy );
  1094. String += BytesToCopy/sizeof(WCHAR);
  1095. StringLength -= BytesToCopy;
  1096. BytesToCopy = ChunkLength;
  1097. RtlCopyMemory( String,
  1098. ((PUCHAR)pToken)
  1099. + TokenLength
  1100. - BytesToCopy,
  1101. BytesToCopy );
  1102. String += BytesToCopy/sizeof(WCHAR);
  1103. StringLength -= BytesToCopy;
  1104. String[0] = UNICODE_NULL;
  1105. String += 1;
  1106. //
  1107. // already subtracted the NULL from the top (see above)
  1108. //
  1109. } else {
  1110. RtlCopyMemory( String,
  1111. pToken,
  1112. TokenLength );
  1113. String += TokenLength/sizeof(WCHAR);
  1114. StringLength -= TokenLength;
  1115. String[0] = UNICODE_NULL;
  1116. String += 1;
  1117. StringLength -= sizeof(WCHAR);
  1118. }
  1119. }
  1120. else
  1121. {
  1122. String[0] = UNICODE_NULL;
  1123. String += 1;
  1124. StringLength -= sizeof(WCHAR);
  1125. }
  1126. IoWriteErrorLogEntry( ErrorLogEntry );
  1127. RETURN(STATUS_SUCCESS);
  1128. } // SrLogError
  1129. UCHAR
  1130. SrIrpCodeFromEventType(
  1131. IN SR_EVENT_TYPE EventType
  1132. )
  1133. {
  1134. UCHAR Irp;
  1135. PAGED_CODE();
  1136. switch (EventType)
  1137. {
  1138. case SrEventStreamChange: Irp = IRP_MJ_WRITE; break;
  1139. case SrEventAclChange: Irp = IRP_MJ_SET_SECURITY; break;
  1140. case SrEventDirectoryCreate:
  1141. case SrEventFileCreate:
  1142. case SrEventStreamOverwrite:Irp = IRP_MJ_CREATE; break;
  1143. case SrEventFileRename:
  1144. case SrEventDirectoryDelete:
  1145. case SrEventDirectoryRename:
  1146. case SrEventFileDelete:
  1147. case SrEventAttribChange: Irp = IRP_MJ_SET_INFORMATION; break;
  1148. case SrEventMountCreate:
  1149. case SrEventMountDelete: Irp = IRP_MJ_FILE_SYSTEM_CONTROL; break;
  1150. default: Irp = IRP_MJ_DEVICE_CONTROL; break;
  1151. } // switch (EventType)
  1152. return Irp;
  1153. } // SrIrpCodeFromEventType