4d42de3dff3d26aa4fe715ba4a6d25fe0b6b4d75
[openafs.git] / src / WINNT / afsrdr / kernel / fs / AFSGeneric.cpp
1 /*
2  * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
3  * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistributions of source code must retain the above copyright notice,
11  *   this list of conditions and the following disclaimer.
12  * - Redistributions in binary form must reproduce the above copyright
13  *   notice,
14  *   this list of conditions and the following disclaimer in the
15  *   documentation
16  *   and/or other materials provided with the distribution.
17  * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
18  *   nor the names of their contributors may be used to endorse or promote
19  *   products derived from this software without specific prior written
20  *   permission from Kernel Drivers, LLC and Your File System, Inc.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
26  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 //
36 // File: AFSGeneric.cpp
37 //
38
39 #include "AFSCommon.h"
40
41 //
42 // Function: AFSExceptionFilter
43 //
44 // Description:
45 //
46 //      This function is the exception handler
47 //
48 // Return:
49 //
50 //      A status is returned for the function
51 //
52
53 ULONG
54 AFSExceptionFilter( IN CHAR *FunctionString,
55                     IN ULONG Code,
56                     IN PEXCEPTION_POINTERS ExceptPtrs)
57 {
58     UNREFERENCED_PARAMETER(Code);
59
60     PEXCEPTION_RECORD ExceptRec;
61     PCONTEXT Context;
62
63     __try
64     {
65
66         ExceptRec = ExceptPtrs->ExceptionRecord;
67
68         Context = ExceptPtrs->ContextRecord;
69
70         AFSDbgTrace(( 0,
71                       0,
72                       "AFSExceptionFilter (Framework) - EXR %p CXR %p Function %s Code %08lX Address %p Routine %p\n",
73                       ExceptRec,
74                       Context,
75                       FunctionString,
76                       ExceptRec->ExceptionCode,
77                       ExceptRec->ExceptionAddress,
78                       (void *)AFSExceptionFilter));
79
80         DbgPrint("**** Exception Caught in AFS Redirector ****\n");
81
82         DbgPrint("\n\nPerform the following WnDbg Cmds:\n");
83         DbgPrint("\n\t.exr %p ;  .cxr %p\n\n", ExceptRec, Context);
84
85         DbgPrint("**** Exception Complete from AFS Redirector ****\n");
86
87         if( BooleanFlagOn( AFSDebugFlags, AFS_DBG_BUGCHECK_EXCEPTION))
88         {
89
90             KeBugCheck( (ULONG)-2);
91         }
92         else
93         {
94
95             AFSBreakPoint();
96         }
97     }
98     __except( EXCEPTION_EXECUTE_HANDLER)
99     {
100
101         NOTHING;
102     }
103
104     return EXCEPTION_EXECUTE_HANDLER;
105 }
106
107 //
108 // Function: AFSAcquireExcl()
109 //
110 // Purpose: Called to acquire a resource exclusive with optional wait
111 //
112 // Parameters:
113 //                PERESOURCE Resource - Resource to acquire
114 //                BOOLEAN Wait - Whether to block
115 //
116 // Return:
117 //                BOOLEAN - Whether the mask was acquired
118 //
119
120 BOOLEAN
121 AFSAcquireExcl( IN PERESOURCE Resource,
122                 IN BOOLEAN wait)
123 {
124
125     BOOLEAN bStatus = FALSE;
126
127     //
128     // Normal kernel APCs must be disabled before calling
129     // ExAcquireResourceExclusiveLite. Otherwise a bugcheck occurs.
130     //
131
132     KeEnterCriticalRegion();
133
134     bStatus = ExAcquireResourceExclusiveLite( Resource,
135                                               wait);
136
137     if( !bStatus)
138     {
139
140         KeLeaveCriticalRegion();
141     }
142
143     return bStatus;
144 }
145
146 BOOLEAN
147 AFSAcquireSharedStarveExclusive( IN PERESOURCE Resource,
148                                  IN BOOLEAN Wait)
149 {
150
151     BOOLEAN bStatus = FALSE;
152
153     KeEnterCriticalRegion();
154
155     bStatus = ExAcquireSharedStarveExclusive( Resource,
156                                               Wait);
157
158     if( !bStatus)
159     {
160
161         KeLeaveCriticalRegion();
162     }
163
164     return bStatus;
165 }
166
167 //
168 // Function: AFSAcquireShared()
169 //
170 // Purpose: Called to acquire a resource shared with optional wait
171 //
172 // Parameters:
173 //                PERESOURCE Resource - Resource to acquire
174 //                BOOLEAN Wait - Whether to block
175 //
176 // Return:
177 //                BOOLEAN - Whether the mask was acquired
178 //
179
180 BOOLEAN
181 AFSAcquireShared( IN PERESOURCE Resource,
182                   IN BOOLEAN wait)
183 {
184
185     BOOLEAN bStatus = FALSE;
186
187     KeEnterCriticalRegion();
188
189     bStatus = ExAcquireResourceSharedLite( Resource,
190                                            wait);
191
192     if( !bStatus)
193     {
194
195         KeLeaveCriticalRegion();
196     }
197
198     return bStatus;
199 }
200
201 //
202 // Function: AFSReleaseResource()
203 //
204 // Purpose: Called to release a resource
205 //
206 // Parameters:
207 //                PERESOURCE Resource - Resource to release
208 //
209 // Return:
210 //                None
211 //
212
213 void
214 AFSReleaseResource( IN PERESOURCE Resource)
215 {
216
217     if( Resource != &AFSDbgLogLock)
218     {
219
220         AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
221                       AFS_TRACE_LEVEL_VERBOSE,
222                       "AFSReleaseResource Releasing lock %p Thread %08lX\n",
223                       Resource,
224                       PsGetCurrentThread()));
225     }
226
227     ExReleaseResourceLite( Resource);
228
229     KeLeaveCriticalRegion();
230
231     return;
232 }
233
234 void
235 AFSConvertToShared( IN PERESOURCE Resource)
236 {
237
238     AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
239                   AFS_TRACE_LEVEL_VERBOSE,
240                   "AFSConvertToShared Converting lock %p Thread %08lX\n",
241                   Resource,
242                   PsGetCurrentThread()));
243
244     ExConvertExclusiveToSharedLite( Resource);
245
246     return;
247 }
248
249 //
250 // Function: AFSCompleteRequest
251 //
252 // Description:
253 //
254 //      This function completes irps
255 //
256 // Return:
257 //
258 //      A status is returned for the function
259 //
260
261 void
262 AFSCompleteRequest( IN PIRP Irp,
263                     IN ULONG Status)
264 {
265
266     Irp->IoStatus.Status = Status;
267
268     IoCompleteRequest( Irp,
269                        IO_NO_INCREMENT);
270
271     return;
272 }
273
274 NTSTATUS
275 AFSReadRegistry( IN PUNICODE_STRING RegistryPath)
276 {
277
278     NTSTATUS ntStatus        = STATUS_SUCCESS;
279     ULONG Default            = 0;
280     UNICODE_STRING paramPath;
281     ULONG Value                = 0;
282     RTL_QUERY_REGISTRY_TABLE paramTable[2];
283     UNICODE_STRING defaultUnicodeName;
284     WCHAR SubKeyString[]    = L"\\Parameters";
285
286     //
287     // Setup the paramPath buffer.
288     //
289
290     paramPath.MaximumLength = RegistryPath->Length + sizeof( SubKeyString);
291     paramPath.Buffer = (PWSTR)AFSExAllocatePoolWithTag( PagedPool,
292                                                         paramPath.MaximumLength,
293                                                         AFS_GENERIC_MEMORY_15_TAG);
294
295     RtlInitUnicodeString( &defaultUnicodeName,
296                           L"NO NAME");
297
298     //
299     // If it exists, setup the path.
300     //
301
302     if( paramPath.Buffer != NULL)
303     {
304
305         //
306         // Move in the paths
307         //
308
309         RtlCopyMemory( &paramPath.Buffer[ 0],
310                        &RegistryPath->Buffer[ 0],
311                        RegistryPath->Length);
312
313         RtlCopyMemory( &paramPath.Buffer[ RegistryPath->Length / 2],
314                        SubKeyString,
315                        sizeof( SubKeyString));
316
317         paramPath.Length = paramPath.MaximumLength;
318
319         RtlZeroMemory( paramTable,
320                        sizeof( paramTable));
321
322         Value = 0;
323
324         //
325         // Setup the table to query the registry for the needed value
326         //
327
328         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
329         paramTable[0].Name = AFS_REG_DEBUG_FLAGS;
330         paramTable[0].EntryContext = &Value;
331
332         paramTable[0].DefaultType = REG_DWORD;
333         paramTable[0].DefaultData = &Default;
334         paramTable[0].DefaultLength = sizeof (ULONG) ;
335
336         //
337         // Query the registry
338         //
339
340         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
341                                            paramPath.Buffer,
342                                            paramTable,
343                                            NULL,
344                                            NULL);
345
346         if( NT_SUCCESS( ntStatus))
347         {
348
349             AFSDebugFlags = Value;
350         }
351
352         RtlZeroMemory( paramTable,
353                        sizeof( paramTable));
354
355         Value = 0;
356
357         //
358         // Setup the table to query the registry for the needed value
359         //
360
361         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
362         paramTable[0].Name = AFS_REG_TRACE_SUBSYSTEM;
363         paramTable[0].EntryContext = &Value;
364
365         paramTable[0].DefaultType = REG_DWORD;
366         paramTable[0].DefaultData = &Default;
367         paramTable[0].DefaultLength = sizeof (ULONG) ;
368
369         //
370         // Query the registry
371         //
372
373         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
374                                            paramPath.Buffer,
375                                            paramTable,
376                                            NULL,
377                                            NULL);
378
379         if( NT_SUCCESS( ntStatus))
380         {
381
382             AFSTraceComponent = Value;
383         }
384
385         RtlZeroMemory( paramTable,
386                        sizeof( paramTable));
387
388         Value = 0;
389
390         //
391         // Setup the table to query the registry for the needed value
392         //
393
394         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
395         paramTable[0].Name = AFS_REG_TRACE_BUFFER_LENGTH;
396         paramTable[0].EntryContext = &Value;
397
398         paramTable[0].DefaultType = REG_DWORD;
399         paramTable[0].DefaultData = &Default;
400         paramTable[0].DefaultLength = sizeof (ULONG);
401
402         //
403         // Query the registry
404         //
405
406         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
407                                            paramPath.Buffer,
408                                            paramTable,
409                                            NULL,
410                                            NULL);
411
412         if( NT_SUCCESS( ntStatus) &&
413             Value > 0)
414         {
415
416             AFSDbgBufferLength = Value;
417
418             //
419             // Let's limit things a bit ...
420             //
421
422             if( AFSDbgBufferLength > 10240)
423             {
424
425                 AFSDbgBufferLength = 1024;
426             }
427         }
428         else
429         {
430
431             AFSDbgBufferLength = 0;
432         }
433
434         //
435         // Make it bytes
436         //
437
438         AFSDbgBufferLength *= 1024;
439
440         //
441         // Now get ready to set up for MaxServerDirty
442         //
443
444         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
445         paramTable[0].Name = AFS_REG_MAX_DIRTY;
446         paramTable[0].EntryContext = &Value;
447
448         paramTable[0].DefaultType = REG_DWORD;
449         paramTable[0].DefaultData = &Default;
450         paramTable[0].DefaultLength = sizeof (ULONG) ;
451
452         //
453         // Query the registry
454         //
455
456         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
457                                            paramPath.Buffer,
458                                            paramTable,
459                                            NULL,
460                                            NULL);
461
462         if( NT_SUCCESS( ntStatus))
463         {
464
465             AFSMaxDirtyFile = Value;
466         }
467
468         RtlZeroMemory( paramTable,
469                        sizeof( paramTable));
470
471         Value = 0;
472
473         //
474         // Setup the table to query the registry for the needed value
475         //
476
477         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
478         paramTable[0].Name = AFS_REG_TRACE_LEVEL;
479         paramTable[0].EntryContext = &Value;
480
481         paramTable[0].DefaultType = REG_DWORD;
482         paramTable[0].DefaultData = &Default;
483         paramTable[0].DefaultLength = sizeof (ULONG) ;
484
485         //
486         // Query the registry
487         //
488
489         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
490                                            paramPath.Buffer,
491                                            paramTable,
492                                            NULL,
493                                            NULL);
494
495         if( NT_SUCCESS( ntStatus))
496         {
497
498             AFSTraceLevel = Value;
499         }
500
501         //
502         // MaxIO
503         //
504
505         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
506         paramTable[0].Name = AFS_REG_MAX_IO;
507         paramTable[0].EntryContext = &Value;
508
509         paramTable[0].DefaultType = REG_DWORD;
510         paramTable[0].DefaultData = &Default;
511         paramTable[0].DefaultLength = sizeof (ULONG) ;
512
513         //
514         // Query the registry
515         //
516
517         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
518                                            paramPath.Buffer,
519                                            paramTable,
520                                            NULL,
521                                            NULL);
522
523         if( NT_SUCCESS( ntStatus))
524         {
525
526             AFSMaxDirectIo = Value;
527         }
528
529         //
530         // Now set up for ShutdownStatus query
531         //
532
533         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
534         paramTable[0].Name = AFS_REG_SHUTDOWN_STATUS;
535         paramTable[0].EntryContext = &Value;
536
537         paramTable[0].DefaultType = REG_DWORD;
538         paramTable[0].DefaultData = &Default;
539         paramTable[0].DefaultLength = sizeof (ULONG) ;
540
541         //
542         // Query the registry
543         //
544
545         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
546                                            paramPath.Buffer,
547                                            paramTable,
548                                            NULL,
549                                            NULL);
550
551         if( !NT_SUCCESS( ntStatus) ||
552             Value != (ULONG)-1)
553         {
554
555             SetFlag( AFSDebugFlags, AFS_DBG_CLEAN_SHUTDOWN);
556         }
557
558         //
559         // Now set up for RequireCleanShutdown query
560         //
561
562         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
563         paramTable[0].Name = AFS_REG_REQUIRE_CLEAN_SHUTDOWN;
564         paramTable[0].EntryContext = &Value;
565
566         paramTable[0].DefaultType = REG_DWORD;
567         paramTable[0].DefaultData = &Default;
568         paramTable[0].DefaultLength = sizeof (ULONG) ;
569
570         //
571         // Query the registry
572         //
573
574         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
575                                            paramPath.Buffer,
576                                            paramTable,
577                                            NULL,
578                                            NULL);
579
580         if( !NT_SUCCESS( ntStatus) ||
581             Value != 0L)
582         {
583
584             SetFlag( AFSDebugFlags, AFS_DBG_REQUIRE_CLEAN_SHUTDOWN);
585         }
586
587         //
588         // Free up the buffer
589         //
590
591         ExFreePool( paramPath.Buffer);
592
593         ntStatus = STATUS_SUCCESS;
594     }
595     else
596     {
597         ntStatus = STATUS_INSUFFICIENT_RESOURCES;
598     }
599
600     return ntStatus;
601 }
602
603 NTSTATUS
604 AFSUpdateRegistryParameter( IN PUNICODE_STRING ValueName,
605                             IN ULONG ValueType,
606                             IN void *ValueData,
607                             IN ULONG ValueDataLength)
608 {
609
610     NTSTATUS ntStatus        = STATUS_SUCCESS;
611     UNICODE_STRING paramPath, uniParamKey;
612     HANDLE hParameters = 0;
613     OBJECT_ATTRIBUTES stObjectAttributes;
614
615     __Enter
616     {
617
618         RtlInitUnicodeString( &uniParamKey,
619                               L"\\Parameters");
620
621         //
622         // Setup the paramPath buffer.
623         //
624
625         paramPath.MaximumLength = AFSRegistryPath.Length + uniParamKey.Length;
626         paramPath.Buffer = (PWSTR)AFSExAllocatePoolWithTag( PagedPool,
627                                                             paramPath.MaximumLength,
628                                                             AFS_GENERIC_MEMORY_16_TAG);
629
630         if( paramPath.Buffer == NULL)
631         {
632
633             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
634         }
635
636         //
637         // Move in the paths
638         //
639
640         RtlCopyMemory( paramPath.Buffer,
641                        AFSRegistryPath.Buffer,
642                        AFSRegistryPath.Length);
643
644         paramPath.Length = AFSRegistryPath.Length;
645
646         RtlCopyMemory( &paramPath.Buffer[ paramPath.Length / 2],
647                        uniParamKey.Buffer,
648                        uniParamKey.Length);
649
650         paramPath.Length += uniParamKey.Length;
651
652         InitializeObjectAttributes( &stObjectAttributes,
653                                     &paramPath,
654                                     OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
655                                     NULL,
656                                     NULL);
657
658         ntStatus = ZwOpenKey( &hParameters,
659                               KEY_ALL_ACCESS,
660                               &stObjectAttributes);
661
662         if( !NT_SUCCESS( ntStatus))
663         {
664
665             try_return( ntStatus);
666         }
667
668         //
669         // Set the value
670         //
671
672         ntStatus = ZwSetValueKey( hParameters,
673                                   ValueName,
674                                   0,
675                                   ValueType,
676                                   ValueData,
677                                   ValueDataLength);
678
679         ZwClose( hParameters);
680
681 try_exit:
682
683         if( paramPath.Buffer != NULL)
684         {
685
686             //
687             // Free up the buffer
688             //
689
690             ExFreePool( paramPath.Buffer);
691         }
692     }
693
694     return ntStatus;
695 }
696
697 NTSTATUS
698 AFSInitializeControlDevice()
699 {
700
701     NTSTATUS ntStatus = STATUS_SUCCESS;
702     AFSDeviceExt *pDeviceExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
703
704     __Enter
705     {
706
707         //
708         // Initialize the comm pool resources
709         //
710
711         ExInitializeResourceLite( &pDeviceExt->Specific.Control.CommServiceCB.IrpPoolLock);
712
713         ExInitializeResourceLite( &pDeviceExt->Specific.Control.CommServiceCB.ResultPoolLock);
714
715         ExInitializeResourceLite( &pDeviceExt->Specific.Control.ExtentReleaseResource);
716
717         ExInitializeResourceLite( &pDeviceExt->Specific.Control.SysName32ListLock);
718
719         ExInitializeResourceLite( &pDeviceExt->Specific.Control.SysName64ListLock);
720
721         //
722         // And the events
723         //
724
725         KeInitializeEvent( &pDeviceExt->Specific.Control.CommServiceCB.IrpPoolHasEntries,
726                            SynchronizationEvent,
727                            FALSE);
728
729         KeInitializeEvent( &pDeviceExt->Specific.Control.CommServiceCB.IrpPoolHasReleaseEntries,
730                            SynchronizationEvent,
731                            FALSE);
732
733         KeInitializeEvent( &pDeviceExt->Specific.Control.ExtentReleaseEvent,
734                            NotificationEvent,
735                            FALSE);
736
737         pDeviceExt->Specific.Control.ExtentReleaseSequence = 0;
738
739         KeInitializeEvent( &pDeviceExt->Specific.Control.VolumeWorkerCloseEvent,
740                            NotificationEvent,
741                            TRUE);
742
743         //
744         // Library support information
745         //
746
747         KeInitializeEvent( &pDeviceExt->Specific.Control.LoadLibraryEvent,
748                            SynchronizationEvent,
749                            TRUE);
750
751         //
752         // Initialize the library queued as cancelled
753         //
754
755         pDeviceExt->Specific.Control.LibraryState = AFS_LIBRARY_QUEUE_CANCELLED;
756
757         ExInitializeResourceLite( &pDeviceExt->Specific.Control.LibraryStateLock);
758
759         pDeviceExt->Specific.Control.InflightLibraryRequests = 0;
760
761         KeInitializeEvent( &pDeviceExt->Specific.Control.InflightLibraryEvent,
762                            NotificationEvent,
763                            FALSE);
764
765         pDeviceExt->Specific.Control.ExtentCount = 0;
766         pDeviceExt->Specific.Control.ExtentsHeldLength = 0;
767
768         KeInitializeEvent( &pDeviceExt->Specific.Control.ExtentsHeldEvent,
769                            NotificationEvent,
770                            TRUE);
771
772         pDeviceExt->Specific.Control.OutstandingServiceRequestCount = 0;
773
774         KeInitializeEvent( &pDeviceExt->Specific.Control.OutstandingServiceRequestEvent,
775                            NotificationEvent,
776                            TRUE);
777
778         pDeviceExt->Specific.Control.WaitingForMemoryCount = 0;
779
780         KeInitializeEvent( &pDeviceExt->Specific.Control.MemoryAvailableEvent,
781                            NotificationEvent,
782                            TRUE);
783
784         ExInitializeResourceLite( &pDeviceExt->Specific.Control.LibraryQueueLock);
785
786         pDeviceExt->Specific.Control.LibraryQueueHead = NULL;
787
788         pDeviceExt->Specific.Control.LibraryQueueTail = NULL;
789
790         //
791         // Set the initial state of the irp pool
792         //
793
794         pDeviceExt->Specific.Control.CommServiceCB.IrpPoolControlFlag = POOL_INACTIVE;
795
796         //
797         // Initialize our process and sid tree information
798         //
799
800         ExInitializeResourceLite( &pDeviceExt->Specific.Control.ProcessTreeLock);
801
802         pDeviceExt->Specific.Control.ProcessTree.TreeLock = &pDeviceExt->Specific.Control.ProcessTreeLock;
803
804         pDeviceExt->Specific.Control.ProcessTree.TreeHead = NULL;
805
806         ExInitializeResourceLite( &pDeviceExt->Specific.Control.AuthGroupTreeLock);
807
808         pDeviceExt->Specific.Control.AuthGroupTree.TreeLock = &pDeviceExt->Specific.Control.AuthGroupTreeLock;
809
810         pDeviceExt->Specific.Control.AuthGroupTree.TreeHead = NULL;
811
812         //
813         // Increase the StackSize to support the extra stack frame required
814         // for use of IoCompletion routines.
815         //
816
817         AFSDeviceObject->StackSize++;
818
819     }
820
821     return ntStatus;
822 }
823
824 NTSTATUS
825 AFSRemoveControlDevice()
826 {
827
828     NTSTATUS ntStatus = STATUS_SUCCESS;
829     AFSDeviceExt *pDeviceExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
830
831     __Enter
832     {
833
834         //
835         // Initialize the comm pool resources
836         //
837
838         ExDeleteResourceLite( &pDeviceExt->Specific.Control.CommServiceCB.IrpPoolLock);
839
840         ExDeleteResourceLite( &pDeviceExt->Specific.Control.CommServiceCB.ResultPoolLock);
841
842         ExDeleteResourceLite( &pDeviceExt->Specific.Control.ExtentReleaseResource);
843
844         ExDeleteResourceLite( &pDeviceExt->Specific.Control.SysName32ListLock);
845
846         ExDeleteResourceLite( &pDeviceExt->Specific.Control.SysName64ListLock);
847
848         ExDeleteResourceLite( &pDeviceExt->Specific.Control.ProcessTreeLock);
849
850         if( pDeviceExt->Specific.Control.ProcessTree.TreeHead != NULL)
851         {
852             ExFreePool( pDeviceExt->Specific.Control.ProcessTree.TreeHead);
853         }
854
855         ExDeleteResourceLite( &pDeviceExt->Specific.Control.AuthGroupTreeLock);
856
857         ExDeleteResourceLite( &pDeviceExt->Specific.Control.LibraryStateLock);
858
859         ExDeleteResourceLite( &pDeviceExt->Specific.Control.LibraryQueueLock);
860     }
861
862     return ntStatus;
863 }
864
865 void
866 AFSInitServerStrings()
867 {
868
869     UNICODE_STRING uniFullName;
870     WCHAR wchBuffer[ 50];
871
872     //
873     // Add the server name into the list of resources
874     //
875
876     uniFullName.Length = (2 * sizeof( WCHAR)) + AFSServerName.Length;
877     uniFullName.MaximumLength = uniFullName.Length + sizeof( WCHAR);
878
879     uniFullName.Buffer = wchBuffer;
880
881     wchBuffer[ 0] = L'\\';
882     wchBuffer[ 1] = L'\\';
883
884     RtlCopyMemory( &wchBuffer[ 2],
885                    AFSServerName.Buffer,
886                    AFSServerName.Length);
887
888     AFSAddConnectionEx( &uniFullName,
889                         RESOURCEDISPLAYTYPE_SERVER,
890                         0);
891
892     //
893     // Add in the global share name
894     //
895
896     wchBuffer[ uniFullName.Length/sizeof( WCHAR)] = L'\\';
897
898     uniFullName.Length += sizeof( WCHAR);
899
900     RtlCopyMemory( &wchBuffer[ uniFullName.Length/sizeof( WCHAR)],
901                    AFSGlobalRootName.Buffer,
902                    AFSGlobalRootName.Length);
903
904     uniFullName.Length += AFSGlobalRootName.Length;
905
906     AFSAddConnectionEx( &uniFullName,
907                         RESOURCEDISPLAYTYPE_SHARE,
908                         AFS_CONNECTION_FLAG_GLOBAL_SHARE);
909
910     return;
911 }
912
913 NTSTATUS
914 AFSReadServerName()
915 {
916
917     NTSTATUS ntStatus        = STATUS_SUCCESS;
918     UNICODE_STRING paramPath;
919     RTL_QUERY_REGISTRY_TABLE paramTable[2];
920
921     __Enter
922     {
923
924         //
925         // Setup the paramPath buffer.
926         //
927
928         paramPath.MaximumLength = PAGE_SIZE;
929         paramPath.Buffer = (PWSTR)AFSExAllocatePoolWithTag( PagedPool,
930                                                             paramPath.MaximumLength,
931                                                             AFS_GENERIC_MEMORY_17_TAG);
932
933         //
934         // If it exists, setup the path.
935         //
936
937         if( paramPath.Buffer == NULL)
938         {
939
940             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
941         }
942
943         //
944         // Move in the paths
945         //
946
947         RtlZeroMemory( paramPath.Buffer,
948                        paramPath.MaximumLength);
949
950         RtlCopyMemory( &paramPath.Buffer[ 0],
951                        L"\\TransarcAFSDaemon\\Parameters",
952                        58);
953
954         paramPath.Length = 58;
955
956         RtlZeroMemory( paramTable,
957                        sizeof( paramTable));
958
959         //
960         // Setup the table to query the registry for the needed value
961         //
962
963         AFSServerName.Length = 0;
964         AFSServerName.MaximumLength = 0;
965         AFSServerName.Buffer = NULL;
966
967         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
968         paramTable[0].Name = AFS_REG_NETBIOS_NAME;
969         paramTable[0].EntryContext = &AFSServerName;
970
971         paramTable[0].DefaultType = REG_NONE;
972         paramTable[0].DefaultData = NULL;
973         paramTable[0].DefaultLength = 0;
974
975         //
976         // Query the registry
977         //
978
979         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES,
980                                            paramPath.Buffer,
981                                            paramTable,
982                                            NULL,
983                                            NULL);
984
985         //
986         // Free up the buffer
987         //
988
989         ExFreePool( paramPath.Buffer);
990
991 try_exit:
992
993         if( !NT_SUCCESS( ntStatus))
994         {
995
996             RtlInitUnicodeString( &AFSServerName,
997                                   L"AFS");
998         }
999     }
1000
1001     return ntStatus;
1002 }
1003
1004 NTSTATUS
1005 AFSReadMountRootName()
1006 {
1007
1008     NTSTATUS ntStatus        = STATUS_SUCCESS;
1009     UNICODE_STRING paramPath;
1010     RTL_QUERY_REGISTRY_TABLE paramTable[2];
1011
1012     __Enter
1013     {
1014
1015         //
1016         // Setup the paramPath buffer.
1017         //
1018
1019         paramPath.MaximumLength = PAGE_SIZE;
1020         paramPath.Buffer = (PWSTR)AFSExAllocatePoolWithTag( PagedPool,
1021                                                             paramPath.MaximumLength,
1022                                                             AFS_GENERIC_MEMORY_17_TAG);
1023
1024         //
1025         // If it exists, setup the path.
1026         //
1027
1028         if( paramPath.Buffer == NULL)
1029         {
1030
1031             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
1032         }
1033
1034         //
1035         // Move in the paths
1036         //
1037
1038         RtlZeroMemory( paramPath.Buffer,
1039                        paramPath.MaximumLength);
1040
1041         RtlCopyMemory( &paramPath.Buffer[ 0],
1042                        L"\\TransarcAFSDaemon\\Parameters",
1043                        58);
1044
1045         paramPath.Length = 58;
1046
1047         RtlZeroMemory( paramTable,
1048                        sizeof( paramTable));
1049
1050         //
1051         // Setup the table to query the registry for the needed value
1052         //
1053
1054         AFSMountRootName.Length = 0;
1055         AFSMountRootName.MaximumLength = 0;
1056         AFSMountRootName.Buffer = NULL;
1057
1058         paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
1059         paramTable[0].Name = AFS_REG_MOUNT_ROOT;
1060         paramTable[0].EntryContext = &AFSMountRootName;
1061
1062         paramTable[0].DefaultType = REG_NONE;
1063         paramTable[0].DefaultData = NULL;
1064         paramTable[0].DefaultLength = 0;
1065
1066         //
1067         // Query the registry
1068         //
1069
1070         ntStatus = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES,
1071                                            paramPath.Buffer,
1072                                            paramTable,
1073                                            NULL,
1074                                            NULL);
1075
1076         if ( NT_SUCCESS( ntStatus))
1077         {
1078             if ( AFSMountRootName.Buffer[0] == L'/')
1079             {
1080
1081                 AFSMountRootName.Buffer[0] = L'\\';
1082             }
1083         }
1084
1085         //
1086         // Free up the buffer
1087         //
1088
1089         ExFreePool( paramPath.Buffer);
1090
1091 try_exit:
1092
1093         if( !NT_SUCCESS( ntStatus))
1094         {
1095
1096             RtlInitUnicodeString( &AFSMountRootName,
1097                                   L"\\afs");
1098         }
1099     }
1100
1101     return ntStatus;
1102 }
1103
1104 NTSTATUS
1105 AFSSetSysNameInformation( IN AFSSysNameNotificationCB *SysNameInfo,
1106                           IN ULONG SysNameInfoBufferLength)
1107 {
1108     UNREFERENCED_PARAMETER(SysNameInfoBufferLength);
1109
1110     NTSTATUS         ntStatus = STATUS_SUCCESS;
1111     AFSDeviceExt    *pControlDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
1112     AFSSysNameCB    *pSysName = NULL;
1113     ERESOURCE       *pSysNameLock = NULL;
1114     AFSSysNameCB   **pSysNameListHead = NULL, **pSysNameListTail = NULL;
1115     ULONG            ulIndex = 0;
1116     __Enter
1117     {
1118
1119         //
1120         // Depending on the architecture of the information, set up the lsit
1121         //
1122
1123         if( SysNameInfo->Architecture == AFS_SYSNAME_ARCH_32BIT)
1124         {
1125
1126             pSysNameLock = &pControlDevExt->Specific.Control.SysName32ListLock;
1127
1128             pSysNameListHead = &pControlDevExt->Specific.Control.SysName32ListHead;
1129
1130             pSysNameListTail = &pControlDevExt->Specific.Control.SysName32ListTail;
1131         }
1132         else
1133         {
1134
1135 #if defined(_WIN64)
1136
1137             pSysNameLock = &pControlDevExt->Specific.Control.SysName64ListLock;
1138
1139             pSysNameListHead = &pControlDevExt->Specific.Control.SysName64ListHead;
1140
1141             pSysNameListTail = &pControlDevExt->Specific.Control.SysName64ListTail;
1142
1143 #else
1144
1145             try_return( ntStatus = STATUS_INVALID_PARAMETER);
1146 #endif
1147         }
1148
1149         //
1150         // Process the request
1151         //
1152
1153         AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
1154                       AFS_TRACE_LEVEL_VERBOSE,
1155                       "AFSSetSysNameInformation Acquiring SysName lock %p EXCL %08lX\n",
1156                       pSysNameLock,
1157                       PsGetCurrentThread()));
1158
1159         AFSAcquireExcl( pSysNameLock,
1160                         TRUE);
1161
1162         //
1163         // If we already have a list, then tear it down
1164         //
1165
1166         if( *pSysNameListHead != NULL)
1167         {
1168
1169             AFSResetSysNameList( *pSysNameListHead);
1170
1171             *pSysNameListHead = NULL;
1172         }
1173
1174         //
1175         // Loop through the entries adding in a node for each
1176         //
1177
1178         while( ulIndex < SysNameInfo->NumberOfNames)
1179         {
1180
1181             pSysName = (AFSSysNameCB *)AFSExAllocatePoolWithTag( PagedPool,
1182                                                                  sizeof( AFSSysNameCB) +
1183                                                                  SysNameInfo->SysNames[ ulIndex].Length +
1184                                                                  sizeof( WCHAR),
1185                                                                  AFS_SYS_NAME_NODE_TAG);
1186
1187             if( pSysName == NULL)
1188             {
1189
1190                 //
1191                 // Reset the current list
1192                 //
1193
1194                 AFSResetSysNameList( *pSysNameListHead);
1195
1196                 *pSysNameListHead = NULL;
1197
1198                 try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
1199             }
1200
1201             RtlZeroMemory( pSysName,
1202                            sizeof( AFSSysNameCB) +
1203                                    SysNameInfo->SysNames[ ulIndex].Length +
1204                                    sizeof( WCHAR));
1205
1206             pSysName->SysName.Length = (USHORT)SysNameInfo->SysNames[ ulIndex].Length;
1207
1208             pSysName->SysName.MaximumLength = pSysName->SysName.Length + sizeof( WCHAR);
1209
1210             pSysName->SysName.Buffer = (WCHAR *)((char *)pSysName + sizeof( AFSSysNameCB));
1211
1212             RtlCopyMemory( pSysName->SysName.Buffer,
1213                            SysNameInfo->SysNames[ ulIndex].String,
1214                            pSysName->SysName.Length);
1215
1216             if( *pSysNameListHead == NULL)
1217             {
1218
1219                 *pSysNameListHead = pSysName;
1220             }
1221             else
1222             {
1223
1224                 (*pSysNameListTail)->fLink = pSysName;
1225             }
1226
1227             *pSysNameListTail = pSysName;
1228
1229             ulIndex++;
1230         }
1231
1232 try_exit:
1233
1234         AFSReleaseResource( pSysNameLock);
1235     }
1236
1237     return ntStatus;
1238 }
1239
1240 void
1241 AFSResetSysNameList( IN AFSSysNameCB *SysNameList)
1242 {
1243
1244     AFSSysNameCB *pNextEntry = NULL, *pCurrentEntry = SysNameList;
1245
1246     while( pCurrentEntry != NULL)
1247     {
1248
1249         pNextEntry = pCurrentEntry->fLink;
1250
1251         ExFreePool( pCurrentEntry);
1252
1253         pCurrentEntry = pNextEntry;
1254     }
1255
1256     return;
1257 }
1258
1259 NTSTATUS
1260 AFSDefaultDispatch( IN PDEVICE_OBJECT DeviceObject,
1261                     IN PIRP Irp)
1262 {
1263     UNREFERENCED_PARAMETER(DeviceObject);
1264
1265     NTSTATUS            ntStatus = STATUS_INVALID_DEVICE_REQUEST;
1266
1267     AFSCompleteRequest( Irp,
1268                         ntStatus);
1269
1270     return ntStatus;
1271 }
1272
1273 NTSTATUS
1274 AFSSendDeviceIoControl( IN DEVICE_OBJECT *TargetDeviceObject,
1275                         IN ULONG IOControl,
1276                         IN void *InputBuffer,
1277                         IN ULONG InputBufferLength,
1278                         IN OUT void *OutputBuffer,
1279                         IN ULONG OutputBufferLength,
1280                         OUT ULONG *ResultLength)
1281 {
1282     UNREFERENCED_PARAMETER(OutputBuffer);
1283     UNREFERENCED_PARAMETER(OutputBufferLength);
1284
1285     NTSTATUS            ntStatus = STATUS_SUCCESS;
1286     PIRP                pIrp = NULL;
1287     KEVENT              kEvent;
1288     PIO_STACK_LOCATION  pIoStackLocation = NULL;
1289
1290     __Enter
1291     {
1292
1293         //
1294         // Initialize the event
1295         //
1296
1297         KeInitializeEvent( &kEvent,
1298                            SynchronizationEvent,
1299                            FALSE);
1300
1301         //
1302         // Allocate an irp for this request.  This could also come from a
1303         // private pool, for instance.
1304         //
1305
1306         pIrp = IoAllocateIrp( TargetDeviceObject->StackSize,
1307                               FALSE);
1308
1309         if( pIrp == NULL)
1310         {
1311
1312             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
1313         }
1314
1315         //
1316         // Build the IRP's main body
1317         //
1318
1319         pIrp->RequestorMode = KernelMode;
1320
1321         //
1322         // Set up the I/O stack location.
1323         //
1324
1325         pIoStackLocation = IoGetNextIrpStackLocation( pIrp);
1326         pIoStackLocation->MajorFunction = IRP_MJ_DEVICE_CONTROL;
1327         pIoStackLocation->DeviceObject = TargetDeviceObject;
1328
1329         pIoStackLocation->Parameters.DeviceIoControl.IoControlCode = IOControl;
1330
1331         pIrp->AssociatedIrp.SystemBuffer = (void *)InputBuffer;
1332         pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
1333
1334         //
1335         // Set the completion routine.
1336         //
1337
1338         AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
1339                       AFS_TRACE_LEVEL_VERBOSE,
1340                       "Setting AFSIrpComplete as IoCompletion Routine Irp %p\n",
1341                       pIrp));
1342
1343         IoSetCompletionRoutine( pIrp,
1344                                 AFSIrpComplete,
1345                                 &kEvent,
1346                                 TRUE,
1347                                 TRUE,
1348                                 TRUE);
1349
1350         //
1351         // Send it to the FSD
1352         //
1353
1354         ntStatus = IoCallDriver( TargetDeviceObject,
1355                                  pIrp);
1356
1357         if( NT_SUCCESS( ntStatus))
1358         {
1359
1360             //
1361             // Wait for the I/O
1362             //
1363
1364             ntStatus = KeWaitForSingleObject( &kEvent,
1365                                               Executive,
1366                                               KernelMode,
1367                                               FALSE,
1368                                               0);
1369
1370             if( NT_SUCCESS( ntStatus))
1371             {
1372
1373                 ntStatus = pIrp->IoStatus.Status;
1374
1375                 if( ResultLength != NULL)
1376                 {
1377                     *ResultLength = (ULONG)pIrp->IoStatus.Information;
1378                 }
1379             }
1380         }
1381
1382 try_exit:
1383
1384         if( pIrp != NULL)
1385         {
1386
1387             if( pIrp->MdlAddress != NULL)
1388             {
1389
1390                 if( FlagOn( pIrp->MdlAddress->MdlFlags, MDL_PAGES_LOCKED))
1391                 {
1392
1393                     MmUnlockPages( pIrp->MdlAddress);
1394                 }
1395
1396                 IoFreeMdl( pIrp->MdlAddress);
1397             }
1398
1399             pIrp->MdlAddress = NULL;
1400
1401             //
1402             // Free the Irp
1403             //
1404
1405             IoFreeIrp( pIrp);
1406         }
1407     }
1408
1409     return ntStatus;
1410 }
1411
1412 NTSTATUS
1413 AFSIrpComplete( IN PDEVICE_OBJECT DeviceObject,
1414                 IN PIRP           Irp,
1415                 IN PVOID          Context)
1416 {
1417     UNREFERENCED_PARAMETER(DeviceObject);
1418     UNREFERENCED_PARAMETER(Irp);
1419
1420     KEVENT *pEvent = (KEVENT *)Context;
1421
1422     KeSetEvent( pEvent,
1423                 0,
1424                 FALSE);
1425
1426     return STATUS_MORE_PROCESSING_REQUIRED;
1427 }
1428
1429 void *
1430 AFSExAllocatePoolWithTag( IN POOL_TYPE  PoolType,
1431                           IN SIZE_T  NumberOfBytes,
1432                           IN ULONG  Tag)
1433 {
1434
1435     AFSDeviceExt *pControlDevExt = NULL;
1436     void *pBuffer = NULL;
1437     BOOLEAN bTimeout = FALSE;
1438     LARGE_INTEGER liTimeout;
1439     NTSTATUS ntStatus;
1440
1441     //
1442     // Attempt to allocation memory from the system.  If the allocation fails
1443     // wait up to 30 seconds for the AFS redirector to free some memory.  As
1444     // long as the wait does not timeout, continue to retry the allocation.
1445     // If the wait does timeout, attempt to allocate one more time in case
1446     // memory was freed by another driver.  Otherwise, fail the request.
1447     //
1448
1449     if ( AFSDeviceObject)
1450     {
1451
1452         pControlDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
1453     }
1454
1455     while( pBuffer == NULL)
1456     {
1457
1458         pBuffer = ExAllocatePoolWithTag( PoolType,
1459                                          NumberOfBytes,
1460                                          Tag);
1461
1462         if( pBuffer == NULL)
1463         {
1464
1465             if ( bTimeout || pControlDevExt == NULL)
1466             {
1467
1468                 AFSDbgTrace(( 0,
1469                               0,
1470                               "AFSExAllocatePoolWithTag failure Type %08lX Size %08lX Tag %08lX %08lX\n",
1471                               PoolType,
1472                               NumberOfBytes,
1473                               Tag,
1474                               PsGetCurrentThread()));
1475
1476                 switch ( Tag ) {
1477
1478                 case AFS_GENERIC_MEMORY_21_TAG:
1479                 case AFS_GENERIC_MEMORY_22_TAG:
1480                     // AFSDumpTraceFiles -- do nothing;
1481                     break;
1482
1483                 default:
1484                     AFSBreakPoint();
1485                 }
1486
1487                 break;
1488             }
1489
1490
1491             //
1492             // Wait up to 30 seconds for a memory deallocation
1493             //
1494
1495             liTimeout.QuadPart = -(30 *AFS_ONE_SECOND);
1496
1497             if( InterlockedIncrement( &pControlDevExt->Specific.Control.WaitingForMemoryCount) == 1)
1498             {
1499                 KeClearEvent( &pControlDevExt->Specific.Control.MemoryAvailableEvent);
1500             }
1501
1502             ntStatus = KeWaitForSingleObject( &pControlDevExt->Specific.Control.MemoryAvailableEvent,
1503                                               Executive,
1504                                               KernelMode,
1505                                               FALSE,
1506                                               &liTimeout);
1507
1508             if( ntStatus == STATUS_TIMEOUT)
1509             {
1510
1511                 bTimeout = TRUE;
1512             }
1513
1514             InterlockedDecrement( &pControlDevExt->Specific.Control.WaitingForMemoryCount);
1515         }
1516     }
1517
1518     return pBuffer;
1519 }
1520
1521 void
1522 AFSExFreePoolWithTag( IN void *Buffer, IN ULONG Tag)
1523 {
1524
1525     AFSDeviceExt *pControlDevExt = NULL;
1526
1527     if ( AFSDeviceObject)
1528     {
1529
1530         pControlDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
1531     }
1532
1533     if ( Tag)
1534     {
1535
1536         ExFreePoolWithTag( Buffer, Tag);
1537     }
1538     else
1539     {
1540
1541         ExFreePool( Buffer);
1542     }
1543
1544     if ( pControlDevExt)
1545     {
1546
1547         KeSetEvent( &pControlDevExt->Specific.Control.MemoryAvailableEvent,
1548                     0,
1549                     FALSE);
1550     }
1551     return;
1552 }
1553
1554 NTSTATUS
1555 AFSShutdownRedirector()
1556 {
1557
1558     NTSTATUS ntStatus = STATUS_SUCCESS;
1559     AFSDeviceExt *pRDRDevExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
1560     AFSDeviceExt *pControlDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
1561     LARGE_INTEGER liTimeout;
1562
1563     __Enter
1564     {
1565
1566         AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
1567                       AFS_TRACE_LEVEL_VERBOSE,
1568                       "%s Shutting down redirector Extent count %08lX Request count %08lX\n",
1569                       __FUNCTION__,
1570                       pControlDevExt->Specific.Control.ExtentCount,
1571                       pControlDevExt->Specific.Control.OutstandingServiceRequestCount));
1572
1573         //
1574         // Set the shutdown flag so the worker is more agressive in tearing down extents
1575         //
1576
1577         SetFlag( pRDRDevExt->DeviceFlags, AFS_DEVICE_FLAG_REDIRECTOR_SHUTDOWN);
1578
1579         //
1580         // Wait on any outstanding service requests
1581         //
1582
1583         liTimeout.QuadPart = -(30 *AFS_ONE_SECOND);
1584
1585         ntStatus = KeWaitForSingleObject( &pControlDevExt->Specific.Control.OutstandingServiceRequestEvent,
1586                                           Executive,
1587                                           KernelMode,
1588                                           FALSE,
1589                                           &liTimeout);
1590
1591         if( ntStatus == STATUS_TIMEOUT)
1592         {
1593
1594             AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
1595                           AFS_TRACE_LEVEL_WARNING,
1596                           "AFSShutdownRedirector Failed to complete all service requests Remaining count %08lX\n",
1597                           pControlDevExt->Specific.Control.OutstandingServiceRequestCount));
1598
1599             try_return( ntStatus = STATUS_UNSUCCESSFUL);
1600         }
1601
1602         AFSProcessQueuedResults( TRUE);
1603
1604         //
1605         // Wait for all extents to be released
1606         //
1607
1608         liTimeout.QuadPart = -(30 *AFS_ONE_SECOND);
1609
1610         ntStatus = KeWaitForSingleObject( &pControlDevExt->Specific.Control.ExtentsHeldEvent,
1611                                           Executive,
1612                                           KernelMode,
1613                                           FALSE,
1614                                           &liTimeout);
1615
1616         if( ntStatus == STATUS_TIMEOUT)
1617         {
1618
1619             AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
1620                           AFS_TRACE_LEVEL_WARNING,
1621                           "AFSShutdownRedirector Failed to purge all extents Remaining count %08lX\n",
1622                           pControlDevExt->Specific.Control.ExtentCount));
1623
1624             try_return( ntStatus = STATUS_UNSUCCESSFUL);
1625         }
1626
1627         ntStatus = AFSUnloadLibrary( TRUE);
1628
1629         if( !NT_SUCCESS( ntStatus))
1630         {
1631
1632             try_return( ntStatus);
1633         }
1634
1635 try_exit:
1636
1637         AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
1638                       AFS_TRACE_LEVEL_VERBOSE,
1639                       "%s Completed shut down of redirector Extent count %08lX Request count %08lX Status %08lX\n",
1640                       __FUNCTION__,
1641                       pControlDevExt->Specific.Control.ExtentCount,
1642                       pControlDevExt->Specific.Control.OutstandingServiceRequestCount,
1643                       ntStatus));
1644     }
1645
1646     return ntStatus;
1647 }
1648
1649 //
1650 // Cache manager callback routines
1651 //
1652
1653 BOOLEAN
1654 AFSAcquireFcbForLazyWrite( IN PVOID Fcb,
1655                            IN BOOLEAN Wait)
1656 {
1657
1658     BOOLEAN bStatus = FALSE;
1659     AFSFcb *pFcb = (AFSFcb *)Fcb;
1660     BOOLEAN bReleaseSectionObject = FALSE, bReleasePaging = FALSE;
1661
1662     //
1663     // Try and acquire the Fcb resource
1664     //
1665
1666     AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
1667                   AFS_TRACE_LEVEL_VERBOSE,
1668                   "AFSAcquireFcbForLazyWrite Acquiring Fcb %p\n",
1669                   Fcb));
1670
1671     //
1672     // Try and grab the paging
1673     //
1674
1675     AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
1676                   AFS_TRACE_LEVEL_VERBOSE,
1677                   "AFSAcquireFcbForLazyWrite Attempt to acquire Fcb PagingIo lock %p SHARED %08lX\n",
1678                   &pFcb->NPFcb->PagingResource,
1679                   PsGetCurrentThread()));
1680
1681     if( AFSAcquireShared( &pFcb->NPFcb->PagingResource,
1682                           Wait))
1683     {
1684
1685         AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
1686                       AFS_TRACE_LEVEL_VERBOSE,
1687                       "AFSAcquireFcbForLazyWrite Acquired Fcb PagingIo lock %p SHARED %08lX\n",
1688                       &pFcb->NPFcb->PagingResource,
1689                       PsGetCurrentThread()));
1690
1691         bReleasePaging = TRUE;
1692
1693         AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
1694                       AFS_TRACE_LEVEL_VERBOSE,
1695                       "AFSAcquireFcbForLazyWrite Attempt to acquire Fcb SectionObject lock %p SHARED %08lX\n",
1696                       &pFcb->NPFcb->SectionObjectResource,
1697                       PsGetCurrentThread()));
1698
1699         if( AFSAcquireShared( &pFcb->NPFcb->SectionObjectResource,
1700                               Wait))
1701         {
1702
1703             AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
1704                           AFS_TRACE_LEVEL_VERBOSE,
1705                           "AFSAcquireFcbForLazyWrite Acquired Fcb SectionObject lock %p SHARED %08lX\n",
1706                           &pFcb->NPFcb->SectionObjectResource,
1707                           PsGetCurrentThread()));
1708
1709             bReleaseSectionObject = TRUE;
1710
1711             //
1712             // All is well ...
1713             //
1714
1715             bStatus = TRUE;
1716
1717             IoSetTopLevelIrp( (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP);
1718         }
1719     }
1720
1721     if( !bStatus)
1722     {
1723
1724         if( bReleaseSectionObject)
1725         {
1726
1727             AFSReleaseResource( &pFcb->NPFcb->SectionObjectResource);
1728         }
1729
1730         if( bReleasePaging)
1731         {
1732
1733             AFSReleaseResource( &pFcb->NPFcb->PagingResource);
1734         }
1735     }
1736
1737     return bStatus;
1738 }
1739
1740 VOID
1741 AFSReleaseFcbFromLazyWrite( IN PVOID Fcb)
1742 {
1743
1744     AFSFcb *pFcb = (AFSFcb *)Fcb;
1745
1746     AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
1747                   AFS_TRACE_LEVEL_VERBOSE,
1748                   "AFSReleaseFcbFromLazyWrite Releasing Fcb %p\n",
1749                   Fcb));
1750
1751     IoSetTopLevelIrp( NULL);
1752
1753     AFSReleaseResource( &pFcb->NPFcb->SectionObjectResource);
1754
1755     AFSReleaseResource( &pFcb->NPFcb->PagingResource);
1756
1757     return;
1758 }
1759
1760 BOOLEAN
1761 AFSAcquireFcbForReadAhead( IN PVOID Fcb,
1762                            IN BOOLEAN Wait)
1763 {
1764
1765     BOOLEAN bStatus = FALSE;
1766     AFSFcb *pFcb = (AFSFcb *)Fcb;
1767
1768     AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
1769                   AFS_TRACE_LEVEL_VERBOSE,
1770                   "AFSAcquireFcbForReadAhead Attempt to acquire Fcb SectionObject lock %p SHARED %08lX\n",
1771                   &pFcb->NPFcb->SectionObjectResource,
1772                   PsGetCurrentThread()));
1773
1774     if( AFSAcquireShared( &pFcb->NPFcb->SectionObjectResource,
1775                           Wait))
1776     {
1777
1778         AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
1779                       AFS_TRACE_LEVEL_VERBOSE,
1780                       "AFSAcquireFcbForReadAhead Acquired Fcb SectionObject lock %p SHARED %08lX\n",
1781                       &pFcb->NPFcb->SectionObjectResource,
1782                       PsGetCurrentThread()));
1783
1784         bStatus = TRUE;
1785
1786         IoSetTopLevelIrp( (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP);
1787     }
1788
1789     return bStatus;
1790 }
1791
1792 VOID
1793 AFSReleaseFcbFromReadAhead( IN PVOID Fcb)
1794 {
1795
1796     AFSFcb *pFcb = (AFSFcb *)Fcb;
1797
1798     IoSetTopLevelIrp( NULL);
1799
1800     AFSReleaseResource( &pFcb->NPFcb->SectionObjectResource);
1801
1802     return;
1803 }
1804
1805 NTSTATUS
1806 AFSGetCallerSID( OUT UNICODE_STRING *SIDString, OUT BOOLEAN *pbImpersonation)
1807 {
1808
1809     NTSTATUS ntStatus = STATUS_SUCCESS;
1810     PACCESS_TOKEN hToken = NULL;
1811     TOKEN_USER *pTokenInfo = NULL;
1812     BOOLEAN bCopyOnOpen = FALSE;
1813     BOOLEAN bEffectiveOnly = FALSE;
1814     BOOLEAN bPrimaryToken = FALSE;
1815     SECURITY_IMPERSONATION_LEVEL stImpersonationLevel;
1816     UNICODE_STRING uniSIDString;
1817
1818     __Enter
1819     {
1820
1821         hToken = PsReferenceImpersonationToken( PsGetCurrentThread(),
1822                                                 &bCopyOnOpen,
1823                                                 &bEffectiveOnly,
1824                                                 &stImpersonationLevel);
1825
1826         if( hToken == NULL)
1827         {
1828
1829             hToken = PsReferencePrimaryToken( PsGetCurrentProcess());
1830
1831             if( hToken == NULL)
1832             {
1833
1834                 AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
1835                               AFS_TRACE_LEVEL_ERROR,
1836                               "AFSGetCallerSID Failed to retrieve impersonation or primary token\n"));
1837
1838                 try_return( ntStatus);
1839             }
1840
1841             bPrimaryToken = TRUE;
1842         }
1843
1844         ntStatus = SeQueryInformationToken( hToken,
1845                                             TokenUser,
1846                                             (PVOID *)&pTokenInfo);
1847
1848         if( !NT_SUCCESS( ntStatus))
1849         {
1850
1851             AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
1852                           AFS_TRACE_LEVEL_ERROR,
1853                           "AFSGetCallerSID Failed to retrieve information Status %08lX\n",
1854                           ntStatus));
1855
1856             try_return( ntStatus);
1857         }
1858
1859         uniSIDString.Length = 0;
1860         uniSIDString.MaximumLength = 0;
1861         uniSIDString.Buffer = NULL;
1862
1863         ntStatus = RtlConvertSidToUnicodeString( &uniSIDString,
1864                                                  pTokenInfo->User.Sid,
1865                                                  TRUE);
1866
1867         if( !NT_SUCCESS( ntStatus))
1868         {
1869
1870             AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
1871                           AFS_TRACE_LEVEL_ERROR,
1872                           "AFSGetCallerSID Failed to convert sid to string Status %08lX\n",
1873                           ntStatus));
1874
1875             try_return( ntStatus);
1876         }
1877
1878         *SIDString = uniSIDString;
1879
1880         AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING | AFS_SUBSYSTEM_AUTHGROUP_PROCESSING,
1881                       AFS_TRACE_LEVEL_VERBOSE_2,
1882                       "AFSGetCallerSID Successfully retrieved SID %wZ\n",
1883                       SIDString));
1884
1885         if ( bPrimaryToken == FALSE &&
1886              pbImpersonation)
1887         {
1888             *pbImpersonation = TRUE;
1889         }
1890
1891 try_exit:
1892
1893         if( hToken != NULL)
1894         {
1895             if( bPrimaryToken)
1896             {
1897                 PsDereferencePrimaryToken( hToken);
1898             }
1899             else
1900             {
1901                 PsDereferenceImpersonationToken( hToken);
1902             }
1903         }
1904
1905         if( pTokenInfo != NULL)
1906         {
1907             ExFreePool( pTokenInfo);    // Allocated by SeQueryInformationToken
1908         }
1909     }
1910
1911     return ntStatus;
1912 }
1913
1914 ULONG
1915 AFSGetSessionId( IN HANDLE ProcessId, OUT BOOLEAN *pbImpersonation)
1916 {
1917     UNREFERENCED_PARAMETER(ProcessId);
1918
1919     NTSTATUS ntStatus = STATUS_SUCCESS;
1920     PACCESS_TOKEN hToken = NULL;
1921     ULONG ulSessionId = (ULONG)-1;
1922     BOOLEAN bCopyOnOpen = FALSE;
1923     BOOLEAN bEffectiveOnly = FALSE;
1924     BOOLEAN bPrimaryToken = FALSE;
1925     SECURITY_IMPERSONATION_LEVEL stImpersonationLevel;
1926
1927     __Enter
1928     {
1929
1930         hToken = PsReferenceImpersonationToken( PsGetCurrentThread(),
1931                                                 &bCopyOnOpen,
1932                                                 &bEffectiveOnly,
1933                                                 &stImpersonationLevel);
1934
1935         if( hToken == NULL)
1936         {
1937
1938             hToken = PsReferencePrimaryToken( PsGetCurrentProcess());
1939
1940             if( hToken == NULL)
1941             {
1942
1943                 AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
1944                               AFS_TRACE_LEVEL_ERROR,
1945                               "AFSGetSessionId Failed to retrieve impersonation or primary token\n"));
1946
1947                 try_return( ntStatus);
1948             }
1949
1950             bPrimaryToken = TRUE;
1951         }
1952
1953         ntStatus = SeQueryInformationToken( hToken,
1954                                             TokenSessionId,
1955                                             (PVOID *)&ulSessionId);
1956
1957         if( !NT_SUCCESS( ntStatus))
1958         {
1959             ulSessionId = (ULONG)-1;
1960
1961             AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
1962                           AFS_TRACE_LEVEL_ERROR,
1963                           "AFSGetSessionId Failed to retrieve session id Status %08lX\n",
1964                           ntStatus));
1965
1966             try_return( ntStatus);
1967         }
1968
1969         AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING | AFS_SUBSYSTEM_AUTHGROUP_PROCESSING,
1970                       AFS_TRACE_LEVEL_VERBOSE_2,
1971                       "AFSGetSessionId found %08lX\n",
1972                       ulSessionId));
1973
1974         if ( bPrimaryToken == FALSE &&
1975              pbImpersonation)
1976         {
1977             *pbImpersonation = TRUE;
1978         }
1979
1980 try_exit:
1981
1982         if( hToken != NULL)
1983         {
1984             if( bPrimaryToken)
1985             {
1986                 PsDereferencePrimaryToken( hToken);
1987             }
1988             else
1989             {
1990                 PsDereferenceImpersonationToken( hToken);
1991             }
1992         }
1993     }
1994
1995     return ulSessionId;
1996 }
1997
1998 NTSTATUS
1999 AFSCheckThreadDacl( OUT GUID *AuthGroup)
2000 {
2001
2002     NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
2003     ULONG idx;
2004     PACCESS_TOKEN token = NULL;
2005     PTOKEN_DEFAULT_DACL defDacl = NULL;
2006     PACE_HEADER ace;
2007     PACCESS_ALLOWED_ACE adace;
2008     BOOLEAN bCopyOnOpen = FALSE, bEffectiveOnly = FALSE;
2009     SECURITY_IMPERSONATION_LEVEL stImpersonationLevel;
2010     BOOLEAN bLocatedACE = FALSE;
2011
2012     __Enter
2013     {
2014
2015         token = PsReferenceImpersonationToken( PsGetCurrentThread(),
2016                                                &bCopyOnOpen,
2017                                                &bEffectiveOnly,
2018                                                &stImpersonationLevel);
2019
2020         if( token == NULL)
2021         {
2022            try_return( ntStatus);
2023         }
2024
2025         ntStatus = SeQueryInformationToken( token,
2026                                             TokenDefaultDacl,
2027                                             (PVOID *)&defDacl);
2028
2029         if( ntStatus != STATUS_SUCCESS)
2030         {
2031            try_return( ntStatus);
2032         }
2033
2034         // scan through all ACEs in the DACL
2035         for (idx = 0, ace = (PACE_HEADER)((char *)defDacl->DefaultDacl + sizeof(ACL)); idx < defDacl->DefaultDacl->AceCount; idx++)
2036         {
2037            if (ace->AceType == ACCESS_ALLOWED_ACE_TYPE)
2038            {
2039               adace = (PACCESS_ALLOWED_ACE)ace;
2040
2041               if (adace->Header.AceSize == (FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + AFS_DACL_SID_LENGTH))
2042               {
2043                  if (RtlCompareMemory( RtlSubAuthoritySid((PSID)&adace->SidStart, 0), &AFSSidGuid, sizeof(GUID)) == sizeof(GUID))
2044                  {
2045
2046                     RtlCopyMemory( AuthGroup,
2047                                    RtlSubAuthoritySid((PSID)&adace->SidStart, 4),
2048                                    sizeof( GUID));
2049
2050                     bLocatedACE = TRUE;
2051
2052                     break;
2053                  }
2054               }
2055            }
2056
2057            // go to next ace
2058            ace = (PACE_HEADER)((char *)ace + ace->AceSize);
2059         }
2060
2061 try_exit:
2062
2063         if( token != NULL)
2064         {
2065             PsDereferenceImpersonationToken( token);
2066         }
2067
2068         if (defDacl != NULL)
2069         {
2070            ExFreePool(defDacl);
2071         }
2072
2073         if( !bLocatedACE)
2074         {
2075             ntStatus = STATUS_UNSUCCESSFUL;
2076         }
2077     }
2078
2079     return ntStatus;
2080 }
2081
2082 NTSTATUS
2083 AFSProcessSetProcessDacl( IN AFSProcessCB *ProcessCB)
2084 {
2085
2086     PTOKEN_DEFAULT_DACL defDacl = NULL;
2087     HANDLE hToken = NULL;
2088     PACE_HEADER ace = NULL;
2089     SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
2090     PACCESS_ALLOWED_ACE aaace;
2091     ULONG bytesNeeded;
2092     ULONG bytesReturned;
2093     ULONG idx;
2094     PSID psid;
2095     NTSTATUS ntStatus = STATUS_SUCCESS;
2096
2097     __Enter
2098     {
2099
2100         ntStatus = ZwOpenProcessTokenEx( NtCurrentProcess(),
2101                                          GENERIC_ALL,
2102                                          OBJ_KERNEL_HANDLE,
2103                                          &hToken);
2104
2105         if( !NT_SUCCESS( ntStatus))
2106         {
2107             try_return( ntStatus);
2108         }
2109
2110         // get the size of the current DACL
2111         ntStatus = ZwQueryInformationToken( hToken,
2112                                             TokenDefaultDacl,
2113                                             NULL,
2114                                             0,
2115                                             &bytesNeeded);
2116
2117         // if we failed to get the buffer size needed
2118         if ((ntStatus != STATUS_SUCCESS) && (ntStatus != STATUS_BUFFER_TOO_SMALL))
2119         {
2120             try_return( ntStatus);
2121         }
2122
2123         // tack on enough space for our ACE if we need to add it...
2124         bytesNeeded += FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + AFS_DACL_SID_LENGTH;
2125
2126         // allocate space for the DACL
2127         defDacl = (PTOKEN_DEFAULT_DACL)ExAllocatePoolWithTag( PagedPool, bytesNeeded, AFS_GENERIC_MEMORY_26_TAG);
2128
2129         if (defDacl == NULL)
2130         {
2131            try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
2132         }
2133
2134         // get the DACL
2135         ntStatus = ZwQueryInformationToken( hToken,
2136                                             TokenDefaultDacl,
2137                                             defDacl,
2138                                             bytesNeeded,
2139                                             &bytesReturned);
2140
2141         if( ntStatus != STATUS_SUCCESS)
2142         {
2143             try_return( ntStatus);
2144         }
2145
2146         // scan through DACL to see if we have the SID set already...
2147         ace = (PACE_HEADER)((char *)defDacl->DefaultDacl + sizeof(ACL));
2148         for (idx = 0; idx < defDacl->DefaultDacl->AceCount; idx++)
2149         {
2150             if (ace->AceType == ACCESS_ALLOWED_ACE_TYPE)
2151             {
2152                 aaace = (PACCESS_ALLOWED_ACE)ace;
2153
2154                 if (aaace->Header.AceSize == (FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + AFS_DACL_SID_LENGTH))
2155                 {
2156                     // if the GUID part matches
2157                     if( RtlCompareMemory( RtlSubAuthoritySid((PSID)&aaace->SidStart, 0),
2158                                           &AFSSidGuid,
2159                                           sizeof(GUID)) == sizeof(GUID))
2160                     {
2161
2162                         if ( RtlCompareMemory( RtlSubAuthoritySid((PSID)&aaace->SidStart, 4),
2163                                                ProcessCB->ActiveAuthGroup,
2164                                                sizeof( GUID)) != sizeof( GUID))
2165                         {
2166
2167                             RtlCopyMemory( RtlSubAuthoritySid((PSID)&aaace->SidStart, 4),
2168                                            ProcessCB->ActiveAuthGroup,
2169                                            sizeof( GUID));
2170
2171                             if( AFSSetInformationToken != NULL)
2172                             {
2173                                 ntStatus = AFSSetInformationToken( hToken,
2174                                                                    TokenDefaultDacl,
2175                                                                    defDacl,
2176                                                                    bytesReturned);
2177                             }
2178                         }
2179
2180                         try_return( ntStatus);
2181                     }
2182                 }
2183             }
2184
2185             // go to next ace
2186             ace = (PACE_HEADER)((char *)ace + ace->AceSize);
2187         }
2188
2189         //
2190         // if we made it here we need to add a new ACE to the DACL
2191         //
2192
2193         aaace = (ACCESS_ALLOWED_ACE *)ace;
2194         aaace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
2195         aaace->Header.AceFlags = 0;
2196         aaace->Mask = GENERIC_ALL;
2197         psid = (PSID)&aaace->SidStart;
2198         RtlInitializeSid( psid, &sia, 8);
2199
2200         RtlCopyMemory( RtlSubAuthoritySid(psid, 0),
2201                        &AFSSidGuid,
2202                        sizeof(GUID));
2203
2204         RtlCopyMemory( RtlSubAuthoritySid(psid, 4),
2205                        ProcessCB->ActiveAuthGroup,
2206                        sizeof( GUID));
2207
2208         aaace->Header.AceSize = (USHORT)(FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + RtlLengthSid( psid));
2209
2210         defDacl->DefaultDacl->AclSize += aaace->Header.AceSize;
2211         defDacl->DefaultDacl->AceCount++;
2212
2213         if( AFSSetInformationToken != NULL)
2214         {
2215             ntStatus = AFSSetInformationToken( hToken,
2216                                               TokenDefaultDacl,
2217                                               defDacl,
2218                                               defDacl->DefaultDacl->AclSize + sizeof(PTOKEN_DEFAULT_DACL));
2219         }
2220
2221 try_exit:
2222
2223         if( hToken != NULL)
2224         {
2225             ZwClose( hToken);
2226         }
2227
2228         if (defDacl != NULL)
2229         {
2230            ExFreePool( defDacl);
2231         }
2232     }
2233
2234     return ntStatus;
2235 }
2236
2237