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.

5952 lines
166 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. FileLock.c
  5. Abstract:
  6. The file lock package provides a set of routines that allow the
  7. caller to handle byte range file lock requests. A variable of
  8. type FILE_LOCK is needed for every file with byte range locking.
  9. The package provides routines to set and clear locks, and to
  10. test for read or write access to a file with byte range locks.
  11. The main idea of the package is to have the file system initialize
  12. a FILE_LOCK variable for every data file as its opened, and then
  13. to simply call a file lock processing routine to handle all IRP's
  14. with a major function code of LOCK_CONTROL. The package is responsible
  15. for keeping track of locks and for completing the LOCK_CONTROL IRPS.
  16. When processing a read or write request the file system can then call
  17. two query routines to check for access.
  18. Most of the code for processing IRPS and checking for access use
  19. paged pool and can encounter a page fault, therefore the check routines
  20. cannot be called at DPC level. To help servers that do call the file
  21. system to do read/write operations at DPC level there is a additional
  22. routine that simply checks for the existence of a lock on a file and
  23. can be run at DPC level.
  24. Concurrent access to the FILE_LOCK variable must be controlled by the
  25. caller.
  26. The functions provided in this package are as follows:
  27. o FsRtlInitializeFileLock - Initialize a new FILE_LOCK structure.
  28. o FsRtlUninitializeFileLock - Uninitialize an existing FILE_LOCK
  29. structure.
  30. o FsRtlProcessFileLock - Process an IRP whose major function code
  31. is LOCK_CONTROL.
  32. o FsRtlCheckLockForReadAccess - Check for read access to a range
  33. of bytes in a file given an IRP.
  34. o FsRtlCheckLockForWriteAccess - Check for write access to a range
  35. of bytes in a file given an IRP.
  36. o FsRtlAreThereCurrentFileLocks - Check if there are any locks
  37. currently assigned to a file.
  38. o FsRtlGetNextFileLock - This procedure enumerates the current locks
  39. of a file lock variable.
  40. o FsRtlFastCheckLockForRead - Check for read access to a range of
  41. bytes in a file given separate parameters.
  42. o FsRtlFastCheckLockForWrite - Check for write access to a range of
  43. bytes in a file given separate parameters.
  44. o FsRtlFastLock - A fast non-Irp based way to get a lock
  45. o FsRtlFastUnlockSingle - A fast non-Irp based way to release a single
  46. lock
  47. o FsRtlFastUnlockAll - A fast non-Irp based way to release all locks
  48. held by a file object.
  49. o FsRtlFastUnlockAllByKey - A fast non-Irp based way to release all
  50. locks held by a file object that match a key.
  51. Authors:
  52. Gary Kimura [GaryKi] 24-Apr-1990
  53. Dan Lovinger [DanLo] 22-Sep-1995
  54. Revision History:
  55. --*/
  56. #include "FsRtlP.h"
  57. //
  58. // Local constants
  59. //
  60. //
  61. // Local debug trace level
  62. //
  63. #define Dbg (0x20000000)
  64. //
  65. // YA definition of INLINE
  66. //
  67. #ifndef INLINE
  68. #define INLINE __inline
  69. #endif
  70. #define TAG_EXCLUSIVE_LOCK 'xeLF'
  71. #define TAG_FILE_LOCK 'lfLF'
  72. #define TAG_LOCK_INFO 'ilLF'
  73. #define TAG_LOCKTREE_NODE 'nlLF'
  74. #define TAG_SHARED_LOCK 'hsLF'
  75. #define TAG_WAITING_LOCK 'lwLF'
  76. //
  77. // Globals
  78. //
  79. //
  80. // This mutex synchronizes threads competing to initialize file lock structures.
  81. //
  82. FAST_MUTEX FsRtlCreateLockInfo;
  83. //
  84. // This spinlock resolves the race between teardown of a file's lock info and
  85. // cancellation of waiting locks for that file. We must always be able to save
  86. // a cancelled IRP for the cancelling thread.
  87. //
  88. KSPIN_LOCK FsRtlFileLockCancelCollideLock;
  89. SINGLE_LIST_ENTRY FsRtlFileLockCancelCollideList;
  90. //
  91. // Lookaside lists
  92. //
  93. // Here is a good place to note why this is still nonpaged. We need to be able
  94. // to cancel lock IRPs at DPC, and the ripple effects of this (esp. granting waiting
  95. // locks and synchronizing the waiting list) implies some unfortunate realities.
  96. //
  97. // This should be reinvestigated post NT 5.0.
  98. //
  99. NPAGED_LOOKASIDE_LIST FsRtlSharedLockLookasideList;
  100. NPAGED_LOOKASIDE_LIST FsRtlExclusiveLockLookasideList;
  101. NPAGED_LOOKASIDE_LIST FsRtlWaitingLockLookasideList;
  102. NPAGED_LOOKASIDE_LIST FsRtlLockTreeNodeLookasideList;
  103. NPAGED_LOOKASIDE_LIST FsRtlLockInfoLookasideList;
  104. PAGED_LOOKASIDE_LIST FsRtlFileLockLookasideList;
  105. //
  106. // Local structures
  107. //
  108. /*++
  109. Some of the decisions made regarding the internal datastructres may not be clear,
  110. so I should discuss the evolution of this design.
  111. The original file lock implementation was a single linked list, extended in the MP
  112. case to a set of linked lists which each held locks in page-aligned segments of the
  113. file. If locks spilled over these page-aligned segments the code fell back to the
  114. UP single linked list. There are clearly peformance implications with substantial
  115. usage of file locks, since these are mandatory locks.
  116. This implementation goes for O(lgn) search performance by using splay trees. In order to
  117. apply simple trees to this problem no node of the tree can overlap, so since shared
  118. locks can in fact overlap something must be done. The solution used here is to have
  119. a meta-structure contain all locks which do overlap and have the tree operations
  120. split and merge these nodes of (potentially) multiple locks. This is the LOCKTREE_NODE.
  121. It should be noted that the worst case add/delete lock times are still linear.
  122. Exclusive locks pose a problem because of an asymmetry in the semantics of applying
  123. locks to a file. If a process applies a shared lock to a section of a file, no application
  124. of an exclusive lock to bytes in that section can succeed. However, if a process
  125. applies an exclusive lock, that same process can get a shared lock as well. This
  126. behavior conflicts with the mergeable node since by applying locks in a given order
  127. we can get a node to have many shared locks and "rogue" exclusive locks which are
  128. hidden except to a linear search, which is what we're designing out. So exclusive locks
  129. must be seperated from the shared locks. This is the reason we have two lock trees.
  130. Since we have two lock trees, the average case search is now O(lgm + lgn) for m exlcusive
  131. and n shared. Also, since no exclusive locks can ever overlap each other it is now
  132. unreasonable to have them use LOCKTREE_NODES - this would impose a memory penalty on code
  133. which was weighted toward exclusive locks. This means that the exclusive locks should
  134. be wired into the splay tree directly. So we need an RTL_SPLAY_LINKS, but this is 64 bits
  135. bigger than the SINGLE_LIST_ENTRY which shared locks need (to be threaded off of a
  136. LOCKTREE_NODE), which dictates seperate shared and exclusive lock structures to avoid
  137. penalizing code which was weighted toward shared locks by having that wasted 64 bits per
  138. lock. Hence EX_LOCK and SH_LOCK (they actually occupy different pool block sizes).
  139. Zero length locks are a bizzare creation, and there is some errata relating to them. It
  140. used to be the case that zero length locks would be granted without exception. This is
  141. flat out bogus, and has been changed (NT 4.0). They are now subject to failure if they
  142. occupy a point interior to a lock of a type that can cause an access failure. A particular
  143. case that was previously allowed was a zero length exclusive lock interior to another
  144. exclusive lock.
  145. Zero length locks cannot conflict with zero length locks. This is the subject of some
  146. special code throughout the module. Note especially that zero length exclusive locks can
  147. "overlap". Zero length locks also cannot conflict at the starting byte and ending byte of a
  148. range - they are points on the line.
  149. --*/
  150. typedef struct _LOCKTREE_NODE {
  151. //
  152. // List of locks under this node
  153. //
  154. SINGLE_LIST_ENTRY Locks;
  155. //
  156. // Flag whether this node is holey as a result of a failed allocation
  157. // during a node split. During deletion of shared locks, we may
  158. // discover that the locks in the node no longer have total overlap
  159. // but cannot allocate resources to create the new nodes in the tree.
  160. //
  161. // Any insert into the region occupied by a holey node will finish by
  162. // trying to split a holey node up. Any split or access check in a
  163. // holey node must completely traverse the locks at the node.
  164. //
  165. BOOLEAN HoleyNode;
  166. //
  167. // Maximum byte offset affected by locks in this node.
  168. // Note: minimum offset is the starting offset of the
  169. // first lock at this node.
  170. //
  171. ULONGLONG Extent;
  172. //
  173. // Splay tree links to parent, lock groups strictly less than
  174. // and lock groups strictly greater than locks in this node.
  175. //
  176. RTL_SPLAY_LINKS Links;
  177. //
  178. // Last lock in the list (useful for node collapse under insert)
  179. //
  180. SINGLE_LIST_ENTRY Tail;
  181. } LOCKTREE_NODE, *PLOCKTREE_NODE;
  182. //
  183. // Define the threading wrappers for lock information
  184. //
  185. //
  186. // Each shared lock record corresponds to a current granted lock and is
  187. // maintained in a queue off of a LOCKTREE_NODE's Locks list. The list
  188. // of current locks is ordered according to the starting byte of the lock.
  189. //
  190. typedef struct _SH_LOCK {
  191. //
  192. // The link structures for the list of shared locks.
  193. //
  194. SINGLE_LIST_ENTRY Link;
  195. //
  196. // The actual locked range
  197. //
  198. FILE_LOCK_INFO LockInfo;
  199. } SH_LOCK, *PSH_LOCK;
  200. //
  201. // Each exclusive lock record corresponds to a current granted lock and is
  202. // threaded into the exclusive lock tree.
  203. //
  204. typedef struct _EX_LOCK {
  205. //
  206. // The link structures for the list of current locks.
  207. //
  208. RTL_SPLAY_LINKS Links;
  209. //
  210. // The actual locked range
  211. //
  212. FILE_LOCK_INFO LockInfo;
  213. } EX_LOCK, *PEX_LOCK;
  214. //
  215. // Each Waiting lock record corresponds to a IRP that is waiting for a
  216. // lock to be granted and is maintained in a queue off of the FILE_LOCK's
  217. // WaitingLockQueue list.
  218. //
  219. typedef struct _WAITING_LOCK {
  220. //
  221. // The link structures for the list of waiting locks
  222. //
  223. SINGLE_LIST_ENTRY Link;
  224. //
  225. // The optional procedure to call to complete a request. We require this
  226. // in the individual waiters so that we can heal the race between cancellation
  227. // and teardown of a lock structure.
  228. //
  229. PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine;
  230. //
  231. // The context field to use when completing the irp via the alternate
  232. // routine
  233. //
  234. PVOID Context;
  235. //
  236. // A pointer to the IRP that is waiting for a lock
  237. //
  238. PIRP Irp;
  239. } WAITING_LOCK, *PWAITING_LOCK;
  240. //
  241. // Each lock or waiting onto some lock queue.
  242. //
  243. typedef struct _LOCK_QUEUE {
  244. //
  245. // Sync to guard queue access.
  246. //
  247. KSPIN_LOCK QueueSpinLock;
  248. //
  249. // The items contain locktrees of the current granted
  250. // locks and a list of the waiting locks
  251. //
  252. PRTL_SPLAY_LINKS SharedLockTree;
  253. PRTL_SPLAY_LINKS ExclusiveLockTree;
  254. SINGLE_LIST_ENTRY WaitingLocks;
  255. SINGLE_LIST_ENTRY WaitingLocksTail;
  256. } LOCK_QUEUE, *PLOCK_QUEUE;
  257. //
  258. // Any file_lock which has had a lock applied gets a non-paged pool
  259. // structure which tracks the current locks applied to the file
  260. //
  261. typedef struct _LOCK_INFO {
  262. //
  263. // LowestLockOffset retains the offset of the lowest existing
  264. // lock. This facilitates a quick check to see if a read or
  265. // write can proceed without locking the lock database. This is
  266. // helpful for applications that use mirrored locks -- all locks
  267. // are higher than file data.
  268. //
  269. // If the lowest lock has an offset > 0xffffffff, LowestLockOffset
  270. // is set to 0xffffffff.
  271. //
  272. ULONG LowestLockOffset;
  273. //
  274. // The optional procedure to call to complete a request
  275. //
  276. PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine;
  277. //
  278. // The optional procedure to call when unlocking a byte range
  279. //
  280. PUNLOCK_ROUTINE UnlockRoutine;
  281. //
  282. // The locked ranges
  283. //
  284. LOCK_QUEUE LockQueue;
  285. } LOCK_INFO, *PLOCK_INFO;
  286. //
  287. // Local Macros
  288. //
  289. //
  290. // The following macros sort out the allocation of internal structures.
  291. //
  292. INLINE
  293. PSH_LOCK
  294. FsRtlAllocateSharedLock (
  295. VOID
  296. )
  297. {
  298. return (PSH_LOCK) ExAllocateFromNPagedLookasideList( &FsRtlSharedLockLookasideList );
  299. }
  300. INLINE
  301. PEX_LOCK
  302. FsRtlAllocateExclusiveLock (
  303. VOID
  304. )
  305. {
  306. return (PEX_LOCK) ExAllocateFromNPagedLookasideList( &FsRtlExclusiveLockLookasideList );
  307. }
  308. INLINE
  309. PWAITING_LOCK
  310. FsRtlAllocateWaitingLock (
  311. VOID
  312. )
  313. {
  314. return (PWAITING_LOCK) ExAllocateFromNPagedLookasideList( &FsRtlWaitingLockLookasideList );
  315. }
  316. INLINE
  317. PLOCKTREE_NODE
  318. FsRtlAllocateLockTreeNode (
  319. VOID
  320. )
  321. {
  322. return (PLOCKTREE_NODE) ExAllocateFromNPagedLookasideList( &FsRtlLockTreeNodeLookasideList );
  323. }
  324. INLINE
  325. PLOCK_INFO
  326. FsRtlAllocateLockInfo (
  327. VOID
  328. )
  329. {
  330. return (PLOCK_INFO) ExAllocateFromNPagedLookasideList( &FsRtlLockInfoLookasideList );
  331. }
  332. INLINE
  333. VOID
  334. FsRtlFreeSharedLock (
  335. IN PSH_LOCK C
  336. )
  337. {
  338. ExFreeToNPagedLookasideList( &FsRtlSharedLockLookasideList, (PVOID)C );
  339. }
  340. INLINE
  341. VOID
  342. FsRtlFreeExclusiveLock (
  343. IN PEX_LOCK C
  344. )
  345. {
  346. ExFreeToNPagedLookasideList( &FsRtlExclusiveLockLookasideList, (PVOID)C );
  347. }
  348. INLINE
  349. VOID
  350. FsRtlFreeWaitingLock (
  351. IN PWAITING_LOCK C
  352. )
  353. {
  354. ExFreeToNPagedLookasideList( &FsRtlWaitingLockLookasideList, (PVOID)C );
  355. }
  356. INLINE
  357. VOID
  358. FsRtlFreeLockTreeNode (
  359. IN PLOCKTREE_NODE C
  360. )
  361. {
  362. ExFreeToNPagedLookasideList( &FsRtlLockTreeNodeLookasideList, (PVOID)C );
  363. }
  364. INLINE
  365. VOID
  366. FsRtlFreeLockInfo (
  367. IN PLOCK_INFO C
  368. )
  369. {
  370. ExFreeToNPagedLookasideList( &FsRtlLockInfoLookasideList, (PVOID)C );
  371. }
  372. #define FsRtlAcquireLockQueue(a,b) ExAcquireSpinLock(&(a)->QueueSpinLock, b)
  373. #define FsRtlReleaseLockQueue(a,b) ExReleaseSpinLock(&(a)->QueueSpinLock, b)
  374. #define FsRtlAcquireLockQueueAtDpc(a) ExAcquireSpinLockAtDpcLevel(&(a)->QueueSpinLock)
  375. #define FsRtlReleaseLockQueueFromDpc(a) ExReleaseSpinLockFromDpcLevel(&(a)->QueueSpinLock)
  376. #define FsRtlAcquireCancelCollide(a) ExAcquireSpinLock(&FsRtlFileLockCancelCollideLock, a)
  377. #define FsRtlReleaseCancelCollide(a) ExReleaseSpinLock(&FsRtlFileLockCancelCollideLock, a)
  378. #define FsRtlAcquireCancelCollideAtDpc(a) ExAcquireSpinLockAtDpcLevel(&FsRtlFileLockCancelCollideLock)
  379. #define FsRtlReleaseCancelCollideFromDpc(a) ExReleaseSpinLockFromDpcLevel(&FsRtlFileLockCancelCollideLock)
  380. //
  381. // Generic way to complete a lock IRP. We like to treat this as an overloaded
  382. // function so it can be used with LOCK_INFO, FILE_LOCK and WAITING_LOCK
  383. // structures, as appropriate using paged/nonpaged pool to discover the completion
  384. // routine.
  385. //
  386. #define FsRtlCompleteLockIrp( A, B, C, D, E, F ) \
  387. FsRtlCompleteLockIrpReal( (A)->CompleteLockIrpRoutine, \
  388. B, \
  389. C, \
  390. D, \
  391. E, \
  392. F )
  393. INLINE
  394. VOID
  395. FsRtlCompleteLockIrpReal (
  396. IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine,
  397. IN PVOID Context,
  398. IN PIRP Irp,
  399. IN NTSTATUS Status,
  400. IN PNTSTATUS NewStatus,
  401. IN PFILE_OBJECT FileObject
  402. )
  403. {
  404. //
  405. // This fools the compiler into generating the Status only once
  406. // if it is calculated from an expression.
  407. //
  408. NTSTATUS LocalStatus = Status;
  409. if (CompleteLockIrpRoutine != NULL) {
  410. if (FileObject != NULL) {
  411. FileObject->LastLock = NULL;
  412. }
  413. Irp->IoStatus.Status = LocalStatus;
  414. *NewStatus = CompleteLockIrpRoutine( Context, Irp );
  415. } else {
  416. FsRtlCompleteRequest( Irp, LocalStatus );
  417. *NewStatus = LocalStatus;
  418. }
  419. }
  420. //
  421. // The following routines are private to this module
  422. //
  423. VOID
  424. FsRtlSplitLocks (
  425. IN PLOCKTREE_NODE ParentNode,
  426. IN PSINGLE_LIST_ENTRY *pStartLink,
  427. IN PLARGE_INTEGER LastShadowedByte,
  428. IN PLARGE_INTEGER GlueOffset
  429. );
  430. PRTL_SPLAY_LINKS
  431. FsRtlFindFirstOverlappingSharedNode (
  432. IN PRTL_SPLAY_LINKS Tree,
  433. IN PLARGE_INTEGER StartingByte,
  434. IN PLARGE_INTEGER EndingByte,
  435. IN OUT PRTL_SPLAY_LINKS *LastEdgeNode,
  436. IN OUT PBOOLEAN GreaterThan
  437. );
  438. PRTL_SPLAY_LINKS
  439. FsRtlFindFirstOverlappingExclusiveNode (
  440. IN PRTL_SPLAY_LINKS Tree,
  441. IN PLARGE_INTEGER StartingByte,
  442. IN PLARGE_INTEGER EndingByte,
  443. IN OUT PRTL_SPLAY_LINKS *LastEdgeNode,
  444. IN OUT PBOOLEAN GreaterThan
  445. );
  446. PSH_LOCK
  447. FsRtlFindFirstOverlapInNode (
  448. IN PLOCKTREE_NODE Node,
  449. IN PLARGE_INTEGER StartingByte,
  450. IN PLARGE_INTEGER EndingByte
  451. );
  452. BOOLEAN
  453. FsRtlPrivateInsertLock (
  454. IN PLOCK_INFO LockInfo,
  455. IN PFILE_OBJECT FileObject,
  456. IN PFILE_LOCK_INFO FileLockInfo
  457. );
  458. BOOLEAN
  459. FsRtlPrivateInsertSharedLock (
  460. IN PLOCK_QUEUE LockQueue,
  461. IN PSH_LOCK NewLock
  462. );
  463. VOID
  464. FsRtlPrivateInsertExclusiveLock (
  465. IN PLOCK_QUEUE LockQueue,
  466. IN PEX_LOCK NewLock
  467. );
  468. VOID
  469. FsRtlPrivateCheckWaitingLocks (
  470. IN PLOCK_INFO LockInfo,
  471. IN PLOCK_QUEUE LockQueue,
  472. IN KIRQL OldIrql
  473. );
  474. VOID
  475. FsRtlPrivateCancelFileLockIrp (
  476. IN PDEVICE_OBJECT DeviceObject,
  477. IN PIRP Irp
  478. );
  479. BOOLEAN
  480. FsRtlPrivateCheckForExclusiveLockAccess (
  481. IN PLOCK_QUEUE LockInfo,
  482. IN PFILE_LOCK_INFO FileLockInfo
  483. );
  484. BOOLEAN
  485. FsRtlPrivateCheckForSharedLockAccess (
  486. IN PLOCK_QUEUE LockInfo,
  487. IN PFILE_LOCK_INFO FileLockInfo
  488. );
  489. NTSTATUS
  490. FsRtlPrivateFastUnlockAll (
  491. IN PFILE_LOCK FileLock,
  492. IN PFILE_OBJECT FileObject,
  493. IN PEPROCESS ProcessId,
  494. IN ULONG Key,
  495. IN BOOLEAN MatchKey,
  496. IN PVOID Context OPTIONAL
  497. );
  498. BOOLEAN
  499. FsRtlPrivateInitializeFileLock (
  500. IN PFILE_LOCK FileLock,
  501. IN BOOLEAN ViaFastCall
  502. );
  503. VOID
  504. FsRtlPrivateRemoveLock (
  505. IN PLOCK_INFO LockInfo,
  506. IN PFILE_LOCK_INFO,
  507. IN BOOLEAN CheckForWaiters
  508. );
  509. BOOLEAN
  510. FsRtlCheckNoSharedConflict (
  511. IN PLOCK_QUEUE LockQueue,
  512. IN PLARGE_INTEGER Starting,
  513. IN PLARGE_INTEGER Ending
  514. );
  515. BOOLEAN
  516. FsRtlCheckNoExclusiveConflict (
  517. IN PLOCK_QUEUE LockQueue,
  518. IN PLARGE_INTEGER Starting,
  519. IN PLARGE_INTEGER Ending,
  520. IN ULONG Key,
  521. IN PFILE_OBJECT FileObject,
  522. IN PVOID ProcessId
  523. );
  524. VOID
  525. FsRtlPrivateResetLowestLockOffset (
  526. PLOCK_INFO LockInfo
  527. );
  528. NTSTATUS
  529. FsRtlFastUnlockSingleShared (
  530. IN PLOCK_INFO LockInfo,
  531. IN PFILE_OBJECT FileObject,
  532. IN LARGE_INTEGER UNALIGNED *FileOffset,
  533. IN PLARGE_INTEGER Length,
  534. IN PEPROCESS ProcessId,
  535. IN ULONG Key,
  536. IN PVOID Context OPTIONAL,
  537. IN BOOLEAN IgnoreUnlockRoutine,
  538. IN BOOLEAN CheckForWaiters
  539. );
  540. NTSTATUS
  541. FsRtlFastUnlockSingleExclusive (
  542. IN PLOCK_INFO LockInfo,
  543. IN PFILE_OBJECT FileObject,
  544. IN LARGE_INTEGER UNALIGNED *FileOffset,
  545. IN PLARGE_INTEGER Length,
  546. IN PEPROCESS ProcessId,
  547. IN ULONG Key,
  548. IN PVOID Context OPTIONAL,
  549. IN BOOLEAN IgnoreUnlockRoutine,
  550. IN BOOLEAN CheckForWaiters
  551. );
  552. #ifdef ALLOC_PRAGMA
  553. #pragma alloc_text(INIT, FsRtlInitializeFileLocks)
  554. #endif
  555. VOID
  556. FsRtlInitializeFileLocks (
  557. VOID
  558. )
  559. /*++
  560. Routine Description:
  561. Initializes the global portion of the filelock package.
  562. Arguments:
  563. None
  564. Return Value:
  565. None.
  566. --*/
  567. {
  568. //
  569. // Build the lookaside lists for our internal structures.
  570. //
  571. ExInitializeNPagedLookasideList( &FsRtlSharedLockLookasideList,
  572. NULL,
  573. NULL,
  574. 0,
  575. sizeof(SH_LOCK),
  576. TAG_SHARED_LOCK,
  577. 16 );
  578. ExInitializeNPagedLookasideList( &FsRtlExclusiveLockLookasideList,
  579. NULL,
  580. NULL,
  581. 0,
  582. sizeof(EX_LOCK),
  583. TAG_EXCLUSIVE_LOCK,
  584. 16 );
  585. ExInitializeNPagedLookasideList( &FsRtlWaitingLockLookasideList,
  586. NULL,
  587. NULL,
  588. 0,
  589. sizeof(WAITING_LOCK),
  590. TAG_WAITING_LOCK,
  591. 16 );
  592. ExInitializeNPagedLookasideList( &FsRtlLockTreeNodeLookasideList,
  593. NULL,
  594. NULL,
  595. 0,
  596. sizeof(LOCKTREE_NODE),
  597. TAG_LOCKTREE_NODE,
  598. 16 );
  599. ExInitializeNPagedLookasideList( &FsRtlLockInfoLookasideList,
  600. NULL,
  601. NULL,
  602. 0,
  603. sizeof(LOCK_INFO),
  604. TAG_LOCK_INFO,
  605. 8 );
  606. ExInitializePagedLookasideList( &FsRtlFileLockLookasideList,
  607. NULL,
  608. NULL,
  609. 0,
  610. sizeof(FILE_LOCK),
  611. TAG_FILE_LOCK,
  612. 8 );
  613. //
  614. // Initialize the LockInfo creation mutex
  615. //
  616. ExInitializeFastMutex(&FsRtlCreateLockInfo);
  617. //
  618. // Initialize the cancel collision lock
  619. //
  620. KeInitializeSpinLock( &FsRtlFileLockCancelCollideLock );
  621. FsRtlFileLockCancelCollideList.Next = NULL;
  622. }
  623. VOID
  624. FsRtlInitializeFileLock (
  625. IN PFILE_LOCK FileLock,
  626. IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
  627. IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
  628. )
  629. /*++
  630. Routine Description:
  631. This routine initializes a new FILE_LOCK structure. The caller must
  632. supply the memory for the structure. This call must precede all other
  633. calls that utilize the FILE_LOCK variable.
  634. Arguments:
  635. FileLock - Supplies a pointer to the FILE_LOCK structure to
  636. initialize.
  637. CompleteLockIrpRoutine - Optionally supplies an alternate routine to
  638. call for completing IRPs. FsRtlProcessFileLock by default will
  639. call IoCompleteRequest to finish up an IRP; however if the caller
  640. want to process the completion itself then it needs to specify
  641. a completion routine here. This routine will then be called in
  642. place of IoCompleteRequest.
  643. UnlockRoutine - Optionally supplies a routine to call when removing
  644. a lock.
  645. Return Value:
  646. None.
  647. --*/
  648. {
  649. DebugTrace(+1, Dbg, "FsRtlInitializeFileLock, FileLock = %08lx\n", FileLock);
  650. //
  651. // Clear non-paged pool pointer
  652. //
  653. FileLock->LockInformation = NULL;
  654. FileLock->CompleteLockIrpRoutine = CompleteLockIrpRoutine;
  655. FileLock->UnlockRoutine = UnlockRoutine;
  656. FileLock->FastIoIsQuestionable = FALSE;
  657. //
  658. // and return to our caller
  659. //
  660. DebugTrace(-1, Dbg, "FsRtlInitializeFileLock -> VOID\n", 0 );
  661. return;
  662. }
  663. BOOLEAN
  664. FsRtlPrivateInitializeFileLock (
  665. IN PFILE_LOCK FileLock,
  666. IN BOOLEAN ViaFastCall
  667. )
  668. /*++
  669. Routine Description:
  670. This routine initializes a new LOCK_INFO structure in non-paged
  671. pool for the FILE_LOCK. This routines only occurs once for a given
  672. FILE_LOCK and it only occurs if any locks are applied to that file.
  673. Arguments:
  674. FileLock - Supplies a pointer to the FILE_LOCK structure to
  675. initialize.
  676. ViaFastCall - Indicates if we are being invoked via a fast call or
  677. via the slow irp based method.
  678. Return Value:
  679. TRUE - If LockInfo structure was allocated and initialized
  680. --*/
  681. {
  682. PLOCK_INFO LockInfo;
  683. BOOLEAN Results = FALSE;
  684. ExAcquireFastMutex( &FsRtlCreateLockInfo );
  685. try {
  686. if (FileLock->LockInformation != NULL) {
  687. //
  688. // Structure is already allocated, just return
  689. //
  690. try_return( Results = TRUE );
  691. }
  692. //
  693. // Allocate pool for lock structures. If we fail then we will either return false or
  694. // raise based on if we know the caller has an try-except to handle a raise.
  695. //
  696. LockInfo = FsRtlAllocateLockInfo();
  697. if (LockInfo == NULL) {
  698. if (ViaFastCall) {
  699. try_return( Results = FALSE );
  700. } else {
  701. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  702. }
  703. }
  704. //
  705. // Allocate and initialize the waiting lock queue
  706. // spinlock, and initialize the queues
  707. //
  708. LockInfo->LowestLockOffset = 0xffffffff;
  709. KeInitializeSpinLock( &LockInfo->LockQueue.QueueSpinLock );
  710. LockInfo->LockQueue.SharedLockTree = NULL;
  711. LockInfo->LockQueue.ExclusiveLockTree = NULL;
  712. LockInfo->LockQueue.WaitingLocks.Next = NULL;
  713. LockInfo->LockQueue.WaitingLocksTail.Next = NULL;
  714. //
  715. // Copy Irp & Unlock routines from pagable FileLock structure
  716. // to non-pagable LockInfo structure
  717. //
  718. LockInfo->CompleteLockIrpRoutine = FileLock->CompleteLockIrpRoutine;
  719. LockInfo->UnlockRoutine = FileLock->UnlockRoutine;
  720. //
  721. // Clear continuation info for enum routine
  722. //
  723. FileLock->LastReturnedLockInfo.FileObject = NULL;
  724. FileLock->LastReturnedLock = NULL;
  725. //
  726. // Link LockInfo into FileLock
  727. //
  728. FileLock->LockInformation = (PVOID) LockInfo;
  729. Results = TRUE;
  730. try_exit: NOTHING;
  731. } finally {
  732. ExReleaseFastMutex( &FsRtlCreateLockInfo );
  733. }
  734. return Results;
  735. }
  736. VOID
  737. FsRtlUninitializeFileLock (
  738. IN PFILE_LOCK FileLock
  739. )
  740. /*++
  741. Routine Description:
  742. This routine uninitializes a FILE_LOCK structure. After calling this
  743. routine the File lock must be reinitialized before being used again.
  744. This routine will free all files locks and completes any outstanding
  745. lock requests as a result of cleaning itself up.
  746. Arguments:
  747. FileLock - Supplies a pointer to the FILE_LOCK struture being
  748. decommissioned.
  749. Return Value:
  750. None.
  751. --*/
  752. {
  753. PLOCK_INFO LockInfo;
  754. PSH_LOCK ShLock;
  755. PEX_LOCK ExLock;
  756. PSINGLE_LIST_ENTRY Link;
  757. PWAITING_LOCK WaitingLock;
  758. PLOCKTREE_NODE LockTreeNode;
  759. PIRP Irp;
  760. NTSTATUS NewStatus;
  761. KIRQL OldIrql;
  762. PKPRCB Prcb;
  763. DebugTrace(+1, Dbg, "FsRtlUninitializeFileLock, FileLock = %08lx\n", FileLock);
  764. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  765. return ;
  766. }
  767. //
  768. // Lock vs. cancels and lock the queue.
  769. //
  770. FsRtlAcquireCancelCollide( &OldIrql );
  771. FsRtlAcquireLockQueueAtDpc( &LockInfo->LockQueue );
  772. //
  773. // Free lock trees
  774. //
  775. while (LockInfo->LockQueue.SharedLockTree != NULL) {
  776. LockTreeNode = CONTAINING_RECORD(LockInfo->LockQueue.SharedLockTree, LOCKTREE_NODE, Links);
  777. //
  778. // Remove all locks associated with the root node
  779. //
  780. while (LockTreeNode->Locks.Next != NULL) {
  781. Link = PopEntryList (&LockTreeNode->Locks);
  782. ShLock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  783. FsRtlFreeSharedLock(ShLock);
  784. }
  785. //
  786. // Slice off the root node of the tree
  787. //
  788. RtlDeleteNoSplay(&LockTreeNode->Links, &LockInfo->LockQueue.SharedLockTree);
  789. FsRtlFreeLockTreeNode(LockTreeNode);
  790. }
  791. while (LockInfo->LockQueue.ExclusiveLockTree != NULL) {
  792. ExLock = CONTAINING_RECORD(LockInfo->LockQueue.ExclusiveLockTree, EX_LOCK, Links);
  793. RtlDeleteNoSplay(&ExLock->Links, &LockInfo->LockQueue.ExclusiveLockTree);
  794. FsRtlFreeExclusiveLock(ExLock);
  795. }
  796. //
  797. // Free WaitingLockQueue.
  798. //
  799. // This will be incredibly rare, requiring a cancel to be pending in an async thread
  800. // while cleanup/close occurs in the owning filesystem, triggering teardown.
  801. //
  802. while (LockInfo->LockQueue.WaitingLocks.Next != NULL) {
  803. Link = PopEntryList( &LockInfo->LockQueue.WaitingLocks );
  804. WaitingLock = CONTAINING_RECORD( Link, WAITING_LOCK, Link );
  805. Irp = WaitingLock->Irp;
  806. //
  807. // To complete an irp in the waiting queue we need to
  808. // void the cancel routine (protected by a spinlock so
  809. // we can know that we beat cancellation) before
  810. // we completing the irp.
  811. //
  812. FsRtlReleaseLockQueueFromDpc( &LockInfo->LockQueue );
  813. IoAcquireCancelSpinLock( &Irp->CancelIrql );
  814. IoSetCancelRoutine( Irp, NULL );
  815. //
  816. // If it got cancelled, the cancel routine is now waiting on the other
  817. // side of the cancel collide for us to push it onto the collide list.
  818. // It'll get the IRP there as opposed to the shortly-to-be-axed lock
  819. // structure.
  820. //
  821. if (Irp->Cancel) {
  822. IoReleaseCancelSpinLock( Irp->CancelIrql );
  823. PushEntryList( &FsRtlFileLockCancelCollideList,
  824. &WaitingLock->Link );
  825. Irp = NULL;
  826. } else {
  827. IoReleaseCancelSpinLock( Irp->CancelIrql );
  828. }
  829. //
  830. // If we got the ownership of the IRP, release the collide and complete
  831. // it, otherwise spin back around for more.
  832. //
  833. if (Irp) {
  834. FsRtlReleaseCancelCollide( OldIrql );
  835. Irp->IoStatus.Information = 0;
  836. FsRtlCompleteLockIrp(
  837. LockInfo,
  838. WaitingLock->Context,
  839. Irp,
  840. STATUS_RANGE_NOT_LOCKED,
  841. &NewStatus,
  842. NULL );
  843. FsRtlFreeWaitingLock( WaitingLock );
  844. FsRtlAcquireCancelCollide( &OldIrql );
  845. }
  846. FsRtlAcquireLockQueueAtDpc( &LockInfo->LockQueue );
  847. }
  848. //
  849. // Release locks and free pool used to track the lock info on this file.
  850. //
  851. FsRtlReleaseLockQueueFromDpc( &LockInfo->LockQueue );
  852. FsRtlReleaseCancelCollide( OldIrql );
  853. FsRtlFreeLockInfo( LockInfo );
  854. //
  855. // Unlink LockInfo from FileLock
  856. //
  857. FileLock->LockInformation = NULL;
  858. //
  859. // And return to our caller
  860. //
  861. DebugTrace(-1, Dbg, "FsRtlUninitializeFileLock -> VOID\n", 0 );
  862. return;
  863. }
  864. PFILE_LOCK
  865. FsRtlAllocateFileLock (
  866. IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
  867. IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
  868. )
  869. {
  870. PFILE_LOCK FileLock;
  871. FileLock = ExAllocateFromPagedLookasideList( &FsRtlFileLockLookasideList );
  872. if (FileLock != NULL) {
  873. FsRtlInitializeFileLock( FileLock,
  874. CompleteLockIrpRoutine,
  875. UnlockRoutine );
  876. }
  877. return FileLock;
  878. }
  879. VOID
  880. FsRtlFreeFileLock (
  881. IN PFILE_LOCK FileLock
  882. )
  883. {
  884. FsRtlUninitializeFileLock( FileLock );
  885. ExFreeToPagedLookasideList( &FsRtlFileLockLookasideList, FileLock );
  886. }
  887. NTSTATUS
  888. FsRtlProcessFileLock (
  889. IN PFILE_LOCK FileLock,
  890. IN PIRP Irp,
  891. IN PVOID Context OPTIONAL
  892. )
  893. /*++
  894. Routine Description:
  895. This routine processes a file lock IRP it does either a lock request,
  896. or an unlock request. It also completes the IRP. Once called the user
  897. (i.e., File System) has relinquished control of the input IRP.
  898. If pool is not available to store the information this routine will raise a
  899. status value indicating insufficient resources.
  900. Arguments:
  901. FileLock - Supplies the File lock being modified/queried.
  902. Irp - Supplies the Irp being processed.
  903. Context - Optionally supplies a context to use when calling the user
  904. alternate IRP completion routine.
  905. Return Value:
  906. NTSTATUS - The return status for the operation.
  907. --*/
  908. {
  909. PIO_STACK_LOCATION IrpSp;
  910. IO_STATUS_BLOCK Iosb;
  911. NTSTATUS Status;
  912. LARGE_INTEGER ByteOffset;
  913. DebugTrace(+1, Dbg, "FsRtlProcessFileLock, FileLock = %08lx\n", FileLock);
  914. Iosb.Information = 0;
  915. //
  916. // Get a pointer to the current Irp stack location and assert that
  917. // the major function code is for a lock operation
  918. //
  919. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  920. ASSERT( IrpSp->MajorFunction == IRP_MJ_LOCK_CONTROL );
  921. //
  922. // Now process the different minor lock operations
  923. //
  924. switch (IrpSp->MinorFunction) {
  925. case IRP_MN_LOCK:
  926. ByteOffset = IrpSp->Parameters.LockControl.ByteOffset;
  927. (VOID) FsRtlPrivateLock( FileLock,
  928. IrpSp->FileObject,
  929. &ByteOffset,
  930. IrpSp->Parameters.LockControl.Length,
  931. IoGetRequestorProcess(Irp),
  932. IrpSp->Parameters.LockControl.Key,
  933. BooleanFlagOn(IrpSp->Flags, SL_FAIL_IMMEDIATELY),
  934. BooleanFlagOn(IrpSp->Flags, SL_EXCLUSIVE_LOCK),
  935. &Iosb,
  936. Irp,
  937. Context,
  938. FALSE );
  939. break;
  940. case IRP_MN_UNLOCK_SINGLE:
  941. ByteOffset = IrpSp->Parameters.LockControl.ByteOffset;
  942. Iosb.Status = FsRtlFastUnlockSingle( FileLock,
  943. IrpSp->FileObject,
  944. &ByteOffset,
  945. IrpSp->Parameters.LockControl.Length,
  946. IoGetRequestorProcess(Irp),
  947. IrpSp->Parameters.LockControl.Key,
  948. Context,
  949. FALSE );
  950. FsRtlCompleteLockIrp( FileLock, Context, Irp, Iosb.Status, &Status, NULL );
  951. break;
  952. case IRP_MN_UNLOCK_ALL:
  953. Iosb.Status = FsRtlFastUnlockAll( FileLock,
  954. IrpSp->FileObject,
  955. IoGetRequestorProcess(Irp),
  956. Context );
  957. FsRtlCompleteLockIrp( FileLock, Context, Irp, Iosb.Status, &Status, NULL );
  958. break;
  959. case IRP_MN_UNLOCK_ALL_BY_KEY:
  960. Iosb.Status = FsRtlFastUnlockAllByKey( FileLock,
  961. IrpSp->FileObject,
  962. IoGetRequestorProcess(Irp),
  963. IrpSp->Parameters.LockControl.Key,
  964. Context );
  965. FsRtlCompleteLockIrp( FileLock, Context, Irp, Iosb.Status, &Status, NULL );
  966. break;
  967. default:
  968. //
  969. // For all other minor function codes we say they're invalid and
  970. // complete the request. Note that the IRP has not been marked
  971. // pending so this error will be returned directly to the caller.
  972. //
  973. DebugTrace(0, 1, "Invalid LockFile Minor Function Code %08lx\n", IrpSp->MinorFunction);
  974. FsRtlCompleteRequest( Irp, STATUS_INVALID_DEVICE_REQUEST );
  975. Iosb.Status = STATUS_INVALID_DEVICE_REQUEST;
  976. break;
  977. }
  978. //
  979. // And return to our caller
  980. //
  981. DebugTrace(-1, Dbg, "FsRtlProcessFileLock -> %08lx\n", Iosb.Status);
  982. return Iosb.Status;
  983. }
  984. BOOLEAN
  985. FsRtlCheckLockForReadAccess (
  986. IN PFILE_LOCK FileLock,
  987. IN PIRP Irp
  988. )
  989. /*++
  990. Routine Description:
  991. This routine checks to see if the caller has read access to the
  992. range indicated in the IRP due to file locks. This call does not
  993. complete the Irp it only uses it to get the lock information and read
  994. information. The IRP must be for a read operation.
  995. Arguments:
  996. FileLock - Supplies the File Lock to check.
  997. Irp - Supplies the Irp being processed.
  998. Return Value:
  999. BOOLEAN - TRUE if the indicated user/request has read access to the
  1000. entire specified byte range, and FALSE otherwise
  1001. --*/
  1002. {
  1003. BOOLEAN Result;
  1004. PIO_STACK_LOCATION IrpSp;
  1005. PLOCK_INFO LockInfo;
  1006. LARGE_INTEGER StartingByte;
  1007. LARGE_INTEGER Length;
  1008. ULONG Key;
  1009. PFILE_OBJECT FileObject;
  1010. PVOID ProcessId;
  1011. LARGE_INTEGER BeyondLastByte;
  1012. DebugTrace(+1, Dbg, "FsRtlCheckLockForReadAccess, FileLock = %08lx\n", FileLock);
  1013. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  1014. DebugTrace(-1, Dbg, "FsRtlCheckLockForReadAccess (No current lock info) -> TRUE\n", 0);
  1015. return TRUE;
  1016. }
  1017. //
  1018. // Do a really fast test to see if there are any exclusive locks to start with
  1019. //
  1020. if (LockInfo->LockQueue.ExclusiveLockTree == NULL) {
  1021. DebugTrace(-1, Dbg, "FsRtlCheckLockForReadAccess (No current locks) -> TRUE\n", 0);
  1022. return TRUE;
  1023. }
  1024. //
  1025. // Get the read offset and compare it to the lowest existing lock.
  1026. //
  1027. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  1028. StartingByte = IrpSp->Parameters.Read.ByteOffset;
  1029. (ULONGLONG)Length.QuadPart = (ULONGLONG)IrpSp->Parameters.Read.Length;
  1030. (ULONGLONG)BeyondLastByte.QuadPart = (ULONGLONG)StartingByte.QuadPart + Length.LowPart;
  1031. if ( (ULONGLONG)BeyondLastByte.QuadPart <= (ULONGLONG)LockInfo->LowestLockOffset ) {
  1032. DebugTrace(-1, Dbg, "FsRtlCheckLockForReadAccess (Below lowest lock) -> TRUE\n", 0);
  1033. return TRUE;
  1034. }
  1035. //
  1036. // Get remaining parameters.
  1037. //
  1038. Key = IrpSp->Parameters.Read.Key;
  1039. FileObject = IrpSp->FileObject;
  1040. ProcessId = IoGetRequestorProcess( Irp );
  1041. //
  1042. // Call our private work routine to do the real check
  1043. //
  1044. Result = FsRtlFastCheckLockForRead( FileLock,
  1045. &StartingByte,
  1046. &Length,
  1047. Key,
  1048. FileObject,
  1049. ProcessId );
  1050. //
  1051. // And return to our caller
  1052. //
  1053. DebugTrace(-1, Dbg, "FsRtlCheckLockForReadAccess -> %08lx\n", Result);
  1054. return Result;
  1055. }
  1056. BOOLEAN
  1057. FsRtlCheckLockForWriteAccess (
  1058. IN PFILE_LOCK FileLock,
  1059. IN PIRP Irp
  1060. )
  1061. /*++
  1062. Routine Description:
  1063. This routine checks to see if the caller has write access to the
  1064. indicated range due to file locks. This call does not complete the
  1065. Irp it only uses it to get the lock information and write information.
  1066. The IRP must be for a write operation.
  1067. Arguments:
  1068. FileLock - Supplies the File Lock to check.
  1069. Irp - Supplies the Irp being processed.
  1070. Return Value:
  1071. BOOLEAN - TRUE if the indicated user/request has write access to the
  1072. entire specified byte range, and FALSE otherwise
  1073. --*/
  1074. {
  1075. BOOLEAN Result;
  1076. PIO_STACK_LOCATION IrpSp;
  1077. PLOCK_INFO LockInfo;
  1078. LARGE_INTEGER StartingByte;
  1079. LARGE_INTEGER Length;
  1080. ULONG Key;
  1081. PFILE_OBJECT FileObject;
  1082. PVOID ProcessId;
  1083. LARGE_INTEGER BeyondLastByte;
  1084. DebugTrace(+1, Dbg, "FsRtlCheckLockForWriteAccess, FileLock = %08lx\n", FileLock);
  1085. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  1086. DebugTrace(-1, Dbg, "FsRtlCheckLockForWriteAccess (No current lock info) -> TRUE\n", 0);
  1087. return TRUE;
  1088. }
  1089. //
  1090. // Do a really fast test to see if there are any locks to start with
  1091. //
  1092. if (LockInfo->LockQueue.ExclusiveLockTree == NULL && LockInfo->LockQueue.SharedLockTree == NULL) {
  1093. DebugTrace(-1, Dbg, "FsRtlCheckLockForWriteAccess (No current locks) -> TRUE\n", 0);
  1094. return TRUE;
  1095. }
  1096. //
  1097. // Get the write offset and compare it to the lowest existing lock.
  1098. //
  1099. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  1100. StartingByte = IrpSp->Parameters.Write.ByteOffset;
  1101. (ULONGLONG)Length.QuadPart = (ULONGLONG)IrpSp->Parameters.Write.Length;
  1102. (ULONGLONG)BeyondLastByte.QuadPart = (ULONGLONG)StartingByte.QuadPart + Length.LowPart;
  1103. if ( (ULONGLONG)BeyondLastByte.QuadPart <= (ULONGLONG)LockInfo->LowestLockOffset ) {
  1104. DebugTrace(-1, Dbg, "FsRtlCheckLockForWriteAccess (Below lowest lock) -> TRUE\n", 0);
  1105. return TRUE;
  1106. }
  1107. //
  1108. // Get remaining parameters.
  1109. //
  1110. Key = IrpSp->Parameters.Write.Key;
  1111. FileObject = IrpSp->FileObject;
  1112. ProcessId = IoGetRequestorProcess( Irp );
  1113. //
  1114. // Call our private work routine to do the real work
  1115. //
  1116. Result = FsRtlFastCheckLockForWrite( FileLock,
  1117. &StartingByte,
  1118. &Length,
  1119. Key,
  1120. FileObject,
  1121. ProcessId );
  1122. //
  1123. // And return to our caller
  1124. //
  1125. DebugTrace(-1, Dbg, "FsRtlCheckLockForWriteAccess -> %08lx\n", Result);
  1126. return Result;
  1127. }
  1128. PRTL_SPLAY_LINKS
  1129. FsRtlFindFirstOverlappingSharedNode (
  1130. IN PRTL_SPLAY_LINKS Tree,
  1131. IN PLARGE_INTEGER StartingByte,
  1132. IN PLARGE_INTEGER EndingByte,
  1133. IN OUT PRTL_SPLAY_LINKS *LastEdgeNode,
  1134. IN OUT PBOOLEAN GreaterThan
  1135. )
  1136. /*++
  1137. Routine Description:
  1138. This routine returns the first node in the shared lock tree which
  1139. overlaps with the range given. No nodes given by RtlRealPredecessor()
  1140. on the result overlap the range.
  1141. Arguments:
  1142. Tree - supplies the splay links of the root node of the shared tree
  1143. to search
  1144. StartingByte - supplies the first byte offset of the range to check
  1145. EndingByte - supplies the last byte offset of the range to check
  1146. LastEdgeNode - optional, will be set to the last node searched in the
  1147. not including returned node (presumeably where a new node will
  1148. be inserted if return is NULL).
  1149. GreaterThan - optional, set according to whether LastEdgeNode is covering
  1150. a range greater than the queried range. !GreaterThan == LessThan, since
  1151. we would have returned this node in the "Equals" (overlap) case.
  1152. Return Value:
  1153. The splay links of the node, if such a node exists, NULL otherwise
  1154. --*/
  1155. {
  1156. PLOCKTREE_NODE Node, LastOverlapNode;
  1157. PRTL_SPLAY_LINKS SplayLinks;
  1158. PSH_LOCK Lock;
  1159. if (LastEdgeNode) *LastEdgeNode = NULL;
  1160. if (GreaterThan) *GreaterThan = FALSE;
  1161. LastOverlapNode = NULL;
  1162. SplayLinks = Tree;
  1163. while (SplayLinks) {
  1164. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1165. //
  1166. // Pull up the first lock on the chain at this node to check
  1167. // the starting byte offset of locks at this node
  1168. //
  1169. Lock = CONTAINING_RECORD( Node->Locks.Next, SH_LOCK, Link );
  1170. //
  1171. // We may have to go right in the tree if this lock covers a range before the start of this
  1172. // range we are looking for overlap on or this lock is [0, 0). This is important since a lock
  1173. // on [0, 0) will look like the extent is from [0, ~0], which is the only case where the zero
  1174. // length lock relation of End < Start does not hold.
  1175. //
  1176. if (Node->Extent < (ULONGLONG)StartingByte->QuadPart ||
  1177. (Lock->LockInfo.StartingByte.QuadPart == 0 && Lock->LockInfo.Length.QuadPart == 0)) {
  1178. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart == (ULONGLONG)EndingByte->QuadPart &&
  1179. (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart == (ULONGLONG)StartingByte->QuadPart) {
  1180. //
  1181. // The extent of the node is less than the starting position of the
  1182. // range we are checking and the first lock on this node is equal to
  1183. // the range, which implies that the range and the lock are zero
  1184. // length.
  1185. //
  1186. // This is a zero length lock node and we are searching for zero
  1187. // length overlap. This makes multiple zero length shared locks
  1188. // occupy the same node, which is a win, but makes application of
  1189. // zero length exclusive locks check the length of the overlapping
  1190. // lock to see if they really conflict.
  1191. //
  1192. break;
  1193. }
  1194. //
  1195. // All locks at this node are strictly less than this
  1196. // byterange, so go right in the tree.
  1197. //
  1198. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1199. if (GreaterThan) *GreaterThan = FALSE;
  1200. SplayLinks = RtlRightChild(SplayLinks);
  1201. continue;
  1202. }
  1203. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart <= (ULONGLONG)EndingByte->QuadPart) {
  1204. //
  1205. // We have an overlap, but we need to see if the byterange starts
  1206. // before this node so that there is the guarantee that we start
  1207. // the search at the correct point. There may be still be predecessor
  1208. // nodes covering the byterange.
  1209. //
  1210. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart <= (ULONGLONG)StartingByte->QuadPart) {
  1211. //
  1212. // This node begins at a byte offset prior to the byterange we
  1213. // are checking, so it must be the correct starting position.
  1214. //
  1215. break;
  1216. }
  1217. //
  1218. // Drop a marker at this node so that we can come back if it turns out
  1219. // that the left subtree does not cover the range of bytes before this
  1220. // node in the byterange.
  1221. //
  1222. LastOverlapNode = Node;
  1223. }
  1224. //
  1225. // It must now be the case that all locks at this node are strictly greater
  1226. // than the byterange, or we have the candidate overlap case above,
  1227. // so go left in the tree.
  1228. //
  1229. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1230. if (GreaterThan) *GreaterThan = TRUE;
  1231. SplayLinks = RtlLeftChild(SplayLinks);
  1232. }
  1233. if (SplayLinks == NULL) {
  1234. //
  1235. // We hit the edge of the tree. If the LastOverlapNode is set, it means that
  1236. // we had kept searching left in the tree for a node that covered the starting
  1237. // byte of the byterange, but didn't find it. If it isn't set, we'll do the
  1238. // right thing anyway since Node <- NULL.
  1239. //
  1240. Node = LastOverlapNode;
  1241. }
  1242. if (Node == NULL) {
  1243. //
  1244. // No overlapping node existed
  1245. //
  1246. return NULL;
  1247. }
  1248. //
  1249. // Return the splay links of the first overlapping node
  1250. //
  1251. return &Node->Links;
  1252. }
  1253. PRTL_SPLAY_LINKS
  1254. FsRtlFindFirstOverlappingExclusiveNode (
  1255. IN PRTL_SPLAY_LINKS Tree,
  1256. IN PLARGE_INTEGER StartingByte,
  1257. IN PLARGE_INTEGER EndingByte,
  1258. IN OUT PRTL_SPLAY_LINKS *LastEdgeNode,
  1259. IN OUT PBOOLEAN GreaterThan
  1260. )
  1261. /*++
  1262. Routine Description:
  1263. This routine returns the first node in the exclusive lock tree which
  1264. overlaps with the range given. No nodes given by RtlRealPredecessor()
  1265. on the result overlap the range.
  1266. Arguments:
  1267. Tree - supplies the splay links of the root node of the exclusive tree
  1268. to search
  1269. StartingByte - supplies the first byte offset of the range to check
  1270. EndingByte - supplies the last byte offset of the range to check
  1271. LastEdgeNode - optional, will be set to the last node searched
  1272. not including returned node (presumeably where a new node will
  1273. be inserted if return is NULL).
  1274. GreaterThan - optional, set according to whether LastEdgeNode is covering
  1275. a range greater than the queried range. !GreaterThan == LessThan, since
  1276. we would have returned this node in the "Equals" (overlap) case.
  1277. Return Value:
  1278. The splay links of the node, if such a node exists, NULL otherwise
  1279. --*/
  1280. {
  1281. PRTL_SPLAY_LINKS SplayLinks;
  1282. PEX_LOCK Lock, LastOverlapNode;
  1283. if (LastEdgeNode) *LastEdgeNode = NULL;
  1284. if (GreaterThan) *GreaterThan = FALSE;
  1285. LastOverlapNode = NULL;
  1286. SplayLinks = Tree;
  1287. while (SplayLinks) {
  1288. Lock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1289. //
  1290. // We may have to go right in the tree if this lock covers a range before the start of this
  1291. // range we are looking for overlap on or this lock is [0, 0). This is important since a lock
  1292. // on [0, 0) will look like the extent is from [0, ~0], which is the only case where the zero
  1293. // length lock relation of End < Start does not hold.
  1294. //
  1295. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart < (ULONGLONG)StartingByte->QuadPart ||
  1296. (Lock->LockInfo.StartingByte.QuadPart == 0 && Lock->LockInfo.Length.QuadPart == 0)) {
  1297. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart == (ULONGLONG)EndingByte->QuadPart &&
  1298. (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart == (ULONGLONG)StartingByte->QuadPart) {
  1299. //
  1300. // The extent of the lock is less than the starting position of the
  1301. // range we are checking and the lock is equal to the range, which
  1302. // implies that the range and the lock are zero length.
  1303. //
  1304. // This is a zero length lock node and we are searching for zero
  1305. // length overlap. Since the exclusive tree is one lock per node,
  1306. // we are in the potential middle of a run of zero length locks in
  1307. // the tree. Go left to find the first zero length lock.
  1308. //
  1309. // This is actually the same logic we'd use for equivalent locks,
  1310. // but the only time that can happen in this tree is for zero length
  1311. // locks.
  1312. //
  1313. LastOverlapNode = Lock;
  1314. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1315. if (GreaterThan) *GreaterThan = FALSE;
  1316. SplayLinks = RtlLeftChild(SplayLinks);
  1317. continue;
  1318. }
  1319. //
  1320. // This lock is strictly less than this byterange, so go
  1321. // right in the tree.
  1322. //
  1323. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1324. if (GreaterThan) *GreaterThan = FALSE;
  1325. SplayLinks = RtlRightChild(SplayLinks);
  1326. continue;
  1327. }
  1328. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart <= (ULONGLONG)EndingByte->QuadPart) {
  1329. //
  1330. // We have an overlap, but we need to see if the byterange starts
  1331. // before this node so that there is the guarantee that we start
  1332. // the search at the correct point. There may be still be predecessor
  1333. // nodes covering the byterange.
  1334. //
  1335. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart <= (ULONGLONG)StartingByte->QuadPart) {
  1336. //
  1337. // This node begins at a byte offset prior to the byterange we
  1338. // are checking, so it must be the correct starting position.
  1339. //
  1340. break;
  1341. }
  1342. //
  1343. // Drop a marker at this node so that we can come back if it turns out
  1344. // that the left subtree does not cover the range of bytes before this
  1345. // node in the byterange.
  1346. //
  1347. LastOverlapNode = Lock;
  1348. }
  1349. //
  1350. // It must now be the case this lock is strictly greater than the byterange,
  1351. // or we have the candidate overlap case above, so go left in the tree.
  1352. //
  1353. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1354. if (GreaterThan) *GreaterThan = TRUE;
  1355. SplayLinks = RtlLeftChild(SplayLinks);
  1356. }
  1357. if (SplayLinks == NULL) {
  1358. //
  1359. // We hit the edge of the tree. If the LastOverlapNode is set, it means that
  1360. // we had kept searching left in the tree for a node that covered the starting
  1361. // byte of the byterange, but didn't find it. If it isn't set, we'll do the
  1362. // right thing anyway since Node <- NULL.
  1363. //
  1364. Lock = LastOverlapNode;
  1365. }
  1366. if (Lock == NULL) {
  1367. //
  1368. // No overlapping lock existed
  1369. //
  1370. return NULL;
  1371. }
  1372. //
  1373. // Return the splay links of the first overlapping lock
  1374. //
  1375. return &Lock->Links;
  1376. }
  1377. PSH_LOCK
  1378. FsRtlFindFirstOverlapInNode (
  1379. IN PLOCKTREE_NODE Node,
  1380. IN PLARGE_INTEGER StartingByte,
  1381. IN PLARGE_INTEGER EndingByte
  1382. )
  1383. /*++
  1384. Routine Description:
  1385. This routine examines a shared lock node, usually a node which is known to be composed
  1386. of several non-overlapping lock segments (holey), for true overlap with the indicated
  1387. range. This is not handled in the normal overlap check (..FindFirstOverlappingSharedLock)
  1388. since the needs for holey checks are rather different than the full node check.
  1389. Arguments:
  1390. Node - the lock tree node to be examined for overlap
  1391. StartingByte - supplies the first byte offset of the range to check
  1392. EndingByte - supplies the last byte offset of the range to check
  1393. Return Value:
  1394. PSH_LOCK - the first lock which overlaps with the specified range.
  1395. --*/
  1396. {
  1397. PSH_LOCK Lock;
  1398. PSINGLE_LIST_ENTRY Link;
  1399. for (Link = Node->Locks.Next;
  1400. Link;
  1401. Link = Link->Next) {
  1402. Lock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  1403. //
  1404. // Logic is the same as above checkers. If the ending byte of the lock is less than the
  1405. // starting byte of the range, OR we have the weird [0, 0) case, then the lock is almost
  1406. // certainly less than the range.
  1407. //
  1408. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart < (ULONGLONG)StartingByte->QuadPart ||
  1409. (Lock->LockInfo.StartingByte.QuadPart == 0 && Lock->LockInfo.Length.QuadPart == 0)) {
  1410. //
  1411. // ... except if the lock and range are equivalent, in which case we have discovered
  1412. // zero lock/range overlap.
  1413. //
  1414. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart == (ULONGLONG)EndingByte->QuadPart &&
  1415. (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart == (ULONGLONG)StartingByte->QuadPart) {
  1416. return Lock;
  1417. }
  1418. //
  1419. // Look forward in the node.
  1420. //
  1421. continue;
  1422. }
  1423. //
  1424. // No overlap at all if the lock begins at a higher byte than the last of the range.
  1425. // We already covered zero length locks (where this is true, and overlap could still
  1426. // occur).
  1427. //
  1428. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)EndingByte->QuadPart) {
  1429. return NULL;
  1430. }
  1431. //
  1432. // Regular overlap has occured. Return this lock.
  1433. //
  1434. return Lock;
  1435. }
  1436. //
  1437. // If we invoke this check and wander off the end of the node without determining what is
  1438. // going on, something is terribly wrong.
  1439. //
  1440. ASSERT( FALSE );
  1441. return NULL;
  1442. }
  1443. PFILE_LOCK_INFO
  1444. FsRtlGetNextFileLock (
  1445. IN PFILE_LOCK FileLock,
  1446. IN BOOLEAN Restart
  1447. )
  1448. /*++
  1449. Routine Description:
  1450. This routine enumerates the individual file locks denoted by the input file lock
  1451. variable. It returns a pointer to the file lock information stored for each lock.
  1452. The caller is responsible for synchronizing call to this procedure and for not
  1453. altering any of the data returned by this procedure. If the caller does not
  1454. synchronize the enumeration will not be reliably complete.
  1455. The way a programmer will use this procedure to enumerate all of the locks
  1456. is as follows:
  1457. for (p = FsRtlGetNextFileLock( FileLock, TRUE );
  1458. p != NULL;
  1459. p = FsRtlGetNextFileLock( FileLock, FALSE )) {
  1460. // Process the lock information referenced by p
  1461. }
  1462. Order is *not* guaranteed.
  1463. Arguments:
  1464. FileLock - Supplies the File Lock to enumerate. The current
  1465. enumeration state is stored in the file lock variable so if multiple
  1466. threads are enumerating the lock at the same time the results will
  1467. be unpredictable.
  1468. Restart - Indicates if the enumeration is to start at the beginning of the
  1469. file lock tree or if we are continuing from a previous call.
  1470. Return Value:
  1471. PFILE_LOCK_INFO - Either it returns a pointer to the next file lock
  1472. record for the input file lock or it returns NULL if there
  1473. are not more locks.
  1474. --*/
  1475. {
  1476. FILE_LOCK_INFO FileLockInfo;
  1477. PVOID ContinuationPointer;
  1478. PLOCK_INFO LockInfo;
  1479. PLOCKTREE_NODE Node;
  1480. PSINGLE_LIST_ENTRY Link;
  1481. PRTL_SPLAY_LINKS SplayLinks, LastSplayLinks;
  1482. PSH_LOCK ShLock;
  1483. PEX_LOCK ExLock;
  1484. BOOLEAN FoundReturnable, GreaterThan;
  1485. KIRQL OldIrql;
  1486. DebugTrace(+1, Dbg, "FsRtlGetNextFileLock, FileLock = %08lx\n", FileLock);
  1487. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  1488. //
  1489. // No lock information on this FileLock
  1490. //
  1491. return NULL;
  1492. }
  1493. FoundReturnable = FALSE;
  1494. //
  1495. // Before getting the spinlock, copy pagable info onto stack
  1496. //
  1497. FileLockInfo = FileLock->LastReturnedLockInfo;
  1498. ContinuationPointer = FileLock->LastReturnedLock;
  1499. FsRtlAcquireLockQueue (&LockInfo->LockQueue, &OldIrql);
  1500. if (!Restart) {
  1501. //
  1502. // Given the last returned lock, find its current successor in the tree.
  1503. // Previous implementations would reset the enumeration if the last returned
  1504. // lock had been removed from the tree but I think we can be better in that
  1505. // case since every other structure modifying event (add new locks, delete
  1506. // other locks) would *not* have caused the reset. Possible minor performance
  1507. // enhancement.
  1508. //
  1509. //
  1510. // Find the node which could contain the last returned lock. We enumerate the
  1511. // exclusive lock tree, then the shared lock tree. Find the one we're enumerating.
  1512. //
  1513. if (FileLockInfo.ExclusiveLock) {
  1514. //
  1515. // Continue enumeration in the exclusive lock tree
  1516. //
  1517. ExLock = NULL;
  1518. SplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockInfo->LockQueue.ExclusiveLockTree,
  1519. &FileLockInfo.StartingByte,
  1520. &FileLockInfo.EndingByte,
  1521. &LastSplayLinks,
  1522. &GreaterThan );
  1523. if (SplayLinks == NULL) {
  1524. //
  1525. // No overlapping nodes were found, try to find successor
  1526. //
  1527. if (GreaterThan) {
  1528. //
  1529. // Last node looked at was greater than the lock so it is
  1530. // the place to pick up the enumeration
  1531. //
  1532. SplayLinks = LastSplayLinks;
  1533. } else {
  1534. //
  1535. // Last node looked at was less than the lock so grab its successor
  1536. //
  1537. if (LastSplayLinks) {
  1538. SplayLinks = RtlRealSuccessor(LastSplayLinks);
  1539. }
  1540. }
  1541. } else {
  1542. //
  1543. // Found an overlapping lock, see if it is the last returned
  1544. //
  1545. for (;
  1546. SplayLinks;
  1547. SplayLinks = RtlRealSuccessor(SplayLinks)) {
  1548. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1549. if (ContinuationPointer == ExLock &&
  1550. (ULONGLONG)FileLockInfo.StartingByte.QuadPart == (ULONGLONG)ExLock->LockInfo.StartingByte.QuadPart &&
  1551. (ULONGLONG)FileLockInfo.Length.QuadPart == (ULONGLONG)ExLock->LockInfo.Length.QuadPart &&
  1552. FileLockInfo.Key == ExLock->LockInfo.Key &&
  1553. FileLockInfo.FileObject == ExLock->LockInfo.FileObject &&
  1554. FileLockInfo.ProcessId == ExLock->LockInfo.ProcessId) {
  1555. //
  1556. // Found last returned, dig up its successor
  1557. //
  1558. SplayLinks = RtlRealSuccessor(SplayLinks);
  1559. //
  1560. // Got the node cold, so we're done
  1561. //
  1562. break;
  1563. }
  1564. //
  1565. // This lock overlapped and was not the last returned. In fact, since this lock would
  1566. // have conflicted with the last returned we know it could not have been returned
  1567. // before, so this should be returned to the caller.
  1568. //
  1569. // However, if it is a zero length lock we are looking for and a zero length lock we hit,
  1570. // we are at the beginning of a run we need to inspect. If we cannot find the last lock
  1571. // we returned, resume the enumeration at the beginning of the run.
  1572. //
  1573. if (ExLock->LockInfo.Length.QuadPart != 0 || FileLockInfo.Length.QuadPart != 0) {
  1574. break;
  1575. }
  1576. //
  1577. // Keep wandering down the run
  1578. //
  1579. }
  1580. }
  1581. //
  1582. // Were we able to find a lock to return?
  1583. //
  1584. if (SplayLinks == NULL) {
  1585. //
  1586. // There aren't any more exclusive locks, fall over to the shared tree
  1587. //
  1588. SplayLinks = LockInfo->LockQueue.SharedLockTree;
  1589. if (SplayLinks) {
  1590. while (RtlLeftChild(SplayLinks)) {
  1591. SplayLinks = RtlLeftChild(SplayLinks);
  1592. }
  1593. Node = CONTAINING_RECORD(SplayLinks, LOCKTREE_NODE, Links);
  1594. ShLock = CONTAINING_RECORD(Node->Locks.Next, SH_LOCK, Link);
  1595. FileLockInfo = ShLock->LockInfo;
  1596. ContinuationPointer = ShLock;
  1597. FoundReturnable = TRUE;
  1598. }
  1599. } else {
  1600. //
  1601. // This is the lock to return
  1602. //
  1603. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1604. FileLockInfo = ExLock->LockInfo;
  1605. ContinuationPointer = ExLock;
  1606. FoundReturnable = TRUE;
  1607. }
  1608. } else {
  1609. //
  1610. // Continue enumeration in the shared lock tree
  1611. //
  1612. Node = NULL;
  1613. SplayLinks = FsRtlFindFirstOverlappingSharedNode( LockInfo->LockQueue.SharedLockTree,
  1614. &FileLockInfo.StartingByte,
  1615. &FileLockInfo.EndingByte,
  1616. &LastSplayLinks,
  1617. &GreaterThan );
  1618. if (SplayLinks == NULL) {
  1619. //
  1620. // No overlapping nodes were found
  1621. //
  1622. if (GreaterThan) {
  1623. //
  1624. // Last node looked at was greater than the lock so it is
  1625. // the place to pick up the enumeration
  1626. //
  1627. if (LastSplayLinks) {
  1628. SplayLinks = LastSplayLinks;
  1629. Node = CONTAINING_RECORD( LastSplayLinks, LOCKTREE_NODE, Links );
  1630. }
  1631. } else {
  1632. //
  1633. // Last node looked at was less than the lock so grab its successor
  1634. //
  1635. if (LastSplayLinks) {
  1636. SplayLinks = RtlRealSuccessor(LastSplayLinks);
  1637. if (SplayLinks) {
  1638. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1639. }
  1640. }
  1641. }
  1642. } else {
  1643. //
  1644. // Grab the node we found
  1645. //
  1646. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1647. }
  1648. //
  1649. // If we have a node to look at, it may still not contain the the last returned lock
  1650. // if this isn't synchronized.
  1651. //
  1652. if (Node != NULL) {
  1653. //
  1654. // Walk down the locks at this node looking for the last returned lock
  1655. //
  1656. for (Link = Node->Locks.Next;
  1657. Link;
  1658. Link = Link->Next) {
  1659. //
  1660. // Get a pointer to the current lock record
  1661. //
  1662. ShLock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  1663. //
  1664. // See if it's a match
  1665. //
  1666. if (ContinuationPointer == ShLock &&
  1667. (ULONGLONG)FileLockInfo.StartingByte.QuadPart == (ULONGLONG)ShLock->LockInfo.StartingByte.QuadPart &&
  1668. (ULONGLONG)FileLockInfo.Length.QuadPart == (ULONGLONG)ShLock->LockInfo.Length.QuadPart &&
  1669. FileLockInfo.Key == ShLock->LockInfo.Key &&
  1670. FileLockInfo.FileObject == ShLock->LockInfo.FileObject &&
  1671. FileLockInfo.ProcessId == ShLock->LockInfo.ProcessId) {
  1672. Link = Link->Next;
  1673. break;
  1674. }
  1675. //
  1676. // See if we passed by its slot
  1677. //
  1678. if ((ULONGLONG)FileLockInfo.StartingByte.QuadPart < (ULONGLONG)ShLock->LockInfo.StartingByte.QuadPart) {
  1679. break;
  1680. }
  1681. }
  1682. if (Link == NULL) {
  1683. //
  1684. // This node doesn't contain the successor, so move
  1685. // up to the successor node in the tree and return the
  1686. // first lock. If we're actually at the end of the tree
  1687. // we just fall off the end correctly.
  1688. //
  1689. SplayLinks = RtlRealSuccessor(SplayLinks);
  1690. if (SplayLinks) {
  1691. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1692. Link = Node->Locks.Next;
  1693. }
  1694. }
  1695. if (Link) {
  1696. //
  1697. // Found a Lock to return, copy it to the stack
  1698. //
  1699. ShLock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  1700. FileLockInfo = ShLock->LockInfo;
  1701. ContinuationPointer = ShLock;
  1702. FoundReturnable = TRUE;
  1703. }
  1704. }
  1705. }
  1706. } else {
  1707. //
  1708. // Restarting the enumeration. Find leftmost node in the exclusive tree and hand back
  1709. // the first lock, falling over to the shared if no exlcusive locks are applied
  1710. //
  1711. if (LockInfo->LockQueue.ExclusiveLockTree) {
  1712. SplayLinks = LockInfo->LockQueue.ExclusiveLockTree;
  1713. while (RtlLeftChild(SplayLinks) != NULL) {
  1714. SplayLinks = RtlLeftChild(SplayLinks);
  1715. }
  1716. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1717. FileLockInfo = ExLock->LockInfo;
  1718. ContinuationPointer = ExLock;
  1719. FoundReturnable = TRUE;
  1720. } else {
  1721. if (LockInfo->LockQueue.SharedLockTree) {
  1722. SplayLinks = LockInfo->LockQueue.SharedLockTree;
  1723. while (RtlLeftChild(SplayLinks) != NULL) {
  1724. SplayLinks = RtlLeftChild(SplayLinks);
  1725. }
  1726. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1727. ShLock = CONTAINING_RECORD( Node->Locks.Next, SH_LOCK, Link );
  1728. FileLockInfo = ShLock->LockInfo;
  1729. ContinuationPointer = ShLock;
  1730. FoundReturnable = TRUE;
  1731. }
  1732. }
  1733. }
  1734. //
  1735. // Release all the lock queues
  1736. //
  1737. FsRtlReleaseLockQueue (&LockInfo->LockQueue, OldIrql);
  1738. if (!FoundReturnable) {
  1739. //
  1740. // No returnable lock was found, end of list
  1741. //
  1742. return NULL;
  1743. }
  1744. //
  1745. // Update current enum location information
  1746. //
  1747. FileLock->LastReturnedLockInfo = FileLockInfo;
  1748. FileLock->LastReturnedLock = ContinuationPointer;
  1749. //
  1750. // Return lock record to caller
  1751. //
  1752. return &FileLock->LastReturnedLockInfo;
  1753. }
  1754. BOOLEAN
  1755. FsRtlCheckNoSharedConflict (
  1756. IN PLOCK_QUEUE LockQueue,
  1757. IN PLARGE_INTEGER Starting,
  1758. IN PLARGE_INTEGER Ending
  1759. )
  1760. /*++
  1761. Routine Description:
  1762. This routine checks to see if there is overlap in the shared locks with
  1763. the given range. It is intended for use in the write access check path
  1764. so that a rebalance will occur.
  1765. Arguments:
  1766. FileLock - Supplies the File Lock to check
  1767. StartingByte - Supplies the first byte (zero based) to check
  1768. Length - Supplies the length, in bytes, to check
  1769. Key - Supplies the key to use in the check
  1770. FileObject - Supplies the file object to use in the check
  1771. ProcessId - Supplies the Process Id to use in the check
  1772. Return Value:
  1773. BOOLEAN - TRUE if the indicated user/request doesn't conflict in
  1774. entire specified byte range, and FALSE otherwise
  1775. --*/
  1776. {
  1777. PRTL_SPLAY_LINKS SplayLinks, BeginLinks;
  1778. PLOCKTREE_NODE Node;
  1779. SplayLinks = FsRtlFindFirstOverlappingSharedNode( LockQueue->SharedLockTree,
  1780. Starting,
  1781. Ending,
  1782. &BeginLinks,
  1783. NULL);
  1784. if (BeginLinks) {
  1785. LockQueue->SharedLockTree = RtlSplay(BeginLinks);
  1786. }
  1787. //
  1788. // If this node is holey, we'll have to walk the whole thing.
  1789. //
  1790. if (SplayLinks) {
  1791. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1792. if (Node->HoleyNode) {
  1793. return (BOOLEAN)(FsRtlFindFirstOverlapInNode( Node, Starting, Ending ) == NULL);
  1794. }
  1795. //
  1796. // Overlapping non-holey node, so we do have shared lock conflict.
  1797. //
  1798. return FALSE;
  1799. }
  1800. //
  1801. // No node overlaps.
  1802. //
  1803. return TRUE;
  1804. }
  1805. BOOLEAN
  1806. FsRtlCheckNoExclusiveConflict (
  1807. IN PLOCK_QUEUE LockQueue,
  1808. IN PLARGE_INTEGER Starting,
  1809. IN PLARGE_INTEGER Ending,
  1810. IN ULONG Key,
  1811. IN PFILE_OBJECT FileObject,
  1812. IN PVOID ProcessId
  1813. )
  1814. /*++
  1815. Routine Description:
  1816. This routine checks to see if there is conflict in the exclusive locks with
  1817. a given range and identifying tuple of key, fileobject and process. This is
  1818. for part of the read access path.
  1819. Arguments:
  1820. FileLock - Supplies the File Lock to check
  1821. StartingByte - Supplies the first byte (zero based) to check
  1822. Length - Supplies the length, in bytes, to check
  1823. Key - Supplies the key to use in the check
  1824. FileObject - Supplies the file object to use in the check
  1825. ProcessId - Supplies the Process Id to use in the check
  1826. Return Value:
  1827. BOOLEAN - TRUE if the indicated user/request doesn't conflict in
  1828. entire specified byte range, and FALSE otherwise
  1829. --*/
  1830. {
  1831. PRTL_SPLAY_LINKS SplayLinks, BeginLinks;
  1832. PEX_LOCK Lock;
  1833. BOOLEAN Status = TRUE;
  1834. //
  1835. // Find the node to begin the search at and go
  1836. //
  1837. for (SplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockQueue->ExclusiveLockTree,
  1838. Starting,
  1839. Ending,
  1840. &BeginLinks,
  1841. NULL);
  1842. SplayLinks;
  1843. SplayLinks = RtlRealSuccessor(SplayLinks)) {
  1844. Lock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1845. //
  1846. // If the current lock is greater than the end of the range we're
  1847. // looking for then the the user doesn't conflict
  1848. //
  1849. // if (Ending < Lock->StartingByte) ...
  1850. //
  1851. if ((ULONGLONG)Ending->QuadPart < (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart) {
  1852. DebugTrace(0, Dbg, "FsRtlCheckForExclusiveConflict, Ending < Lock->StartingByte\n", 0);
  1853. break;
  1854. }
  1855. //
  1856. // Check for any overlap with the request. The test for
  1857. // overlap is that starting byte is less than or equal to the locks
  1858. // ending byte, and the ending byte is greater than or equal to the
  1859. // locks starting byte. We already tested for this latter case in
  1860. // the preceding statement.
  1861. //
  1862. // if (Starting <= Lock->StartingByte + Lock->Length - 1) ...
  1863. //
  1864. if ((ULONGLONG)Starting->QuadPart <= (ULONGLONG)Lock->LockInfo.EndingByte.QuadPart) {
  1865. //
  1866. // This request overlaps the lock. We cannot grant the request
  1867. // if the file object, process id, and key do not match. Otherwise
  1868. // we'll continue looping looking at locks
  1869. //
  1870. if ((Lock->LockInfo.FileObject != FileObject) ||
  1871. (Lock->LockInfo.ProcessId != ProcessId) ||
  1872. (Lock->LockInfo.Key != Key)) {
  1873. DebugTrace(0, Dbg, "FsRtlCheckForExclusiveConflict, Range locked already\n", 0);
  1874. Status = FALSE;
  1875. break;
  1876. }
  1877. }
  1878. }
  1879. if (BeginLinks) {
  1880. LockQueue->ExclusiveLockTree = RtlSplay(BeginLinks);
  1881. }
  1882. //
  1883. // We searched the entire range without a conflict so we'll note no conflict
  1884. //
  1885. return Status;
  1886. }
  1887. BOOLEAN
  1888. FsRtlFastCheckLockForRead (
  1889. IN PFILE_LOCK FileLock,
  1890. IN PLARGE_INTEGER StartingByte,
  1891. IN PLARGE_INTEGER Length,
  1892. IN ULONG Key,
  1893. IN PFILE_OBJECT FileObject,
  1894. IN PVOID ProcessId
  1895. )
  1896. /*++
  1897. Routine Description:
  1898. This routine checks to see if the caller has read access to the
  1899. indicated range due to file locks.
  1900. Arguments:
  1901. FileLock - Supplies the File Lock to check
  1902. StartingByte - Supplies the first byte (zero based) to check
  1903. Length - Supplies the length, in bytes, to check
  1904. Key - Supplies the to use in the check
  1905. FileObject - Supplies the file object to use in the check
  1906. ProcessId - Supplies the Process Id to use in the check
  1907. Return Value:
  1908. BOOLEAN - TRUE if the indicated user/request has read access to the
  1909. entire specified byte range, and FALSE otherwise
  1910. --*/
  1911. {
  1912. LARGE_INTEGER Starting;
  1913. LARGE_INTEGER Ending;
  1914. PLOCK_INFO LockInfo;
  1915. PLOCK_QUEUE LockQueue;
  1916. KIRQL OldIrql;
  1917. PFILE_LOCK_INFO LastLock;
  1918. BOOLEAN Status;
  1919. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  1920. //
  1921. // No lock information on this FileLock
  1922. //
  1923. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead, No lock info\n", 0);
  1924. return TRUE;
  1925. }
  1926. //
  1927. // If there isn't an exclusive lock then we can immediately grant access
  1928. //
  1929. if (LockInfo->LockQueue.ExclusiveLockTree == NULL) {
  1930. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead, No exlocks present\n", 0);
  1931. return TRUE;
  1932. }
  1933. //
  1934. // If length is zero then automatically give grant access
  1935. //
  1936. if ((ULONGLONG)Length->QuadPart == 0) {
  1937. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead, Length == 0\n", 0);
  1938. return TRUE;
  1939. }
  1940. //
  1941. // Get our starting and ending byte position
  1942. //
  1943. Starting = *StartingByte;
  1944. (ULONGLONG)Ending.QuadPart = (ULONGLONG)Starting.QuadPart + (ULONGLONG)Length->QuadPart - 1;
  1945. //
  1946. // Now check lock queue
  1947. //
  1948. LockQueue = &LockInfo->LockQueue;
  1949. //
  1950. // Grab the waiting lock queue spinlock to exclude anyone from messing
  1951. // with the queue while we're using it
  1952. //
  1953. FsRtlAcquireLockQueue(LockQueue, &OldIrql);
  1954. //
  1955. // If the range ends below the lowest existing lock, this read is OK.
  1956. //
  1957. if ( ((ULONGLONG)Ending.QuadPart < (ULONGLONG)LockInfo->LowestLockOffset) ) {
  1958. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead (below lowest lock)\n", 0);
  1959. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  1960. return TRUE;
  1961. }
  1962. //
  1963. // If the caller just locked this range, he can read it.
  1964. //
  1965. LastLock = (PFILE_LOCK_INFO)FileObject->LastLock;
  1966. if ((LastLock != NULL) &&
  1967. ((ULONGLONG)Starting.QuadPart >= (ULONGLONG)LastLock->StartingByte.QuadPart) &&
  1968. ((ULONGLONG)Ending.QuadPart <= (ULONGLONG)LastLock->EndingByte.QuadPart) &&
  1969. (LastLock->Key == Key) &&
  1970. (LastLock->ProcessId == ProcessId)) {
  1971. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  1972. return TRUE;
  1973. }
  1974. //
  1975. // Check the exclusive locks for a conflict. It is impossible to have
  1976. // a read conflict with any shared lock.
  1977. //
  1978. Status = FsRtlCheckNoExclusiveConflict(LockQueue, &Starting, &Ending, Key, FileObject, ProcessId);
  1979. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  1980. return Status;
  1981. }
  1982. BOOLEAN
  1983. FsRtlFastCheckLockForWrite (
  1984. IN PFILE_LOCK FileLock,
  1985. IN PLARGE_INTEGER StartingByte,
  1986. IN PLARGE_INTEGER Length,
  1987. IN ULONG Key,
  1988. IN PVOID FileObject,
  1989. IN PVOID ProcessId
  1990. )
  1991. /*++
  1992. Routine Description:
  1993. This routine checks to see if the caller has write access to the
  1994. indicated range due to file locks
  1995. Arguments:
  1996. FileLock - Supplies the File Lock to check
  1997. StartingByte - Supplies the first byte (zero based) to check
  1998. Length - Supplies the length, in bytes, to check
  1999. Key - Supplies the to use in the check
  2000. FileObject - Supplies the file object to use in the check
  2001. ProcessId - Supplies the Process Id to use in the check
  2002. Return Value:
  2003. BOOLEAN - TRUE if the indicated user/request has write access to the
  2004. entire specified byte range, and FALSE otherwise
  2005. --*/
  2006. {
  2007. LARGE_INTEGER Starting;
  2008. LARGE_INTEGER Ending;
  2009. PLOCK_INFO LockInfo;
  2010. PLOCK_QUEUE LockQueue;
  2011. KIRQL OldIrql;
  2012. PFILE_LOCK_INFO LastLock;
  2013. BOOLEAN Status;
  2014. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  2015. //
  2016. // No lock information on this FileLock
  2017. //
  2018. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead, No lock info\n", 0);
  2019. return TRUE;
  2020. }
  2021. //
  2022. // If there isn't a lock then we can immediately grant access
  2023. //
  2024. if (LockInfo->LockQueue.SharedLockTree == NULL && LockInfo->LockQueue.ExclusiveLockTree == NULL) {
  2025. DebugTrace(0, Dbg, "FsRtlFastCheckLockForWrite, No locks present\n", 0);
  2026. return TRUE;
  2027. }
  2028. //
  2029. // If length is zero then automatically grant access
  2030. //
  2031. if ((ULONGLONG)Length->QuadPart == 0) {
  2032. DebugTrace(0, Dbg, "FsRtlFastCheckLockForWrite, Length == 0\n", 0);
  2033. return TRUE;
  2034. }
  2035. //
  2036. // Get our starting and ending byte position
  2037. //
  2038. Starting = *StartingByte;
  2039. (ULONGLONG)Ending.QuadPart = (ULONGLONG)Starting.QuadPart + (ULONGLONG)Length->QuadPart - 1;
  2040. //
  2041. // Now check lock queue
  2042. //
  2043. LockQueue = &LockInfo->LockQueue;
  2044. //
  2045. // Grab the waiting lock queue spinlock to exclude anyone from messing
  2046. // with the queue while we're using it
  2047. //
  2048. FsRtlAcquireLockQueue(LockQueue, &OldIrql);
  2049. //
  2050. // If the range ends below the lowest existing lock, this write is OK.
  2051. //
  2052. if ( ((ULONGLONG)Ending.QuadPart < (ULONGLONG)LockInfo->LowestLockOffset) ) {
  2053. DebugTrace(0, Dbg, "FsRtlFastCheckLockForWrite (below lowest lock)\n", 0);
  2054. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  2055. return TRUE;
  2056. }
  2057. //
  2058. // If the caller just locked this range exclusively, he can write it.
  2059. //
  2060. LastLock = (PFILE_LOCK_INFO)((PFILE_OBJECT)FileObject)->LastLock;
  2061. if ((LastLock != NULL) &&
  2062. ((ULONGLONG)Starting.QuadPart >= (ULONGLONG)LastLock->StartingByte.QuadPart) &&
  2063. ((ULONGLONG)Ending.QuadPart <= (ULONGLONG)LastLock->EndingByte.QuadPart) &&
  2064. (LastLock->Key == Key) &&
  2065. (LastLock->ProcessId == ProcessId) &&
  2066. LastLock->ExclusiveLock) {
  2067. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  2068. return TRUE;
  2069. }
  2070. //
  2071. // Check the shared locks for overlap. Any overlap in the shared locks is fatal.
  2072. //
  2073. Status = FsRtlCheckNoSharedConflict(LockQueue, &Starting, &Ending);
  2074. if (Status == TRUE) {
  2075. //
  2076. // No overlap in the shared locks, so check the exclusive locks for overlap.
  2077. //
  2078. Status = FsRtlCheckNoExclusiveConflict(LockQueue, &Starting, &Ending, Key, FileObject, ProcessId);
  2079. }
  2080. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  2081. return Status;
  2082. }
  2083. VOID
  2084. FsRtlSplitLocks (
  2085. IN PLOCKTREE_NODE ParentNode,
  2086. IN PSINGLE_LIST_ENTRY *pStartLink,
  2087. IN PLARGE_INTEGER LastShadowedByte,
  2088. IN PLARGE_INTEGER GlueOffset
  2089. )
  2090. /*++
  2091. Routine Description:
  2092. This routine examines and possibly splits off shared locks associated
  2093. with a node into new nodes of the lock tree. Called from routines that
  2094. have just deleted locks.
  2095. The arguments that supply the initial conditions for the operation are
  2096. optional if the node is known to be holey.
  2097. Arguments:
  2098. ParentNode- Supplies the node the locks are coming from
  2099. pStartLink - Supplies the pointer to the link address of the start of the
  2100. range of locks in the ParentNode's locklist that need to be checked
  2101. LastShadowedByte - Supplies the last byte offset that needs to be checked
  2102. GlueOffset - Supplies the maximum offset affected by locks prior to this
  2103. point in the list
  2104. Return Value:
  2105. BOOLEAN - True if the split was successful, False otherwise. The node will
  2106. be marked as Holey if the split could not occur.
  2107. --*/
  2108. {
  2109. PSH_LOCK Lock;
  2110. PLOCKTREE_NODE NewNode;
  2111. PSINGLE_LIST_ENTRY Link, *pLink, *NextpLink;
  2112. LARGE_INTEGER MaxOffset, StartOffset, HaltOffset;
  2113. BOOLEAN ExtentValid;
  2114. BOOLEAN FailedHoleySplit = FALSE;
  2115. //
  2116. // There are two cases: the node is holey or not. If the node is holey, at some
  2117. // point we failed to get resources to complete a split, so despite our caller's
  2118. // good intentions we need to go over the entire node.
  2119. //
  2120. if (ParentNode->HoleyNode) {
  2121. //
  2122. // Just move the starting link back to the front. The maximum offset and
  2123. // starting offset of the node will be initialized in the loop. We also turn
  2124. // off the holey flag, which will be turned on again as appropriate.
  2125. //
  2126. pStartLink = &ParentNode->Locks.Next;
  2127. ParentNode->HoleyNode = FALSE;
  2128. HaltOffset.QuadPart = ParentNode->Extent;
  2129. } else {
  2130. HaltOffset = *LastShadowedByte;
  2131. MaxOffset = *GlueOffset;
  2132. StartOffset.QuadPart = 0;
  2133. if (!ParentNode->Locks.Next ||
  2134. (ULONGLONG)HaltOffset.QuadPart <= (ULONGLONG)MaxOffset.QuadPart) {
  2135. //
  2136. // The parent node is not there, doesn't have links associated, or the
  2137. // last possible byte that is affected by the operation our caller made
  2138. // is interior to the max extent of all locks still in this node - in
  2139. // which case there is nothing that needs to be done.
  2140. //
  2141. return;
  2142. }
  2143. }
  2144. //
  2145. // If the extent of the node is past the last byte affected by whatever
  2146. // operations were done to this node, we can avoid the linear scan of
  2147. // the list past that last affected byte since we already know the
  2148. // extent of the entire list! If it is not (note that it would have to
  2149. // be equal - by defintion) then we need to recalculate the extents of
  2150. // all nodes we touch in this operation.
  2151. //
  2152. ExtentValid = (ParentNode->Extent > (ULONGLONG)HaltOffset.QuadPart);
  2153. for (pLink = pStartLink;
  2154. (Link = *pLink) != NULL;
  2155. pLink = NextpLink) {
  2156. NextpLink = &Link->Next;
  2157. Lock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  2158. if (ParentNode->Locks.Next == *pLink) {
  2159. //
  2160. // We're at the first lock in the node, and we know that we're going to leave
  2161. // at least one lock here. Skip over that lock. We also know that the max
  2162. // offset must be that locks's ending byte - make sure it is. Note that this
  2163. // code is *exactly* the same as the update MaxOffset code at the bottom of
  2164. // the loop.
  2165. //
  2166. MaxOffset.QuadPart = Lock->LockInfo.EndingByte.QuadPart;
  2167. //
  2168. // Set the starting offset of the node. This is only an issue for zero length
  2169. // locks, so that we can figure out what is going on if we split a node and wind
  2170. // up with some number of "overlapped" zero length locks at the front of the new
  2171. // node. We must be able to notice this case, and not think that each needs to
  2172. // be in a seperate node.
  2173. //
  2174. StartOffset.QuadPart = Lock->LockInfo.StartingByte.QuadPart;
  2175. //
  2176. // If extents are invalid we also need to set it in case this turns out to
  2177. // be the only lock at this node.
  2178. //
  2179. if (!ExtentValid) {
  2180. ParentNode->Extent = (ULONGLONG)MaxOffset.QuadPart;
  2181. }
  2182. continue;
  2183. }
  2184. //
  2185. // If the lock begins at a byte offset greater than the maximum offset seen to this
  2186. // point, AND this is not a zero length node starting at the beginning of this node,
  2187. // break the node. The second half of the test keeps co-incident zero length locks
  2188. // in the same node. (zero length lock ---> starting = ending + 1).
  2189. //
  2190. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)MaxOffset.QuadPart &&
  2191. !(Lock->LockInfo.Length.QuadPart == 0 &&
  2192. Lock->LockInfo.StartingByte.QuadPart == StartOffset.QuadPart)) {
  2193. //
  2194. // Break the node up here
  2195. //
  2196. NewNode = FsRtlAllocateLockTreeNode();
  2197. if (NewNode == NULL) {
  2198. //
  2199. // If we are out of resources, this node is now holey - we know that the locks at
  2200. // this node do not completely cover the indicated range. Keep splitting for two
  2201. // reasons: more resources may become avaliable, and we must keep updating the
  2202. // node's extent if it is known to be invalid.
  2203. //
  2204. //
  2205. // Now if this node was already holey it is not possible to state that, if we
  2206. // manage to split if further as we keep walking, that the resulting "left" node
  2207. // is not holey. See below.
  2208. //
  2209. if (ParentNode->HoleyNode) {
  2210. FailedHoleySplit = TRUE;
  2211. }
  2212. ParentNode->HoleyNode = TRUE;
  2213. } else {
  2214. //
  2215. // Initialize the node.
  2216. //
  2217. RtlInitializeSplayLinks(&NewNode->Links);
  2218. NewNode->HoleyNode = FALSE;
  2219. //
  2220. // Find the spot in the tree to take the new node(s). If the current node has
  2221. // a free right child, we use it, else find the successor node and use its
  2222. // left child. One of these cases must be avaliable since we know there are
  2223. // no nodes between this node and its successor.
  2224. //
  2225. if (RtlRightChild(&ParentNode->Links) == NULL) {
  2226. RtlInsertAsRightChild(&ParentNode->Links, &NewNode->Links);
  2227. } else {
  2228. ASSERT(RtlLeftChild(RtlRealSuccessor(&ParentNode->Links)) == NULL);
  2229. RtlInsertAsLeftChild(RtlRealSuccessor(&ParentNode->Links), &NewNode->Links);
  2230. }
  2231. //
  2232. // Move the remaining locks over to the new node and fix up extents
  2233. //
  2234. NewNode->Locks.Next = *pLink;
  2235. *pLink = NULL;
  2236. NewNode->Tail.Next = ParentNode->Tail.Next;
  2237. ParentNode->Tail.Next = CONTAINING_RECORD( pLink, SINGLE_LIST_ENTRY, Next );
  2238. //
  2239. // This will cause us to fall into the first-lock clause above on the next pass
  2240. //
  2241. NextpLink = &NewNode->Locks.Next;
  2242. //
  2243. // The new node's extent is now copied from the parent. The old node's extent must be
  2244. // the maximum offset we have seen to this point.
  2245. //
  2246. // Note that if ExtentValid is true, that must mean that the lock ending at that extent
  2247. // is in the new node since if it was in the old node we wouldn't have been able to split.
  2248. //
  2249. NewNode->Extent = ParentNode->Extent;
  2250. ParentNode->Extent = (ULONGLONG)MaxOffset.QuadPart;
  2251. //
  2252. // The parent node can no longer be holey if we have not failed a split in this node.
  2253. //
  2254. if (!FailedHoleySplit) {
  2255. ParentNode->HoleyNode = FALSE;
  2256. } else {
  2257. //
  2258. // So reset the failure flag for the new node.
  2259. //
  2260. FailedHoleySplit = FALSE;
  2261. }
  2262. //
  2263. // Move over to the new node.
  2264. //
  2265. ParentNode = NewNode;
  2266. continue;
  2267. }
  2268. }
  2269. if (ExtentValid &&
  2270. (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)HaltOffset.QuadPart) {
  2271. //
  2272. // Our extents are good and this lock is past the shadow, so we can stop
  2273. //
  2274. return;
  2275. }
  2276. if ((ULONGLONG)MaxOffset.QuadPart < (ULONGLONG)Lock->LockInfo.EndingByte.QuadPart) {
  2277. //
  2278. // Update maximum offset
  2279. //
  2280. MaxOffset.QuadPart = Lock->LockInfo.EndingByte.QuadPart;
  2281. if (!ExtentValid) {
  2282. //
  2283. // Extents are not good so we must update the extent
  2284. //
  2285. ParentNode->Extent = (ULONGLONG)MaxOffset.QuadPart;
  2286. }
  2287. }
  2288. }
  2289. //
  2290. // Reached the end of the list, so update the extent (case of all subsequent locks
  2291. // having been interior to GlueOffset)
  2292. //
  2293. ParentNode->Extent = (ULONGLONG)MaxOffset.QuadPart;
  2294. return;
  2295. }
  2296. VOID
  2297. FsRtlPrivateRemoveLock (
  2298. IN PLOCK_INFO LockInfo,
  2299. IN PFILE_LOCK_INFO FileLockInfo,
  2300. IN BOOLEAN CheckForWaiters
  2301. )
  2302. /*++
  2303. Routine Description:
  2304. General purpose cleanup routine. Finds the given lock structure
  2305. and removes it from the file lock list. Differs from UnlockSingle
  2306. only in that it disables the UnlockRoutine of the FileLock and
  2307. optionalizes walking the waiting locks list.
  2308. Arguments:
  2309. FileLock - Supplies the file's lock structure supposedly containing a stale lock
  2310. FileLockInfo - Supplies file lock data being freed
  2311. CheckForWaiters - If true check for possible waiting locks, caused
  2312. by freeing the locked range
  2313. Return Value:
  2314. None.
  2315. --*/
  2316. {
  2317. NTSTATUS Status;
  2318. if (FileLockInfo->ExclusiveLock) {
  2319. //
  2320. // We must find it in the exclusive lock tree
  2321. //
  2322. Status = FsRtlFastUnlockSingleExclusive( LockInfo,
  2323. FileLockInfo->FileObject,
  2324. &FileLockInfo->StartingByte,
  2325. &FileLockInfo->Length,
  2326. FileLockInfo->ProcessId,
  2327. FileLockInfo->Key,
  2328. NULL,
  2329. TRUE,
  2330. CheckForWaiters );
  2331. ASSERT( Status == STATUS_SUCCESS);
  2332. } else {
  2333. //
  2334. // We must find it in the shared lock tree
  2335. //
  2336. Status = FsRtlFastUnlockSingleShared( LockInfo,
  2337. FileLockInfo->FileObject,
  2338. &FileLockInfo->StartingByte,
  2339. &FileLockInfo->Length,
  2340. FileLockInfo->ProcessId,
  2341. FileLockInfo->Key,
  2342. NULL,
  2343. TRUE,
  2344. CheckForWaiters );
  2345. ASSERT( Status == STATUS_SUCCESS);
  2346. }
  2347. return;
  2348. }
  2349. NTSTATUS
  2350. FsRtlFastUnlockSingle (
  2351. IN PFILE_LOCK FileLock,
  2352. IN PFILE_OBJECT FileObject,
  2353. IN LARGE_INTEGER UNALIGNED *FileOffset,
  2354. IN PLARGE_INTEGER Length,
  2355. IN PEPROCESS ProcessId,
  2356. IN ULONG Key,
  2357. IN PVOID Context OPTIONAL,
  2358. IN BOOLEAN AlreadySynchronized
  2359. )
  2360. /*++
  2361. Routine Description:
  2362. This routine performs an Unlock Single operation on the current locks
  2363. associated with the specified file lock. Only the lock with a matching
  2364. file object, process id, key, and range is freed.
  2365. Arguments:
  2366. FileLock - Supplies the file lock being freed.
  2367. FileObject - Supplies the file object holding the locks
  2368. FileOffset - Supplies the offset to be unlocked
  2369. Length - Supplies the length in bytes to be unlocked
  2370. ProcessId - Supplies the process Id to use in this operation
  2371. Key - Supplies the key to use in this operation
  2372. Context - Optionally supplies context to use when completing Irps
  2373. AlreadySynchronized - Indicates that the caller has already synchronized
  2374. access to the file lock so the fields in the file lock and
  2375. be updated without further locking, but not the queues.
  2376. Return Value:
  2377. NTSTATUS - The completion status for this operation
  2378. --*/
  2379. {
  2380. NTSTATUS Status;
  2381. //
  2382. // XXX AlreadySynchronized is obsolete. It was apparently added for the dead
  2383. // XXX SoloLock code.
  2384. //
  2385. if (FileLock->LockInformation == NULL) {
  2386. //
  2387. // Fast exit - no locks are applied
  2388. //
  2389. return STATUS_RANGE_NOT_LOCKED;
  2390. }
  2391. Status = FsRtlFastUnlockSingleExclusive( FileLock->LockInformation,
  2392. FileObject,
  2393. FileOffset,
  2394. Length,
  2395. ProcessId,
  2396. Key,
  2397. Context,
  2398. FALSE,
  2399. TRUE );
  2400. if (Status == STATUS_SUCCESS) {
  2401. //
  2402. // Found and unlocked in the exclusive tree, so we're done
  2403. //
  2404. return Status;
  2405. }
  2406. Status = FsRtlFastUnlockSingleShared( FileLock->LockInformation,
  2407. FileObject,
  2408. FileOffset,
  2409. Length,
  2410. ProcessId,
  2411. Key,
  2412. Context,
  2413. FALSE,
  2414. TRUE );
  2415. return Status;
  2416. }
  2417. NTSTATUS
  2418. FsRtlFastUnlockSingleShared (
  2419. IN PLOCK_INFO LockInfo,
  2420. IN PFILE_OBJECT FileObject,
  2421. IN LARGE_INTEGER UNALIGNED *FileOffset,
  2422. IN PLARGE_INTEGER Length,
  2423. IN PEPROCESS ProcessId,
  2424. IN ULONG Key,
  2425. IN PVOID Context OPTIONAL,
  2426. IN BOOLEAN IgnoreUnlockRoutine,
  2427. IN BOOLEAN CheckForWaiters
  2428. )
  2429. /*++
  2430. Routine Description:
  2431. This routine performs an Unlock Single operation on the current locks
  2432. associated with the specified file lock. Only the lock with a matching
  2433. file object, process id, key, and range is freed.
  2434. Arguments:
  2435. LockInfo - Supplies the lock data being operated on
  2436. FileObject - Supplies the file object holding the locks
  2437. FileOffset - Supplies the offset to be unlocked
  2438. Length - Supplies the length in bytes to be unlocked
  2439. ProcessId - Supplies the process Id to use in this operation
  2440. Key - Supplies the key to use in this operation
  2441. Context - Optionally supplies context to use when completing Irps
  2442. IgnoreUnlockRoutine - inidicates that the filelock's unlock routine
  2443. should not be called on lock removal (for removal of aborted
  2444. locks)
  2445. CheckForWaiters - If true check for possible waiting locks, caused
  2446. by freeing the locked range
  2447. Return Value:
  2448. NTSTATUS - The completion status for this operation
  2449. --*/
  2450. {
  2451. PSINGLE_LIST_ENTRY *pLink, Link;
  2452. KIRQL OldIrql;
  2453. PLOCK_QUEUE LockQueue;
  2454. PRTL_SPLAY_LINKS SplayLinks;
  2455. LARGE_INTEGER EndingOffset, MaxOffset;
  2456. PLOCKTREE_NODE Node;
  2457. LARGE_INTEGER AlignedFileOffset;
  2458. //
  2459. // General case - search the outstanding lock queue for this lock
  2460. //
  2461. AlignedFileOffset = *FileOffset;
  2462. LockQueue = &LockInfo->LockQueue;
  2463. FsRtlAcquireLockQueue(LockQueue, &OldIrql);
  2464. //
  2465. // Check for the no locks currently held
  2466. //
  2467. if (LockQueue->SharedLockTree == NULL) {
  2468. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  2469. return STATUS_RANGE_NOT_LOCKED;
  2470. }
  2471. //
  2472. // Find the overlapping node, if it exists, to search. Note that
  2473. // we don't have to go through more than one node in the tree
  2474. // since we are assuming this is an existing lock.
  2475. //
  2476. EndingOffset.QuadPart = (ULONGLONG)AlignedFileOffset.QuadPart + (ULONGLONG)Length->QuadPart - 1;
  2477. SplayLinks = FsRtlFindFirstOverlappingSharedNode( LockQueue->SharedLockTree,
  2478. &AlignedFileOffset,
  2479. &EndingOffset,
  2480. NULL,
  2481. NULL );
  2482. if (SplayLinks == NULL) {
  2483. //
  2484. // No node in the tree overlaps this range, so we're done
  2485. //
  2486. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  2487. return STATUS_RANGE_NOT_LOCKED;
  2488. }
  2489. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  2490. MaxOffset.QuadPart = 0;
  2491. for (pLink = &Node->Locks.Next;
  2492. (Link = *pLink) != NULL;
  2493. pLink = &Link->Next) {
  2494. PSH_LOCK Lock;
  2495. Lock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  2496. DebugTrace(0, Dbg, "Sh Top of Loop, Lock = %08lx\n", Lock );
  2497. if ((Lock->LockInfo.FileObject == FileObject) &&
  2498. (Lock->LockInfo.ProcessId == ProcessId) &&
  2499. (Lock->LockInfo.Key == Key) &&
  2500. ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart == (ULONGLONG)AlignedFileOffset.QuadPart) &&
  2501. ((ULONGLONG)Lock->LockInfo.Length.QuadPart == (ULONGLONG)Length->QuadPart)) {
  2502. DebugTrace(0, Dbg, "Sh Found one to unlock\n", 0);
  2503. //
  2504. // We have an exact match so now is the time to delete this
  2505. // lock. Remove the lock from the list, then call the
  2506. // optional unlock routine, then delete the lock.
  2507. //
  2508. if (FileObject->LastLock == &Lock->LockInfo) {
  2509. FileObject->LastLock = NULL;
  2510. }
  2511. if (*pLink == Node->Tail.Next) {
  2512. //
  2513. // Deleting the tail node of the list. Safe even if deleting the
  2514. // first node since this implies we're also deleting the last node
  2515. // in the node which means we'll delete the node ...
  2516. //
  2517. Node->Tail.Next = CONTAINING_RECORD( pLink, SINGLE_LIST_ENTRY, Next );
  2518. }
  2519. //
  2520. // Snip the deleted lock
  2521. //
  2522. *pLink = Link->Next;
  2523. if (pLink == &Node->Locks.Next) {
  2524. //
  2525. // Deleted first lock in node
  2526. //
  2527. if (Node->Locks.Next == NULL) {
  2528. //
  2529. // Just deleted last lock on this node, so free it
  2530. //
  2531. LockQueue->SharedLockTree = RtlDelete(SplayLinks);
  2532. FsRtlFreeLockTreeNode(Node);
  2533. Node = NULL;
  2534. }
  2535. if (LockInfo->LowestLockOffset != 0xffffffff &&
  2536. LockInfo->LowestLockOffset == Lock->LockInfo.StartingByte.LowPart) {
  2537. //
  2538. // This was the lowest lock in the trees, reset the lowest lock offset
  2539. //
  2540. FsRtlPrivateResetLowestLockOffset(LockInfo);
  2541. }
  2542. }
  2543. //
  2544. // Now the fun begins. It may be the case that the lock just snipped from
  2545. // the chain was gluing locks at this node together, so we need to
  2546. // inspect the chain.
  2547. //
  2548. if (Node) {
  2549. FsRtlSplitLocks(Node, pLink, &Lock->LockInfo.EndingByte, &MaxOffset);
  2550. }
  2551. if (!IgnoreUnlockRoutine && LockInfo->UnlockRoutine != NULL) {
  2552. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  2553. LockInfo->UnlockRoutine( Context, &Lock->LockInfo );
  2554. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  2555. }
  2556. FsRtlFreeSharedLock( Lock );
  2557. //
  2558. // See if there are additional waiting locks that we can
  2559. // now release.
  2560. //
  2561. if (CheckForWaiters && LockQueue->WaitingLocks.Next) {
  2562. FsRtlPrivateCheckWaitingLocks( LockInfo, LockQueue, OldIrql );
  2563. }
  2564. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  2565. return STATUS_SUCCESS;
  2566. }
  2567. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)AlignedFileOffset.QuadPart) {
  2568. //
  2569. // The current lock begins at a byte offset greater than the range we are seeking
  2570. // to unlock. This range must therefore not be locked.
  2571. //
  2572. break;
  2573. }
  2574. if ((ULONGLONG)MaxOffset.QuadPart < (ULONGLONG)Lock->LockInfo.EndingByte.QuadPart) {
  2575. //
  2576. // Maintain the maximum offset affected by locks up to this point.
  2577. //
  2578. MaxOffset.QuadPart = Lock->LockInfo.EndingByte.QuadPart;
  2579. }
  2580. }
  2581. //
  2582. // Lock was not found, return to our caller
  2583. //
  2584. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  2585. return STATUS_RANGE_NOT_LOCKED;
  2586. }
  2587. NTSTATUS
  2588. FsRtlFastUnlockSingleExclusive (
  2589. IN PLOCK_INFO LockInfo,
  2590. IN PFILE_OBJECT FileObject,
  2591. IN LARGE_INTEGER UNALIGNED *FileOffset,
  2592. IN PLARGE_INTEGER Length,
  2593. IN PEPROCESS ProcessId,
  2594. IN ULONG Key,
  2595. IN PVOID Context OPTIONAL,
  2596. IN BOOLEAN IgnoreUnlockRoutine,
  2597. IN BOOLEAN CheckForWaiters
  2598. )
  2599. /*++
  2600. Routine Description:
  2601. This routine performs an Unlock Single operation on the exclusive locks
  2602. associated with the specified lock data. Only the lock with a matching
  2603. file object, process id, key, and range is freed.
  2604. Arguments:
  2605. LockInfo - Supplies the lock data being operated on
  2606. FileObject - Supplies the file object holding the locks
  2607. FileOffset - Supplies the offset to be unlocked
  2608. Length - Supplies the length in bytes to be unlocked
  2609. ProcessId - Supplies the process Id to use in this operation
  2610. Key - Supplies the key to use in this operation
  2611. Context - Optionally supplies context to use when completing Irps
  2612. IgnoreUnlockRoutine - inidicates that the filelock's unlock routine
  2613. should not be called on lock removal (for removal of aborted
  2614. locks)
  2615. CheckForWaiters - If true check for possible waiting locks, caused
  2616. by freeing the locked range
  2617. Return Value:
  2618. NTSTATUS - The completion status for this operation
  2619. --*/
  2620. {
  2621. KIRQL OldIrql;
  2622. PLOCK_QUEUE LockQueue;
  2623. PRTL_SPLAY_LINKS SplayLinks;
  2624. LARGE_INTEGER EndingOffset;
  2625. PEX_LOCK Lock;
  2626. LARGE_INTEGER AlignedFileOffset;
  2627. //
  2628. // General case - search the outstanding lock queue for this lock
  2629. //
  2630. AlignedFileOffset = *FileOffset;
  2631. LockQueue = &LockInfo->LockQueue;
  2632. FsRtlAcquireLockQueue(LockQueue, &OldIrql);
  2633. //
  2634. // Check for the no locks currently held
  2635. //
  2636. if (LockQueue->ExclusiveLockTree == NULL) {
  2637. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  2638. return STATUS_RANGE_NOT_LOCKED;
  2639. }
  2640. //
  2641. // Find the overlapping lock, if it exists. Note that this is usually
  2642. // the only lock we need to check since we are assuming this is an
  2643. // existing lock. However, if the lock is a zero length lock we will
  2644. // have a run of locks to check.
  2645. //
  2646. EndingOffset.QuadPart = (ULONGLONG)AlignedFileOffset.QuadPart + (ULONGLONG)Length->QuadPart - 1;
  2647. for (SplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockQueue->ExclusiveLockTree,
  2648. &AlignedFileOffset,
  2649. &EndingOffset,
  2650. NULL,
  2651. NULL );
  2652. SplayLinks;
  2653. SplayLinks = RtlRealSuccessor(SplayLinks)) {
  2654. Lock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  2655. if ((Lock->LockInfo.FileObject == FileObject) &&
  2656. (Lock->LockInfo.ProcessId == ProcessId) &&
  2657. (Lock->LockInfo.Key == Key) &&
  2658. ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart == (ULONGLONG)AlignedFileOffset.QuadPart) &&
  2659. ((ULONGLONG)Lock->LockInfo.Length.QuadPart == (ULONGLONG)Length->QuadPart)) {
  2660. DebugTrace(0, Dbg, "Ex Found one to unlock\n", 0);
  2661. //
  2662. // We have an exact match so now is the time to delete this
  2663. // lock. Remove the lock from the list, then call the
  2664. // optional unlock routine, then delete the lock.
  2665. //
  2666. if (FileObject->LastLock == &Lock->LockInfo) {
  2667. FileObject->LastLock = NULL;
  2668. }
  2669. //
  2670. // Snip the deleted lock
  2671. //
  2672. LockQueue->ExclusiveLockTree = RtlDelete(&Lock->Links);
  2673. if (LockInfo->LowestLockOffset != 0xffffffff &&
  2674. LockInfo->LowestLockOffset == Lock->LockInfo.StartingByte.LowPart) {
  2675. //
  2676. // This was the lowest lock in the tree, so reset the lowest lock
  2677. // offset
  2678. //
  2679. FsRtlPrivateResetLowestLockOffset(LockInfo);
  2680. }
  2681. if (!IgnoreUnlockRoutine && LockInfo->UnlockRoutine != NULL) {
  2682. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  2683. LockInfo->UnlockRoutine( Context, &Lock->LockInfo );
  2684. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  2685. }
  2686. FsRtlFreeExclusiveLock( Lock );
  2687. //
  2688. // See if there are additional waiting locks that we can
  2689. // now release.
  2690. //
  2691. if (CheckForWaiters && LockQueue->WaitingLocks.Next) {
  2692. FsRtlPrivateCheckWaitingLocks( LockInfo, LockQueue, OldIrql );
  2693. }
  2694. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  2695. return STATUS_SUCCESS;
  2696. }
  2697. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)AlignedFileOffset.QuadPart) {
  2698. //
  2699. // The current lock begins at a byte offset greater than the range we are seeking
  2700. // to unlock. This range must therefore not be locked.
  2701. //
  2702. break;
  2703. }
  2704. }
  2705. //
  2706. // Lock was not found, return to our caller
  2707. //
  2708. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  2709. return STATUS_RANGE_NOT_LOCKED;
  2710. }
  2711. NTSTATUS
  2712. FsRtlFastUnlockAll (
  2713. IN PFILE_LOCK FileLock,
  2714. IN PFILE_OBJECT FileObject,
  2715. IN PEPROCESS ProcessId,
  2716. IN PVOID Context OPTIONAL
  2717. )
  2718. /*++
  2719. Routine Description:
  2720. This routine performs an Unlock all operation on the current locks
  2721. associated with the specified file lock. Only those locks with
  2722. a matching file object and process id are freed.
  2723. Arguments:
  2724. FileLock - Supplies the file lock being freed.
  2725. FileObject - Supplies the file object associated with the file lock
  2726. ProcessId - Supplies the Process Id assoicated with the locks to be
  2727. freed
  2728. Context - Supplies an optional context to use when completing waiting
  2729. lock irps.
  2730. Return Value:
  2731. None
  2732. --*/
  2733. {
  2734. return FsRtlPrivateFastUnlockAll(
  2735. FileLock,
  2736. FileObject,
  2737. ProcessId,
  2738. 0, FALSE, // No Key
  2739. Context );
  2740. }
  2741. NTSTATUS
  2742. FsRtlFastUnlockAllByKey (
  2743. IN PFILE_LOCK FileLock,
  2744. IN PFILE_OBJECT FileObject,
  2745. IN PEPROCESS ProcessId,
  2746. IN ULONG Key,
  2747. IN PVOID Context OPTIONAL
  2748. )
  2749. /*++
  2750. Routine Description:
  2751. This routine performs an Unlock All by Key operation on the current locks
  2752. associated with the specified file lock. Only those locks with
  2753. a matching file object, process id, and key are freed. The input Irp
  2754. is completed by this procedure
  2755. Arguments:
  2756. FileLock - Supplies the file lock being freed.
  2757. FileObject - Supplies the file object associated with the file lock
  2758. ProcessId - Supplies the Process Id assoicated with the locks to be
  2759. freed
  2760. Key - Supplies the Key to use in this operation
  2761. Context - Supplies an optional context to use when completing waiting
  2762. lock irps.
  2763. Return Value:
  2764. NTSTATUS - The return status for the operation.
  2765. --*/
  2766. {
  2767. return FsRtlPrivateFastUnlockAll(
  2768. FileLock,
  2769. FileObject,
  2770. ProcessId,
  2771. Key, TRUE,
  2772. Context );
  2773. }
  2774. //
  2775. // Local Support Routine
  2776. //
  2777. BOOLEAN
  2778. FsRtlPrivateLock (
  2779. IN PFILE_LOCK FileLock,
  2780. IN PFILE_OBJECT FileObject,
  2781. IN PLARGE_INTEGER FileOffset,
  2782. IN PLARGE_INTEGER Length,
  2783. IN PEPROCESS ProcessId,
  2784. IN ULONG Key,
  2785. IN BOOLEAN FailImmediately,
  2786. IN BOOLEAN ExclusiveLock,
  2787. OUT PIO_STATUS_BLOCK Iosb,
  2788. IN PIRP Irp OPTIONAL,
  2789. IN PVOID Context,
  2790. IN BOOLEAN AlreadySynchronized
  2791. )
  2792. /*++
  2793. Routine Description:
  2794. This routine preforms a lock operation request. This handles both the fast
  2795. get lock and the Irp based get lock. If the Irp is supplied then
  2796. this routine will either complete the Irp or enqueue it as a waiting
  2797. lock request.
  2798. Arguments:
  2799. FileLock - Supplies the File Lock to work against
  2800. FileObject - Supplies the file object used in this operation
  2801. FileOffset - Supplies the file offset used in this operation
  2802. Length - Supplies the length used in this operation
  2803. ProcessId - Supplies the process ID used in this operation
  2804. Key - Supplies the key used in this operation
  2805. FailImmediately - Indicates if the request should fail immediately
  2806. if the lock cannot be granted.
  2807. ExclusiveLock - Indicates if this is a request for an exclusive or
  2808. shared lock
  2809. Iosb - Receives the Status if this operation is successful
  2810. Context - Supplies the context with which to complete Irp with
  2811. AlreadySynchronized - Indicates that the caller has already synchronized
  2812. access to the file lock so the fields in the file lock and
  2813. be updated without further locking, but not the queues.
  2814. Return Value:
  2815. BOOLEAN - TRUE if this operation completed and FALSE otherwise.
  2816. --*/
  2817. {
  2818. BOOLEAN Results;
  2819. BOOLEAN AccessGranted;
  2820. BOOLEAN ViaFastCall;
  2821. BOOLEAN ReleaseQueue = FALSE;
  2822. PLOCK_INFO LockInfo;
  2823. PLOCK_QUEUE LockQueue;
  2824. KIRQL OldIrql;
  2825. FILE_LOCK_INFO FileLockInfo;
  2826. DebugTrace(+1, Dbg, "FsRtlPrivateLock, FileLock = %08lx\n", FileLock);
  2827. //
  2828. // If the irp is null then this is being called via the fast call method.
  2829. //
  2830. ViaFastCall = (BOOLEAN) !ARGUMENT_PRESENT( Irp );
  2831. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  2832. DebugTrace(+2, Dbg, "FsRtlPrivateLock, New LockInfo required\n", 0);
  2833. //
  2834. // No lock information on this FileLock, create the structure.
  2835. //
  2836. //
  2837. if (!FsRtlPrivateInitializeFileLock (FileLock, ViaFastCall)) {
  2838. return FALSE;
  2839. }
  2840. //
  2841. // Set flag so file locks will be checked on the fast io
  2842. // code paths
  2843. //
  2844. FileLock->FastIoIsQuestionable = TRUE;
  2845. //
  2846. // Pickup allocated lockinfo structure
  2847. //
  2848. LockInfo = (PLOCK_INFO) FileLock->LockInformation;
  2849. }
  2850. //
  2851. // Assume success and build LockData structure prior to acquiring
  2852. // the lock queue spinlock. (mp perf enhancement)
  2853. //
  2854. FileLockInfo.StartingByte = *FileOffset;
  2855. FileLockInfo.Length = *Length;
  2856. (ULONGLONG)FileLockInfo.EndingByte.QuadPart =
  2857. (ULONGLONG)FileLockInfo.StartingByte.QuadPart + (ULONGLONG)FileLockInfo.Length.QuadPart - 1;
  2858. FileLockInfo.Key = Key;
  2859. FileLockInfo.FileObject = FileObject;
  2860. FileLockInfo.ProcessId = ProcessId;
  2861. FileLockInfo.ExclusiveLock = ExclusiveLock;
  2862. LockQueue = &LockInfo->LockQueue;
  2863. //
  2864. // Now we need to actually run through our current lock queue.
  2865. //
  2866. FsRtlAcquireLockQueue(LockQueue, &OldIrql);
  2867. ReleaseQueue = TRUE;
  2868. try {
  2869. //
  2870. // Case on whether we're trying to take out an exclusive lock or
  2871. // a shared lock. And in both cases try to get appropriate access.
  2872. //
  2873. if (ExclusiveLock) {
  2874. DebugTrace(0, Dbg, "Check for write access\n", 0);
  2875. AccessGranted = FsRtlPrivateCheckForExclusiveLockAccess(
  2876. LockQueue,
  2877. &FileLockInfo );
  2878. } else {
  2879. DebugTrace(0, Dbg, "Check for read access\n", 0);
  2880. AccessGranted = FsRtlPrivateCheckForSharedLockAccess(
  2881. LockQueue,
  2882. &FileLockInfo );
  2883. }
  2884. //
  2885. // Now AccessGranted tells us whether we can really get the access
  2886. // for the range we want
  2887. //
  2888. if (!AccessGranted) {
  2889. DebugTrace(0, Dbg, "We do not have access\n", 0);
  2890. //
  2891. // We cannot read/write to the range, so we cannot take out
  2892. // the lock. Now if the user wanted to fail immediately then
  2893. // we'll complete the Irp, otherwise we'll enqueue this Irp
  2894. // to the waiting lock queue
  2895. //
  2896. if (FailImmediately) {
  2897. //
  2898. // Set our status and return, the finally clause will
  2899. // complete the request
  2900. //
  2901. DebugTrace(0, Dbg, "And we fail immediately\n", 0);
  2902. Iosb->Status = STATUS_LOCK_NOT_GRANTED;
  2903. try_return( Results = TRUE );
  2904. } else if (ARGUMENT_PRESENT(Irp)) {
  2905. PWAITING_LOCK WaitingLock;
  2906. DebugTrace(0, Dbg, "And we enqueue the Irp for later\n", 0);
  2907. //
  2908. // Allocate a new waiting record, set it to point to the
  2909. // waiting Irp, and insert it in the tail of the waiting
  2910. // locks queue
  2911. //
  2912. WaitingLock = FsRtlAllocateWaitingLock();
  2913. //
  2914. // Simply raise out if we can't allocate.
  2915. //
  2916. if (WaitingLock == NULL) {
  2917. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2918. }
  2919. WaitingLock->Irp = Irp;
  2920. WaitingLock->Context = Context;
  2921. WaitingLock->CompleteLockIrpRoutine = LockInfo->CompleteLockIrpRoutine;
  2922. IoMarkIrpPending( Irp );
  2923. //
  2924. // Add WaitingLock WaitingLockQueue
  2925. //
  2926. WaitingLock->Link.Next = NULL;
  2927. if (LockQueue->WaitingLocks.Next == NULL) {
  2928. //
  2929. // Create new list
  2930. //
  2931. LockQueue->WaitingLocks.Next = &WaitingLock->Link;
  2932. LockQueue->WaitingLocksTail.Next = &WaitingLock->Link;
  2933. } else {
  2934. //
  2935. // Add waiter to tail of list
  2936. //
  2937. LockQueue->WaitingLocksTail.Next->Next = &WaitingLock->Link;
  2938. LockQueue->WaitingLocksTail.Next = &WaitingLock->Link;
  2939. }
  2940. //
  2941. // Setup IRP in case it's canceled - then set the
  2942. // IRP's cancel routine
  2943. //
  2944. Irp->IoStatus.Information = (ULONG_PTR)LockInfo;
  2945. IoSetCancelRoutine( Irp, FsRtlPrivateCancelFileLockIrp );
  2946. if (Irp->Cancel) {
  2947. //
  2948. // Pull the cancel routine off of the IRP - if it is not
  2949. // NULL, this means we won the race with IoCancelIrp and
  2950. // will be responsible for cancelling the IRP synchronously.
  2951. // If NULL, we lost and our cancel routine is already being
  2952. // called for us.
  2953. //
  2954. // This must be done while holding the lock queue down since
  2955. // this is how we synchronize with the cancel.
  2956. //
  2957. if (IoSetCancelRoutine( Irp, NULL )) {
  2958. //
  2959. // Irp's cancel routine was not called, do it ourselves.
  2960. // Indicate to the cancel routine that he does not need
  2961. // to release the cancel spinlock by passing a NULL DO.
  2962. //
  2963. // The queue will be dropped in order to complete the Irp.
  2964. // We communicate the previous IRQL through the Irp itself.
  2965. //
  2966. Irp->CancelIrql = OldIrql;
  2967. FsRtlPrivateCancelFileLockIrp( NULL, Irp );
  2968. ReleaseQueue = FALSE;
  2969. }
  2970. }
  2971. Iosb->Status = STATUS_PENDING;
  2972. try_return( Results = TRUE );
  2973. } else {
  2974. try_return( Results = FALSE );
  2975. }
  2976. }
  2977. DebugTrace(0, Dbg, "We have access\n", 0);
  2978. if (!FsRtlPrivateInsertLock( LockInfo, FileObject, &FileLockInfo )) {
  2979. //
  2980. // Resource exhaustion will cause us to fail here. Via the fast call, indicate
  2981. // that it may be worthwhile to go around again via the Irp based path. If we
  2982. // are already there, simply raise out.
  2983. //
  2984. if (ViaFastCall) {
  2985. try_return( Results = FALSE );
  2986. } else {
  2987. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2988. }
  2989. } else {
  2990. Iosb->Status = STATUS_SUCCESS;
  2991. }
  2992. //
  2993. // At long last, we're done.
  2994. //
  2995. Results = TRUE;
  2996. try_exit: NOTHING;
  2997. } finally {
  2998. if (ReleaseQueue) {
  2999. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  3000. }
  3001. //
  3002. // Complete the request provided we were given one and it is not a pending status
  3003. //
  3004. if (!AbnormalTermination() && ARGUMENT_PRESENT(Irp) && (Iosb->Status != STATUS_PENDING)) {
  3005. NTSTATUS NewStatus;
  3006. //
  3007. // We must reference the fileobject for the case that the IRP completion
  3008. // fails and we need to lift the lock. Although the only reason we have
  3009. // to touch the fileobject in the remove case is to unset the LastLock field,
  3010. // we have no way of knowing if we will race with a reference count drop
  3011. // and lose.
  3012. //
  3013. ObReferenceObject( FileObject );
  3014. //
  3015. // Complete the request, if the don't get back success then
  3016. // we need to possibly remove the lock that we just
  3017. // inserted.
  3018. //
  3019. FsRtlCompleteLockIrp(
  3020. LockInfo,
  3021. Context,
  3022. Irp,
  3023. Iosb->Status,
  3024. &NewStatus,
  3025. FileObject );
  3026. if (!NT_SUCCESS(NewStatus) && NT_SUCCESS(Iosb->Status) ) {
  3027. //
  3028. // Irp failed, remove the lock which was added
  3029. //
  3030. FsRtlPrivateRemoveLock (
  3031. LockInfo,
  3032. &FileLockInfo,
  3033. TRUE );
  3034. }
  3035. //
  3036. // Lift our private reference to the fileobject. This may induce deletion.
  3037. //
  3038. ObDereferenceObject( FileObject );
  3039. Iosb->Status = NewStatus;
  3040. }
  3041. DebugTrace(-1, Dbg, "FsRtlPrivateLock -> %08lx\n", Results);
  3042. }
  3043. //
  3044. // and return to our caller
  3045. //
  3046. return Results;
  3047. }
  3048. //
  3049. // Internal Support Routine
  3050. //
  3051. BOOLEAN
  3052. FsRtlPrivateInsertLock (
  3053. IN PLOCK_INFO LockInfo,
  3054. IN PFILE_OBJECT FileObject,
  3055. IN PFILE_LOCK_INFO FileLockInfo
  3056. )
  3057. /*++
  3058. Routine Description:
  3059. This routine fills in a new lock record of the appropriate type and inserts
  3060. it into the lock information.
  3061. Arguments:
  3062. LockInfo - Supplies the lock being modified
  3063. FileObject - The associated file object to update hints in
  3064. FileLockInfo - Supplies the new lock data to add to the lock queue
  3065. Return Value:
  3066. BOOLEAN - True if the insert was successful, False if no resources were avaliable
  3067. to complete the operation.
  3068. --*/
  3069. {
  3070. //
  3071. // Now add the lock to the appropriate tree.
  3072. //
  3073. if (FileLockInfo->ExclusiveLock) {
  3074. PEX_LOCK ExLock;
  3075. ExLock = FsRtlAllocateExclusiveLock();
  3076. if (ExLock == NULL) {
  3077. return FALSE;
  3078. }
  3079. ExLock->LockInfo = *FileLockInfo;
  3080. FsRtlPrivateInsertExclusiveLock( &LockInfo->LockQueue, ExLock );
  3081. FileObject->LastLock = &ExLock->LockInfo;
  3082. } else {
  3083. PSH_LOCK ShLock;
  3084. ShLock = FsRtlAllocateSharedLock();
  3085. if (ShLock == NULL) {
  3086. return FALSE;
  3087. }
  3088. ShLock->LockInfo = *FileLockInfo;
  3089. if (!FsRtlPrivateInsertSharedLock( &LockInfo->LockQueue, ShLock )) {
  3090. return FALSE;
  3091. }
  3092. FileObject->LastLock = &ShLock->LockInfo;
  3093. }
  3094. //
  3095. // Fix up the lowest lock offset if need be
  3096. //
  3097. if ((ULONGLONG)FileLockInfo->StartingByte.QuadPart < (ULONGLONG)LockInfo->LowestLockOffset) {
  3098. ASSERT( FileLockInfo->StartingByte.HighPart == 0 );
  3099. LockInfo->LowestLockOffset = FileLockInfo->StartingByte.LowPart;
  3100. }
  3101. return TRUE;
  3102. }
  3103. //
  3104. // Internal Support Routine
  3105. //
  3106. BOOLEAN
  3107. FsRtlPrivateInsertSharedLock (
  3108. IN PLOCK_QUEUE LockQueue,
  3109. IN PSH_LOCK NewLock
  3110. )
  3111. /*++
  3112. Routine Description:
  3113. This routine adds a new shared lock record to the File lock's current
  3114. lock queue. Locks are inserted into nodes ordered by their starting byte.
  3115. Arguments:
  3116. LockQueue - Supplies the lock queue being modified
  3117. NewLock - Supplies the new shared lock to add to the lock queue
  3118. Return Value:
  3119. BOOLEAN - True if the insert was successful, False if no resources were avaliable
  3120. to complete the operation.
  3121. --*/
  3122. {
  3123. PSINGLE_LIST_ENTRY pLink, Link;
  3124. PRTL_SPLAY_LINKS OverlappedSplayLinks, ParentSplayLinks;
  3125. PLOCKTREE_NODE Node, NextNode;
  3126. PSH_LOCK NextLock;
  3127. BOOLEAN GreaterThan;
  3128. OverlappedSplayLinks = FsRtlFindFirstOverlappingSharedNode( LockQueue->SharedLockTree,
  3129. &NewLock->LockInfo.StartingByte,
  3130. &NewLock->LockInfo.EndingByte,
  3131. &ParentSplayLinks,
  3132. &GreaterThan );
  3133. if (OverlappedSplayLinks == NULL) {
  3134. //
  3135. // Simple insert case, build a new node
  3136. //
  3137. NextNode = FsRtlAllocateLockTreeNode();
  3138. //
  3139. // If no resources are avaliable, simply fail now.
  3140. //
  3141. if (NextNode == NULL) {
  3142. return FALSE;
  3143. }
  3144. RtlInitializeSplayLinks(&NextNode->Links);
  3145. NextNode->HoleyNode = FALSE;
  3146. NextNode->Locks.Next = NextNode->Tail.Next = &NewLock->Link;
  3147. NextNode->Extent = (ULONGLONG)NewLock->LockInfo.EndingByte.QuadPart;
  3148. NewLock->Link.Next = NULL;
  3149. if (ParentSplayLinks) {
  3150. //
  3151. // We have a real parent node in the tree
  3152. //
  3153. if (GreaterThan) {
  3154. ASSERT(RtlLeftChild(ParentSplayLinks) == NULL);
  3155. RtlInsertAsLeftChild(ParentSplayLinks, &NextNode->Links);
  3156. } else {
  3157. ASSERT(RtlRightChild(ParentSplayLinks) == NULL);
  3158. RtlInsertAsRightChild(ParentSplayLinks, &NextNode->Links);
  3159. }
  3160. //
  3161. // Splay all new nodes in the tree
  3162. //
  3163. LockQueue->SharedLockTree = RtlSplay(&NextNode->Links);
  3164. } else {
  3165. //
  3166. // First node in the tree
  3167. //
  3168. LockQueue->SharedLockTree = &NextNode->Links;
  3169. }
  3170. return TRUE;
  3171. }
  3172. //
  3173. // Now we examine the node to see if it is holey as a result of a resource-failed split.
  3174. // If it is, we must complete the split before adding the new lock.
  3175. //
  3176. Node = CONTAINING_RECORD( OverlappedSplayLinks, LOCKTREE_NODE, Links );
  3177. //
  3178. // Search down the overlapped node finding the position for the new lock
  3179. //
  3180. for (pLink = &Node->Locks;
  3181. (Link = pLink->Next) != NULL;
  3182. pLink = Link) {
  3183. PSH_LOCK Lock;
  3184. Lock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  3185. //
  3186. // We sort locks on this list first by starting byte, then by whether the length is zero or not.
  3187. // This is important so that zero length locks appear prior to non-zero length locks, so that
  3188. // they are split out of nodes into the tree in the correct order.
  3189. //
  3190. // if (NewLock->StartingByte <= Lock->StartingByte) ...
  3191. //
  3192. if (((ULONGLONG)NewLock->LockInfo.StartingByte.QuadPart < (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart) ||
  3193. ((ULONGLONG)NewLock->LockInfo.StartingByte.QuadPart == (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart &&
  3194. (NewLock->LockInfo.Length.QuadPart == 0 || Lock->LockInfo.Length.QuadPart != 0))) {
  3195. break;
  3196. }
  3197. }
  3198. //
  3199. // At this point pLink points to the record that comes right after
  3200. // the new lock that we're inserting so we can simply push the
  3201. // newlock into the entrylist
  3202. //
  3203. DebugTrace(0, Dbg, "InsertSharedLock, Insert Before = %08lx\n", Link);
  3204. if (pLink->Next == NULL) {
  3205. //
  3206. // Adding onto the tail of the list
  3207. //
  3208. Node->Tail.Next = &NewLock->Link;
  3209. }
  3210. NewLock->Link.Next = pLink->Next;
  3211. pLink->Next = &NewLock->Link;
  3212. //
  3213. // And splay the node we inserted into
  3214. //
  3215. LockQueue->SharedLockTree = RtlSplay(OverlappedSplayLinks);
  3216. if ((ULONGLONG)NewLock->LockInfo.EndingByte.QuadPart > Node->Extent) {
  3217. //
  3218. // The new lock extends the range of this node, so fix up the extent
  3219. //
  3220. Node->Extent = NewLock->LockInfo.EndingByte.QuadPart;
  3221. //
  3222. // Walk across the remainder of the tree integrating newly overlapping
  3223. // nodes into the node we just inserted the new lock into. Note that
  3224. // this isn't so much a walk as a repeated examination of our successor's
  3225. // until one does not overlap (or we hit the end).
  3226. //
  3227. ParentSplayLinks = OverlappedSplayLinks;
  3228. for (OverlappedSplayLinks = RtlRealSuccessor(ParentSplayLinks);
  3229. OverlappedSplayLinks;
  3230. OverlappedSplayLinks = RtlRealSuccessor(ParentSplayLinks)) {
  3231. NextNode = CONTAINING_RECORD( OverlappedSplayLinks, LOCKTREE_NODE, Links );
  3232. NextLock = CONTAINING_RECORD( NextNode->Locks.Next, SH_LOCK, Link );
  3233. if ((ULONGLONG)NextLock->LockInfo.StartingByte.QuadPart > Node->Extent) {
  3234. //
  3235. // This node is not overlapped, so stop
  3236. //
  3237. break;
  3238. }
  3239. //
  3240. // If we are intergrating a holey node into a non-holey node, try to split
  3241. // the node first. It will be better to get this done with a smaller node
  3242. // than a big, fully integrated one. Note that we are guaranteed that the
  3243. // node will remain a candidate for integration since the first lock on the
  3244. // node will still be there, and overlaps.
  3245. //
  3246. if (!Node->HoleyNode && NextNode->HoleyNode) {
  3247. FsRtlSplitLocks( NextNode, NULL, NULL, NULL );
  3248. }
  3249. //
  3250. // Integrate the locks in this node into our list
  3251. //
  3252. Node->Tail.Next->Next = NextNode->Locks.Next;
  3253. Node->Tail.Next = NextNode->Tail.Next;
  3254. if (NextNode->Extent > Node->Extent) {
  3255. //
  3256. // If the node we just swallowed was (still!) holey, we perhaps made this
  3257. // node holey too. The resolution of this is left to the lock split we will
  3258. // perform after integration is complete.
  3259. //
  3260. // Note that if the extent of the node we are swallowing is interior
  3261. // to the current node, we just covered whatever holes it contained.
  3262. //
  3263. if (NextNode->HoleyNode) {
  3264. Node->HoleyNode = TRUE;
  3265. }
  3266. Node->Extent = NextNode->Extent;
  3267. }
  3268. //
  3269. // Free the now empty node.
  3270. //
  3271. RtlDeleteNoSplay( OverlappedSplayLinks, &LockQueue->SharedLockTree );
  3272. FsRtlFreeLockTreeNode( NextNode );
  3273. }
  3274. }
  3275. //
  3276. // Now, perhaps this node is still holey. For grins lets try one more time to split
  3277. // this thing apart.
  3278. //
  3279. if (Node->HoleyNode) {
  3280. FsRtlSplitLocks( Node, NULL, NULL, NULL );
  3281. }
  3282. //
  3283. // And return to our caller
  3284. //
  3285. return TRUE;
  3286. }
  3287. //
  3288. // Internal Support Routine
  3289. //
  3290. VOID
  3291. FsRtlPrivateInsertExclusiveLock (
  3292. IN PLOCK_QUEUE LockQueue,
  3293. IN PEX_LOCK NewLock
  3294. )
  3295. /*++
  3296. Routine Description:
  3297. This routine adds a new exclusive lock record to the File lock's current
  3298. lock queue.
  3299. Arguments:
  3300. LockQueue - Supplies the lock queue being modified
  3301. NewLock - Supplies the new exclusive lock to add to the lock queue
  3302. Return Value:
  3303. None.
  3304. --*/
  3305. {
  3306. PRTL_SPLAY_LINKS OverlappedSplayLinks, ParentSplayLinks;
  3307. BOOLEAN GreaterThan;
  3308. OverlappedSplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockQueue->ExclusiveLockTree,
  3309. &NewLock->LockInfo.StartingByte,
  3310. &NewLock->LockInfo.EndingByte,
  3311. &ParentSplayLinks,
  3312. &GreaterThan );
  3313. //
  3314. // This is the exclusive tree. Nothing can overlap (caller is supposed to insure this) unless
  3315. // the lock is a zero length lock, in which case we just insert it - still.
  3316. //
  3317. ASSERT(!OverlappedSplayLinks || NewLock->LockInfo.Length.QuadPart == 0);
  3318. //
  3319. // Simple insert ...
  3320. //
  3321. RtlInitializeSplayLinks(&NewLock->Links);
  3322. if (OverlappedSplayLinks) {
  3323. //
  3324. // With zero length locks we have OverlappedSplayLinks at the starting point
  3325. // of a run of zero length locks, so we have to e flexible about where the new
  3326. // node is inserted.
  3327. //
  3328. if (RtlRightChild(OverlappedSplayLinks)) {
  3329. //
  3330. // Right slot taken. We can use the left slot or go to the sucessor's left slot
  3331. //
  3332. if (RtlLeftChild(OverlappedSplayLinks)) {
  3333. ASSERT(RtlLeftChild(RtlRealSuccessor(OverlappedSplayLinks)) == NULL);
  3334. RtlInsertAsLeftChild(RtlRealSuccessor(OverlappedSplayLinks), &NewLock->Links);
  3335. } else {
  3336. RtlInsertAsLeftChild(OverlappedSplayLinks, &NewLock->Links);
  3337. }
  3338. } else {
  3339. RtlInsertAsRightChild(OverlappedSplayLinks, &NewLock->Links);
  3340. }
  3341. } else if (ParentSplayLinks) {
  3342. //
  3343. // We have a real parent node in the tree, and must be at a leaf since
  3344. // there was no overlap
  3345. //
  3346. if (GreaterThan) {
  3347. ASSERT(RtlLeftChild(ParentSplayLinks) == NULL);
  3348. RtlInsertAsLeftChild(ParentSplayLinks, &NewLock->Links);
  3349. } else {
  3350. ASSERT(RtlRightChild(ParentSplayLinks) == NULL);
  3351. RtlInsertAsRightChild(ParentSplayLinks, &NewLock->Links);
  3352. }
  3353. } else {
  3354. //
  3355. // First node in the tree
  3356. //
  3357. LockQueue->ExclusiveLockTree = &NewLock->Links;
  3358. }
  3359. //
  3360. // And return to our caller
  3361. //
  3362. return;
  3363. }
  3364. //
  3365. // Internal Support Routine
  3366. //
  3367. VOID
  3368. FsRtlPrivateCheckWaitingLocks (
  3369. IN PLOCK_INFO LockInfo,
  3370. IN PLOCK_QUEUE LockQueue,
  3371. IN KIRQL OldIrql
  3372. )
  3373. /*++
  3374. Routine Description:
  3375. This routine checks to see if any of the current waiting locks are now
  3376. be satisfied, and if so it completes their IRPs.
  3377. Arguments:
  3378. LockInfo - LockInfo which LockQueue is member of
  3379. LockQueue - Supplies queue which needs to be checked
  3380. OldIrql - Irql to restore when LockQueue is released
  3381. Return Value:
  3382. None.
  3383. --*/
  3384. {
  3385. PSINGLE_LIST_ENTRY *pLink, Link;
  3386. NTSTATUS NewStatus;
  3387. BOOLEAN Result;
  3388. pLink = &LockQueue->WaitingLocks.Next;
  3389. while ((Link = *pLink) != NULL) {
  3390. PWAITING_LOCK WaitingLock;
  3391. PIRP Irp;
  3392. PIO_STACK_LOCATION IrpSp;
  3393. BOOLEAN AccessGranted;
  3394. FILE_LOCK_INFO FileLockInfo;
  3395. //
  3396. // Get a pointer to the waiting lock record
  3397. //
  3398. WaitingLock = CONTAINING_RECORD( Link, WAITING_LOCK, Link );
  3399. DebugTrace(0, Dbg, "FsRtlCheckWaitingLocks, Loop top, WaitingLock = %08lx\n", WaitingLock);
  3400. //
  3401. // Get a local copy of the necessary fields we'll need to use
  3402. //
  3403. Irp = WaitingLock->Irp;
  3404. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  3405. FileLockInfo.StartingByte = IrpSp->Parameters.LockControl.ByteOffset;
  3406. FileLockInfo.Length = *IrpSp->Parameters.LockControl.Length;
  3407. (ULONGLONG)FileLockInfo.EndingByte.QuadPart =
  3408. (ULONGLONG)FileLockInfo.StartingByte.QuadPart + (ULONGLONG)FileLockInfo.Length.QuadPart - 1;
  3409. FileLockInfo.FileObject = IrpSp->FileObject;
  3410. FileLockInfo.ProcessId = IoGetRequestorProcess( Irp );
  3411. FileLockInfo.Key = IrpSp->Parameters.LockControl.Key;
  3412. FileLockInfo.ExclusiveLock = BooleanFlagOn(IrpSp->Flags, SL_EXCLUSIVE_LOCK);
  3413. //
  3414. // Now case on whether we're trying to take out an exclusive lock or
  3415. // a shared lock. And in both cases try to get the appropriate access
  3416. // For the exclusive case we send in a NULL file object and process
  3417. // id, this will ensure that the lookup does not give us write
  3418. // access through an exclusive lock.
  3419. //
  3420. if (FileLockInfo.ExclusiveLock) {
  3421. DebugTrace(0, Dbg, "FsRtlCheckWaitingLocks do we have write access?\n", 0);
  3422. AccessGranted = FsRtlPrivateCheckForExclusiveLockAccess(
  3423. LockQueue,
  3424. &FileLockInfo );
  3425. } else {
  3426. DebugTrace(0, Dbg, "FsRtlCheckWaitingLocks do we have read access?\n", 0);
  3427. AccessGranted = FsRtlPrivateCheckForSharedLockAccess(
  3428. LockQueue,
  3429. &FileLockInfo );
  3430. }
  3431. //
  3432. // Now AccessGranted tells us whether we can really get the access for
  3433. // the range we want.
  3434. //
  3435. // No matter what happens, this Irp must be completed now - even if we
  3436. // are resource starved. User mode deadlock could be induced since there
  3437. // may no longer be a pending unlock to cause a rescan of the waiting
  3438. // list.
  3439. //
  3440. if (AccessGranted) {
  3441. DebugTrace(0, Dbg, "FsRtlCheckWaitingLocks now has access\n", 0);
  3442. //
  3443. // Clear the cancel routine
  3444. //
  3445. IoAcquireCancelSpinLock( &Irp->CancelIrql );
  3446. IoSetCancelRoutine( Irp, NULL );
  3447. //
  3448. // If the IRP got itself cancelled, it is cancelled and we won't grant it.
  3449. // The canceller is waiting for the queue spinlock right now.
  3450. //
  3451. if (Irp->Cancel) {
  3452. AccessGranted = FALSE;
  3453. }
  3454. IoReleaseCancelSpinLock( Irp->CancelIrql );
  3455. if (AccessGranted) {
  3456. Result = FsRtlPrivateInsertLock( LockInfo, IrpSp->FileObject, &FileLockInfo );
  3457. //
  3458. // Now we need to remove this granted waiter and complete
  3459. // it's irp.
  3460. //
  3461. *pLink = Link->Next;
  3462. if (Link == LockQueue->WaitingLocksTail.Next) {
  3463. LockQueue->WaitingLocksTail.Next = (PSINGLE_LIST_ENTRY) pLink;
  3464. }
  3465. //
  3466. // Release LockQueue and complete this waiter
  3467. //
  3468. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  3469. //
  3470. // Reference the fileobject over the completion attempt so we can have a
  3471. // chance to cleanup safely if we fail
  3472. //
  3473. ObReferenceObject( FileLockInfo.FileObject );
  3474. //
  3475. // Now we can complete the IRP, if we don't get back success
  3476. // from the completion routine then we remove the lock we just
  3477. // inserted.
  3478. //
  3479. FsRtlCompleteLockIrp( LockInfo,
  3480. WaitingLock->Context,
  3481. Irp,
  3482. (Result? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES),
  3483. &NewStatus,
  3484. FileLockInfo.FileObject );
  3485. if (Result && !NT_SUCCESS(NewStatus)) {
  3486. //
  3487. // Irp was not sucessfull, remove lock if it was added.
  3488. //
  3489. FsRtlPrivateRemoveLock (
  3490. LockInfo,
  3491. &FileLockInfo,
  3492. FALSE );
  3493. }
  3494. //
  3495. // Drop our private reference to the fileobject
  3496. //
  3497. ObDereferenceObject( FileLockInfo.FileObject );
  3498. //
  3499. // Re-acquire queue lock
  3500. //
  3501. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  3502. //
  3503. // Start scan over from begining
  3504. //
  3505. pLink = &LockQueue->WaitingLocks.Next;
  3506. //
  3507. // Free up pool
  3508. //
  3509. FsRtlFreeWaitingLock( WaitingLock );
  3510. continue;
  3511. }
  3512. }
  3513. DebugTrace( 0, Dbg, "FsRtlCheckWaitingLocks still no access\n", 0);
  3514. //
  3515. // Move to next lock
  3516. //
  3517. pLink = &Link->Next;
  3518. }
  3519. //
  3520. // And return to our caller
  3521. //
  3522. return;
  3523. }
  3524. BOOLEAN
  3525. FsRtlPrivateCheckForExclusiveLockAccess (
  3526. IN PLOCK_QUEUE LockQueue,
  3527. IN PFILE_LOCK_INFO FileLockInfo
  3528. )
  3529. /*++
  3530. Routine Description:
  3531. This routine checks to see if the caller can get an exclusive lock on
  3532. the indicated range due to file locks in the passed in lock queue.
  3533. Assumes Lock queue is held by caller
  3534. Arguments:
  3535. LockQueue - Queue which needs to be checked for collision
  3536. FileLockInfo - Lock which is being checked
  3537. Return Value:
  3538. BOOLEAN - TRUE if the indicated user can place the exclusive lock over the
  3539. entire specified byte range, and FALSE otherwise
  3540. --*/
  3541. {
  3542. PRTL_SPLAY_LINKS SplayLinks, LastSplayLinks = NULL;
  3543. PLOCKTREE_NODE Node;
  3544. PSH_LOCK ShLock;
  3545. PEX_LOCK ExLock;
  3546. if (LockQueue->SharedLockTree &&
  3547. (SplayLinks = FsRtlFindFirstOverlappingSharedNode( LockQueue->SharedLockTree,
  3548. &FileLockInfo->StartingByte,
  3549. &FileLockInfo->EndingByte,
  3550. &LastSplayLinks, NULL))) {
  3551. Node = CONTAINING_RECORD(SplayLinks, LOCKTREE_NODE, Links);
  3552. //
  3553. // If this node is holey, we'll have to walk the whole thing.
  3554. //
  3555. if (Node->HoleyNode) {
  3556. ShLock = FsRtlFindFirstOverlapInNode( Node,
  3557. &FileLockInfo->StartingByte,
  3558. &FileLockInfo->EndingByte );
  3559. } else {
  3560. ShLock = CONTAINING_RECORD(Node->Locks.Next, SH_LOCK, Link);
  3561. }
  3562. //
  3563. // Look for overlap that we care about. Perhaps no overlap existed in the holey case.
  3564. //
  3565. if (ShLock &&
  3566. (FileLockInfo->Length.QuadPart || ShLock->LockInfo.Length.QuadPart)) {
  3567. //
  3568. // If we are checking a nonzero extent and overlapped, it is fatal. If we
  3569. // are checking a zero extent and overlapped a nonzero extent, it is fatal.
  3570. //
  3571. return FALSE;
  3572. }
  3573. }
  3574. if (LastSplayLinks) {
  3575. LockQueue->SharedLockTree = RtlSplay(LastSplayLinks);
  3576. LastSplayLinks = NULL;
  3577. }
  3578. if (LockQueue->ExclusiveLockTree &&
  3579. (SplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockQueue->ExclusiveLockTree,
  3580. &FileLockInfo->StartingByte,
  3581. &FileLockInfo->EndingByte,
  3582. &LastSplayLinks, NULL))) {
  3583. ExLock = CONTAINING_RECORD(SplayLinks, EX_LOCK, Links);
  3584. if (FileLockInfo->Length.QuadPart || ExLock->LockInfo.Length.QuadPart) {
  3585. //
  3586. // If we are checking a nonzero extent and overlapped, it is fatal. If we
  3587. // are checking a zero extent and overlapped a nonzero extent, it is fatal.
  3588. //
  3589. return FALSE;
  3590. }
  3591. }
  3592. if (LastSplayLinks) {
  3593. LockQueue->ExclusiveLockTree = RtlSplay(LastSplayLinks);
  3594. }
  3595. //
  3596. // We searched the entire range without a conflict so we can grant
  3597. // the exclusive lock
  3598. //
  3599. return TRUE;
  3600. }
  3601. BOOLEAN
  3602. FsRtlPrivateCheckForSharedLockAccess (
  3603. IN PLOCK_QUEUE LockQueue,
  3604. IN PFILE_LOCK_INFO FileLockInfo
  3605. )
  3606. /*++
  3607. Routine Description:
  3608. This routine checks to see if the caller can get a shared lock on
  3609. the indicated range due to file locks in the passed in lock queue.
  3610. Assumes Lock queue is held by caller
  3611. Arguments:
  3612. LockQueue - Queue which needs to be checked for collision
  3613. FileLockInfo - Lock which is being checked
  3614. Arguments:
  3615. Return Value:
  3616. BOOLEAN - TRUE if the indicated user can place the shared lock over
  3617. entire specified byte range, and FALSE otherwise
  3618. --*/
  3619. {
  3620. PEX_LOCK Lock;
  3621. PRTL_SPLAY_LINKS SplayLinks, LastSplayLinks;
  3622. BOOLEAN Status = TRUE;
  3623. //
  3624. // If there are no exclusive locks, this is quick ...
  3625. //
  3626. if (LockQueue->ExclusiveLockTree == NULL) {
  3627. return TRUE;
  3628. }
  3629. //
  3630. // No lock in the shared lock tree can prevent access, so just search the exclusive
  3631. // tree for conflict.
  3632. //
  3633. for (SplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockQueue->ExclusiveLockTree,
  3634. &FileLockInfo->StartingByte,
  3635. &FileLockInfo->EndingByte,
  3636. &LastSplayLinks, NULL);
  3637. SplayLinks;
  3638. SplayLinks = RtlRealSuccessor(SplayLinks)) {
  3639. Lock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  3640. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)FileLockInfo->EndingByte.QuadPart) {
  3641. //
  3642. // This node is covering a range greater than the range we care about,
  3643. // so we're done
  3644. //
  3645. break;
  3646. }
  3647. //
  3648. // We may not be able to grant the request if the fileobject, processid,
  3649. // and key do not match.
  3650. //
  3651. if ((Lock->LockInfo.FileObject != FileLockInfo->FileObject) ||
  3652. (Lock->LockInfo.ProcessId != FileLockInfo->ProcessId) ||
  3653. (Lock->LockInfo.Key != FileLockInfo->Key)) {
  3654. //
  3655. // We have a mismatch between caller and owner. It is ok not to conflict
  3656. // if the caller and owner will have/have zero length locks (zero length
  3657. // locks cannot conflict).
  3658. //
  3659. if (FileLockInfo->Length.QuadPart || Lock->LockInfo.Length.QuadPart) {
  3660. Status = FALSE;
  3661. break;
  3662. }
  3663. }
  3664. }
  3665. if (LastSplayLinks) {
  3666. LockQueue->ExclusiveLockTree = RtlSplay(LastSplayLinks);
  3667. }
  3668. //
  3669. // We searched the entire range without a conflict so we can grant
  3670. // the shared lock
  3671. //
  3672. return Status;
  3673. }
  3674. VOID
  3675. FsRtlPrivateResetLowestLockOffset (
  3676. PLOCK_INFO LockInfo
  3677. )
  3678. /*++
  3679. Routine Description:
  3680. This routine resets the lowest lock offset hint in a LOCK_INFO to
  3681. the lowest lock offset currently held by a lock inside of the LOCK_INFO.
  3682. Arguments:
  3683. LockInfo - the lock data to operate on
  3684. Return Value:
  3685. None
  3686. --*/
  3687. {
  3688. PEX_LOCK ExLock = NULL;
  3689. PSH_LOCK ShLock = NULL;
  3690. PFILE_LOCK_INFO LowestLockInfo = NULL;
  3691. PRTL_SPLAY_LINKS SplayLinks;
  3692. PLOCKTREE_NODE Node;
  3693. //
  3694. // Fix up the lowest lock offset if we have non-empty trees and there was
  3695. // a lock in the low 32 bit region
  3696. //
  3697. if (LockInfo->LowestLockOffset != 0xffffffff &&
  3698. (LockInfo->LockQueue.SharedLockTree != NULL ||
  3699. LockInfo->LockQueue.ExclusiveLockTree != NULL)) {
  3700. //
  3701. // Grab the lowest nodes in the trees
  3702. //
  3703. if (LockInfo->LockQueue.SharedLockTree) {
  3704. SplayLinks = LockInfo->LockQueue.SharedLockTree;
  3705. while (RtlLeftChild(SplayLinks) != NULL) {
  3706. SplayLinks = RtlLeftChild(SplayLinks);
  3707. }
  3708. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  3709. ShLock = CONTAINING_RECORD( Node->Locks.Next, SH_LOCK, Link );
  3710. }
  3711. if (LockInfo->LockQueue.ExclusiveLockTree) {
  3712. SplayLinks = LockInfo->LockQueue.ExclusiveLockTree;
  3713. while (RtlLeftChild(SplayLinks) != NULL) {
  3714. SplayLinks = RtlLeftChild(SplayLinks);
  3715. }
  3716. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  3717. }
  3718. //
  3719. // Figure out which of the lowest locks is actually lowest. We know that one of the lock
  3720. // trees at least has a lock, so if we have don't have exclusive locks then we do know
  3721. // we have shared locks ...
  3722. //
  3723. if (ExLock &&
  3724. (!ShLock ||
  3725. (ULONGLONG)ExLock->LockInfo.StartingByte.QuadPart < (ULONGLONG)ShLock->LockInfo.StartingByte.QuadPart)) {
  3726. LowestLockInfo = &ExLock->LockInfo;
  3727. } else {
  3728. LowestLockInfo = &ShLock->LockInfo;
  3729. }
  3730. if (LowestLockInfo->StartingByte.HighPart == 0) {
  3731. LockInfo->LowestLockOffset = LowestLockInfo->StartingByte.LowPart;
  3732. } else {
  3733. LockInfo->LowestLockOffset = 0xffffffff;
  3734. }
  3735. } else {
  3736. //
  3737. // If there are no locks, set the lock offset high
  3738. //
  3739. LockInfo->LowestLockOffset = 0xffffffff;
  3740. }
  3741. }
  3742. NTSTATUS
  3743. FsRtlPrivateFastUnlockAll (
  3744. IN PFILE_LOCK FileLock,
  3745. IN PFILE_OBJECT FileObject,
  3746. IN PEPROCESS ProcessId,
  3747. IN ULONG Key,
  3748. IN BOOLEAN MatchKey,
  3749. IN PVOID Context OPTIONAL
  3750. )
  3751. /*++
  3752. Routine Description:
  3753. This routine performs an Unlock all operation on the current locks
  3754. associated with the specified file lock. Only those locks with
  3755. a matching file object and process id are freed. Additionally,
  3756. it is possible to free only those locks which also match a given
  3757. key.
  3758. Arguments:
  3759. FileLock - Supplies the file lock being freed.
  3760. FileObject - Supplies the file object associated with the file lock
  3761. ProcessId - Supplies the Process Id assoicated with the locks to be
  3762. freed
  3763. Key - Supplies the Key to use in this operation
  3764. MatchKey - Whether or not the Key must also match for lock to be freed.
  3765. Context - Supplies an optional context to use when completing waiting
  3766. lock irps.
  3767. Return Value:
  3768. None
  3769. --*/
  3770. {
  3771. PLOCK_INFO LockInfo;
  3772. PLOCK_QUEUE LockQueue;
  3773. PSINGLE_LIST_ENTRY *pLink, *SavepLink, Link;
  3774. NTSTATUS NewStatus;
  3775. KIRQL OldIrql;
  3776. LARGE_INTEGER GlueOffset, EndingDeletedByte;
  3777. BOOLEAN UnlockRoutine;
  3778. PSH_LOCK ShLock;
  3779. PEX_LOCK ExLock;
  3780. PRTL_SPLAY_LINKS SplayLinks, SuccessorLinks;
  3781. PLOCKTREE_NODE Node;
  3782. DebugTrace(+1, Dbg, "FsRtlPrivateFastUnlockAll, FileLock = %08lx\n", FileLock);
  3783. if ((LockInfo = FileLock->LockInformation) == NULL) {
  3784. //
  3785. // No lock information on this FileLock
  3786. //
  3787. DebugTrace(+1, Dbg, "FsRtlPrivateFastUnlockAll, No LockInfo\n", FileLock);
  3788. return STATUS_RANGE_NOT_LOCKED;
  3789. }
  3790. FileObject->LastLock = NULL;
  3791. LockQueue = &LockInfo->LockQueue;
  3792. //
  3793. // Grab the waiting lock queue spinlock to exclude anyone from messing
  3794. // with the queue while we're using it
  3795. //
  3796. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  3797. if (LockQueue->SharedLockTree == NULL && LockQueue->ExclusiveLockTree == NULL) {
  3798. //
  3799. // No locks on this FileLock
  3800. //
  3801. DebugTrace(+1, Dbg, "FsRtlPrivateFastUnlockAll, No LockTrees\n", FileLock);
  3802. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  3803. return STATUS_RANGE_NOT_LOCKED;
  3804. }
  3805. //
  3806. // Remove all matching locks in the shared lock tree
  3807. //
  3808. if (LockQueue->SharedLockTree != NULL) {
  3809. //
  3810. // Grab the lowest node in the tree
  3811. //
  3812. SplayLinks = LockQueue->SharedLockTree;
  3813. while (RtlLeftChild(SplayLinks) != NULL) {
  3814. SplayLinks = RtlLeftChild(SplayLinks);
  3815. }
  3816. //
  3817. // Walk all nodes in the tree
  3818. //
  3819. UnlockRoutine = FALSE;
  3820. for (;
  3821. SplayLinks;
  3822. SplayLinks = SuccessorLinks) {
  3823. Node = CONTAINING_RECORD(SplayLinks, LOCKTREE_NODE, Links );
  3824. //
  3825. // Save the next node because we may split this node apart in the process
  3826. // of deleting locks. It would be a waste of time to traverse those split
  3827. // nodes. The only case in which we will not have traversed the entire list
  3828. // before doing the split will be if there is an unlock routine attached
  3829. // to this FileLock in which case we will be restarting the entire scan
  3830. // anyway.
  3831. //
  3832. SuccessorLinks = RtlRealSuccessor(SplayLinks);
  3833. //
  3834. // Search down the current lock queue looking for a match on
  3835. // the file object and process id
  3836. //
  3837. SavepLink = NULL;
  3838. EndingDeletedByte.QuadPart = 0;
  3839. GlueOffset.QuadPart = 0;
  3840. pLink = &Node->Locks.Next;
  3841. while ((Link = *pLink) != NULL) {
  3842. ShLock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  3843. DebugTrace(0, Dbg, "Top of ShLock Loop, Lock = %08lx\n", ShLock );
  3844. if ((ShLock->LockInfo.FileObject == FileObject) &&
  3845. (ShLock->LockInfo.ProcessId == ProcessId) &&
  3846. (!MatchKey || ShLock->LockInfo.Key == Key)) {
  3847. DebugTrace(0, Dbg, "Found one to unlock\n", 0);
  3848. //
  3849. // We have a match so now is the time to delete this lock.
  3850. // Save the neccesary information to do the split node check.
  3851. // Remove the lock from the list, then call the
  3852. // optional unlock routine, then delete the lock.
  3853. //
  3854. if (SavepLink == NULL) {
  3855. //
  3856. // Need to remember where the first lock was deleted
  3857. //
  3858. SavepLink = pLink;
  3859. }
  3860. if ((ULONGLONG)ShLock->LockInfo.EndingByte.QuadPart > (ULONGLONG)EndingDeletedByte.QuadPart) {
  3861. //
  3862. // Need to remember where the last offset affected by deleted locks is
  3863. //
  3864. EndingDeletedByte.QuadPart = ShLock->LockInfo.EndingByte.QuadPart;
  3865. }
  3866. if (*pLink == Node->Tail.Next) {
  3867. //
  3868. // Deleting the tail node of the list. Safe even if deleting the
  3869. // first node since this implies we're also deleting the last node
  3870. // in the node which means we'll delete the node ...
  3871. //
  3872. Node->Tail.Next = CONTAINING_RECORD( pLink, SINGLE_LIST_ENTRY, Next );
  3873. }
  3874. *pLink = Link->Next;
  3875. if (LockInfo->UnlockRoutine != NULL) {
  3876. //
  3877. // Signal a lock that needs to have a special unlock routine
  3878. // called on it. This is complex to deal with since we'll have
  3879. // to release the queue, call it, and reacquire - meaning we
  3880. // also have to restart. But we still need to reorder the node
  3881. // first ...
  3882. //
  3883. UnlockRoutine = TRUE;
  3884. break;
  3885. }
  3886. FsRtlFreeSharedLock( ShLock );
  3887. } else {
  3888. //
  3889. // Move to next lock
  3890. //
  3891. pLink = &Link->Next;
  3892. }
  3893. if (SavepLink == NULL && (ULONGLONG)ShLock->LockInfo.EndingByte.QuadPart > (ULONGLONG)GlueOffset.QuadPart) {
  3894. //
  3895. // Save the max offset until we have deleted our first node
  3896. //
  3897. GlueOffset.QuadPart = ShLock->LockInfo.EndingByte.QuadPart;
  3898. }
  3899. }
  3900. if (SavepLink) {
  3901. //
  3902. // Locks were actually deleted here, so we have to check the state of the node
  3903. //
  3904. if (Node->Locks.Next == NULL) {
  3905. //
  3906. // We have just deleted everything at this node
  3907. //
  3908. LockQueue->SharedLockTree = RtlDelete( SplayLinks );
  3909. FsRtlFreeLockTreeNode( Node );
  3910. } else {
  3911. //
  3912. // Now that we have deleted all matching locks in this node, we do the
  3913. // check on the node to split out any now non-overlapping locks. Conceptually,
  3914. // we have deleted just one big lock that starts at the starting byte of the
  3915. // first deleted lock and extends to the last byte of the last deleted lock.
  3916. //
  3917. FsRtlSplitLocks(Node, SavepLink, &EndingDeletedByte, &GlueOffset);
  3918. }
  3919. }
  3920. if (UnlockRoutine) {
  3921. //
  3922. // We dropped out of the node scan because we had a lock that needs extra
  3923. // processing during unlock. Do it.
  3924. //
  3925. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  3926. LockInfo->UnlockRoutine( Context, &ShLock->LockInfo );
  3927. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  3928. FsRtlFreeSharedLock( ShLock );
  3929. UnlockRoutine = FALSE;
  3930. //
  3931. // We have to restart the scan, because the list may have changed while
  3932. // we were in the unlock routine. Careful, because the tree may be empty.
  3933. //
  3934. if (SuccessorLinks = LockQueue->SharedLockTree) {
  3935. while (RtlLeftChild(SuccessorLinks) != NULL) {
  3936. SuccessorLinks = RtlLeftChild(SuccessorLinks);
  3937. }
  3938. }
  3939. }
  3940. }
  3941. }
  3942. //
  3943. // Remove all matching locks in the exclusive lock tree
  3944. //
  3945. if (LockQueue->ExclusiveLockTree != NULL) {
  3946. SplayLinks = LockQueue->ExclusiveLockTree;
  3947. while (RtlLeftChild(SplayLinks) != NULL) {
  3948. SplayLinks = RtlLeftChild(SplayLinks);
  3949. }
  3950. //
  3951. // Walk all nodes in the tree
  3952. //
  3953. UnlockRoutine = FALSE;
  3954. for (; SplayLinks;
  3955. SplayLinks = SuccessorLinks ) {
  3956. SuccessorLinks = RtlRealSuccessor( SplayLinks );
  3957. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  3958. DebugTrace(0, Dbg, "Top of ExLock Loop, Lock = %08lx\n", ExLock );
  3959. if ((ExLock->LockInfo.FileObject == FileObject) &&
  3960. (ExLock->LockInfo.ProcessId == ProcessId) &&
  3961. (!MatchKey || ExLock->LockInfo.Key == Key)) {
  3962. LockQueue->ExclusiveLockTree = RtlDelete( &ExLock->Links );
  3963. if (LockInfo->UnlockRoutine != NULL) {
  3964. //
  3965. // We're dropping out of the node scan because we have a lock
  3966. // that needs extra processing during unlock. Do it.
  3967. //
  3968. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  3969. LockInfo->UnlockRoutine( Context, &ExLock->LockInfo );
  3970. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  3971. //
  3972. // We have to restart the scan, because the list may have changed while
  3973. // we were in the unlock routine. Careful, because the tree may be empty.
  3974. //
  3975. if (SuccessorLinks = LockQueue->ExclusiveLockTree) {
  3976. while (RtlLeftChild( SuccessorLinks ) != NULL) {
  3977. SuccessorLinks = RtlLeftChild( SuccessorLinks );
  3978. }
  3979. }
  3980. }
  3981. FsRtlFreeExclusiveLock( ExLock );
  3982. }
  3983. }
  3984. }
  3985. //
  3986. // Search down the waiting lock queue looking for a match on the
  3987. // file object and process id.
  3988. //
  3989. pLink = &LockQueue->WaitingLocks.Next;
  3990. while ((Link = *pLink) != NULL) {
  3991. PWAITING_LOCK WaitingLock;
  3992. PIRP WaitingIrp;
  3993. PIO_STACK_LOCATION WaitingIrpSp;
  3994. WaitingLock = CONTAINING_RECORD( Link, WAITING_LOCK, Link );
  3995. DebugTrace(0, Dbg, "Top of Waiting Loop, WaitingLock = %08lx\n", WaitingLock);
  3996. //
  3997. // Get a copy of the necessary fields we'll need to use
  3998. //
  3999. WaitingIrp = WaitingLock->Irp;
  4000. WaitingIrpSp = IoGetCurrentIrpStackLocation( WaitingIrp );
  4001. if ((FileObject == WaitingIrpSp->FileObject) &&
  4002. (ProcessId == IoGetRequestorProcess( WaitingIrp )) &&
  4003. (!MatchKey || Key == WaitingIrpSp->Parameters.LockControl.Key)) {
  4004. DebugTrace(0, Dbg, "Found a waiting lock to abort\n", 0);
  4005. //
  4006. // We now void the cancel routine in the irp
  4007. //
  4008. IoAcquireCancelSpinLock( &WaitingIrp->CancelIrql );
  4009. IoSetCancelRoutine( WaitingIrp, NULL );
  4010. //
  4011. // If this IRP got itself cancelled, it is cancelled.
  4012. //
  4013. if (WaitingIrp->Cancel) {
  4014. WaitingIrp = NULL;
  4015. }
  4016. IoReleaseCancelSpinLock( WaitingIrp->CancelIrql );
  4017. if (WaitingIrp) {
  4018. WaitingIrp->IoStatus.Information = 0;
  4019. //
  4020. // We have a match and the IRP, so now is the time to delete
  4021. // this waiter. But we must not mess up our link iteration
  4022. // variable. We do this by simply starting the iteration over
  4023. // again, after we delete ourselves. We also will deallocate
  4024. // the lock after we delete it.
  4025. //
  4026. *pLink = Link->Next;
  4027. if (Link == LockQueue->WaitingLocksTail.Next) {
  4028. LockQueue->WaitingLocksTail.Next = (PSINGLE_LIST_ENTRY) pLink;
  4029. }
  4030. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  4031. //
  4032. // And complete this lock request Irp
  4033. //
  4034. FsRtlCompleteLockIrp( LockInfo,
  4035. WaitingLock->Context,
  4036. WaitingIrp,
  4037. STATUS_RANGE_NOT_LOCKED,
  4038. &NewStatus,
  4039. NULL );
  4040. //
  4041. // Reaqcuire lock queue spinlock and start over
  4042. //
  4043. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  4044. //
  4045. // Start over
  4046. //
  4047. pLink = &LockQueue->WaitingLocks.Next;
  4048. //
  4049. // Put memory onto free list
  4050. //
  4051. FsRtlFreeWaitingLock( WaitingLock );
  4052. continue;
  4053. }
  4054. }
  4055. //
  4056. // Move to next lock
  4057. //
  4058. pLink = &Link->Next;
  4059. }
  4060. //
  4061. // At this point we've gone through unlocking everything. So
  4062. // now try and release any waiting locks.
  4063. //
  4064. FsRtlPrivateCheckWaitingLocks( LockInfo, LockQueue, OldIrql );
  4065. //
  4066. // We deleted a (possible) bunch of locks, go repair the lowest lock offset
  4067. //
  4068. FsRtlPrivateResetLowestLockOffset( LockInfo );
  4069. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  4070. //
  4071. // and return to our caller
  4072. //
  4073. DebugTrace(-1, Dbg, "FsRtlFastUnlockAll -> VOID\n", 0);
  4074. return STATUS_SUCCESS;
  4075. }
  4076. VOID
  4077. FsRtlPrivateCancelFileLockIrp (
  4078. IN PDEVICE_OBJECT DeviceObject,
  4079. IN PIRP Irp
  4080. )
  4081. /*++
  4082. Routine Description:
  4083. This routine implements the cancel function for an irp saved in a
  4084. waiting lock queue
  4085. Arguments:
  4086. DeviceObject - Ignored
  4087. Irp - Supplies the Irp being cancelled. A pointer to the FileLock
  4088. structure for the lock is stored in the information field of the
  4089. irp's iosb.
  4090. Return Value:
  4091. none.
  4092. --*/
  4093. {
  4094. PSINGLE_LIST_ENTRY *pLink, Link;
  4095. PLOCK_INFO LockInfo;
  4096. PLOCK_QUEUE LockQueue;
  4097. KIRQL OldIrql;
  4098. NTSTATUS NewStatus;
  4099. BOOLEAN CollideCheck = FALSE;
  4100. UNREFERENCED_PARAMETER( DeviceObject );
  4101. //
  4102. // The information field is used to store a pointer to the file lock
  4103. // containing the irp
  4104. //
  4105. LockInfo = (PLOCK_INFO) (Irp->IoStatus.Information);
  4106. //
  4107. // Iterate through the lock queue.
  4108. //
  4109. LockQueue = &LockInfo->LockQueue;
  4110. //
  4111. // Release the cancel spinlock and lock cancel collide if this is initiated by Io.
  4112. //
  4113. // We already have the lock queue if this is the race fixup from ourselves,
  4114. // and the cancel Irql is in the Irp.
  4115. //
  4116. if (DeviceObject) {
  4117. IoReleaseCancelSpinLock( Irp->CancelIrql );
  4118. FsRtlAcquireCancelCollide( &OldIrql );
  4119. //
  4120. // Indicate we will check the collide list first, as the lockqueue itself
  4121. // may be deallocated in a race with lock teardown.
  4122. //
  4123. CollideCheck = TRUE;
  4124. pLink = &FsRtlFileLockCancelCollideList.Next;
  4125. } else {
  4126. OldIrql = Irp->CancelIrql;
  4127. //
  4128. // We will iterate only the locks off of this specific queue.
  4129. //
  4130. pLink = &LockQueue->WaitingLocks.Next;
  4131. }
  4132. while (TRUE) {
  4133. //
  4134. // Iterate through the waiting locks looking for the canceled one.
  4135. //
  4136. while ((Link = *pLink) != NULL) {
  4137. PWAITING_LOCK WaitingLock;
  4138. //
  4139. // Get a pointer to the waiting lock record
  4140. //
  4141. WaitingLock = CONTAINING_RECORD( Link, WAITING_LOCK, Link );
  4142. DebugTrace(0, Dbg, "FsRtlPrivateCancelFileLockIrp, Loop top, WaitingLock = %08lx\n", WaitingLock);
  4143. if( WaitingLock->Irp != Irp ) {
  4144. pLink = &Link->Next;
  4145. continue;
  4146. }
  4147. //
  4148. // We've found it -- remove it from the list
  4149. //
  4150. *pLink = Link->Next;
  4151. if (!CollideCheck && Link == LockQueue->WaitingLocksTail.Next) {
  4152. LockQueue->WaitingLocksTail.Next = (PSINGLE_LIST_ENTRY) pLink;
  4153. }
  4154. Irp->IoStatus.Information = 0;
  4155. //
  4156. // Release the right lock and complete this waiter
  4157. //
  4158. if (CollideCheck) {
  4159. FsRtlReleaseCancelCollide( OldIrql );
  4160. } else {
  4161. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  4162. }
  4163. //
  4164. // Complete this waiter. Note we pick the completion routine out
  4165. // of the waiting lock structure so we can surf over the collided
  4166. // cancel case.
  4167. //
  4168. FsRtlCompleteLockIrp( WaitingLock,
  4169. WaitingLock->Context,
  4170. Irp,
  4171. STATUS_CANCELLED,
  4172. &NewStatus,
  4173. NULL );
  4174. //
  4175. // Free up pool
  4176. //
  4177. FsRtlFreeWaitingLock( WaitingLock );
  4178. //
  4179. // Our job is done!
  4180. //
  4181. return;
  4182. }
  4183. //
  4184. // Flip over to the lock queue if we didn't find it on the collided list.
  4185. //
  4186. if (CollideCheck) {
  4187. CollideCheck = FALSE;
  4188. FsRtlAcquireLockQueueAtDpc( LockQueue );
  4189. FsRtlReleaseCancelCollideFromDpc( OldIrql );
  4190. pLink = &LockQueue->WaitingLocks.Next;
  4191. continue;
  4192. }
  4193. break;
  4194. }
  4195. //
  4196. // Release lock queue. This must actually not happen or we will have the
  4197. // potential to have had the IRP we were looking for come back unaware
  4198. // we wanted to cancel it.
  4199. //
  4200. ASSERT( FALSE );
  4201. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  4202. return;
  4203. }