Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5959 lines
171 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. DebugTrace(+1, Dbg, "FsRtlUninitializeFileLock, FileLock = %08lx\n", FileLock);
  763. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  764. return ;
  765. }
  766. //
  767. // Lock vs. cancels and lock the queue.
  768. //
  769. FsRtlAcquireCancelCollide( &OldIrql );
  770. FsRtlAcquireLockQueueAtDpc( &LockInfo->LockQueue );
  771. //
  772. // Free lock trees
  773. //
  774. while (LockInfo->LockQueue.SharedLockTree != NULL) {
  775. LockTreeNode = CONTAINING_RECORD(LockInfo->LockQueue.SharedLockTree, LOCKTREE_NODE, Links);
  776. //
  777. // Remove all locks associated with the root node
  778. //
  779. while (LockTreeNode->Locks.Next != NULL) {
  780. Link = PopEntryList (&LockTreeNode->Locks);
  781. ShLock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  782. FsRtlFreeSharedLock(ShLock);
  783. }
  784. //
  785. // Slice off the root node of the tree
  786. //
  787. RtlDeleteNoSplay(&LockTreeNode->Links, &LockInfo->LockQueue.SharedLockTree);
  788. FsRtlFreeLockTreeNode(LockTreeNode);
  789. }
  790. while (LockInfo->LockQueue.ExclusiveLockTree != NULL) {
  791. ExLock = CONTAINING_RECORD(LockInfo->LockQueue.ExclusiveLockTree, EX_LOCK, Links);
  792. RtlDeleteNoSplay(&ExLock->Links, &LockInfo->LockQueue.ExclusiveLockTree);
  793. FsRtlFreeExclusiveLock(ExLock);
  794. }
  795. //
  796. // Free WaitingLockQueue.
  797. //
  798. // This will be incredibly rare, requiring a cancel to be pending in an async thread
  799. // while cleanup/close occurs in the owning filesystem, triggering teardown.
  800. //
  801. while (LockInfo->LockQueue.WaitingLocks.Next != NULL) {
  802. Link = PopEntryList( &LockInfo->LockQueue.WaitingLocks );
  803. WaitingLock = CONTAINING_RECORD( Link, WAITING_LOCK, Link );
  804. Irp = WaitingLock->Irp;
  805. //
  806. // To complete an irp in the waiting queue we need to
  807. // void the cancel routine (protected by a spinlock so
  808. // we can know that we beat cancellation) before
  809. // we completing the irp.
  810. //
  811. FsRtlReleaseLockQueueFromDpc( &LockInfo->LockQueue );
  812. IoAcquireCancelSpinLock( &Irp->CancelIrql );
  813. IoSetCancelRoutine( Irp, NULL );
  814. //
  815. // If it got cancelled, the cancel routine is now waiting on the other
  816. // side of the cancel collide for us to push it onto the collide list.
  817. // It'll get the IRP there as opposed to the shortly-to-be-axed lock
  818. // structure.
  819. //
  820. if (Irp->Cancel) {
  821. IoReleaseCancelSpinLock( Irp->CancelIrql );
  822. PushEntryList( &FsRtlFileLockCancelCollideList,
  823. &WaitingLock->Link );
  824. Irp = NULL;
  825. } else {
  826. IoReleaseCancelSpinLock( Irp->CancelIrql );
  827. }
  828. //
  829. // If we got the ownership of the IRP, release the collide and complete
  830. // it, otherwise spin back around for more.
  831. //
  832. if (Irp) {
  833. FsRtlReleaseCancelCollide( OldIrql );
  834. Irp->IoStatus.Information = 0;
  835. FsRtlCompleteLockIrp(
  836. LockInfo,
  837. WaitingLock->Context,
  838. Irp,
  839. STATUS_RANGE_NOT_LOCKED,
  840. &NewStatus,
  841. NULL );
  842. FsRtlFreeWaitingLock( WaitingLock );
  843. FsRtlAcquireCancelCollide( &OldIrql );
  844. }
  845. FsRtlAcquireLockQueueAtDpc( &LockInfo->LockQueue );
  846. }
  847. //
  848. // Release locks and free pool used to track the lock info on this file.
  849. //
  850. FsRtlReleaseLockQueueFromDpc( &LockInfo->LockQueue );
  851. FsRtlReleaseCancelCollide( OldIrql );
  852. FsRtlFreeLockInfo( LockInfo );
  853. //
  854. // Unlink LockInfo from FileLock
  855. //
  856. FileLock->LockInformation = NULL;
  857. //
  858. // And return to our caller
  859. //
  860. DebugTrace(-1, Dbg, "FsRtlUninitializeFileLock -> VOID\n", 0 );
  861. return;
  862. }
  863. PFILE_LOCK
  864. FsRtlAllocateFileLock (
  865. IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
  866. IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
  867. )
  868. {
  869. PFILE_LOCK FileLock;
  870. FileLock = ExAllocateFromPagedLookasideList( &FsRtlFileLockLookasideList );
  871. if (FileLock != NULL) {
  872. FsRtlInitializeFileLock( FileLock,
  873. CompleteLockIrpRoutine,
  874. UnlockRoutine );
  875. }
  876. return FileLock;
  877. }
  878. VOID
  879. FsRtlFreeFileLock (
  880. IN PFILE_LOCK FileLock
  881. )
  882. {
  883. FsRtlUninitializeFileLock( FileLock );
  884. ExFreeToPagedLookasideList( &FsRtlFileLockLookasideList, FileLock );
  885. }
  886. NTSTATUS
  887. FsRtlProcessFileLock (
  888. IN PFILE_LOCK FileLock,
  889. IN PIRP Irp,
  890. IN PVOID Context OPTIONAL
  891. )
  892. /*++
  893. Routine Description:
  894. This routine processes a file lock IRP it does either a lock request,
  895. or an unlock request. It also completes the IRP. Once called the user
  896. (i.e., File System) has relinquished control of the input IRP.
  897. If pool is not available to store the information this routine will raise a
  898. status value indicating insufficient resources.
  899. Arguments:
  900. FileLock - Supplies the File lock being modified/queried.
  901. Irp - Supplies the Irp being processed.
  902. Context - Optionally supplies a context to use when calling the user
  903. alternate IRP completion routine.
  904. Return Value:
  905. NTSTATUS - The return status for the operation.
  906. --*/
  907. {
  908. PIO_STACK_LOCATION IrpSp;
  909. IO_STATUS_BLOCK Iosb;
  910. NTSTATUS Status;
  911. LARGE_INTEGER ByteOffset;
  912. DebugTrace(+1, Dbg, "FsRtlProcessFileLock, FileLock = %08lx\n", FileLock);
  913. Iosb.Information = 0;
  914. //
  915. // Get a pointer to the current Irp stack location and assert that
  916. // the major function code is for a lock operation
  917. //
  918. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  919. ASSERT( IrpSp->MajorFunction == IRP_MJ_LOCK_CONTROL );
  920. //
  921. // Now process the different minor lock operations
  922. //
  923. switch (IrpSp->MinorFunction) {
  924. case IRP_MN_LOCK:
  925. ByteOffset = IrpSp->Parameters.LockControl.ByteOffset;
  926. (VOID) FsRtlPrivateLock( FileLock,
  927. IrpSp->FileObject,
  928. &ByteOffset,
  929. IrpSp->Parameters.LockControl.Length,
  930. IoGetRequestorProcess(Irp),
  931. IrpSp->Parameters.LockControl.Key,
  932. BooleanFlagOn(IrpSp->Flags, SL_FAIL_IMMEDIATELY),
  933. BooleanFlagOn(IrpSp->Flags, SL_EXCLUSIVE_LOCK),
  934. &Iosb,
  935. Irp,
  936. Context,
  937. FALSE );
  938. break;
  939. case IRP_MN_UNLOCK_SINGLE:
  940. ByteOffset = IrpSp->Parameters.LockControl.ByteOffset;
  941. Iosb.Status = FsRtlFastUnlockSingle( FileLock,
  942. IrpSp->FileObject,
  943. &ByteOffset,
  944. IrpSp->Parameters.LockControl.Length,
  945. IoGetRequestorProcess(Irp),
  946. IrpSp->Parameters.LockControl.Key,
  947. Context,
  948. FALSE );
  949. FsRtlCompleteLockIrp( FileLock, Context, Irp, Iosb.Status, &Status, NULL );
  950. break;
  951. case IRP_MN_UNLOCK_ALL:
  952. Iosb.Status = FsRtlFastUnlockAll( FileLock,
  953. IrpSp->FileObject,
  954. IoGetRequestorProcess(Irp),
  955. Context );
  956. FsRtlCompleteLockIrp( FileLock, Context, Irp, Iosb.Status, &Status, NULL );
  957. break;
  958. case IRP_MN_UNLOCK_ALL_BY_KEY:
  959. Iosb.Status = FsRtlFastUnlockAllByKey( FileLock,
  960. IrpSp->FileObject,
  961. IoGetRequestorProcess(Irp),
  962. IrpSp->Parameters.LockControl.Key,
  963. Context );
  964. FsRtlCompleteLockIrp( FileLock, Context, Irp, Iosb.Status, &Status, NULL );
  965. break;
  966. default:
  967. //
  968. // For all other minor function codes we say they're invalid and
  969. // complete the request. Note that the IRP has not been marked
  970. // pending so this error will be returned directly to the caller.
  971. //
  972. DebugTrace(0, 1, "Invalid LockFile Minor Function Code %08lx\n", IrpSp->MinorFunction);
  973. FsRtlCompleteRequest( Irp, STATUS_INVALID_DEVICE_REQUEST );
  974. Iosb.Status = STATUS_INVALID_DEVICE_REQUEST;
  975. break;
  976. }
  977. //
  978. // And return to our caller
  979. //
  980. DebugTrace(-1, Dbg, "FsRtlProcessFileLock -> %08lx\n", Iosb.Status);
  981. return Iosb.Status;
  982. }
  983. BOOLEAN
  984. FsRtlCheckLockForReadAccess (
  985. IN PFILE_LOCK FileLock,
  986. IN PIRP Irp
  987. )
  988. /*++
  989. Routine Description:
  990. This routine checks to see if the caller has read access to the
  991. range indicated in the IRP due to file locks. This call does not
  992. complete the Irp it only uses it to get the lock information and read
  993. information. The IRP must be for a read operation.
  994. Arguments:
  995. FileLock - Supplies the File Lock to check.
  996. Irp - Supplies the Irp being processed.
  997. Return Value:
  998. BOOLEAN - TRUE if the indicated user/request has read access to the
  999. entire specified byte range, and FALSE otherwise
  1000. --*/
  1001. {
  1002. BOOLEAN Result;
  1003. PIO_STACK_LOCATION IrpSp;
  1004. PLOCK_INFO LockInfo;
  1005. LARGE_INTEGER StartingByte;
  1006. LARGE_INTEGER Length;
  1007. ULONG Key;
  1008. PFILE_OBJECT FileObject;
  1009. PVOID ProcessId;
  1010. LARGE_INTEGER BeyondLastByte;
  1011. DebugTrace(+1, Dbg, "FsRtlCheckLockForReadAccess, FileLock = %08lx\n", FileLock);
  1012. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  1013. DebugTrace(-1, Dbg, "FsRtlCheckLockForReadAccess (No current lock info) -> TRUE\n", 0);
  1014. return TRUE;
  1015. }
  1016. //
  1017. // Do a really fast test to see if there are any exclusive locks to start with
  1018. //
  1019. if (LockInfo->LockQueue.ExclusiveLockTree == NULL) {
  1020. DebugTrace(-1, Dbg, "FsRtlCheckLockForReadAccess (No current locks) -> TRUE\n", 0);
  1021. return TRUE;
  1022. }
  1023. //
  1024. // Get the read offset and compare it to the lowest existing lock.
  1025. //
  1026. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  1027. StartingByte = IrpSp->Parameters.Read.ByteOffset;
  1028. Length.QuadPart = (ULONGLONG)IrpSp->Parameters.Read.Length;
  1029. BeyondLastByte.QuadPart = (ULONGLONG)StartingByte.QuadPart + Length.LowPart;
  1030. if ( (ULONGLONG)BeyondLastByte.QuadPart <= (ULONGLONG)LockInfo->LowestLockOffset ) {
  1031. DebugTrace(-1, Dbg, "FsRtlCheckLockForReadAccess (Below lowest lock) -> TRUE\n", 0);
  1032. return TRUE;
  1033. }
  1034. //
  1035. // Get remaining parameters.
  1036. //
  1037. Key = IrpSp->Parameters.Read.Key;
  1038. FileObject = IrpSp->FileObject;
  1039. ProcessId = IoGetRequestorProcess( Irp );
  1040. //
  1041. // Call our private work routine to do the real check
  1042. //
  1043. Result = FsRtlFastCheckLockForRead( FileLock,
  1044. &StartingByte,
  1045. &Length,
  1046. Key,
  1047. FileObject,
  1048. ProcessId );
  1049. //
  1050. // And return to our caller
  1051. //
  1052. DebugTrace(-1, Dbg, "FsRtlCheckLockForReadAccess -> %08lx\n", Result);
  1053. return Result;
  1054. }
  1055. BOOLEAN
  1056. FsRtlCheckLockForWriteAccess (
  1057. IN PFILE_LOCK FileLock,
  1058. IN PIRP Irp
  1059. )
  1060. /*++
  1061. Routine Description:
  1062. This routine checks to see if the caller has write access to the
  1063. indicated range due to file locks. This call does not complete the
  1064. Irp it only uses it to get the lock information and write information.
  1065. The IRP must be for a write operation.
  1066. Arguments:
  1067. FileLock - Supplies the File Lock to check.
  1068. Irp - Supplies the Irp being processed.
  1069. Return Value:
  1070. BOOLEAN - TRUE if the indicated user/request has write access to the
  1071. entire specified byte range, and FALSE otherwise
  1072. --*/
  1073. {
  1074. BOOLEAN Result;
  1075. PIO_STACK_LOCATION IrpSp;
  1076. PLOCK_INFO LockInfo;
  1077. LARGE_INTEGER StartingByte;
  1078. LARGE_INTEGER Length;
  1079. ULONG Key;
  1080. PFILE_OBJECT FileObject;
  1081. PVOID ProcessId;
  1082. LARGE_INTEGER BeyondLastByte;
  1083. DebugTrace(+1, Dbg, "FsRtlCheckLockForWriteAccess, FileLock = %08lx\n", FileLock);
  1084. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  1085. DebugTrace(-1, Dbg, "FsRtlCheckLockForWriteAccess (No current lock info) -> TRUE\n", 0);
  1086. return TRUE;
  1087. }
  1088. //
  1089. // Do a really fast test to see if there are any locks to start with
  1090. //
  1091. if (LockInfo->LockQueue.ExclusiveLockTree == NULL && LockInfo->LockQueue.SharedLockTree == NULL) {
  1092. DebugTrace(-1, Dbg, "FsRtlCheckLockForWriteAccess (No current locks) -> TRUE\n", 0);
  1093. return TRUE;
  1094. }
  1095. //
  1096. // Get the write offset and compare it to the lowest existing lock.
  1097. //
  1098. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  1099. StartingByte = IrpSp->Parameters.Write.ByteOffset;
  1100. Length.QuadPart = (ULONGLONG)IrpSp->Parameters.Write.Length;
  1101. BeyondLastByte.QuadPart = (ULONGLONG)StartingByte.QuadPart + Length.LowPart;
  1102. if ( (ULONGLONG)BeyondLastByte.QuadPart <= (ULONGLONG)LockInfo->LowestLockOffset ) {
  1103. DebugTrace(-1, Dbg, "FsRtlCheckLockForWriteAccess (Below lowest lock) -> TRUE\n", 0);
  1104. return TRUE;
  1105. }
  1106. //
  1107. // Get remaining parameters.
  1108. //
  1109. Key = IrpSp->Parameters.Write.Key;
  1110. FileObject = IrpSp->FileObject;
  1111. ProcessId = IoGetRequestorProcess( Irp );
  1112. //
  1113. // Call our private work routine to do the real work
  1114. //
  1115. Result = FsRtlFastCheckLockForWrite( FileLock,
  1116. &StartingByte,
  1117. &Length,
  1118. Key,
  1119. FileObject,
  1120. ProcessId );
  1121. //
  1122. // And return to our caller
  1123. //
  1124. DebugTrace(-1, Dbg, "FsRtlCheckLockForWriteAccess -> %08lx\n", Result);
  1125. return Result;
  1126. }
  1127. PRTL_SPLAY_LINKS
  1128. FsRtlFindFirstOverlappingSharedNode (
  1129. IN PRTL_SPLAY_LINKS Tree,
  1130. IN PLARGE_INTEGER StartingByte,
  1131. IN PLARGE_INTEGER EndingByte,
  1132. IN OUT PRTL_SPLAY_LINKS *LastEdgeNode,
  1133. IN OUT PBOOLEAN GreaterThan
  1134. )
  1135. /*++
  1136. Routine Description:
  1137. This routine returns the first node in the shared lock tree which
  1138. overlaps with the range given. No nodes given by RtlRealPredecessor()
  1139. on the result overlap the range.
  1140. Arguments:
  1141. Tree - supplies the splay links of the root node of the shared tree
  1142. to search
  1143. StartingByte - supplies the first byte offset of the range to check
  1144. EndingByte - supplies the last byte offset of the range to check
  1145. LastEdgeNode - optional, will be set to the last node searched in the
  1146. not including returned node (presumeably where a new node will
  1147. be inserted if return is NULL).
  1148. GreaterThan - optional, set according to whether LastEdgeNode is covering
  1149. a range greater than the queried range. !GreaterThan == LessThan, since
  1150. we would have returned this node in the "Equals" (overlap) case.
  1151. Return Value:
  1152. The splay links of the node, if such a node exists, NULL otherwise
  1153. --*/
  1154. {
  1155. PLOCKTREE_NODE Node = NULL, LastOverlapNode;
  1156. PRTL_SPLAY_LINKS SplayLinks;
  1157. PSH_LOCK Lock;
  1158. if (LastEdgeNode) *LastEdgeNode = NULL;
  1159. if (GreaterThan) *GreaterThan = FALSE;
  1160. LastOverlapNode = NULL;
  1161. SplayLinks = Tree;
  1162. while (SplayLinks) {
  1163. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1164. //
  1165. // Pull up the first lock on the chain at this node to check
  1166. // the starting byte offset of locks at this node
  1167. //
  1168. Lock = CONTAINING_RECORD( Node->Locks.Next, SH_LOCK, Link );
  1169. //
  1170. // We may have to go right in the tree if this lock covers a range before the start of this
  1171. // range we are looking for overlap on or this lock is [0, 0). This is important since a lock
  1172. // on [0, 0) will look like the extent is from [0, ~0], which is the only case where the zero
  1173. // length lock relation of End < Start does not hold.
  1174. //
  1175. if (Node->Extent < (ULONGLONG)StartingByte->QuadPart ||
  1176. (Lock->LockInfo.StartingByte.QuadPart == 0 && Lock->LockInfo.Length.QuadPart == 0)) {
  1177. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart == (ULONGLONG)EndingByte->QuadPart &&
  1178. (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart == (ULONGLONG)StartingByte->QuadPart) {
  1179. //
  1180. // The extent of the node is less than the starting position of the
  1181. // range we are checking and the first lock on this node is equal to
  1182. // the range, which implies that the range and the lock are zero
  1183. // length.
  1184. //
  1185. // This is a zero length lock node and we are searching for zero
  1186. // length overlap. This makes multiple zero length shared locks
  1187. // occupy the same node, which is a win, but makes application of
  1188. // zero length exclusive locks check the length of the overlapping
  1189. // lock to see if they really conflict.
  1190. //
  1191. break;
  1192. }
  1193. //
  1194. // All locks at this node are strictly less than this
  1195. // byterange, so go right in the tree.
  1196. //
  1197. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1198. if (GreaterThan) *GreaterThan = FALSE;
  1199. SplayLinks = RtlRightChild(SplayLinks);
  1200. continue;
  1201. }
  1202. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart <= (ULONGLONG)EndingByte->QuadPart) {
  1203. //
  1204. // We have an overlap, but we need to see if the byterange starts
  1205. // before this node so that there is the guarantee that we start
  1206. // the search at the correct point. There may be still be predecessor
  1207. // nodes covering the byterange.
  1208. //
  1209. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart <= (ULONGLONG)StartingByte->QuadPart) {
  1210. //
  1211. // This node begins at a byte offset prior to the byterange we
  1212. // are checking, so it must be the correct starting position.
  1213. //
  1214. break;
  1215. }
  1216. //
  1217. // Drop a marker at this node so that we can come back if it turns out
  1218. // that the left subtree does not cover the range of bytes before this
  1219. // node in the byterange.
  1220. //
  1221. LastOverlapNode = Node;
  1222. }
  1223. //
  1224. // It must now be the case that all locks at this node are strictly greater
  1225. // than the byterange, or we have the candidate overlap case above,
  1226. // so go left in the tree.
  1227. //
  1228. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1229. if (GreaterThan) *GreaterThan = TRUE;
  1230. SplayLinks = RtlLeftChild(SplayLinks);
  1231. }
  1232. if (SplayLinks == NULL) {
  1233. //
  1234. // We hit the edge of the tree. If the LastOverlapNode is set, it means that
  1235. // we had kept searching left in the tree for a node that covered the starting
  1236. // byte of the byterange, but didn't find it. If it isn't set, we'll do the
  1237. // right thing anyway since Node <- NULL.
  1238. //
  1239. Node = LastOverlapNode;
  1240. }
  1241. if (Node == NULL) {
  1242. //
  1243. // No overlapping node existed
  1244. //
  1245. return NULL;
  1246. }
  1247. //
  1248. // Return the splay links of the first overlapping node
  1249. //
  1250. return &Node->Links;
  1251. }
  1252. PRTL_SPLAY_LINKS
  1253. FsRtlFindFirstOverlappingExclusiveNode (
  1254. IN PRTL_SPLAY_LINKS Tree,
  1255. IN PLARGE_INTEGER StartingByte,
  1256. IN PLARGE_INTEGER EndingByte,
  1257. IN OUT PRTL_SPLAY_LINKS *LastEdgeNode,
  1258. IN OUT PBOOLEAN GreaterThan
  1259. )
  1260. /*++
  1261. Routine Description:
  1262. This routine returns the first node in the exclusive lock tree which
  1263. overlaps with the range given. No nodes given by RtlRealPredecessor()
  1264. on the result overlap the range.
  1265. Arguments:
  1266. Tree - supplies the splay links of the root node of the exclusive tree
  1267. to search
  1268. StartingByte - supplies the first byte offset of the range to check
  1269. EndingByte - supplies the last byte offset of the range to check
  1270. LastEdgeNode - optional, will be set to the last node searched
  1271. not including returned node (presumeably where a new node will
  1272. be inserted if return is NULL).
  1273. GreaterThan - optional, set according to whether LastEdgeNode is covering
  1274. a range greater than the queried range. !GreaterThan == LessThan, since
  1275. we would have returned this node in the "Equals" (overlap) case.
  1276. Return Value:
  1277. The splay links of the node, if such a node exists, NULL otherwise
  1278. --*/
  1279. {
  1280. PRTL_SPLAY_LINKS SplayLinks;
  1281. PEX_LOCK Lock = NULL, LastOverlapNode;
  1282. if (LastEdgeNode) *LastEdgeNode = NULL;
  1283. if (GreaterThan) *GreaterThan = FALSE;
  1284. LastOverlapNode = NULL;
  1285. SplayLinks = Tree;
  1286. while (SplayLinks) {
  1287. Lock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1288. //
  1289. // We may have to go right in the tree if this lock covers a range before the start of this
  1290. // range we are looking for overlap on or this lock is [0, 0). This is important since a lock
  1291. // on [0, 0) will look like the extent is from [0, ~0], which is the only case where the zero
  1292. // length lock relation of End < Start does not hold.
  1293. //
  1294. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart < (ULONGLONG)StartingByte->QuadPart ||
  1295. (Lock->LockInfo.StartingByte.QuadPart == 0 && Lock->LockInfo.Length.QuadPart == 0)) {
  1296. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart == (ULONGLONG)EndingByte->QuadPart &&
  1297. (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart == (ULONGLONG)StartingByte->QuadPart) {
  1298. //
  1299. // The extent of the lock is less than the starting position of the
  1300. // range we are checking and the lock is equal to the range, which
  1301. // implies that the range and the lock are zero length.
  1302. //
  1303. // This is a zero length lock node and we are searching for zero
  1304. // length overlap. Since the exclusive tree is one lock per node,
  1305. // we are in the potential middle of a run of zero length locks in
  1306. // the tree. Go left to find the first zero length lock.
  1307. //
  1308. // This is actually the same logic we'd use for equivalent locks,
  1309. // but the only time that can happen in this tree is for zero length
  1310. // locks.
  1311. //
  1312. LastOverlapNode = Lock;
  1313. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1314. if (GreaterThan) *GreaterThan = FALSE;
  1315. SplayLinks = RtlLeftChild(SplayLinks);
  1316. continue;
  1317. }
  1318. //
  1319. // This lock is strictly less than this byterange, so go
  1320. // right in the tree.
  1321. //
  1322. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1323. if (GreaterThan) *GreaterThan = FALSE;
  1324. SplayLinks = RtlRightChild(SplayLinks);
  1325. continue;
  1326. }
  1327. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart <= (ULONGLONG)EndingByte->QuadPart) {
  1328. //
  1329. // We have an overlap, but we need to see if the byterange starts
  1330. // before this node so that there is the guarantee that we start
  1331. // the search at the correct point. There may be still be predecessor
  1332. // nodes covering the byterange.
  1333. //
  1334. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart <= (ULONGLONG)StartingByte->QuadPart) {
  1335. //
  1336. // This node begins at a byte offset prior to the byterange we
  1337. // are checking, so it must be the correct starting position.
  1338. //
  1339. break;
  1340. }
  1341. //
  1342. // Drop a marker at this node so that we can come back if it turns out
  1343. // that the left subtree does not cover the range of bytes before this
  1344. // node in the byterange.
  1345. //
  1346. LastOverlapNode = Lock;
  1347. }
  1348. //
  1349. // It must now be the case this lock is strictly greater than the byterange,
  1350. // or we have the candidate overlap case above, so go left in the tree.
  1351. //
  1352. if (LastEdgeNode) *LastEdgeNode = SplayLinks;
  1353. if (GreaterThan) *GreaterThan = TRUE;
  1354. SplayLinks = RtlLeftChild(SplayLinks);
  1355. }
  1356. if (SplayLinks == NULL) {
  1357. //
  1358. // We hit the edge of the tree. If the LastOverlapNode is set, it means that
  1359. // we had kept searching left in the tree for a node that covered the starting
  1360. // byte of the byterange, but didn't find it. If it isn't set, we'll do the
  1361. // right thing anyway since Node <- NULL.
  1362. //
  1363. Lock = LastOverlapNode;
  1364. }
  1365. if (Lock == NULL) {
  1366. //
  1367. // No overlapping lock existed
  1368. //
  1369. return NULL;
  1370. }
  1371. //
  1372. // Return the splay links of the first overlapping lock
  1373. //
  1374. return &Lock->Links;
  1375. }
  1376. PSH_LOCK
  1377. FsRtlFindFirstOverlapInNode (
  1378. IN PLOCKTREE_NODE Node,
  1379. IN PLARGE_INTEGER StartingByte,
  1380. IN PLARGE_INTEGER EndingByte
  1381. )
  1382. /*++
  1383. Routine Description:
  1384. This routine examines a shared lock node, usually a node which is known to be composed
  1385. of several non-overlapping lock segments (holey), for true overlap with the indicated
  1386. range. This is not handled in the normal overlap check (..FindFirstOverlappingSharedLock)
  1387. since the needs for holey checks are rather different than the full node check.
  1388. Arguments:
  1389. Node - the lock tree node to be examined for overlap
  1390. StartingByte - supplies the first byte offset of the range to check
  1391. EndingByte - supplies the last byte offset of the range to check
  1392. Return Value:
  1393. PSH_LOCK - the first lock which overlaps with the specified range.
  1394. --*/
  1395. {
  1396. PSH_LOCK Lock;
  1397. PSINGLE_LIST_ENTRY Link;
  1398. for (Link = Node->Locks.Next;
  1399. Link;
  1400. Link = Link->Next) {
  1401. Lock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  1402. //
  1403. // Logic is the same as above checkers. If the ending byte of the lock is less than the
  1404. // starting byte of the range, OR we have the weird [0, 0) case, then the lock is almost
  1405. // certainly less than the range.
  1406. //
  1407. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart < (ULONGLONG)StartingByte->QuadPart ||
  1408. (Lock->LockInfo.StartingByte.QuadPart == 0 && Lock->LockInfo.Length.QuadPart == 0)) {
  1409. //
  1410. // ... except if the lock and range are equivalent, in which case we have discovered
  1411. // zero lock/range overlap.
  1412. //
  1413. if ((ULONGLONG)Lock->LockInfo.EndingByte.QuadPart == (ULONGLONG)EndingByte->QuadPart &&
  1414. (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart == (ULONGLONG)StartingByte->QuadPart) {
  1415. return Lock;
  1416. }
  1417. //
  1418. // Look forward in the node.
  1419. //
  1420. continue;
  1421. }
  1422. //
  1423. // No overlap at all if the lock begins at a higher byte than the last of the range.
  1424. // We already covered zero length locks (where this is true, and overlap could still
  1425. // occur).
  1426. //
  1427. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)EndingByte->QuadPart) {
  1428. return NULL;
  1429. }
  1430. //
  1431. // Regular overlap has occured. Return this lock.
  1432. //
  1433. return Lock;
  1434. }
  1435. //
  1436. // If we invoke this check and wander off the end of the node without determining what is
  1437. // going on, something is terribly wrong.
  1438. //
  1439. ASSERT( FALSE );
  1440. return NULL;
  1441. }
  1442. PFILE_LOCK_INFO
  1443. FsRtlGetNextFileLock (
  1444. IN PFILE_LOCK FileLock,
  1445. IN BOOLEAN Restart
  1446. )
  1447. /*++
  1448. Routine Description:
  1449. This routine enumerates the individual file locks denoted by the input file lock
  1450. variable. It returns a pointer to the file lock information stored for each lock.
  1451. The caller is responsible for synchronizing call to this procedure and for not
  1452. altering any of the data returned by this procedure. If the caller does not
  1453. synchronize the enumeration will not be reliably complete.
  1454. The way a programmer will use this procedure to enumerate all of the locks
  1455. is as follows:
  1456. for (p = FsRtlGetNextFileLock( FileLock, TRUE );
  1457. p != NULL;
  1458. p = FsRtlGetNextFileLock( FileLock, FALSE )) {
  1459. // Process the lock information referenced by p
  1460. }
  1461. Order is *not* guaranteed.
  1462. Arguments:
  1463. FileLock - Supplies the File Lock to enumerate. The current
  1464. enumeration state is stored in the file lock variable so if multiple
  1465. threads are enumerating the lock at the same time the results will
  1466. be unpredictable.
  1467. Restart - Indicates if the enumeration is to start at the beginning of the
  1468. file lock tree or if we are continuing from a previous call.
  1469. Return Value:
  1470. PFILE_LOCK_INFO - Either it returns a pointer to the next file lock
  1471. record for the input file lock or it returns NULL if there
  1472. are not more locks.
  1473. --*/
  1474. {
  1475. FILE_LOCK_INFO FileLockInfo;
  1476. PVOID ContinuationPointer;
  1477. PLOCK_INFO LockInfo;
  1478. PLOCKTREE_NODE Node;
  1479. PSINGLE_LIST_ENTRY Link;
  1480. PRTL_SPLAY_LINKS SplayLinks, LastSplayLinks;
  1481. PSH_LOCK ShLock;
  1482. PEX_LOCK ExLock;
  1483. BOOLEAN FoundReturnable, GreaterThan;
  1484. KIRQL OldIrql;
  1485. DebugTrace(+1, Dbg, "FsRtlGetNextFileLock, FileLock = %08lx\n", FileLock);
  1486. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  1487. //
  1488. // No lock information on this FileLock
  1489. //
  1490. return NULL;
  1491. }
  1492. FoundReturnable = FALSE;
  1493. //
  1494. // Before getting the spinlock, copy pagable info onto stack
  1495. //
  1496. FileLockInfo = FileLock->LastReturnedLockInfo;
  1497. ContinuationPointer = FileLock->LastReturnedLock;
  1498. FsRtlAcquireLockQueue (&LockInfo->LockQueue, &OldIrql);
  1499. if (!Restart) {
  1500. //
  1501. // Given the last returned lock, find its current successor in the tree.
  1502. // Previous implementations would reset the enumeration if the last returned
  1503. // lock had been removed from the tree but I think we can be better in that
  1504. // case since every other structure modifying event (add new locks, delete
  1505. // other locks) would *not* have caused the reset. Possible minor performance
  1506. // enhancement.
  1507. //
  1508. //
  1509. // Find the node which could contain the last returned lock. We enumerate the
  1510. // exclusive lock tree, then the shared lock tree. Find the one we're enumerating.
  1511. //
  1512. if (FileLockInfo.ExclusiveLock) {
  1513. //
  1514. // Continue enumeration in the exclusive lock tree
  1515. //
  1516. ExLock = NULL;
  1517. SplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockInfo->LockQueue.ExclusiveLockTree,
  1518. &FileLockInfo.StartingByte,
  1519. &FileLockInfo.EndingByte,
  1520. &LastSplayLinks,
  1521. &GreaterThan );
  1522. if (SplayLinks == NULL) {
  1523. //
  1524. // No overlapping nodes were found, try to find successor
  1525. //
  1526. if (GreaterThan) {
  1527. //
  1528. // Last node looked at was greater than the lock so it is
  1529. // the place to pick up the enumeration
  1530. //
  1531. SplayLinks = LastSplayLinks;
  1532. } else {
  1533. //
  1534. // Last node looked at was less than the lock so grab its successor
  1535. //
  1536. if (LastSplayLinks) {
  1537. SplayLinks = RtlRealSuccessor(LastSplayLinks);
  1538. }
  1539. }
  1540. } else {
  1541. //
  1542. // Found an overlapping lock, see if it is the last returned
  1543. //
  1544. for (;
  1545. SplayLinks;
  1546. SplayLinks = RtlRealSuccessor(SplayLinks)) {
  1547. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1548. if (ContinuationPointer == ExLock &&
  1549. (ULONGLONG)FileLockInfo.StartingByte.QuadPart == (ULONGLONG)ExLock->LockInfo.StartingByte.QuadPart &&
  1550. (ULONGLONG)FileLockInfo.Length.QuadPart == (ULONGLONG)ExLock->LockInfo.Length.QuadPart &&
  1551. FileLockInfo.Key == ExLock->LockInfo.Key &&
  1552. FileLockInfo.FileObject == ExLock->LockInfo.FileObject &&
  1553. FileLockInfo.ProcessId == ExLock->LockInfo.ProcessId) {
  1554. //
  1555. // Found last returned, dig up its successor
  1556. //
  1557. SplayLinks = RtlRealSuccessor(SplayLinks);
  1558. //
  1559. // Got the node cold, so we're done
  1560. //
  1561. break;
  1562. }
  1563. //
  1564. // This lock overlapped and was not the last returned. In fact, since this lock would
  1565. // have conflicted with the last returned we know it could not have been returned
  1566. // before, so this should be returned to the caller.
  1567. //
  1568. // However, if it is a zero length lock we are looking for and a zero length lock we hit,
  1569. // we are at the beginning of a run we need to inspect. If we cannot find the last lock
  1570. // we returned, resume the enumeration at the beginning of the run.
  1571. //
  1572. if (ExLock->LockInfo.Length.QuadPart != 0 || FileLockInfo.Length.QuadPart != 0) {
  1573. break;
  1574. }
  1575. //
  1576. // Keep wandering down the run
  1577. //
  1578. }
  1579. }
  1580. //
  1581. // Were we able to find a lock to return?
  1582. //
  1583. if (SplayLinks == NULL) {
  1584. //
  1585. // There aren't any more exclusive locks, fall over to the shared tree
  1586. //
  1587. SplayLinks = LockInfo->LockQueue.SharedLockTree;
  1588. if (SplayLinks) {
  1589. while (RtlLeftChild(SplayLinks)) {
  1590. SplayLinks = RtlLeftChild(SplayLinks);
  1591. }
  1592. Node = CONTAINING_RECORD(SplayLinks, LOCKTREE_NODE, Links);
  1593. ShLock = CONTAINING_RECORD(Node->Locks.Next, SH_LOCK, Link);
  1594. FileLockInfo = ShLock->LockInfo;
  1595. ContinuationPointer = ShLock;
  1596. FoundReturnable = TRUE;
  1597. }
  1598. } else {
  1599. //
  1600. // This is the lock to return
  1601. //
  1602. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1603. FileLockInfo = ExLock->LockInfo;
  1604. ContinuationPointer = ExLock;
  1605. FoundReturnable = TRUE;
  1606. }
  1607. } else {
  1608. //
  1609. // Continue enumeration in the shared lock tree
  1610. //
  1611. Node = NULL;
  1612. SplayLinks = FsRtlFindFirstOverlappingSharedNode( LockInfo->LockQueue.SharedLockTree,
  1613. &FileLockInfo.StartingByte,
  1614. &FileLockInfo.EndingByte,
  1615. &LastSplayLinks,
  1616. &GreaterThan );
  1617. if (SplayLinks == NULL) {
  1618. //
  1619. // No overlapping nodes were found
  1620. //
  1621. if (GreaterThan) {
  1622. //
  1623. // Last node looked at was greater than the lock so it is
  1624. // the place to pick up the enumeration
  1625. //
  1626. if (LastSplayLinks) {
  1627. SplayLinks = LastSplayLinks;
  1628. Node = CONTAINING_RECORD( LastSplayLinks, LOCKTREE_NODE, Links );
  1629. }
  1630. } else {
  1631. //
  1632. // Last node looked at was less than the lock so grab its successor
  1633. //
  1634. if (LastSplayLinks) {
  1635. SplayLinks = RtlRealSuccessor(LastSplayLinks);
  1636. if (SplayLinks) {
  1637. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1638. }
  1639. }
  1640. }
  1641. } else {
  1642. //
  1643. // Grab the node we found
  1644. //
  1645. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1646. }
  1647. //
  1648. // If we have a node to look at, it may still not contain the the last returned lock
  1649. // if this isn't synchronized.
  1650. //
  1651. if (Node != NULL) {
  1652. //
  1653. // Walk down the locks at this node looking for the last returned lock
  1654. //
  1655. for (Link = Node->Locks.Next;
  1656. Link;
  1657. Link = Link->Next) {
  1658. //
  1659. // Get a pointer to the current lock record
  1660. //
  1661. ShLock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  1662. //
  1663. // See if it's a match
  1664. //
  1665. if (ContinuationPointer == ShLock &&
  1666. (ULONGLONG)FileLockInfo.StartingByte.QuadPart == (ULONGLONG)ShLock->LockInfo.StartingByte.QuadPart &&
  1667. (ULONGLONG)FileLockInfo.Length.QuadPart == (ULONGLONG)ShLock->LockInfo.Length.QuadPart &&
  1668. FileLockInfo.Key == ShLock->LockInfo.Key &&
  1669. FileLockInfo.FileObject == ShLock->LockInfo.FileObject &&
  1670. FileLockInfo.ProcessId == ShLock->LockInfo.ProcessId) {
  1671. Link = Link->Next;
  1672. break;
  1673. }
  1674. //
  1675. // See if we passed by its slot
  1676. //
  1677. if ((ULONGLONG)FileLockInfo.StartingByte.QuadPart < (ULONGLONG)ShLock->LockInfo.StartingByte.QuadPart) {
  1678. break;
  1679. }
  1680. }
  1681. if (Link == NULL) {
  1682. //
  1683. // This node doesn't contain the successor, so move
  1684. // up to the successor node in the tree and return the
  1685. // first lock. If we're actually at the end of the tree
  1686. // we just fall off the end correctly.
  1687. //
  1688. SplayLinks = RtlRealSuccessor(SplayLinks);
  1689. if (SplayLinks) {
  1690. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1691. Link = Node->Locks.Next;
  1692. }
  1693. }
  1694. if (Link) {
  1695. //
  1696. // Found a Lock to return, copy it to the stack
  1697. //
  1698. ShLock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  1699. FileLockInfo = ShLock->LockInfo;
  1700. ContinuationPointer = ShLock;
  1701. FoundReturnable = TRUE;
  1702. }
  1703. }
  1704. }
  1705. } else {
  1706. //
  1707. // Restarting the enumeration. Find leftmost node in the exclusive tree and hand back
  1708. // the first lock, falling over to the shared if no exlcusive locks are applied
  1709. //
  1710. if (LockInfo->LockQueue.ExclusiveLockTree) {
  1711. SplayLinks = LockInfo->LockQueue.ExclusiveLockTree;
  1712. while (RtlLeftChild(SplayLinks) != NULL) {
  1713. SplayLinks = RtlLeftChild(SplayLinks);
  1714. }
  1715. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1716. FileLockInfo = ExLock->LockInfo;
  1717. ContinuationPointer = ExLock;
  1718. FoundReturnable = TRUE;
  1719. } else {
  1720. if (LockInfo->LockQueue.SharedLockTree) {
  1721. SplayLinks = LockInfo->LockQueue.SharedLockTree;
  1722. while (RtlLeftChild(SplayLinks) != NULL) {
  1723. SplayLinks = RtlLeftChild(SplayLinks);
  1724. }
  1725. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1726. ShLock = CONTAINING_RECORD( Node->Locks.Next, SH_LOCK, Link );
  1727. FileLockInfo = ShLock->LockInfo;
  1728. ContinuationPointer = ShLock;
  1729. FoundReturnable = TRUE;
  1730. }
  1731. }
  1732. }
  1733. //
  1734. // Release all the lock queues
  1735. //
  1736. FsRtlReleaseLockQueue (&LockInfo->LockQueue, OldIrql);
  1737. if (!FoundReturnable) {
  1738. //
  1739. // No returnable lock was found, end of list
  1740. //
  1741. return NULL;
  1742. }
  1743. //
  1744. // Update current enum location information
  1745. //
  1746. FileLock->LastReturnedLockInfo = FileLockInfo;
  1747. FileLock->LastReturnedLock = ContinuationPointer;
  1748. //
  1749. // Return lock record to caller
  1750. //
  1751. return &FileLock->LastReturnedLockInfo;
  1752. }
  1753. BOOLEAN
  1754. FsRtlCheckNoSharedConflict (
  1755. IN PLOCK_QUEUE LockQueue,
  1756. IN PLARGE_INTEGER Starting,
  1757. IN PLARGE_INTEGER Ending
  1758. )
  1759. /*++
  1760. Routine Description:
  1761. This routine checks to see if there is overlap in the shared locks with
  1762. the given range. It is intended for use in the write access check path
  1763. so that a rebalance will occur.
  1764. Arguments:
  1765. FileLock - Supplies the File Lock to check
  1766. StartingByte - Supplies the first byte (zero based) to check
  1767. Length - Supplies the length, in bytes, to check
  1768. Key - Supplies the key to use in the check
  1769. FileObject - Supplies the file object to use in the check
  1770. ProcessId - Supplies the Process Id to use in the check
  1771. Return Value:
  1772. BOOLEAN - TRUE if the indicated user/request doesn't conflict in
  1773. entire specified byte range, and FALSE otherwise
  1774. --*/
  1775. {
  1776. PRTL_SPLAY_LINKS SplayLinks, BeginLinks;
  1777. PLOCKTREE_NODE Node;
  1778. SplayLinks = FsRtlFindFirstOverlappingSharedNode( LockQueue->SharedLockTree,
  1779. Starting,
  1780. Ending,
  1781. &BeginLinks,
  1782. NULL);
  1783. if (BeginLinks) {
  1784. LockQueue->SharedLockTree = RtlSplay(BeginLinks);
  1785. }
  1786. //
  1787. // If this node is holey, we'll have to walk the whole thing.
  1788. //
  1789. if (SplayLinks) {
  1790. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  1791. if (Node->HoleyNode) {
  1792. return (BOOLEAN)(FsRtlFindFirstOverlapInNode( Node, Starting, Ending ) == NULL);
  1793. }
  1794. //
  1795. // Overlapping non-holey node, so we do have shared lock conflict.
  1796. //
  1797. return FALSE;
  1798. }
  1799. //
  1800. // No node overlaps.
  1801. //
  1802. return TRUE;
  1803. }
  1804. BOOLEAN
  1805. FsRtlCheckNoExclusiveConflict (
  1806. IN PLOCK_QUEUE LockQueue,
  1807. IN PLARGE_INTEGER Starting,
  1808. IN PLARGE_INTEGER Ending,
  1809. IN ULONG Key,
  1810. IN PFILE_OBJECT FileObject,
  1811. IN PVOID ProcessId
  1812. )
  1813. /*++
  1814. Routine Description:
  1815. This routine checks to see if there is conflict in the exclusive locks with
  1816. a given range and identifying tuple of key, fileobject and process. This is
  1817. for part of the read access path.
  1818. Arguments:
  1819. FileLock - Supplies the File Lock to check
  1820. StartingByte - Supplies the first byte (zero based) to check
  1821. Length - Supplies the length, in bytes, to check
  1822. Key - Supplies the key to use in the check
  1823. FileObject - Supplies the file object to use in the check
  1824. ProcessId - Supplies the Process Id to use in the check
  1825. Return Value:
  1826. BOOLEAN - TRUE if the indicated user/request doesn't conflict in
  1827. entire specified byte range, and FALSE otherwise
  1828. --*/
  1829. {
  1830. PRTL_SPLAY_LINKS SplayLinks, BeginLinks;
  1831. PEX_LOCK Lock;
  1832. BOOLEAN Status = TRUE;
  1833. //
  1834. // Find the node to begin the search at and go
  1835. //
  1836. for (SplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockQueue->ExclusiveLockTree,
  1837. Starting,
  1838. Ending,
  1839. &BeginLinks,
  1840. NULL);
  1841. SplayLinks;
  1842. SplayLinks = RtlRealSuccessor(SplayLinks)) {
  1843. Lock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  1844. //
  1845. // If the current lock is greater than the end of the range we're
  1846. // looking for then the the user doesn't conflict
  1847. //
  1848. // if (Ending < Lock->StartingByte) ...
  1849. //
  1850. if ((ULONGLONG)Ending->QuadPart < (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart) {
  1851. DebugTrace(0, Dbg, "FsRtlCheckForExclusiveConflict, Ending < Lock->StartingByte\n", 0);
  1852. break;
  1853. }
  1854. //
  1855. // Check for any overlap with the request. The test for
  1856. // overlap is that starting byte is less than or equal to the locks
  1857. // ending byte, and the ending byte is greater than or equal to the
  1858. // locks starting byte. We already tested for this latter case in
  1859. // the preceding statement.
  1860. //
  1861. // if (Starting <= Lock->StartingByte + Lock->Length - 1) ...
  1862. //
  1863. if ((ULONGLONG)Starting->QuadPart <= (ULONGLONG)Lock->LockInfo.EndingByte.QuadPart) {
  1864. //
  1865. // This request overlaps the lock. We cannot grant the request
  1866. // if the file object, process id, and key do not match. Otherwise
  1867. // we'll continue looping looking at locks
  1868. //
  1869. if ((Lock->LockInfo.FileObject != FileObject) ||
  1870. (Lock->LockInfo.ProcessId != ProcessId) ||
  1871. (Lock->LockInfo.Key != Key)) {
  1872. DebugTrace(0, Dbg, "FsRtlCheckForExclusiveConflict, Range locked already\n", 0);
  1873. Status = FALSE;
  1874. break;
  1875. }
  1876. }
  1877. }
  1878. if (BeginLinks) {
  1879. LockQueue->ExclusiveLockTree = RtlSplay(BeginLinks);
  1880. }
  1881. //
  1882. // We searched the entire range without a conflict so we'll note no conflict
  1883. //
  1884. return Status;
  1885. }
  1886. BOOLEAN
  1887. FsRtlFastCheckLockForRead (
  1888. IN PFILE_LOCK FileLock,
  1889. IN PLARGE_INTEGER StartingByte,
  1890. IN PLARGE_INTEGER Length,
  1891. IN ULONG Key,
  1892. IN PFILE_OBJECT FileObject,
  1893. IN PVOID ProcessId
  1894. )
  1895. /*++
  1896. Routine Description:
  1897. This routine checks to see if the caller has read access to the
  1898. indicated range due to file locks.
  1899. Arguments:
  1900. FileLock - Supplies the File Lock to check
  1901. StartingByte - Supplies the first byte (zero based) to check
  1902. Length - Supplies the length, in bytes, to check
  1903. Key - Supplies the to use in the check
  1904. FileObject - Supplies the file object to use in the check
  1905. ProcessId - Supplies the Process Id to use in the check
  1906. Return Value:
  1907. BOOLEAN - TRUE if the indicated user/request has read access to the
  1908. entire specified byte range, and FALSE otherwise
  1909. --*/
  1910. {
  1911. LARGE_INTEGER Starting;
  1912. LARGE_INTEGER Ending;
  1913. PLOCK_INFO LockInfo;
  1914. PLOCK_QUEUE LockQueue;
  1915. KIRQL OldIrql;
  1916. PFILE_LOCK_INFO LastLock;
  1917. BOOLEAN Status;
  1918. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  1919. //
  1920. // No lock information on this FileLock
  1921. //
  1922. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead, No lock info\n", 0);
  1923. return TRUE;
  1924. }
  1925. //
  1926. // If there isn't an exclusive lock then we can immediately grant access
  1927. //
  1928. if (LockInfo->LockQueue.ExclusiveLockTree == NULL) {
  1929. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead, No exlocks present\n", 0);
  1930. return TRUE;
  1931. }
  1932. //
  1933. // If length is zero then automatically give grant access
  1934. //
  1935. if ((ULONGLONG)Length->QuadPart == 0) {
  1936. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead, Length == 0\n", 0);
  1937. return TRUE;
  1938. }
  1939. //
  1940. // Get our starting and ending byte position
  1941. //
  1942. Starting = *StartingByte;
  1943. Ending.QuadPart = (ULONGLONG)Starting.QuadPart + (ULONGLONG)Length->QuadPart - 1;
  1944. //
  1945. // Now check lock queue
  1946. //
  1947. LockQueue = &LockInfo->LockQueue;
  1948. //
  1949. // Grab the waiting lock queue spinlock to exclude anyone from messing
  1950. // with the queue while we're using it
  1951. //
  1952. FsRtlAcquireLockQueue(LockQueue, &OldIrql);
  1953. //
  1954. // If the range ends below the lowest existing lock, this read is OK.
  1955. //
  1956. if ( ((ULONGLONG)Ending.QuadPart < (ULONGLONG)LockInfo->LowestLockOffset) ) {
  1957. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead (below lowest lock)\n", 0);
  1958. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  1959. return TRUE;
  1960. }
  1961. //
  1962. // If the caller just locked this range, he can read it.
  1963. //
  1964. LastLock = (PFILE_LOCK_INFO)FileObject->LastLock;
  1965. if ((LastLock != NULL) &&
  1966. ((ULONGLONG)Starting.QuadPart >= (ULONGLONG)LastLock->StartingByte.QuadPart) &&
  1967. ((ULONGLONG)Ending.QuadPart <= (ULONGLONG)LastLock->EndingByte.QuadPart) &&
  1968. (LastLock->Key == Key) &&
  1969. (LastLock->ProcessId == ProcessId)) {
  1970. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  1971. return TRUE;
  1972. }
  1973. //
  1974. // Check the exclusive locks for a conflict. It is impossible to have
  1975. // a read conflict with any shared lock.
  1976. //
  1977. Status = FsRtlCheckNoExclusiveConflict(LockQueue, &Starting, &Ending, Key, FileObject, ProcessId);
  1978. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  1979. return Status;
  1980. }
  1981. BOOLEAN
  1982. FsRtlFastCheckLockForWrite (
  1983. IN PFILE_LOCK FileLock,
  1984. IN PLARGE_INTEGER StartingByte,
  1985. IN PLARGE_INTEGER Length,
  1986. IN ULONG Key,
  1987. IN PVOID FileObject,
  1988. IN PVOID ProcessId
  1989. )
  1990. /*++
  1991. Routine Description:
  1992. This routine checks to see if the caller has write access to the
  1993. indicated range due to file locks
  1994. Arguments:
  1995. FileLock - Supplies the File Lock to check
  1996. StartingByte - Supplies the first byte (zero based) to check
  1997. Length - Supplies the length, in bytes, to check
  1998. Key - Supplies the to use in the check
  1999. FileObject - Supplies the file object to use in the check
  2000. ProcessId - Supplies the Process Id to use in the check
  2001. Return Value:
  2002. BOOLEAN - TRUE if the indicated user/request has write access to the
  2003. entire specified byte range, and FALSE otherwise
  2004. --*/
  2005. {
  2006. LARGE_INTEGER Starting;
  2007. LARGE_INTEGER Ending;
  2008. PLOCK_INFO LockInfo;
  2009. PLOCK_QUEUE LockQueue;
  2010. KIRQL OldIrql;
  2011. PFILE_LOCK_INFO LastLock;
  2012. BOOLEAN Status;
  2013. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  2014. //
  2015. // No lock information on this FileLock
  2016. //
  2017. DebugTrace(0, Dbg, "FsRtlFastCheckLockForRead, No lock info\n", 0);
  2018. return TRUE;
  2019. }
  2020. //
  2021. // If there isn't a lock then we can immediately grant access
  2022. //
  2023. if (LockInfo->LockQueue.SharedLockTree == NULL && LockInfo->LockQueue.ExclusiveLockTree == NULL) {
  2024. DebugTrace(0, Dbg, "FsRtlFastCheckLockForWrite, No locks present\n", 0);
  2025. return TRUE;
  2026. }
  2027. //
  2028. // If length is zero then automatically grant access
  2029. //
  2030. if ((ULONGLONG)Length->QuadPart == 0) {
  2031. DebugTrace(0, Dbg, "FsRtlFastCheckLockForWrite, Length == 0\n", 0);
  2032. return TRUE;
  2033. }
  2034. //
  2035. // Get our starting and ending byte position
  2036. //
  2037. Starting = *StartingByte;
  2038. Ending.QuadPart = (ULONGLONG)Starting.QuadPart + (ULONGLONG)Length->QuadPart - 1;
  2039. //
  2040. // Now check lock queue
  2041. //
  2042. LockQueue = &LockInfo->LockQueue;
  2043. //
  2044. // Grab the waiting lock queue spinlock to exclude anyone from messing
  2045. // with the queue while we're using it
  2046. //
  2047. FsRtlAcquireLockQueue(LockQueue, &OldIrql);
  2048. //
  2049. // If the range ends below the lowest existing lock, this write is OK.
  2050. //
  2051. if ( ((ULONGLONG)Ending.QuadPart < (ULONGLONG)LockInfo->LowestLockOffset) ) {
  2052. DebugTrace(0, Dbg, "FsRtlFastCheckLockForWrite (below lowest lock)\n", 0);
  2053. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  2054. return TRUE;
  2055. }
  2056. //
  2057. // If the caller just locked this range exclusively, he can write it.
  2058. //
  2059. LastLock = (PFILE_LOCK_INFO)((PFILE_OBJECT)FileObject)->LastLock;
  2060. if ((LastLock != NULL) &&
  2061. ((ULONGLONG)Starting.QuadPart >= (ULONGLONG)LastLock->StartingByte.QuadPart) &&
  2062. ((ULONGLONG)Ending.QuadPart <= (ULONGLONG)LastLock->EndingByte.QuadPart) &&
  2063. (LastLock->Key == Key) &&
  2064. (LastLock->ProcessId == ProcessId) &&
  2065. LastLock->ExclusiveLock) {
  2066. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  2067. return TRUE;
  2068. }
  2069. //
  2070. // Check the shared locks for overlap. Any overlap in the shared locks is fatal.
  2071. //
  2072. Status = FsRtlCheckNoSharedConflict(LockQueue, &Starting, &Ending);
  2073. if (Status == TRUE) {
  2074. //
  2075. // No overlap in the shared locks, so check the exclusive locks for overlap.
  2076. //
  2077. Status = FsRtlCheckNoExclusiveConflict(LockQueue, &Starting, &Ending, Key, FileObject, ProcessId);
  2078. }
  2079. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  2080. return Status;
  2081. }
  2082. VOID
  2083. FsRtlSplitLocks (
  2084. IN PLOCKTREE_NODE ParentNode,
  2085. IN PSINGLE_LIST_ENTRY *pStartLink,
  2086. IN PLARGE_INTEGER LastShadowedByte,
  2087. IN PLARGE_INTEGER GlueOffset
  2088. )
  2089. /*++
  2090. Routine Description:
  2091. This routine examines and possibly splits off shared locks associated
  2092. with a node into new nodes of the lock tree. Called from routines that
  2093. have just deleted locks.
  2094. The arguments that supply the initial conditions for the operation are
  2095. optional if the node is known to be holey.
  2096. Arguments:
  2097. ParentNode- Supplies the node the locks are coming from
  2098. pStartLink - Supplies the pointer to the link address of the start of the
  2099. range of locks in the ParentNode's locklist that need to be checked
  2100. LastShadowedByte - Supplies the last byte offset that needs to be checked
  2101. GlueOffset - Supplies the maximum offset affected by locks prior to this
  2102. point in the list
  2103. Return Value:
  2104. BOOLEAN - True if the split was successful, False otherwise. The node will
  2105. be marked as Holey if the split could not occur.
  2106. --*/
  2107. {
  2108. PSH_LOCK Lock;
  2109. PLOCKTREE_NODE NewNode;
  2110. PSINGLE_LIST_ENTRY Link, *pLink, *NextpLink;
  2111. LARGE_INTEGER MaxOffset = {0}, StartOffset = {0}, HaltOffset;
  2112. LOGICAL ExtentValid;
  2113. LOGICAL FailedHoleySplit = FALSE;
  2114. //
  2115. // There are two cases: the node is holey or not. If the node is holey, at some
  2116. // point we failed to get resources to complete a split, so despite our caller's
  2117. // good intentions we need to go over the entire node.
  2118. //
  2119. if (ParentNode->HoleyNode) {
  2120. //
  2121. // Just move the starting link back to the front. The maximum offset and
  2122. // starting offset of the node will be initialized in the loop. We also turn
  2123. // off the holey flag, which will be turned on again as appropriate.
  2124. //
  2125. pStartLink = &ParentNode->Locks.Next;
  2126. ParentNode->HoleyNode = FALSE;
  2127. HaltOffset.QuadPart = ParentNode->Extent;
  2128. } else {
  2129. HaltOffset = *LastShadowedByte;
  2130. MaxOffset = *GlueOffset;
  2131. StartOffset.QuadPart = 0;
  2132. if (!ParentNode->Locks.Next ||
  2133. (ULONGLONG)HaltOffset.QuadPart <= (ULONGLONG)MaxOffset.QuadPart) {
  2134. //
  2135. // The parent node is not there, doesn't have links associated, or the
  2136. // last possible byte that is affected by the operation our caller made
  2137. // is interior to the max extent of all locks still in this node - in
  2138. // which case there is nothing that needs to be done.
  2139. //
  2140. return;
  2141. }
  2142. }
  2143. //
  2144. // If the extent of the node is past the last byte affected by whatever
  2145. // operations were done to this node, we can avoid the linear scan of
  2146. // the list past that last affected byte since we already know the
  2147. // extent of the entire list! If it is not (note that it would have to
  2148. // be equal - by defintion) then we need to recalculate the extents of
  2149. // all nodes we touch in this operation.
  2150. //
  2151. ExtentValid = (ParentNode->Extent > (ULONGLONG)HaltOffset.QuadPart);
  2152. for (pLink = pStartLink;
  2153. (Link = *pLink) != NULL;
  2154. pLink = NextpLink) {
  2155. NextpLink = &Link->Next;
  2156. Lock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  2157. if (ParentNode->Locks.Next == *pLink) {
  2158. //
  2159. // We're at the first lock in the node, and we know that we're going to leave
  2160. // at least one lock here. Skip over that lock. We also know that the max
  2161. // offset must be that locks's ending byte - make sure it is. Note that this
  2162. // code is *exactly* the same as the update MaxOffset code at the bottom of
  2163. // the loop.
  2164. //
  2165. MaxOffset.QuadPart = Lock->LockInfo.EndingByte.QuadPart;
  2166. //
  2167. // Set the starting offset of the node. This is only an issue for zero length
  2168. // locks, so that we can figure out what is going on if we split a node and wind
  2169. // up with some number of "overlapped" zero length locks at the front of the new
  2170. // node. We must be able to notice this case, and not think that each needs to
  2171. // be in a seperate node.
  2172. //
  2173. StartOffset.QuadPart = Lock->LockInfo.StartingByte.QuadPart;
  2174. //
  2175. // If extents are invalid we also need to set it in case this turns out to
  2176. // be the only lock at this node.
  2177. //
  2178. if (!ExtentValid) {
  2179. ParentNode->Extent = (ULONGLONG)MaxOffset.QuadPart;
  2180. }
  2181. continue;
  2182. }
  2183. //
  2184. // If the lock begins at a byte offset greater than the maximum offset seen to this
  2185. // point, AND this is not a zero length node starting at the beginning of this node,
  2186. // break the node. The second half of the test keeps co-incident zero length locks
  2187. // in the same node. (zero length lock ---> starting = ending + 1).
  2188. //
  2189. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)MaxOffset.QuadPart &&
  2190. !(Lock->LockInfo.Length.QuadPart == 0 &&
  2191. Lock->LockInfo.StartingByte.QuadPart == StartOffset.QuadPart)) {
  2192. //
  2193. // Break the node up here
  2194. //
  2195. NewNode = FsRtlAllocateLockTreeNode();
  2196. if (NewNode == NULL) {
  2197. //
  2198. // If we are out of resources, this node is now holey - we know that the locks at
  2199. // this node do not completely cover the indicated range. Keep splitting for two
  2200. // reasons: more resources may become avaliable, and we must keep updating the
  2201. // node's extent if it is known to be invalid.
  2202. //
  2203. //
  2204. // Now if this node was already holey it is not possible to state that, if we
  2205. // manage to split if further as we keep walking, that the resulting "left" node
  2206. // is not holey. See below.
  2207. //
  2208. if (ParentNode->HoleyNode) {
  2209. FailedHoleySplit = TRUE;
  2210. }
  2211. ParentNode->HoleyNode = TRUE;
  2212. } else {
  2213. //
  2214. // Initialize the node.
  2215. //
  2216. RtlInitializeSplayLinks(&NewNode->Links);
  2217. NewNode->HoleyNode = FALSE;
  2218. //
  2219. // Find the spot in the tree to take the new node(s). If the current node has
  2220. // a free right child, we use it, else find the successor node and use its
  2221. // left child. One of these cases must be avaliable since we know there are
  2222. // no nodes between this node and its successor.
  2223. //
  2224. if (RtlRightChild(&ParentNode->Links) == NULL) {
  2225. RtlInsertAsRightChild(&ParentNode->Links, &NewNode->Links);
  2226. } else {
  2227. ASSERT(RtlLeftChild(RtlRealSuccessor(&ParentNode->Links)) == NULL);
  2228. RtlInsertAsLeftChild(RtlRealSuccessor(&ParentNode->Links), &NewNode->Links);
  2229. }
  2230. //
  2231. // Move the remaining locks over to the new node and fix up extents
  2232. //
  2233. NewNode->Locks.Next = *pLink;
  2234. *pLink = NULL;
  2235. NewNode->Tail.Next = ParentNode->Tail.Next;
  2236. ParentNode->Tail.Next = CONTAINING_RECORD( pLink, SINGLE_LIST_ENTRY, Next );
  2237. //
  2238. // This will cause us to fall into the first-lock clause above on the next pass
  2239. //
  2240. NextpLink = &NewNode->Locks.Next;
  2241. //
  2242. // The new node's extent is now copied from the parent. The old node's extent must be
  2243. // the maximum offset we have seen to this point.
  2244. //
  2245. // Note that if ExtentValid is true, that must mean that the lock ending at that extent
  2246. // is in the new node since if it was in the old node we wouldn't have been able to split.
  2247. //
  2248. NewNode->Extent = ParentNode->Extent;
  2249. ParentNode->Extent = (ULONGLONG)MaxOffset.QuadPart;
  2250. //
  2251. // The parent node can no longer be holey if we have not failed a split in this node.
  2252. //
  2253. if (!FailedHoleySplit) {
  2254. ParentNode->HoleyNode = FALSE;
  2255. } else {
  2256. //
  2257. // So reset the failure flag for the new node.
  2258. //
  2259. FailedHoleySplit = FALSE;
  2260. }
  2261. //
  2262. // Move over to the new node.
  2263. //
  2264. ParentNode = NewNode;
  2265. continue;
  2266. }
  2267. }
  2268. if (ExtentValid &&
  2269. (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)HaltOffset.QuadPart) {
  2270. //
  2271. // Our extents are good and this lock is past the shadow, so we can stop
  2272. //
  2273. return;
  2274. }
  2275. if ((ULONGLONG)MaxOffset.QuadPart < (ULONGLONG)Lock->LockInfo.EndingByte.QuadPart) {
  2276. //
  2277. // Update maximum offset
  2278. //
  2279. MaxOffset.QuadPart = Lock->LockInfo.EndingByte.QuadPart;
  2280. if (!ExtentValid) {
  2281. //
  2282. // Extents are not good so we must update the extent
  2283. //
  2284. ParentNode->Extent = (ULONGLONG)MaxOffset.QuadPart;
  2285. }
  2286. }
  2287. }
  2288. //
  2289. // Reached the end of the list, so update the extent (case of all subsequent locks
  2290. // having been interior to GlueOffset)
  2291. //
  2292. ParentNode->Extent = (ULONGLONG)MaxOffset.QuadPart;
  2293. return;
  2294. }
  2295. VOID
  2296. FsRtlPrivateRemoveLock (
  2297. IN PLOCK_INFO LockInfo,
  2298. IN PFILE_LOCK_INFO FileLockInfo,
  2299. IN BOOLEAN CheckForWaiters
  2300. )
  2301. /*++
  2302. Routine Description:
  2303. General purpose cleanup routine. Finds the given lock structure
  2304. and removes it from the file lock list. Differs from UnlockSingle
  2305. only in that it disables the UnlockRoutine of the FileLock and
  2306. optionalizes walking the waiting locks list.
  2307. Arguments:
  2308. FileLock - Supplies the file's lock structure supposedly containing a stale lock
  2309. FileLockInfo - Supplies file lock data being freed
  2310. CheckForWaiters - If true check for possible waiting locks, caused
  2311. by freeing the locked range
  2312. Return Value:
  2313. None.
  2314. --*/
  2315. {
  2316. NTSTATUS Status;
  2317. if (FileLockInfo->ExclusiveLock) {
  2318. //
  2319. // We must find it in the exclusive lock tree
  2320. //
  2321. Status = FsRtlFastUnlockSingleExclusive( LockInfo,
  2322. FileLockInfo->FileObject,
  2323. &FileLockInfo->StartingByte,
  2324. &FileLockInfo->Length,
  2325. FileLockInfo->ProcessId,
  2326. FileLockInfo->Key,
  2327. NULL,
  2328. TRUE,
  2329. CheckForWaiters );
  2330. ASSERT( Status == STATUS_SUCCESS);
  2331. } else {
  2332. //
  2333. // We must find it in the shared lock tree
  2334. //
  2335. Status = FsRtlFastUnlockSingleShared( LockInfo,
  2336. FileLockInfo->FileObject,
  2337. &FileLockInfo->StartingByte,
  2338. &FileLockInfo->Length,
  2339. FileLockInfo->ProcessId,
  2340. FileLockInfo->Key,
  2341. NULL,
  2342. TRUE,
  2343. CheckForWaiters );
  2344. ASSERT( Status == STATUS_SUCCESS);
  2345. }
  2346. return;
  2347. }
  2348. NTSTATUS
  2349. FsRtlFastUnlockSingle (
  2350. IN PFILE_LOCK FileLock,
  2351. IN PFILE_OBJECT FileObject,
  2352. IN LARGE_INTEGER UNALIGNED *FileOffset,
  2353. IN PLARGE_INTEGER Length,
  2354. IN PEPROCESS ProcessId,
  2355. IN ULONG Key,
  2356. IN PVOID Context OPTIONAL,
  2357. IN BOOLEAN AlreadySynchronized
  2358. )
  2359. /*++
  2360. Routine Description:
  2361. This routine performs an Unlock Single operation on the current locks
  2362. associated with the specified file lock. Only the lock with a matching
  2363. file object, process id, key, and range is freed.
  2364. Arguments:
  2365. FileLock - Supplies the file lock being freed.
  2366. FileObject - Supplies the file object holding the locks
  2367. FileOffset - Supplies the offset to be unlocked
  2368. Length - Supplies the length in bytes to be unlocked
  2369. ProcessId - Supplies the process Id to use in this operation
  2370. Key - Supplies the key to use in this operation
  2371. Context - Optionally supplies context to use when completing Irps
  2372. AlreadySynchronized - Indicates that the caller has already synchronized
  2373. access to the file lock so the fields in the file lock and
  2374. be updated without further locking, but not the queues.
  2375. Return Value:
  2376. NTSTATUS - The completion status for this operation
  2377. --*/
  2378. {
  2379. NTSTATUS Status;
  2380. //
  2381. // XXX AlreadySynchronized is obsolete. It was apparently added for
  2382. // the dead SoloLock code.
  2383. //
  2384. UNREFERENCED_PARAMETER (AlreadySynchronized);
  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 = FALSE;
  2819. BOOLEAN AccessGranted;
  2820. BOOLEAN ViaFastCall;
  2821. BOOLEAN ReleaseQueue;
  2822. PLOCK_INFO LockInfo;
  2823. PLOCK_QUEUE LockQueue;
  2824. KIRQL OldIrql;
  2825. FILE_LOCK_INFO FileLockInfo;
  2826. UNREFERENCED_PARAMETER (AlreadySynchronized);
  2827. DebugTrace(+1, Dbg, "FsRtlPrivateLock, FileLock = %08lx\n", FileLock);
  2828. //
  2829. // If the irp is null then this is being called via the fast call method.
  2830. //
  2831. ViaFastCall = (BOOLEAN) !ARGUMENT_PRESENT( Irp );
  2832. if ((LockInfo = (PLOCK_INFO) FileLock->LockInformation) == NULL) {
  2833. DebugTrace(+2, Dbg, "FsRtlPrivateLock, New LockInfo required\n", 0);
  2834. //
  2835. // No lock information on this FileLock, create the structure.
  2836. //
  2837. //
  2838. if (!FsRtlPrivateInitializeFileLock (FileLock, ViaFastCall)) {
  2839. return FALSE;
  2840. }
  2841. //
  2842. // Set flag so file locks will be checked on the fast io
  2843. // code paths
  2844. //
  2845. FileLock->FastIoIsQuestionable = TRUE;
  2846. //
  2847. // Pickup allocated lockinfo structure
  2848. //
  2849. LockInfo = (PLOCK_INFO) FileLock->LockInformation;
  2850. }
  2851. //
  2852. // Assume success and build LockData structure prior to acquiring
  2853. // the lock queue spinlock. (mp perf enhancement)
  2854. //
  2855. FileLockInfo.StartingByte = *FileOffset;
  2856. FileLockInfo.Length = *Length;
  2857. FileLockInfo.EndingByte.QuadPart =
  2858. (ULONGLONG)FileLockInfo.StartingByte.QuadPart + (ULONGLONG)FileLockInfo.Length.QuadPart - 1;
  2859. FileLockInfo.Key = Key;
  2860. FileLockInfo.FileObject = FileObject;
  2861. FileLockInfo.ProcessId = ProcessId;
  2862. FileLockInfo.ExclusiveLock = ExclusiveLock;
  2863. LockQueue = &LockInfo->LockQueue;
  2864. //
  2865. // Now we need to actually run through our current lock queue.
  2866. //
  2867. ReleaseQueue = TRUE;
  2868. FsRtlAcquireLockQueue(LockQueue, &OldIrql);
  2869. try {
  2870. //
  2871. // Case on whether we're trying to take out an exclusive lock or
  2872. // a shared lock. And in both cases try to get appropriate access.
  2873. //
  2874. if (ExclusiveLock) {
  2875. DebugTrace(0, Dbg, "Check for write access\n", 0);
  2876. AccessGranted = FsRtlPrivateCheckForExclusiveLockAccess(
  2877. LockQueue,
  2878. &FileLockInfo );
  2879. } else {
  2880. DebugTrace(0, Dbg, "Check for read access\n", 0);
  2881. AccessGranted = FsRtlPrivateCheckForSharedLockAccess(
  2882. LockQueue,
  2883. &FileLockInfo );
  2884. }
  2885. //
  2886. // Now AccessGranted tells us whether we can really get the access
  2887. // for the range we want
  2888. //
  2889. if (!AccessGranted) {
  2890. DebugTrace(0, Dbg, "We do not have access\n", 0);
  2891. //
  2892. // We cannot read/write to the range, so we cannot take out
  2893. // the lock. Now if the user wanted to fail immediately then
  2894. // we'll complete the Irp, otherwise we'll enqueue this Irp
  2895. // to the waiting lock queue
  2896. //
  2897. if (FailImmediately) {
  2898. //
  2899. // Set our status and return, the finally clause will
  2900. // complete the request
  2901. //
  2902. DebugTrace(0, Dbg, "And we fail immediately\n", 0);
  2903. Iosb->Status = STATUS_LOCK_NOT_GRANTED;
  2904. try_return( Results = TRUE );
  2905. } else if (ARGUMENT_PRESENT(Irp)) {
  2906. PWAITING_LOCK WaitingLock;
  2907. DebugTrace(0, Dbg, "And we enqueue the Irp for later\n", 0);
  2908. //
  2909. // Allocate a new waiting record, set it to point to the
  2910. // waiting Irp, and insert it in the tail of the waiting
  2911. // locks queue
  2912. //
  2913. WaitingLock = FsRtlAllocateWaitingLock();
  2914. //
  2915. // Simply raise out if we can't allocate.
  2916. //
  2917. if (WaitingLock == NULL) {
  2918. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2919. }
  2920. WaitingLock->Irp = Irp;
  2921. WaitingLock->Context = Context;
  2922. WaitingLock->CompleteLockIrpRoutine = LockInfo->CompleteLockIrpRoutine;
  2923. IoMarkIrpPending( Irp );
  2924. //
  2925. // Add WaitingLock WaitingLockQueue
  2926. //
  2927. WaitingLock->Link.Next = NULL;
  2928. if (LockQueue->WaitingLocks.Next == NULL) {
  2929. //
  2930. // Create new list
  2931. //
  2932. LockQueue->WaitingLocks.Next = &WaitingLock->Link;
  2933. LockQueue->WaitingLocksTail.Next = &WaitingLock->Link;
  2934. } else {
  2935. //
  2936. // Add waiter to tail of list
  2937. //
  2938. LockQueue->WaitingLocksTail.Next->Next = &WaitingLock->Link;
  2939. LockQueue->WaitingLocksTail.Next = &WaitingLock->Link;
  2940. }
  2941. //
  2942. // Setup IRP in case it's canceled - then set the
  2943. // IRP's cancel routine
  2944. //
  2945. Irp->IoStatus.Information = (ULONG_PTR)LockInfo;
  2946. IoSetCancelRoutine( Irp, FsRtlPrivateCancelFileLockIrp );
  2947. if (Irp->Cancel) {
  2948. //
  2949. // Pull the cancel routine off of the IRP - if it is not
  2950. // NULL, this means we won the race with IoCancelIrp and
  2951. // will be responsible for cancelling the IRP synchronously.
  2952. // If NULL, we lost and our cancel routine is already being
  2953. // called for us.
  2954. //
  2955. // This must be done while holding the lock queue down since
  2956. // this is how we synchronize with the cancel.
  2957. //
  2958. if (IoSetCancelRoutine( Irp, NULL )) {
  2959. //
  2960. // Irp's cancel routine was not called, do it ourselves.
  2961. // Indicate to the cancel routine that he does not need
  2962. // to release the cancel spinlock by passing a NULL DO.
  2963. //
  2964. // The queue will be dropped in order to complete the Irp.
  2965. // We communicate the previous IRQL through the Irp itself.
  2966. //
  2967. Irp->CancelIrql = OldIrql;
  2968. FsRtlPrivateCancelFileLockIrp( NULL, Irp );
  2969. ReleaseQueue = FALSE;
  2970. }
  2971. }
  2972. Iosb->Status = STATUS_PENDING;
  2973. try_return( Results = TRUE );
  2974. } else {
  2975. try_return( Results = FALSE );
  2976. }
  2977. }
  2978. DebugTrace(0, Dbg, "We have access\n", 0);
  2979. if (!FsRtlPrivateInsertLock( LockInfo, FileObject, &FileLockInfo )) {
  2980. //
  2981. // Resource exhaustion will cause us to fail here. Via the fast call, indicate
  2982. // that it may be worthwhile to go around again via the Irp based path. If we
  2983. // are already there, simply raise out.
  2984. //
  2985. if (ViaFastCall) {
  2986. try_return( Results = FALSE );
  2987. } else {
  2988. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2989. }
  2990. } else {
  2991. Iosb->Status = STATUS_SUCCESS;
  2992. }
  2993. //
  2994. // At long last, we're done.
  2995. //
  2996. Results = TRUE;
  2997. try_exit: NOTHING;
  2998. } finally {
  2999. if (ReleaseQueue) {
  3000. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  3001. }
  3002. //
  3003. // Complete the request provided we were given one and it is not a pending status
  3004. //
  3005. if (!AbnormalTermination() && ARGUMENT_PRESENT(Irp) && (Iosb->Status != STATUS_PENDING)) {
  3006. NTSTATUS NewStatus;
  3007. //
  3008. // We must reference the fileobject for the case that the IRP completion
  3009. // fails and we need to lift the lock. Although the only reason we have
  3010. // to touch the fileobject in the remove case is to unset the LastLock field,
  3011. // we have no way of knowing if we will race with a reference count drop
  3012. // and lose.
  3013. //
  3014. ObReferenceObject( FileObject );
  3015. //
  3016. // Complete the request, if the don't get back success then
  3017. // we need to possibly remove the lock that we just
  3018. // inserted.
  3019. //
  3020. FsRtlCompleteLockIrp(
  3021. LockInfo,
  3022. Context,
  3023. Irp,
  3024. Iosb->Status,
  3025. &NewStatus,
  3026. FileObject );
  3027. if (!NT_SUCCESS(NewStatus) && NT_SUCCESS(Iosb->Status) ) {
  3028. //
  3029. // Irp failed, remove the lock which was added
  3030. //
  3031. FsRtlPrivateRemoveLock (
  3032. LockInfo,
  3033. &FileLockInfo,
  3034. TRUE );
  3035. }
  3036. //
  3037. // Lift our private reference to the fileobject. This may induce deletion.
  3038. //
  3039. ObDereferenceObject( FileObject );
  3040. Iosb->Status = NewStatus;
  3041. }
  3042. DebugTrace(-1, Dbg, "FsRtlPrivateLock -> %08lx\n", Results);
  3043. }
  3044. //
  3045. // and return to our caller
  3046. //
  3047. return Results;
  3048. }
  3049. //
  3050. // Internal Support Routine
  3051. //
  3052. BOOLEAN
  3053. FsRtlPrivateInsertLock (
  3054. IN PLOCK_INFO LockInfo,
  3055. IN PFILE_OBJECT FileObject,
  3056. IN PFILE_LOCK_INFO FileLockInfo
  3057. )
  3058. /*++
  3059. Routine Description:
  3060. This routine fills in a new lock record of the appropriate type and inserts
  3061. it into the lock information.
  3062. Arguments:
  3063. LockInfo - Supplies the lock being modified
  3064. FileObject - The associated file object to update hints in
  3065. FileLockInfo - Supplies the new lock data to add to the lock queue
  3066. Return Value:
  3067. BOOLEAN - True if the insert was successful, False if no resources were avaliable
  3068. to complete the operation.
  3069. --*/
  3070. {
  3071. //
  3072. // Now add the lock to the appropriate tree.
  3073. //
  3074. if (FileLockInfo->ExclusiveLock) {
  3075. PEX_LOCK ExLock;
  3076. ExLock = FsRtlAllocateExclusiveLock();
  3077. if (ExLock == NULL) {
  3078. return FALSE;
  3079. }
  3080. ExLock->LockInfo = *FileLockInfo;
  3081. FsRtlPrivateInsertExclusiveLock( &LockInfo->LockQueue, ExLock );
  3082. FileObject->LastLock = &ExLock->LockInfo;
  3083. } else {
  3084. PSH_LOCK ShLock;
  3085. ShLock = FsRtlAllocateSharedLock();
  3086. if (ShLock == NULL) {
  3087. return FALSE;
  3088. }
  3089. ShLock->LockInfo = *FileLockInfo;
  3090. if (!FsRtlPrivateInsertSharedLock( &LockInfo->LockQueue, ShLock )) {
  3091. return FALSE;
  3092. }
  3093. FileObject->LastLock = &ShLock->LockInfo;
  3094. }
  3095. //
  3096. // Fix up the lowest lock offset if need be
  3097. //
  3098. if ((ULONGLONG)FileLockInfo->StartingByte.QuadPart < (ULONGLONG)LockInfo->LowestLockOffset) {
  3099. ASSERT( FileLockInfo->StartingByte.HighPart == 0 );
  3100. LockInfo->LowestLockOffset = FileLockInfo->StartingByte.LowPart;
  3101. }
  3102. return TRUE;
  3103. }
  3104. //
  3105. // Internal Support Routine
  3106. //
  3107. BOOLEAN
  3108. FsRtlPrivateInsertSharedLock (
  3109. IN PLOCK_QUEUE LockQueue,
  3110. IN PSH_LOCK NewLock
  3111. )
  3112. /*++
  3113. Routine Description:
  3114. This routine adds a new shared lock record to the File lock's current
  3115. lock queue. Locks are inserted into nodes ordered by their starting byte.
  3116. Arguments:
  3117. LockQueue - Supplies the lock queue being modified
  3118. NewLock - Supplies the new shared lock to add to the lock queue
  3119. Return Value:
  3120. BOOLEAN - True if the insert was successful, False if no resources were avaliable
  3121. to complete the operation.
  3122. --*/
  3123. {
  3124. PSINGLE_LIST_ENTRY pLink, Link;
  3125. PRTL_SPLAY_LINKS OverlappedSplayLinks, ParentSplayLinks;
  3126. PLOCKTREE_NODE Node, NextNode;
  3127. PSH_LOCK NextLock;
  3128. BOOLEAN GreaterThan;
  3129. OverlappedSplayLinks = FsRtlFindFirstOverlappingSharedNode( LockQueue->SharedLockTree,
  3130. &NewLock->LockInfo.StartingByte,
  3131. &NewLock->LockInfo.EndingByte,
  3132. &ParentSplayLinks,
  3133. &GreaterThan );
  3134. if (OverlappedSplayLinks == NULL) {
  3135. //
  3136. // Simple insert case, build a new node
  3137. //
  3138. NextNode = FsRtlAllocateLockTreeNode();
  3139. //
  3140. // If no resources are avaliable, simply fail now.
  3141. //
  3142. if (NextNode == NULL) {
  3143. return FALSE;
  3144. }
  3145. RtlInitializeSplayLinks(&NextNode->Links);
  3146. NextNode->HoleyNode = FALSE;
  3147. NextNode->Locks.Next = NextNode->Tail.Next = &NewLock->Link;
  3148. NextNode->Extent = (ULONGLONG)NewLock->LockInfo.EndingByte.QuadPart;
  3149. NewLock->Link.Next = NULL;
  3150. if (ParentSplayLinks) {
  3151. //
  3152. // We have a real parent node in the tree
  3153. //
  3154. if (GreaterThan) {
  3155. ASSERT(RtlLeftChild(ParentSplayLinks) == NULL);
  3156. RtlInsertAsLeftChild(ParentSplayLinks, &NextNode->Links);
  3157. } else {
  3158. ASSERT(RtlRightChild(ParentSplayLinks) == NULL);
  3159. RtlInsertAsRightChild(ParentSplayLinks, &NextNode->Links);
  3160. }
  3161. //
  3162. // Splay all new nodes in the tree
  3163. //
  3164. LockQueue->SharedLockTree = RtlSplay(&NextNode->Links);
  3165. } else {
  3166. //
  3167. // First node in the tree
  3168. //
  3169. LockQueue->SharedLockTree = &NextNode->Links;
  3170. }
  3171. return TRUE;
  3172. }
  3173. //
  3174. // Now we examine the node to see if it is holey as a result of a resource-failed split.
  3175. // If it is, we must complete the split before adding the new lock.
  3176. //
  3177. Node = CONTAINING_RECORD( OverlappedSplayLinks, LOCKTREE_NODE, Links );
  3178. //
  3179. // Search down the overlapped node finding the position for the new lock
  3180. //
  3181. for (pLink = &Node->Locks;
  3182. (Link = pLink->Next) != NULL;
  3183. pLink = Link) {
  3184. PSH_LOCK Lock;
  3185. Lock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  3186. //
  3187. // We sort locks on this list first by starting byte, then by whether the length is zero or not.
  3188. // This is important so that zero length locks appear prior to non-zero length locks, so that
  3189. // they are split out of nodes into the tree in the correct order.
  3190. //
  3191. // if (NewLock->StartingByte <= Lock->StartingByte) ...
  3192. //
  3193. if (((ULONGLONG)NewLock->LockInfo.StartingByte.QuadPart < (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart) ||
  3194. ((ULONGLONG)NewLock->LockInfo.StartingByte.QuadPart == (ULONGLONG)Lock->LockInfo.StartingByte.QuadPart &&
  3195. (NewLock->LockInfo.Length.QuadPart == 0 || Lock->LockInfo.Length.QuadPart != 0))) {
  3196. break;
  3197. }
  3198. }
  3199. //
  3200. // At this point pLink points to the record that comes right after
  3201. // the new lock that we're inserting so we can simply push the
  3202. // newlock into the entrylist
  3203. //
  3204. DebugTrace(0, Dbg, "InsertSharedLock, Insert Before = %08lx\n", Link);
  3205. if (pLink->Next == NULL) {
  3206. //
  3207. // Adding onto the tail of the list
  3208. //
  3209. Node->Tail.Next = &NewLock->Link;
  3210. }
  3211. NewLock->Link.Next = pLink->Next;
  3212. pLink->Next = &NewLock->Link;
  3213. //
  3214. // And splay the node we inserted into
  3215. //
  3216. LockQueue->SharedLockTree = RtlSplay(OverlappedSplayLinks);
  3217. if ((ULONGLONG)NewLock->LockInfo.EndingByte.QuadPart > Node->Extent) {
  3218. //
  3219. // The new lock extends the range of this node, so fix up the extent
  3220. //
  3221. Node->Extent = NewLock->LockInfo.EndingByte.QuadPart;
  3222. //
  3223. // Walk across the remainder of the tree integrating newly overlapping
  3224. // nodes into the node we just inserted the new lock into. Note that
  3225. // this isn't so much a walk as a repeated examination of our successor's
  3226. // until one does not overlap (or we hit the end).
  3227. //
  3228. ParentSplayLinks = OverlappedSplayLinks;
  3229. for (OverlappedSplayLinks = RtlRealSuccessor(ParentSplayLinks);
  3230. OverlappedSplayLinks;
  3231. OverlappedSplayLinks = RtlRealSuccessor(ParentSplayLinks)) {
  3232. NextNode = CONTAINING_RECORD( OverlappedSplayLinks, LOCKTREE_NODE, Links );
  3233. NextLock = CONTAINING_RECORD( NextNode->Locks.Next, SH_LOCK, Link );
  3234. if ((ULONGLONG)NextLock->LockInfo.StartingByte.QuadPart > Node->Extent) {
  3235. //
  3236. // This node is not overlapped, so stop
  3237. //
  3238. break;
  3239. }
  3240. //
  3241. // If we are intergrating a holey node into a non-holey node, try to split
  3242. // the node first. It will be better to get this done with a smaller node
  3243. // than a big, fully integrated one. Note that we are guaranteed that the
  3244. // node will remain a candidate for integration since the first lock on the
  3245. // node will still be there, and overlaps.
  3246. //
  3247. if (!Node->HoleyNode && NextNode->HoleyNode) {
  3248. FsRtlSplitLocks( NextNode, NULL, NULL, NULL );
  3249. }
  3250. //
  3251. // Integrate the locks in this node into our list
  3252. //
  3253. Node->Tail.Next->Next = NextNode->Locks.Next;
  3254. Node->Tail.Next = NextNode->Tail.Next;
  3255. if (NextNode->Extent > Node->Extent) {
  3256. //
  3257. // If the node we just swallowed was (still!) holey, we perhaps made this
  3258. // node holey too. The resolution of this is left to the lock split we will
  3259. // perform after integration is complete.
  3260. //
  3261. // Note that if the extent of the node we are swallowing is interior
  3262. // to the current node, we just covered whatever holes it contained.
  3263. //
  3264. if (NextNode->HoleyNode) {
  3265. Node->HoleyNode = TRUE;
  3266. }
  3267. Node->Extent = NextNode->Extent;
  3268. }
  3269. //
  3270. // Free the now empty node.
  3271. //
  3272. RtlDeleteNoSplay( OverlappedSplayLinks, &LockQueue->SharedLockTree );
  3273. FsRtlFreeLockTreeNode( NextNode );
  3274. }
  3275. }
  3276. //
  3277. // Now, perhaps this node is still holey. For grins lets try one more time to split
  3278. // this thing apart.
  3279. //
  3280. if (Node->HoleyNode) {
  3281. FsRtlSplitLocks( Node, NULL, NULL, NULL );
  3282. }
  3283. //
  3284. // And return to our caller
  3285. //
  3286. return TRUE;
  3287. }
  3288. //
  3289. // Internal Support Routine
  3290. //
  3291. VOID
  3292. FsRtlPrivateInsertExclusiveLock (
  3293. IN PLOCK_QUEUE LockQueue,
  3294. IN PEX_LOCK NewLock
  3295. )
  3296. /*++
  3297. Routine Description:
  3298. This routine adds a new exclusive lock record to the File lock's current
  3299. lock queue.
  3300. Arguments:
  3301. LockQueue - Supplies the lock queue being modified
  3302. NewLock - Supplies the new exclusive lock to add to the lock queue
  3303. Return Value:
  3304. None.
  3305. --*/
  3306. {
  3307. PRTL_SPLAY_LINKS OverlappedSplayLinks, ParentSplayLinks;
  3308. BOOLEAN GreaterThan;
  3309. OverlappedSplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockQueue->ExclusiveLockTree,
  3310. &NewLock->LockInfo.StartingByte,
  3311. &NewLock->LockInfo.EndingByte,
  3312. &ParentSplayLinks,
  3313. &GreaterThan );
  3314. //
  3315. // This is the exclusive tree. Nothing can overlap (caller is supposed to insure this) unless
  3316. // the lock is a zero length lock, in which case we just insert it - still.
  3317. //
  3318. ASSERT(!OverlappedSplayLinks || NewLock->LockInfo.Length.QuadPart == 0);
  3319. //
  3320. // Simple insert ...
  3321. //
  3322. RtlInitializeSplayLinks(&NewLock->Links);
  3323. if (OverlappedSplayLinks) {
  3324. //
  3325. // With zero length locks we have OverlappedSplayLinks at the starting point
  3326. // of a run of zero length locks, so we have to e flexible about where the new
  3327. // node is inserted.
  3328. //
  3329. if (RtlRightChild(OverlappedSplayLinks)) {
  3330. //
  3331. // Right slot taken. We can use the left slot or go to the sucessor's left slot
  3332. //
  3333. if (RtlLeftChild(OverlappedSplayLinks)) {
  3334. ASSERT(RtlLeftChild(RtlRealSuccessor(OverlappedSplayLinks)) == NULL);
  3335. RtlInsertAsLeftChild(RtlRealSuccessor(OverlappedSplayLinks), &NewLock->Links);
  3336. } else {
  3337. RtlInsertAsLeftChild(OverlappedSplayLinks, &NewLock->Links);
  3338. }
  3339. } else {
  3340. RtlInsertAsRightChild(OverlappedSplayLinks, &NewLock->Links);
  3341. }
  3342. } else if (ParentSplayLinks) {
  3343. //
  3344. // We have a real parent node in the tree, and must be at a leaf since
  3345. // there was no overlap
  3346. //
  3347. if (GreaterThan) {
  3348. ASSERT(RtlLeftChild(ParentSplayLinks) == NULL);
  3349. RtlInsertAsLeftChild(ParentSplayLinks, &NewLock->Links);
  3350. } else {
  3351. ASSERT(RtlRightChild(ParentSplayLinks) == NULL);
  3352. RtlInsertAsRightChild(ParentSplayLinks, &NewLock->Links);
  3353. }
  3354. } else {
  3355. //
  3356. // First node in the tree
  3357. //
  3358. LockQueue->ExclusiveLockTree = &NewLock->Links;
  3359. }
  3360. //
  3361. // And return to our caller
  3362. //
  3363. return;
  3364. }
  3365. //
  3366. // Internal Support Routine
  3367. //
  3368. VOID
  3369. FsRtlPrivateCheckWaitingLocks (
  3370. IN PLOCK_INFO LockInfo,
  3371. IN PLOCK_QUEUE LockQueue,
  3372. IN KIRQL OldIrql
  3373. )
  3374. /*++
  3375. Routine Description:
  3376. This routine checks to see if any of the current waiting locks are now
  3377. be satisfied, and if so it completes their IRPs.
  3378. Arguments:
  3379. LockInfo - LockInfo which LockQueue is member of
  3380. LockQueue - Supplies queue which needs to be checked
  3381. OldIrql - Irql to restore when LockQueue is released
  3382. Return Value:
  3383. None.
  3384. --*/
  3385. {
  3386. PSINGLE_LIST_ENTRY *pLink, Link;
  3387. NTSTATUS NewStatus;
  3388. BOOLEAN Result;
  3389. pLink = &LockQueue->WaitingLocks.Next;
  3390. while ((Link = *pLink) != NULL) {
  3391. PWAITING_LOCK WaitingLock;
  3392. PIRP Irp;
  3393. PIO_STACK_LOCATION IrpSp;
  3394. BOOLEAN AccessGranted;
  3395. FILE_LOCK_INFO FileLockInfo;
  3396. //
  3397. // Get a pointer to the waiting lock record
  3398. //
  3399. WaitingLock = CONTAINING_RECORD( Link, WAITING_LOCK, Link );
  3400. DebugTrace(0, Dbg, "FsRtlCheckWaitingLocks, Loop top, WaitingLock = %08lx\n", WaitingLock);
  3401. //
  3402. // Get a local copy of the necessary fields we'll need to use
  3403. //
  3404. Irp = WaitingLock->Irp;
  3405. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  3406. FileLockInfo.StartingByte = IrpSp->Parameters.LockControl.ByteOffset;
  3407. FileLockInfo.Length = *IrpSp->Parameters.LockControl.Length;
  3408. FileLockInfo.EndingByte.QuadPart =
  3409. (ULONGLONG)FileLockInfo.StartingByte.QuadPart + (ULONGLONG)FileLockInfo.Length.QuadPart - 1;
  3410. FileLockInfo.FileObject = IrpSp->FileObject;
  3411. FileLockInfo.ProcessId = IoGetRequestorProcess( Irp );
  3412. FileLockInfo.Key = IrpSp->Parameters.LockControl.Key;
  3413. FileLockInfo.ExclusiveLock = BooleanFlagOn(IrpSp->Flags, SL_EXCLUSIVE_LOCK);
  3414. //
  3415. // Now case on whether we're trying to take out an exclusive lock or
  3416. // a shared lock. And in both cases try to get the appropriate access
  3417. // For the exclusive case we send in a NULL file object and process
  3418. // id, this will ensure that the lookup does not give us write
  3419. // access through an exclusive lock.
  3420. //
  3421. if (FileLockInfo.ExclusiveLock) {
  3422. DebugTrace(0, Dbg, "FsRtlCheckWaitingLocks do we have write access?\n", 0);
  3423. AccessGranted = FsRtlPrivateCheckForExclusiveLockAccess(
  3424. LockQueue,
  3425. &FileLockInfo );
  3426. } else {
  3427. DebugTrace(0, Dbg, "FsRtlCheckWaitingLocks do we have read access?\n", 0);
  3428. AccessGranted = FsRtlPrivateCheckForSharedLockAccess(
  3429. LockQueue,
  3430. &FileLockInfo );
  3431. }
  3432. //
  3433. // Now AccessGranted tells us whether we can really get the access for
  3434. // the range we want.
  3435. //
  3436. // No matter what happens, this Irp must be completed now - even if we
  3437. // are resource starved. User mode deadlock could be induced since there
  3438. // may no longer be a pending unlock to cause a rescan of the waiting
  3439. // list.
  3440. //
  3441. if (AccessGranted) {
  3442. DebugTrace(0, Dbg, "FsRtlCheckWaitingLocks now has access\n", 0);
  3443. //
  3444. // Clear the cancel routine
  3445. //
  3446. IoAcquireCancelSpinLock( &Irp->CancelIrql );
  3447. IoSetCancelRoutine( Irp, NULL );
  3448. //
  3449. // If the IRP got itself cancelled, it is cancelled and we won't grant it.
  3450. // The canceller is waiting for the queue spinlock right now.
  3451. //
  3452. if (Irp->Cancel) {
  3453. AccessGranted = FALSE;
  3454. }
  3455. IoReleaseCancelSpinLock( Irp->CancelIrql );
  3456. if (AccessGranted) {
  3457. Result = FsRtlPrivateInsertLock( LockInfo, IrpSp->FileObject, &FileLockInfo );
  3458. //
  3459. // Now we need to remove this granted waiter and complete
  3460. // it's irp.
  3461. //
  3462. *pLink = Link->Next;
  3463. if (Link == LockQueue->WaitingLocksTail.Next) {
  3464. LockQueue->WaitingLocksTail.Next = (PSINGLE_LIST_ENTRY) pLink;
  3465. }
  3466. //
  3467. // Release LockQueue and complete this waiter
  3468. //
  3469. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  3470. //
  3471. // Reference the fileobject over the completion attempt so we can have a
  3472. // chance to cleanup safely if we fail
  3473. //
  3474. ObReferenceObject( FileLockInfo.FileObject );
  3475. //
  3476. // Now we can complete the IRP, if we don't get back success
  3477. // from the completion routine then we remove the lock we just
  3478. // inserted.
  3479. //
  3480. FsRtlCompleteLockIrp( LockInfo,
  3481. WaitingLock->Context,
  3482. Irp,
  3483. (Result? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES),
  3484. &NewStatus,
  3485. FileLockInfo.FileObject );
  3486. if (Result && !NT_SUCCESS(NewStatus)) {
  3487. //
  3488. // Irp was not sucessfull, remove lock if it was added.
  3489. //
  3490. FsRtlPrivateRemoveLock (
  3491. LockInfo,
  3492. &FileLockInfo,
  3493. FALSE );
  3494. }
  3495. //
  3496. // Drop our private reference to the fileobject
  3497. //
  3498. ObDereferenceObject( FileLockInfo.FileObject );
  3499. //
  3500. // Re-acquire queue lock
  3501. //
  3502. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  3503. //
  3504. // Start scan over from begining
  3505. //
  3506. pLink = &LockQueue->WaitingLocks.Next;
  3507. //
  3508. // Free up pool
  3509. //
  3510. FsRtlFreeWaitingLock( WaitingLock );
  3511. continue;
  3512. }
  3513. }
  3514. DebugTrace( 0, Dbg, "FsRtlCheckWaitingLocks still no access\n", 0);
  3515. //
  3516. // Move to next lock
  3517. //
  3518. pLink = &Link->Next;
  3519. }
  3520. //
  3521. // And return to our caller
  3522. //
  3523. return;
  3524. }
  3525. BOOLEAN
  3526. FsRtlPrivateCheckForExclusiveLockAccess (
  3527. IN PLOCK_QUEUE LockQueue,
  3528. IN PFILE_LOCK_INFO FileLockInfo
  3529. )
  3530. /*++
  3531. Routine Description:
  3532. This routine checks to see if the caller can get an exclusive lock on
  3533. the indicated range due to file locks in the passed in lock queue.
  3534. Assumes Lock queue is held by caller
  3535. Arguments:
  3536. LockQueue - Queue which needs to be checked for collision
  3537. FileLockInfo - Lock which is being checked
  3538. Return Value:
  3539. BOOLEAN - TRUE if the indicated user can place the exclusive lock over the
  3540. entire specified byte range, and FALSE otherwise
  3541. --*/
  3542. {
  3543. PRTL_SPLAY_LINKS SplayLinks, LastSplayLinks = NULL;
  3544. PLOCKTREE_NODE Node;
  3545. PSH_LOCK ShLock;
  3546. PEX_LOCK ExLock;
  3547. if (LockQueue->SharedLockTree &&
  3548. (SplayLinks = FsRtlFindFirstOverlappingSharedNode( LockQueue->SharedLockTree,
  3549. &FileLockInfo->StartingByte,
  3550. &FileLockInfo->EndingByte,
  3551. &LastSplayLinks, NULL))) {
  3552. Node = CONTAINING_RECORD(SplayLinks, LOCKTREE_NODE, Links);
  3553. //
  3554. // If this node is holey, we'll have to walk the whole thing.
  3555. //
  3556. if (Node->HoleyNode) {
  3557. ShLock = FsRtlFindFirstOverlapInNode( Node,
  3558. &FileLockInfo->StartingByte,
  3559. &FileLockInfo->EndingByte );
  3560. } else {
  3561. ShLock = CONTAINING_RECORD(Node->Locks.Next, SH_LOCK, Link);
  3562. }
  3563. //
  3564. // Look for overlap that we care about. Perhaps no overlap existed in the holey case.
  3565. //
  3566. if (ShLock &&
  3567. (FileLockInfo->Length.QuadPart || ShLock->LockInfo.Length.QuadPart)) {
  3568. //
  3569. // If we are checking a nonzero extent and overlapped, it is fatal. If we
  3570. // are checking a zero extent and overlapped a nonzero extent, it is fatal.
  3571. //
  3572. return FALSE;
  3573. }
  3574. }
  3575. if (LastSplayLinks) {
  3576. LockQueue->SharedLockTree = RtlSplay(LastSplayLinks);
  3577. LastSplayLinks = NULL;
  3578. }
  3579. if (LockQueue->ExclusiveLockTree &&
  3580. (SplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockQueue->ExclusiveLockTree,
  3581. &FileLockInfo->StartingByte,
  3582. &FileLockInfo->EndingByte,
  3583. &LastSplayLinks, NULL))) {
  3584. ExLock = CONTAINING_RECORD(SplayLinks, EX_LOCK, Links);
  3585. if (FileLockInfo->Length.QuadPart || ExLock->LockInfo.Length.QuadPart) {
  3586. //
  3587. // If we are checking a nonzero extent and overlapped, it is fatal. If we
  3588. // are checking a zero extent and overlapped a nonzero extent, it is fatal.
  3589. //
  3590. return FALSE;
  3591. }
  3592. }
  3593. if (LastSplayLinks) {
  3594. LockQueue->ExclusiveLockTree = RtlSplay(LastSplayLinks);
  3595. }
  3596. //
  3597. // We searched the entire range without a conflict so we can grant
  3598. // the exclusive lock
  3599. //
  3600. return TRUE;
  3601. }
  3602. BOOLEAN
  3603. FsRtlPrivateCheckForSharedLockAccess (
  3604. IN PLOCK_QUEUE LockQueue,
  3605. IN PFILE_LOCK_INFO FileLockInfo
  3606. )
  3607. /*++
  3608. Routine Description:
  3609. This routine checks to see if the caller can get a shared lock on
  3610. the indicated range due to file locks in the passed in lock queue.
  3611. Assumes Lock queue is held by caller
  3612. Arguments:
  3613. LockQueue - Queue which needs to be checked for collision
  3614. FileLockInfo - Lock which is being checked
  3615. Arguments:
  3616. Return Value:
  3617. BOOLEAN - TRUE if the indicated user can place the shared lock over
  3618. entire specified byte range, and FALSE otherwise
  3619. --*/
  3620. {
  3621. PEX_LOCK Lock;
  3622. PRTL_SPLAY_LINKS SplayLinks, LastSplayLinks;
  3623. BOOLEAN Status = TRUE;
  3624. //
  3625. // If there are no exclusive locks, this is quick ...
  3626. //
  3627. if (LockQueue->ExclusiveLockTree == NULL) {
  3628. return TRUE;
  3629. }
  3630. //
  3631. // No lock in the shared lock tree can prevent access, so just search the exclusive
  3632. // tree for conflict.
  3633. //
  3634. for (SplayLinks = FsRtlFindFirstOverlappingExclusiveNode( LockQueue->ExclusiveLockTree,
  3635. &FileLockInfo->StartingByte,
  3636. &FileLockInfo->EndingByte,
  3637. &LastSplayLinks, NULL);
  3638. SplayLinks;
  3639. SplayLinks = RtlRealSuccessor(SplayLinks)) {
  3640. Lock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  3641. if ((ULONGLONG)Lock->LockInfo.StartingByte.QuadPart > (ULONGLONG)FileLockInfo->EndingByte.QuadPart) {
  3642. //
  3643. // This node is covering a range greater than the range we care about,
  3644. // so we're done
  3645. //
  3646. break;
  3647. }
  3648. //
  3649. // We may not be able to grant the request if the fileobject, processid,
  3650. // and key do not match.
  3651. //
  3652. if ((Lock->LockInfo.FileObject != FileLockInfo->FileObject) ||
  3653. (Lock->LockInfo.ProcessId != FileLockInfo->ProcessId) ||
  3654. (Lock->LockInfo.Key != FileLockInfo->Key)) {
  3655. //
  3656. // We have a mismatch between caller and owner. It is ok not to conflict
  3657. // if the caller and owner will have/have zero length locks (zero length
  3658. // locks cannot conflict).
  3659. //
  3660. if (FileLockInfo->Length.QuadPart || Lock->LockInfo.Length.QuadPart) {
  3661. Status = FALSE;
  3662. break;
  3663. }
  3664. }
  3665. }
  3666. if (LastSplayLinks) {
  3667. LockQueue->ExclusiveLockTree = RtlSplay(LastSplayLinks);
  3668. }
  3669. //
  3670. // We searched the entire range without a conflict so we can grant
  3671. // the shared lock
  3672. //
  3673. return Status;
  3674. }
  3675. VOID
  3676. FsRtlPrivateResetLowestLockOffset (
  3677. PLOCK_INFO LockInfo
  3678. )
  3679. /*++
  3680. Routine Description:
  3681. This routine resets the lowest lock offset hint in a LOCK_INFO to
  3682. the lowest lock offset currently held by a lock inside of the LOCK_INFO.
  3683. Arguments:
  3684. LockInfo - the lock data to operate on
  3685. Return Value:
  3686. None
  3687. --*/
  3688. {
  3689. PEX_LOCK ExLock = NULL;
  3690. PSH_LOCK ShLock = NULL;
  3691. PFILE_LOCK_INFO LowestLockInfo = NULL;
  3692. PRTL_SPLAY_LINKS SplayLinks;
  3693. PLOCKTREE_NODE Node;
  3694. //
  3695. // Fix up the lowest lock offset if we have non-empty trees and there was
  3696. // a lock in the low 32 bit region
  3697. //
  3698. if (LockInfo->LowestLockOffset != 0xffffffff &&
  3699. (LockInfo->LockQueue.SharedLockTree != NULL ||
  3700. LockInfo->LockQueue.ExclusiveLockTree != NULL)) {
  3701. //
  3702. // Grab the lowest nodes in the trees
  3703. //
  3704. if (LockInfo->LockQueue.SharedLockTree) {
  3705. SplayLinks = LockInfo->LockQueue.SharedLockTree;
  3706. while (RtlLeftChild(SplayLinks) != NULL) {
  3707. SplayLinks = RtlLeftChild(SplayLinks);
  3708. }
  3709. Node = CONTAINING_RECORD( SplayLinks, LOCKTREE_NODE, Links );
  3710. ShLock = CONTAINING_RECORD( Node->Locks.Next, SH_LOCK, Link );
  3711. }
  3712. if (LockInfo->LockQueue.ExclusiveLockTree) {
  3713. SplayLinks = LockInfo->LockQueue.ExclusiveLockTree;
  3714. while (RtlLeftChild(SplayLinks) != NULL) {
  3715. SplayLinks = RtlLeftChild(SplayLinks);
  3716. }
  3717. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  3718. }
  3719. //
  3720. // Figure out which of the lowest locks is actually lowest. We know that one of the lock
  3721. // trees at least has a lock, so if we have don't have exclusive locks then we do know
  3722. // we have shared locks ...
  3723. //
  3724. if (ExLock &&
  3725. (!ShLock ||
  3726. (ULONGLONG)ExLock->LockInfo.StartingByte.QuadPart < (ULONGLONG)ShLock->LockInfo.StartingByte.QuadPart)) {
  3727. LowestLockInfo = &ExLock->LockInfo;
  3728. } else {
  3729. LowestLockInfo = &ShLock->LockInfo;
  3730. }
  3731. if (LowestLockInfo->StartingByte.HighPart == 0) {
  3732. LockInfo->LowestLockOffset = LowestLockInfo->StartingByte.LowPart;
  3733. } else {
  3734. LockInfo->LowestLockOffset = 0xffffffff;
  3735. }
  3736. } else {
  3737. //
  3738. // If there are no locks, set the lock offset high
  3739. //
  3740. LockInfo->LowestLockOffset = 0xffffffff;
  3741. }
  3742. }
  3743. NTSTATUS
  3744. FsRtlPrivateFastUnlockAll (
  3745. IN PFILE_LOCK FileLock,
  3746. IN PFILE_OBJECT FileObject,
  3747. IN PEPROCESS ProcessId,
  3748. IN ULONG Key,
  3749. IN BOOLEAN MatchKey,
  3750. IN PVOID Context OPTIONAL
  3751. )
  3752. /*++
  3753. Routine Description:
  3754. This routine performs an Unlock all operation on the current locks
  3755. associated with the specified file lock. Only those locks with
  3756. a matching file object and process id are freed. Additionally,
  3757. it is possible to free only those locks which also match a given
  3758. key.
  3759. Arguments:
  3760. FileLock - Supplies the file lock being freed.
  3761. FileObject - Supplies the file object associated with the file lock
  3762. ProcessId - Supplies the Process Id assoicated with the locks to be
  3763. freed
  3764. Key - Supplies the Key to use in this operation
  3765. MatchKey - Whether or not the Key must also match for lock to be freed.
  3766. Context - Supplies an optional context to use when completing waiting
  3767. lock irps.
  3768. Return Value:
  3769. None
  3770. --*/
  3771. {
  3772. PLOCK_INFO LockInfo;
  3773. PLOCK_QUEUE LockQueue;
  3774. PSINGLE_LIST_ENTRY *pLink, *SavepLink, Link;
  3775. NTSTATUS NewStatus;
  3776. KIRQL OldIrql;
  3777. LARGE_INTEGER GlueOffset, EndingDeletedByte;
  3778. BOOLEAN UnlockRoutine;
  3779. PSH_LOCK ShLock = NULL;
  3780. PEX_LOCK ExLock;
  3781. PRTL_SPLAY_LINKS SplayLinks, SuccessorLinks;
  3782. PLOCKTREE_NODE Node;
  3783. DebugTrace(+1, Dbg, "FsRtlPrivateFastUnlockAll, FileLock = %08lx\n", FileLock);
  3784. if ((LockInfo = FileLock->LockInformation) == NULL) {
  3785. //
  3786. // No lock information on this FileLock
  3787. //
  3788. DebugTrace(+1, Dbg, "FsRtlPrivateFastUnlockAll, No LockInfo\n", FileLock);
  3789. return STATUS_RANGE_NOT_LOCKED;
  3790. }
  3791. FileObject->LastLock = NULL;
  3792. LockQueue = &LockInfo->LockQueue;
  3793. //
  3794. // Grab the waiting lock queue spinlock to exclude anyone from messing
  3795. // with the queue while we're using it
  3796. //
  3797. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  3798. if (LockQueue->SharedLockTree == NULL && LockQueue->ExclusiveLockTree == NULL) {
  3799. //
  3800. // No locks on this FileLock
  3801. //
  3802. DebugTrace(+1, Dbg, "FsRtlPrivateFastUnlockAll, No LockTrees\n", FileLock);
  3803. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  3804. return STATUS_RANGE_NOT_LOCKED;
  3805. }
  3806. //
  3807. // Remove all matching locks in the shared lock tree
  3808. //
  3809. if (LockQueue->SharedLockTree != NULL) {
  3810. //
  3811. // Grab the lowest node in the tree
  3812. //
  3813. SplayLinks = LockQueue->SharedLockTree;
  3814. while (RtlLeftChild(SplayLinks) != NULL) {
  3815. SplayLinks = RtlLeftChild(SplayLinks);
  3816. }
  3817. //
  3818. // Walk all nodes in the tree
  3819. //
  3820. UnlockRoutine = FALSE;
  3821. for (;
  3822. SplayLinks;
  3823. SplayLinks = SuccessorLinks) {
  3824. Node = CONTAINING_RECORD(SplayLinks, LOCKTREE_NODE, Links );
  3825. //
  3826. // Save the next node because we may split this node apart in the process
  3827. // of deleting locks. It would be a waste of time to traverse those split
  3828. // nodes. The only case in which we will not have traversed the entire list
  3829. // before doing the split will be if there is an unlock routine attached
  3830. // to this FileLock in which case we will be restarting the entire scan
  3831. // anyway.
  3832. //
  3833. SuccessorLinks = RtlRealSuccessor(SplayLinks);
  3834. //
  3835. // Search down the current lock queue looking for a match on
  3836. // the file object and process id
  3837. //
  3838. SavepLink = NULL;
  3839. EndingDeletedByte.QuadPart = 0;
  3840. GlueOffset.QuadPart = 0;
  3841. pLink = &Node->Locks.Next;
  3842. while ((Link = *pLink) != NULL) {
  3843. ShLock = CONTAINING_RECORD( Link, SH_LOCK, Link );
  3844. DebugTrace(0, Dbg, "Top of ShLock Loop, Lock = %08lx\n", ShLock );
  3845. if ((ShLock->LockInfo.FileObject == FileObject) &&
  3846. (ShLock->LockInfo.ProcessId == ProcessId) &&
  3847. (!MatchKey || ShLock->LockInfo.Key == Key)) {
  3848. DebugTrace(0, Dbg, "Found one to unlock\n", 0);
  3849. //
  3850. // We have a match so now is the time to delete this lock.
  3851. // Save the neccesary information to do the split node check.
  3852. // Remove the lock from the list, then call the
  3853. // optional unlock routine, then delete the lock.
  3854. //
  3855. if (SavepLink == NULL) {
  3856. //
  3857. // Need to remember where the first lock was deleted
  3858. //
  3859. SavepLink = pLink;
  3860. }
  3861. if ((ULONGLONG)ShLock->LockInfo.EndingByte.QuadPart > (ULONGLONG)EndingDeletedByte.QuadPart) {
  3862. //
  3863. // Need to remember where the last offset affected by deleted locks is
  3864. //
  3865. EndingDeletedByte.QuadPart = ShLock->LockInfo.EndingByte.QuadPart;
  3866. }
  3867. if (*pLink == Node->Tail.Next) {
  3868. //
  3869. // Deleting the tail node of the list. Safe even if deleting the
  3870. // first node since this implies we're also deleting the last node
  3871. // in the node which means we'll delete the node ...
  3872. //
  3873. Node->Tail.Next = CONTAINING_RECORD( pLink, SINGLE_LIST_ENTRY, Next );
  3874. }
  3875. *pLink = Link->Next;
  3876. if (LockInfo->UnlockRoutine != NULL) {
  3877. //
  3878. // Signal a lock that needs to have a special unlock routine
  3879. // called on it. This is complex to deal with since we'll have
  3880. // to release the queue, call it, and reacquire - meaning we
  3881. // also have to restart. But we still need to reorder the node
  3882. // first ...
  3883. //
  3884. UnlockRoutine = TRUE;
  3885. break;
  3886. }
  3887. FsRtlFreeSharedLock( ShLock );
  3888. } else {
  3889. //
  3890. // Move to next lock
  3891. //
  3892. pLink = &Link->Next;
  3893. }
  3894. if (SavepLink == NULL && (ULONGLONG)ShLock->LockInfo.EndingByte.QuadPart > (ULONGLONG)GlueOffset.QuadPart) {
  3895. //
  3896. // Save the max offset until we have deleted our first node
  3897. //
  3898. GlueOffset.QuadPart = ShLock->LockInfo.EndingByte.QuadPart;
  3899. }
  3900. }
  3901. if (SavepLink) {
  3902. //
  3903. // Locks were actually deleted here, so we have to check the state of the node
  3904. //
  3905. if (Node->Locks.Next == NULL) {
  3906. //
  3907. // We have just deleted everything at this node
  3908. //
  3909. LockQueue->SharedLockTree = RtlDelete( SplayLinks );
  3910. FsRtlFreeLockTreeNode( Node );
  3911. } else {
  3912. //
  3913. // Now that we have deleted all matching locks in this node, we do the
  3914. // check on the node to split out any now non-overlapping locks. Conceptually,
  3915. // we have deleted just one big lock that starts at the starting byte of the
  3916. // first deleted lock and extends to the last byte of the last deleted lock.
  3917. //
  3918. FsRtlSplitLocks(Node, SavepLink, &EndingDeletedByte, &GlueOffset);
  3919. }
  3920. }
  3921. if (UnlockRoutine) {
  3922. //
  3923. // We dropped out of the node scan because we had a lock that needs extra
  3924. // processing during unlock. Do it.
  3925. //
  3926. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  3927. LockInfo->UnlockRoutine( Context, &ShLock->LockInfo );
  3928. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  3929. FsRtlFreeSharedLock( ShLock );
  3930. UnlockRoutine = FALSE;
  3931. //
  3932. // We have to restart the scan, because the list may have changed while
  3933. // we were in the unlock routine. Careful, because the tree may be empty.
  3934. //
  3935. if (SuccessorLinks = LockQueue->SharedLockTree) {
  3936. while (RtlLeftChild(SuccessorLinks) != NULL) {
  3937. SuccessorLinks = RtlLeftChild(SuccessorLinks);
  3938. }
  3939. }
  3940. }
  3941. }
  3942. }
  3943. //
  3944. // Remove all matching locks in the exclusive lock tree
  3945. //
  3946. if (LockQueue->ExclusiveLockTree != NULL) {
  3947. SplayLinks = LockQueue->ExclusiveLockTree;
  3948. while (RtlLeftChild(SplayLinks) != NULL) {
  3949. SplayLinks = RtlLeftChild(SplayLinks);
  3950. }
  3951. //
  3952. // Walk all nodes in the tree
  3953. //
  3954. UnlockRoutine = FALSE;
  3955. for (; SplayLinks;
  3956. SplayLinks = SuccessorLinks ) {
  3957. SuccessorLinks = RtlRealSuccessor( SplayLinks );
  3958. ExLock = CONTAINING_RECORD( SplayLinks, EX_LOCK, Links );
  3959. DebugTrace(0, Dbg, "Top of ExLock Loop, Lock = %08lx\n", ExLock );
  3960. if ((ExLock->LockInfo.FileObject == FileObject) &&
  3961. (ExLock->LockInfo.ProcessId == ProcessId) &&
  3962. (!MatchKey || ExLock->LockInfo.Key == Key)) {
  3963. LockQueue->ExclusiveLockTree = RtlDelete( &ExLock->Links );
  3964. if (LockInfo->UnlockRoutine != NULL) {
  3965. //
  3966. // We're dropping out of the node scan because we have a lock
  3967. // that needs extra processing during unlock. Do it.
  3968. //
  3969. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  3970. LockInfo->UnlockRoutine( Context, &ExLock->LockInfo );
  3971. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  3972. //
  3973. // We have to restart the scan, because the list may have changed while
  3974. // we were in the unlock routine. Careful, because the tree may be empty.
  3975. //
  3976. if (SuccessorLinks = LockQueue->ExclusiveLockTree) {
  3977. while (RtlLeftChild( SuccessorLinks ) != NULL) {
  3978. SuccessorLinks = RtlLeftChild( SuccessorLinks );
  3979. }
  3980. }
  3981. }
  3982. FsRtlFreeExclusiveLock( ExLock );
  3983. }
  3984. }
  3985. }
  3986. //
  3987. // Search down the waiting lock queue looking for a match on the
  3988. // file object and process id.
  3989. //
  3990. pLink = &LockQueue->WaitingLocks.Next;
  3991. while ((Link = *pLink) != NULL) {
  3992. PWAITING_LOCK WaitingLock;
  3993. PIRP WaitingIrp;
  3994. PIO_STACK_LOCATION WaitingIrpSp;
  3995. KIRQL CancelIrql;
  3996. WaitingLock = CONTAINING_RECORD( Link, WAITING_LOCK, Link );
  3997. DebugTrace(0, Dbg, "Top of Waiting Loop, WaitingLock = %08lx\n", WaitingLock);
  3998. //
  3999. // Get a copy of the necessary fields we'll need to use
  4000. //
  4001. WaitingIrp = WaitingLock->Irp;
  4002. WaitingIrpSp = IoGetCurrentIrpStackLocation( WaitingIrp );
  4003. if ((FileObject == WaitingIrpSp->FileObject) &&
  4004. (ProcessId == IoGetRequestorProcess( WaitingIrp )) &&
  4005. (!MatchKey || Key == WaitingIrpSp->Parameters.LockControl.Key)) {
  4006. DebugTrace(0, Dbg, "Found a waiting lock to abort\n", 0);
  4007. //
  4008. // We now void the cancel routine in the irp
  4009. //
  4010. IoAcquireCancelSpinLock( &WaitingIrp->CancelIrql );
  4011. IoSetCancelRoutine( WaitingIrp, NULL );
  4012. //
  4013. // If this IRP got itself cancelled, it is cancelled.
  4014. //
  4015. CancelIrql = WaitingIrp->CancelIrql;
  4016. if (WaitingIrp->Cancel) {
  4017. WaitingIrp = NULL;
  4018. }
  4019. IoReleaseCancelSpinLock( CancelIrql );
  4020. if (WaitingIrp) {
  4021. WaitingIrp->IoStatus.Information = 0;
  4022. //
  4023. // We have a match and the IRP, so now is the time to delete
  4024. // this waiter. But we must not mess up our link iteration
  4025. // variable. We do this by simply starting the iteration over
  4026. // again, after we delete ourselves. We also will deallocate
  4027. // the lock after we delete it.
  4028. //
  4029. *pLink = Link->Next;
  4030. if (Link == LockQueue->WaitingLocksTail.Next) {
  4031. LockQueue->WaitingLocksTail.Next = (PSINGLE_LIST_ENTRY) pLink;
  4032. }
  4033. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  4034. //
  4035. // And complete this lock request Irp
  4036. //
  4037. FsRtlCompleteLockIrp( LockInfo,
  4038. WaitingLock->Context,
  4039. WaitingIrp,
  4040. STATUS_RANGE_NOT_LOCKED,
  4041. &NewStatus,
  4042. NULL );
  4043. //
  4044. // Reaqcuire lock queue spinlock and start over
  4045. //
  4046. FsRtlAcquireLockQueue( LockQueue, &OldIrql );
  4047. //
  4048. // Start over
  4049. //
  4050. pLink = &LockQueue->WaitingLocks.Next;
  4051. //
  4052. // Put memory onto free list
  4053. //
  4054. FsRtlFreeWaitingLock( WaitingLock );
  4055. continue;
  4056. }
  4057. }
  4058. //
  4059. // Move to next lock
  4060. //
  4061. pLink = &Link->Next;
  4062. }
  4063. //
  4064. // At this point we've gone through unlocking everything. So
  4065. // now try and release any waiting locks.
  4066. //
  4067. FsRtlPrivateCheckWaitingLocks( LockInfo, LockQueue, OldIrql );
  4068. //
  4069. // We deleted a (possible) bunch of locks, go repair the lowest lock offset
  4070. //
  4071. FsRtlPrivateResetLowestLockOffset( LockInfo );
  4072. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  4073. //
  4074. // and return to our caller
  4075. //
  4076. DebugTrace(-1, Dbg, "FsRtlFastUnlockAll -> VOID\n", 0);
  4077. return STATUS_SUCCESS;
  4078. }
  4079. VOID
  4080. FsRtlPrivateCancelFileLockIrp (
  4081. IN PDEVICE_OBJECT DeviceObject,
  4082. IN PIRP Irp
  4083. )
  4084. /*++
  4085. Routine Description:
  4086. This routine implements the cancel function for an irp saved in a
  4087. waiting lock queue
  4088. Arguments:
  4089. DeviceObject - Ignored
  4090. Irp - Supplies the Irp being cancelled. A pointer to the FileLock
  4091. structure for the lock is stored in the information field of the
  4092. irp's iosb.
  4093. Return Value:
  4094. none.
  4095. --*/
  4096. {
  4097. PSINGLE_LIST_ENTRY *pLink, Link;
  4098. PLOCK_INFO LockInfo;
  4099. PLOCK_QUEUE LockQueue;
  4100. KIRQL OldIrql;
  4101. NTSTATUS NewStatus;
  4102. BOOLEAN CollideCheck = FALSE;
  4103. UNREFERENCED_PARAMETER( DeviceObject );
  4104. //
  4105. // The information field is used to store a pointer to the file lock
  4106. // containing the irp
  4107. //
  4108. LockInfo = (PLOCK_INFO) (Irp->IoStatus.Information);
  4109. //
  4110. // Iterate through the lock queue.
  4111. //
  4112. LockQueue = &LockInfo->LockQueue;
  4113. //
  4114. // Release the cancel spinlock and lock cancel collide if this is initiated by Io.
  4115. //
  4116. // We already have the lock queue if this is the race fixup from ourselves,
  4117. // and the cancel Irql is in the Irp.
  4118. //
  4119. if (DeviceObject) {
  4120. IoReleaseCancelSpinLock( Irp->CancelIrql );
  4121. FsRtlAcquireCancelCollide( &OldIrql );
  4122. //
  4123. // Indicate we will check the collide list first, as the lockqueue itself
  4124. // may be deallocated in a race with lock teardown.
  4125. //
  4126. CollideCheck = TRUE;
  4127. pLink = &FsRtlFileLockCancelCollideList.Next;
  4128. } else {
  4129. OldIrql = Irp->CancelIrql;
  4130. //
  4131. // We will iterate only the locks off of this specific queue.
  4132. //
  4133. pLink = &LockQueue->WaitingLocks.Next;
  4134. }
  4135. while (TRUE) {
  4136. //
  4137. // Iterate through the waiting locks looking for the canceled one.
  4138. //
  4139. while ((Link = *pLink) != NULL) {
  4140. PWAITING_LOCK WaitingLock;
  4141. //
  4142. // Get a pointer to the waiting lock record
  4143. //
  4144. WaitingLock = CONTAINING_RECORD( Link, WAITING_LOCK, Link );
  4145. DebugTrace(0, Dbg, "FsRtlPrivateCancelFileLockIrp, Loop top, WaitingLock = %08lx\n", WaitingLock);
  4146. if( WaitingLock->Irp != Irp ) {
  4147. pLink = &Link->Next;
  4148. continue;
  4149. }
  4150. //
  4151. // We've found it -- remove it from the list
  4152. //
  4153. *pLink = Link->Next;
  4154. if (!CollideCheck && Link == LockQueue->WaitingLocksTail.Next) {
  4155. LockQueue->WaitingLocksTail.Next = (PSINGLE_LIST_ENTRY) pLink;
  4156. }
  4157. Irp->IoStatus.Information = 0;
  4158. //
  4159. // Release the right lock and complete this waiter
  4160. //
  4161. if (CollideCheck) {
  4162. FsRtlReleaseCancelCollide( OldIrql );
  4163. } else {
  4164. FsRtlReleaseLockQueue( LockQueue, OldIrql );
  4165. }
  4166. //
  4167. // Complete this waiter. Note we pick the completion routine out
  4168. // of the waiting lock structure so we can surf over the collided
  4169. // cancel case.
  4170. //
  4171. FsRtlCompleteLockIrp( WaitingLock,
  4172. WaitingLock->Context,
  4173. Irp,
  4174. STATUS_CANCELLED,
  4175. &NewStatus,
  4176. NULL );
  4177. //
  4178. // Free up pool
  4179. //
  4180. FsRtlFreeWaitingLock( WaitingLock );
  4181. //
  4182. // Our job is done!
  4183. //
  4184. return;
  4185. }
  4186. //
  4187. // Flip over to the lock queue if we didn't find it on the collided list.
  4188. //
  4189. if (CollideCheck) {
  4190. CollideCheck = FALSE;
  4191. FsRtlAcquireLockQueueAtDpc( LockQueue );
  4192. FsRtlReleaseCancelCollideFromDpc( OldIrql );
  4193. pLink = &LockQueue->WaitingLocks.Next;
  4194. continue;
  4195. }
  4196. break;
  4197. }
  4198. //
  4199. // Release lock queue. This must actually not happen or we will have the
  4200. // potential to have had the IRP we were looking for come back unaware
  4201. // we wanted to cancel it.
  4202. //
  4203. ASSERT( FALSE );
  4204. FsRtlReleaseLockQueue(LockQueue, OldIrql);
  4205. return;
  4206. }