[NTOS:IO] Implement IopAcquireFileObjectLock and use it to fix IopLockFileObject
[reactos.git] / ntoskrnl / ob / obsdcach.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obsdcach.c
5 * PURPOSE: Security Descriptor Caching
6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 #define SD_CACHE_ENTRIES 0x100
18 OB_SD_CACHE_LIST ObsSecurityDescriptorCache[SD_CACHE_ENTRIES];
19
20 /* PRIVATE FUNCTIONS **********************************************************/
21
22 FORCEINLINE
23 VOID
24 ObpSdAcquireLock(IN POB_SD_CACHE_LIST CacheEntry)
25 {
26 /* Acquire the lock */
27 KeEnterCriticalRegion();
28 ExAcquirePushLockExclusive(&CacheEntry->PushLock);
29 }
30
31 FORCEINLINE
32 VOID
33 ObpSdReleaseLock(IN POB_SD_CACHE_LIST CacheEntry)
34 {
35 /* Release the lock */
36 ExReleasePushLockExclusive(&CacheEntry->PushLock);
37 KeLeaveCriticalRegion();
38 }
39
40 FORCEINLINE
41 VOID
42 ObpSdAcquireLockShared(IN POB_SD_CACHE_LIST CacheEntry)
43 {
44 /* Acquire the lock */
45 KeEnterCriticalRegion();
46 ExAcquirePushLockShared(&CacheEntry->PushLock);
47 }
48
49 FORCEINLINE
50 VOID
51 ObpSdReleaseLockShared(IN POB_SD_CACHE_LIST CacheEntry)
52 {
53 /* Release the lock */
54 ExReleasePushLock(&CacheEntry->PushLock);
55 KeLeaveCriticalRegion();
56 }
57
58 NTSTATUS
59 NTAPI
60 INIT_FUNCTION
61 ObpInitSdCache(VOID)
62 {
63 ULONG i;
64
65 /* Loop each cache entry */
66 for (i = 0; i < SD_CACHE_ENTRIES; i++)
67 {
68 /* Initialize the lock and the list */
69 InitializeListHead(&ObsSecurityDescriptorCache[i].Head);
70 ExInitializePushLock(&ObsSecurityDescriptorCache[i].PushLock);
71 }
72
73 /* Return success */
74 return STATUS_SUCCESS;
75 }
76
77 ULONG
78 NTAPI
79 ObpHash(IN PVOID Buffer,
80 IN ULONG Length)
81 {
82 PULONG p, pp;
83 PUCHAR pb, ppb;
84 ULONG Hash = 0;
85
86 /* Setup aligned and byte buffers */
87 p = Buffer;
88 pb = (PUCHAR)p;
89 ppb = (PUCHAR)((ULONG_PTR)Buffer + Length);
90 pp = (PULONG)ALIGN_DOWN(pb + Length, ULONG);
91
92 /* Loop aligned data */
93 while (p < pp)
94 {
95 /* XOR-rotate */
96 Hash ^= *p++;
97 Hash = _rotl(Hash, 3);
98 }
99
100 /* Loop non-aligned data */
101 pb = (PUCHAR)p;
102 while (pb < ppb)
103 {
104 /* XOR-rotate */
105 Hash ^= *pb++;
106 Hash = _rotl(Hash, 3);
107 }
108
109 /* Return the hash */
110 return Hash;
111 }
112
113 ULONG
114 NTAPI
115 ObpHashSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
116 IN ULONG Length)
117 {
118 /* Just hash the entire SD */
119 return ObpHash(SecurityDescriptor, Length);
120 }
121
122 PSECURITY_DESCRIPTOR_HEADER
123 NTAPI
124 ObpCreateCacheEntry(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
125 IN ULONG Length,
126 IN ULONG FullHash,
127 IN ULONG RefCount)
128 {
129 ULONG CacheSize;
130 PSECURITY_DESCRIPTOR_HEADER SdHeader;
131 ASSERT(Length == RtlLengthSecurityDescriptor(SecurityDescriptor));
132
133 /* Calculate the memory we'll need to allocate and allocate it */
134 CacheSize = Length + (sizeof(SECURITY_DESCRIPTOR_HEADER) - sizeof(QUAD));
135 SdHeader = ExAllocatePoolWithTag(PagedPool, CacheSize, TAG_OB_SD_CACHE);
136 if (!SdHeader) return NULL;
137
138 /* Setup the header */
139 SdHeader->RefCount = RefCount;
140 SdHeader->FullHash = FullHash;
141
142 /* Copy the descriptor */
143 RtlCopyMemory(&SdHeader->SecurityDescriptor, SecurityDescriptor, Length);
144
145 /* Return it */
146 return SdHeader;
147 }
148
149 BOOLEAN
150 NTAPI
151 ObpCompareSecurityDescriptors(IN PSECURITY_DESCRIPTOR Sd1,
152 IN ULONG Length1,
153 IN PSECURITY_DESCRIPTOR Sd2)
154 {
155 ULONG Length2;
156 ASSERT(Length1 == RtlLengthSecurityDescriptor(Sd1));
157
158 /* Get the length of the second SD */
159 Length2 = RtlLengthSecurityDescriptor(Sd2);
160
161 /* Compare lengths */
162 if (Length1 != Length2) return FALSE;
163
164 /* Compare contents */
165 return RtlEqualMemory(Sd1, Sd2, Length1);
166 }
167
168 PVOID
169 NTAPI
170 ObpDestroySecurityDescriptorHeader(IN PSECURITY_DESCRIPTOR_HEADER SdHeader)
171 {
172 ASSERT(SdHeader->RefCount == 0);
173
174 /* Just unlink the SD and return it back to the caller */
175 RemoveEntryList(&SdHeader->Link);
176 return SdHeader;
177 }
178
179 PSECURITY_DESCRIPTOR
180 NTAPI
181 ObpReferenceSecurityDescriptor(IN POBJECT_HEADER ObjectHeader)
182 {
183 PSECURITY_DESCRIPTOR SecurityDescriptor;
184 PSECURITY_DESCRIPTOR_HEADER SdHeader;
185 PEX_FAST_REF FastRef;
186 EX_FAST_REF OldValue;
187 ULONG_PTR Count;
188
189 /* Acquire a reference to the security descriptor */
190 FastRef = (PEX_FAST_REF)&ObjectHeader->SecurityDescriptor;
191 OldValue = ExAcquireFastReference(FastRef);
192
193 /* Get the descriptor and reference count */
194 SecurityDescriptor = ExGetObjectFastReference(OldValue);
195 Count = ExGetCountFastReference(OldValue);
196
197 /* Check if there's no descriptor or if there's still cached references */
198 if ((Count >= 1) || !(SecurityDescriptor))
199 {
200 /* Check if this is the last reference */
201 if (Count == 1)
202 {
203 /* Add the extra references that we'll take */
204 SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
205 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, MAX_FAST_REFS);
206
207 /* Now insert them */
208 if (!ExInsertFastReference(FastRef, SecurityDescriptor))
209 {
210 /* Undo the references since we failed */
211 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount,
212 -MAX_FAST_REFS);
213 }
214 }
215
216 /* Return the SD */
217 return SecurityDescriptor;
218 }
219
220 /* Lock the object */
221 ObpAcquireObjectLockShared(ObjectHeader);
222
223 /* Get the object header */
224 SecurityDescriptor = ExGetObjectFastReference(*FastRef);
225 SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
226
227 /* Do the reference */
228 InterlockedIncrement((PLONG)&SdHeader->RefCount);
229
230 /* Release the lock and return */
231 ObpReleaseObjectLock(ObjectHeader);
232 return SecurityDescriptor;
233 }
234
235 /* PUBLIC FUNCTIONS ***********************************************************/
236
237 /*++
238 * @name ObReferenceSecurityDescriptor
239 * @implemented NT5.2
240 *
241 * The ObReferenceSecurityDescriptor routine <FILLMEIN>
242 *
243 * @param SecurityDescriptor
244 * <FILLMEIN>
245 *
246 * @param Count
247 * <FILLMEIN>
248 *
249 * @return STATUS_SUCCESS or appropriate error value.
250 *
251 * @remarks None.
252 *
253 *--*/
254 VOID
255 NTAPI
256 ObReferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
257 IN ULONG Count)
258 {
259 PSECURITY_DESCRIPTOR_HEADER SdHeader;
260
261 /* Get the header */
262 SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
263
264 /* Do the references */
265 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, Count);
266 }
267
268 /*++
269 * @name ObDereferenceSecurityDescriptor
270 * @implemented NT5.2
271 *
272 * The ObDereferenceSecurityDescriptor routine <FILLMEIN>
273 *
274 * @param SecurityDescriptor
275 * <FILLMEIN>
276 *
277 * @param Count
278 * <FILLMEIN>
279 *
280 * @return STATUS_SUCCESS or appropriate error value.
281 *
282 * @remarks None.
283 *
284 *--*/
285 VOID
286 NTAPI
287 ObDereferenceSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
288 IN ULONG Count)
289 {
290 PSECURITY_DESCRIPTOR_HEADER SdHeader;
291 LONG OldValue, NewValue;
292 ULONG Index;
293 POB_SD_CACHE_LIST CacheEntry;
294
295 /* Get the header */
296 SdHeader = ObpGetHeaderForSd(SecurityDescriptor);
297
298 /* Get the current reference count */
299 OldValue = SdHeader->RefCount;
300
301 /* Check if the caller is destroying this SD -- we need the lock for that */
302 while (OldValue != Count)
303 {
304 /* He isn't, we can just try to derefeference atomically */
305 NewValue = InterlockedCompareExchange((PLONG)&SdHeader->RefCount,
306 OldValue - Count,
307 OldValue);
308 if (NewValue == OldValue) return;
309
310 /* Try again */
311 OldValue = NewValue;
312 }
313
314 /* At this point, we need the lock, so choose an entry */
315 Index = SdHeader->FullHash % SD_CACHE_ENTRIES;
316 CacheEntry = &ObsSecurityDescriptorCache[Index];
317
318 /* Acquire the lock for it */
319 ObpSdAcquireLock(CacheEntry);
320 ASSERT(SdHeader->RefCount != 0);
321
322 /* Now do the dereference */
323 if (InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, -(LONG)Count) == Count)
324 {
325 /* We're down to zero -- destroy the header */
326 SdHeader = ObpDestroySecurityDescriptorHeader(SdHeader);
327
328 /* Release the lock */
329 ObpSdReleaseLock(CacheEntry);
330
331 /* Free the header */
332 ExFreePool(SdHeader);
333 }
334 else
335 {
336 /* Just release the lock */
337 ObpSdReleaseLock(CacheEntry);
338 }
339
340 }
341
342 /*++
343 * @name ObLogSecurityDescriptor
344 * @implemented NT5.2
345 *
346 * The ObLogSecurityDescriptor routine <FILLMEIN>
347 *
348 * @param InputSecurityDescriptor
349 * <FILLMEIN>
350 *
351 * @param OutputSecurityDescriptor
352 * <FILLMEIN>
353 *
354 * @param RefBias
355 * <FILLMEIN>
356 *
357 * @return STATUS_SUCCESS or appropriate error value.
358 *
359 * @remarks None.
360 *
361 *--*/
362 NTSTATUS
363 NTAPI
364 ObLogSecurityDescriptor(IN PSECURITY_DESCRIPTOR InputSecurityDescriptor,
365 OUT PSECURITY_DESCRIPTOR *OutputSecurityDescriptor,
366 IN ULONG RefBias)
367 {
368 PSECURITY_DESCRIPTOR_HEADER SdHeader = NULL, NewHeader = NULL;
369 ULONG Length, Hash, Index;
370 POB_SD_CACHE_LIST CacheEntry;
371 BOOLEAN Result;
372 PLIST_ENTRY NextEntry;
373
374 /* Get the length */
375 Length = RtlLengthSecurityDescriptor(InputSecurityDescriptor);
376
377 /* Get the hash */
378 Hash = ObpHashSecurityDescriptor(InputSecurityDescriptor, Length);
379
380 /* Now select the appropriate cache entry */
381 Index = Hash % SD_CACHE_ENTRIES;
382 CacheEntry = &ObsSecurityDescriptorCache[Index];
383
384 /* Lock it shared */
385 ObpSdAcquireLockShared(CacheEntry);
386
387 /* Start our search */
388 while (TRUE)
389 {
390 /* Reset result found */
391 Result = FALSE;
392
393 /* Loop the hash list */
394 NextEntry = CacheEntry->Head.Flink;
395 while (NextEntry != &CacheEntry->Head)
396 {
397 /* Get the header */
398 SdHeader = ObpGetHeaderForEntry(NextEntry);
399
400 /* Our hashes are ordered, so quickly check if we should stop now */
401 if (SdHeader->FullHash > Hash) break;
402
403 /* We survived the quick hash check, now check for equalness */
404 if (SdHeader->FullHash == Hash)
405 {
406 /* Hashes match, now compare descriptors */
407 Result = ObpCompareSecurityDescriptors(InputSecurityDescriptor,
408 Length,
409 &SdHeader->SecurityDescriptor);
410 if (Result) break;
411 }
412
413 /* Go to the next entry */
414 NextEntry = NextEntry->Flink;
415 }
416
417 /* Check if we found anything */
418 if (Result)
419 {
420 /* Increment its reference count */
421 InterlockedExchangeAdd((PLONG)&SdHeader->RefCount, RefBias);
422
423 /* Release the lock */
424 ObpSdReleaseLockShared(CacheEntry);
425
426 /* Return the descriptor */
427 *OutputSecurityDescriptor = &SdHeader->SecurityDescriptor;
428
429 /* Free anything that we may have had to create */
430 if (NewHeader) ExFreePoolWithTag(NewHeader, TAG_OB_SD_CACHE);
431 return STATUS_SUCCESS;
432 }
433
434 /* Check if we got here, and didn't create a descriptor yet */
435 if (!NewHeader)
436 {
437 /* Release the lock */
438 ObpSdReleaseLockShared(CacheEntry);
439
440 /* This should be our first time in the loop, create it */
441 NewHeader = ObpCreateCacheEntry(InputSecurityDescriptor,
442 Length,
443 Hash,
444 RefBias);
445 if (!NewHeader) return STATUS_INSUFFICIENT_RESOURCES;
446
447 /* Now acquire the exclusive lock and we should hit the right path */
448 ObpSdAcquireLock(CacheEntry);
449 }
450 else
451 {
452 /* We have inserted the SD, we're fine now */
453 break;
454 }
455 }
456
457 /* Okay, now let's do the insert, we should have the exclusive lock */
458 InsertTailList(NextEntry, &NewHeader->Link);
459
460 /* Release the lock */
461 ObpSdReleaseLock(CacheEntry);
462
463 /* Return the SD*/
464 *OutputSecurityDescriptor = &NewHeader->SecurityDescriptor;
465 return STATUS_SUCCESS;
466 }
467
468 /* EOF */