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.

670 lines
19 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. oplock.c
  5. Abstract:
  6. This module contains the SPUDCreateFile service.
  7. Author:
  8. John Ballard (jballard) 13-Dec-1996
  9. Revision History:
  10. Keith Moore (keithmo) 02-Feb-1998
  11. Made it work, added much needed comments.
  12. --*/
  13. #include "spudp.h"
  14. //
  15. // Private prototypes.
  16. //
  17. NTSTATUS
  18. SpudpOplockCompletion(
  19. PDEVICE_OBJECT DeviceObject,
  20. PIRP Irp,
  21. PVOID Context
  22. );
  23. #ifdef ALLOC_PRAGMA
  24. #pragma alloc_text( PAGE, SPUDCreateFile )
  25. #endif
  26. #if 0
  27. NOT PAGEABLE -- SpudpOplockCompletion
  28. #endif
  29. //
  30. // Public routines.
  31. //
  32. NTSTATUS
  33. SPUDCreateFile(
  34. OUT PHANDLE FileHandle,
  35. IN POBJECT_ATTRIBUTES ObjectAttributes,
  36. OUT PIO_STATUS_BLOCK IoStatusBlock,
  37. IN ULONG FileAttributes,
  38. IN ULONG ShareAccess,
  39. IN ULONG CreateOptions,
  40. IN SECURITY_INFORMATION SecurityInformation,
  41. OUT PSECURITY_DESCRIPTOR SecDescBuffer,
  42. IN ULONG SecDescLength,
  43. OUT PULONG SecDescLengthNeeded,
  44. IN PVOID OplockContext,
  45. IN LARGE_INTEGER OplockMaxFileSize,
  46. OUT PBOOLEAN OplockGranted,
  47. OUT PSPUD_FILE_INFORMATION FileInfo
  48. )
  49. /*++
  50. Routine Description:
  51. This service opens a file and queries information about the file. This
  52. service can also optionally retrieve any security descriptor associated
  53. with the file and issue an oplock request.
  54. Arguments:
  55. FileHandle - A pointer to a variable to receive the handle to the open
  56. file.
  57. ObjectAttributes - Supplies the attributes to be used for file object
  58. (name, SECURITY_DESCRIPTOR, etc.)
  59. IoStatusBlock - Specifies the address of the caller's I/O status block.
  60. FileAttributes - Specifies the attributes that should be set on the file,
  61. if it is created.
  62. ShareAccess - Supplies the types of share access that the caller would
  63. like to the file.
  64. CreateOptions - Caller options for how to perform the create/open.
  65. SecurityInformation - Indicates the type of security information to
  66. retrieve.
  67. SecDescBuffer - Supplies a buffer to receive the file's security
  68. descriptor.
  69. SecDescLength - Supplies the length of the security descriptor buffer.
  70. SecDescLengthNeeded - Receives the length needed to store the security
  71. descriptor.
  72. OplockContext - Supplies an uninterpreted context used during oplock
  73. break notifications. If this value is NULL, then no oplock request
  74. is issued.
  75. OplockMaxFileSize = If the size of the file opened is larger than
  76. OplockMaxFileSize then no oplock request is issued.
  77. OplockGranted - A pointer to a variable to receive the status of the
  78. oplock request. This parameter is ignored if OplockContext is NULL.
  79. FileInfo - Supplies a buffer to receive information about the file.
  80. Return Value:
  81. NTSTATUS - Completion status.
  82. --*/
  83. {
  84. NTSTATUS status;
  85. PFILE_OBJECT fileObject;
  86. PIRP irp;
  87. PIO_STACK_LOCATION irpSp;
  88. PDEVICE_OBJECT deviceObject;
  89. HANDLE localFileHandle;
  90. IO_STATUS_BLOCK localIoStatusBlock;
  91. FILE_BASIC_INFORMATION basicInfo;
  92. FILE_STANDARD_INFORMATION standardInfo;
  93. PVOID completionPort;
  94. //
  95. // Sanity check.
  96. //
  97. PAGED_CODE();
  98. ASSERT( SPUD_OPLOCK_BREAK_OPEN == FILE_OPLOCK_BROKEN_TO_LEVEL_2 );
  99. ASSERT( SPUD_OPLOCK_BREAK_CLOSE == FILE_OPLOCK_BROKEN_TO_NONE );
  100. status = SPUD_ENTER_SERVICE( "SPUDCreateFile", TRUE );
  101. if( !NT_SUCCESS(status) ) {
  102. return status;
  103. }
  104. //
  105. // SPUD doesn't support kernel-mode callers. In fact, we don't
  106. // even build the "system stubs" necessary to invoke SPUD from
  107. // kernel-mode.
  108. //
  109. ASSERT( ExGetPreviousMode() == UserMode );
  110. //
  111. // Probe the generic arguments. We don't need to probe the FileHandle
  112. // and IoStatusBlock parameters, as they will be probed by IoCreateFile().
  113. //
  114. try {
  115. //
  116. // The FileInfo parameter must be writeable by the caller.
  117. //
  118. ProbeForWrite( FileInfo, sizeof(*FileInfo), sizeof(ULONG) );
  119. } except(EXCEPTION_EXECUTE_HANDLER) {
  120. status = GetExceptionCode();
  121. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  122. return status;
  123. }
  124. //
  125. // Open the file. If successful, this will write the newly-opened
  126. // file handle into *FileHandle.
  127. //
  128. status = IoCreateFile(
  129. FileHandle, // FileHandle
  130. GENERIC_READ // DesiredAccess
  131. | SYNCHRONIZE //
  132. | FILE_READ_ATTRIBUTES, //
  133. ObjectAttributes, // ObjectAttributes
  134. IoStatusBlock, // IoStatusBlock
  135. NULL, // AllocationSize
  136. FileAttributes, // FileAttributes
  137. ShareAccess, // ShareAccess
  138. FILE_OPEN, // Disposition
  139. CreateOptions, // CreateOptions
  140. NULL, // EaBuffer
  141. 0, // EaLength
  142. CreateFileTypeNone, // CreateFileType
  143. NULL, // ExtraCreateParameters
  144. 0 // Options
  145. );
  146. if( !NT_SUCCESS(status) ) {
  147. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  148. return status;
  149. }
  150. //
  151. // Snag the handle from the user-mode buffer.
  152. //
  153. try {
  154. localFileHandle = *FileHandle;
  155. } except( EXCEPTION_EXECUTE_HANDLER ) {
  156. //
  157. // We faulted trying to read the file handle from user-mode memory.
  158. // The user-mode code must have mucked with the virtual address
  159. // space after we called IoCreateFile(). Since we cannot get the
  160. // file handle, we cannot close the file, and the user-mode code is
  161. // going to leak the handle.
  162. //
  163. status = GetExceptionCode();
  164. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  165. return status;
  166. }
  167. //
  168. // Query the file attributes.
  169. //
  170. status = ZwQueryInformationFile(
  171. localFileHandle, // FileHandle
  172. &localIoStatusBlock, // IoStatusBlock
  173. &basicInfo, // FileInformation
  174. sizeof(basicInfo), // Length
  175. FileBasicInformation // FileInformationClass
  176. );
  177. if( NT_SUCCESS(status) ) {
  178. status = ZwQueryInformationFile(
  179. localFileHandle, // FileHandle
  180. &localIoStatusBlock, // IoStatusBlock
  181. &standardInfo, // FileInformation
  182. sizeof(standardInfo), // Length
  183. FileStandardInformation // FileInformationClass
  184. );
  185. }
  186. if( NT_SUCCESS(status) ) {
  187. //
  188. // Copy the file attributes to the user-mode buffer.
  189. //
  190. try {
  191. RtlCopyMemory(
  192. &FileInfo->BasicInformation,
  193. &basicInfo,
  194. sizeof(basicInfo)
  195. );
  196. RtlCopyMemory(
  197. &FileInfo->StandardInformation,
  198. &standardInfo,
  199. sizeof(standardInfo)
  200. );
  201. } except( EXCEPTION_EXECUTE_HANDLER ) {
  202. status = GetExceptionCode();
  203. }
  204. }
  205. //
  206. // If we failed for any reason (either we failed to query the attributes
  207. // or we faulted trying to copy them to user-mode) then close the file
  208. // handle and bail.
  209. //
  210. if( !NT_SUCCESS(status) ) {
  211. NtClose( localFileHandle );
  212. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  213. return status;
  214. }
  215. //
  216. // If the caller passed in a security descriptor buffer, then try to
  217. // query the security descriptor.
  218. //
  219. if( SecDescLength > 0 ) {
  220. //
  221. // Query the security descriptor from the file. Note that
  222. // since the previous mode == UserMode, we don't need to
  223. // probe SecDescBuffer or SecDescLengthNeeded (they will be
  224. // probed by the NtQuerySecurityObject() API).
  225. //
  226. status = NtQuerySecurityObject(
  227. localFileHandle, // Handle
  228. SecurityInformation, // SecurityInformation
  229. SecDescBuffer, // SecurityDescriptor
  230. SecDescLength, // Length
  231. SecDescLengthNeeded // LengthNeeded
  232. );
  233. if( !NT_SUCCESS(status) ) {
  234. if( status == STATUS_NOT_SUPPORTED ) {
  235. //
  236. // This status code is returned for filesystems that don't
  237. // support security. We'll just fake an empty descriptor.
  238. //
  239. try {
  240. *SecDescLengthNeeded = 0;
  241. status = STATUS_SUCCESS;
  242. } except( EXCEPTION_EXECUTE_HANDLER ) {
  243. status = GetExceptionCode();
  244. }
  245. } else if( status == STATUS_BUFFER_TOO_SMALL ) {
  246. //
  247. // Mapping STATUS_BUFFER_TOO_SMALL to STATUS_SUCCESS seems
  248. // a bit bizarre. The intent here is to succeed the
  249. // SPUDCreateFile() call. The fact that *SecDescLengthNeeded
  250. // is returned with a value larger than SecDescLength is the
  251. // user-mode code's signal that it should allocate a new
  252. // buffer & retrieve the security descriptor "out-of-band".
  253. //
  254. #if DBG
  255. try {
  256. ASSERT( *SecDescLengthNeeded > SecDescLength );
  257. } except( EXCEPTION_EXECUTE_HANDLER ) {
  258. NOTHING;
  259. }
  260. #endif
  261. status = STATUS_SUCCESS;
  262. }
  263. if( !NT_SUCCESS(status) ) {
  264. NtClose( localFileHandle );
  265. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  266. return status;
  267. }
  268. }
  269. }
  270. //
  271. // If OplockContext == NULL then the caller is not interested in
  272. // oplocks, so we can just return successfully right now.
  273. //
  274. if( OplockContext == NULL ) {
  275. ASSERT( status == STATUS_SUCCESS );
  276. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  277. return status;
  278. }
  279. //
  280. // Probe the oplock-specific parameters.
  281. //
  282. try {
  283. //
  284. // The OplockGranted parameter must be writeable. Set it to
  285. // FALSE until proven otherwise.
  286. //
  287. ProbeAndWriteBoolean( OplockGranted, FALSE );
  288. } except(EXCEPTION_EXECUTE_HANDLER) {
  289. status = GetExceptionCode();
  290. NtClose( localFileHandle );
  291. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  292. return status;
  293. }
  294. //
  295. // If the entity just opened is actually a directory (rather than
  296. // a "normal" file) then there's no point in trying to acquire the
  297. // oplock.
  298. //
  299. // Note that this check must be after the OplockGranted parameter
  300. // is probed so that we know it is set to FALSE.
  301. //
  302. if( standardInfo.Directory ) {
  303. ASSERT( status == STATUS_SUCCESS );
  304. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  305. return status;
  306. }
  307. //
  308. // If the file is smaller than the size specified in the
  309. // OplockMaxFileSize parameter, then the user doesn't want
  310. // an oplock.
  311. //
  312. if ( standardInfo.EndOfFile.QuadPart > OplockMaxFileSize.QuadPart ) {
  313. ASSERT( status == STATUS_SUCCESS );
  314. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  315. return status;
  316. }
  317. //
  318. // See if we can acquire the completion port.
  319. //
  320. completionPort = SpudReferenceCompletionPort();
  321. if( completionPort == NULL ) {
  322. status = STATUS_INVALID_DEVICE_REQUEST;
  323. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  324. return status;
  325. }
  326. //
  327. // Reference the file handle
  328. //
  329. status = ObReferenceObjectByHandle(
  330. localFileHandle, // Handle
  331. 0L, // DesiredAccess
  332. *IoFileObjectType, // ObjectType
  333. UserMode, // AccessMode
  334. (PVOID *)&fileObject, // Object
  335. NULL // HandleInformation
  336. );
  337. if( !NT_SUCCESS(status) ) {
  338. NtClose( localFileHandle );
  339. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, TRUE );
  340. return status;
  341. }
  342. TRACE_OB_REFERENCE( fileObject );
  343. //
  344. // Chase down the device object associated with this file object.
  345. //
  346. deviceObject = IoGetRelatedDeviceObject( fileObject );
  347. //
  348. // Allocate and initialize the IRP.
  349. //
  350. irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
  351. if( !irp ) {
  352. TRACE_OB_DEREFERENCE( fileObject );
  353. ObDereferenceObject( fileObject );
  354. NtClose( localFileHandle );
  355. status = STATUS_INSUFFICIENT_RESOURCES;
  356. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, TRUE );
  357. return status;
  358. }
  359. irp->RequestorMode = UserMode;
  360. irp->Tail.Overlay.OriginalFileObject = fileObject;
  361. irp->Tail.Overlay.Thread = PsGetCurrentThread();
  362. irpSp = IoGetNextIrpStackLocation( irp );
  363. irpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
  364. irpSp->FileObject = fileObject;
  365. irpSp->DeviceObject = deviceObject;
  366. // irpSp->Parameters.FileSystemControl.OutputBufferLength = 0;
  367. // irpSp->Parameters.FileSystemControl.InputBufferLength = 0;
  368. irpSp->Parameters.FileSystemControl.FsControlCode = FSCTL_REQUEST_BATCH_OPLOCK;
  369. IoSetCompletionRoutine(
  370. irp, // Irp
  371. SpudpOplockCompletion, // CompletionRoutine
  372. OplockContext, // Context
  373. TRUE, // InvokeOnSuccess
  374. TRUE, // InvokeOnError
  375. TRUE // InvokeOnCancel
  376. );
  377. //
  378. // Issue the IRP to the file system.
  379. //
  380. status = IoCallDriver( deviceObject, irp );
  381. if( NT_SUCCESS(status) ) {
  382. //
  383. // The oplock IRP was successfully issued to the file system. We
  384. // can now assume the IRP will complete later, therefore we will
  385. // set the user's OplockGranted flag.
  386. //
  387. try {
  388. *OplockGranted = TRUE;
  389. } except( EXCEPTION_EXECUTE_HANDLER ) {
  390. //
  391. // Grr...
  392. //
  393. // This is a sticky situation. We've already opened the file
  394. // and successfully acquired the oplock. Closing the file handle
  395. // here would probably confuse the user-mode code, as it would
  396. // see the oplock break after an unsuccessful SPUDCreateFile().
  397. //
  398. // The only thing we can do here is just drop the failure on the
  399. // floor. This is not as bad as it seems, as the user-mode code
  400. // will presumably fault when it tries to access OplockGranted.
  401. //
  402. }
  403. }
  404. //
  405. // Regardless of the completion status of the oplock IRP, return
  406. // STATUS_SUCCESS to the caller. Failure to acquire the oplock is
  407. // insufficient grounds to fail the SPUDCreateFile() call.
  408. //
  409. status = STATUS_SUCCESS;
  410. SPUD_LEAVE_SERVICE( "SPUDCreateFile", status, FALSE );
  411. return status;
  412. } // SPUDCreateFile
  413. //
  414. // Private routines.
  415. //
  416. NTSTATUS
  417. SpudpOplockCompletion(
  418. PDEVICE_OBJECT DeviceObject,
  419. PIRP Irp,
  420. PVOID Context
  421. )
  422. /*++
  423. Routine Description:
  424. Completion routine for oplock IRPs.
  425. Arguments:
  426. DeviceObject - The device object completing the request (unused).
  427. Irp - The IRP being completed.
  428. Context - The context associated with the request. This is actually
  429. the user's OplockContext passed into SPUDCreateFile().
  430. Return Value:
  431. NTSTATUS - Completion status.
  432. --*/
  433. {
  434. PFILE_OBJECT fileObject;
  435. //
  436. // Dereference the file object since we're done with it.
  437. //
  438. fileObject = Irp->Tail.Overlay.OriginalFileObject;
  439. TRACE_OB_DEREFERENCE( fileObject );
  440. ObDereferenceObject( fileObject );
  441. if( NT_SUCCESS(Irp->IoStatus.Status) ) {
  442. //
  443. // Sanity check.
  444. //
  445. ASSERT( Irp->IoStatus.Information == SPUD_OPLOCK_BREAK_OPEN ||
  446. Irp->IoStatus.Information == SPUD_OPLOCK_BREAK_CLOSE );
  447. //
  448. // Post the IRP to the completion port. The completion key must be
  449. // the OplockContext passed into SPUDCreateFile().
  450. //
  451. // Note that NULL is used as a distinguished value in UserApcContext.
  452. // This is an indicator to AtqpProcessContext() that an I/O
  453. // completion is actually an oplock break.
  454. //
  455. Irp->Tail.CompletionKey = Context;
  456. Irp->Overlay.AsynchronousParameters.UserApcContext = NULL;
  457. //
  458. // Hack-O-Rama. This is absolutely required to get the I/O completion
  459. // port stuff to work. It turns out that the CurrentStackLocation
  460. // field overlays the PacketType field. Since PacketType must be set
  461. // to IopCompletionPacketIrp (which just happens to be zero), we'll
  462. // set CurrentStackLocation to NULL. Ugly. We should really make this
  463. // part of the kernel. Maybe something like this:
  464. //
  465. // VOID
  466. // IoSetIrpCompletion(
  467. // IN PVOID IoCompletion,
  468. // IN PVOID KeyContext,
  469. // IN PVOID ApcContext,
  470. // IN PIRP Irp
  471. // );
  472. //
  473. // We could then replace the following lines (and a few lines above)
  474. // with:
  475. //
  476. // IoSetIrpCompletion(
  477. // SpudCompletionPort,
  478. // Context,
  479. // NULL,
  480. // Irp
  481. // );
  482. //
  483. Irp->Tail.Overlay.CurrentStackLocation = NULL;
  484. KeInsertQueue(
  485. (PKQUEUE)SpudCompletionPort,
  486. &Irp->Tail.Overlay.ListEntry
  487. );
  488. } else {
  489. //
  490. // The oplock IRP failed. We'll just drop this IRP on the floor and
  491. // free it. Since we already notified the caller (through the
  492. // OplockGranted parameter to SPUDCreateFile) that the oplock could
  493. // not be acquired, we don't want to notify them again through the
  494. // completion port.
  495. //
  496. // Also note that pending oplock IRPs are not cancelled in the
  497. // "normal" sense (i.e. with STATUS_CANCELLED) when the file handle
  498. // is closed. Rather, they are completed *successfully* with the
  499. // FILE_OPLOCK_BROKEN_TO_NONE (SPUD_OPLOCK_BREAK_CLOSE) completion
  500. // code. Ergo, cancelled oplock IRPs should never go through this
  501. // code path.
  502. //
  503. IoFreeIrp( Irp );
  504. }
  505. //
  506. // We're done with the completion port. Remove the reference we added
  507. // in SPUDCreateFile().
  508. //
  509. SpudDereferenceCompletionPort();
  510. //
  511. // Tell IO to stop processing this IRP. The IRP will be freed in
  512. // NtRemoveIoCompletion (the kernel-mode worker for the
  513. // GetQueuedCompletionStatus() API).
  514. //
  515. return STATUS_MORE_PROCESSING_REQUIRED;
  516. } // SpudpOplockCompletion