xref: /reactos/ntoskrnl/po/povolume.c (revision 5100859e)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/po/povolume.c
5  * PURPOSE:         Power Manager DOPE and Volume Management
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* GLOBALS *******************************************************************/
16 
17 typedef struct _POP_FLUSH_VOLUME
18 {
19     LIST_ENTRY List;
20     LONG Count;
21     KEVENT Wait;
22 } POP_FLUSH_VOLUME, *PPOP_FLUSH_VOLUME;
23 
24 ULONG PopFlushPolicy = 0;
25 
26 KGUARDED_MUTEX PopVolumeLock;
27 LIST_ENTRY PopVolumeDevices;
28 KSPIN_LOCK PopDopeGlobalLock;
29 
30 #define TAG_PO_DOPE 'EPOD'
31 
32 /* PRIVATE FUNCTIONS *********************************************************/
33 
34 PDEVICE_OBJECT_POWER_EXTENSION
35 NTAPI
36 PopGetDope(IN PDEVICE_OBJECT DeviceObject)
37 {
38     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
39     PDEVICE_OBJECT_POWER_EXTENSION Dope;
40     KIRQL OldIrql;
41     PAGED_CODE();
42 
43     /* If the device already has the dope, return it */
44     DeviceExtension = IoGetDevObjExtension(DeviceObject);
45     if (DeviceExtension->Dope) goto Return;
46 
47     /* Allocate some dope for the device */
48     Dope = ExAllocatePoolWithTag(NonPagedPool,
49                                  sizeof(DEVICE_OBJECT_POWER_EXTENSION),
50                                  TAG_PO_DOPE);
51     if (!Dope) goto Return;
52 
53     /* Initialize the initial contents of the dope */
54     RtlZeroMemory(Dope, sizeof(DEVICE_OBJECT_POWER_EXTENSION));
55     Dope->DeviceObject = DeviceObject;
56     Dope->State = PowerDeviceUnspecified;
57     InitializeListHead(&Dope->IdleList);
58 
59     /* Make sure only one caller can assign dope to a device */
60     KeAcquireSpinLock(&PopDopeGlobalLock, &OldIrql);
61 
62     /* Make sure the device still has no dope */
63     if (!DeviceExtension->Dope)
64     {
65         /* Give the local dope to this device, and remember we won the race */
66         DeviceExtension->Dope = (PVOID)Dope;
67         Dope = NULL;
68     }
69 
70     /* Allow other dope transactions now */
71     KeReleaseSpinLock(&PopDopeGlobalLock, OldIrql);
72 
73     /* Check if someone other than us already assigned the dope, so free ours */
74     if (Dope) ExFreePoolWithTag(Dope, TAG_PO_DOPE);
75 
76     /* Return the dope to the caller */
77 Return:
78     return (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope;
79 }
80 
81 VOID
82 NTAPI
83 PoVolumeDevice(IN PDEVICE_OBJECT DeviceObject)
84 {
85     PDEVICE_OBJECT_POWER_EXTENSION Dope;
86     PAGED_CODE();
87 
88     /* Get dope from the device (if the device has no dope, it will receive some) */
89     Dope = PopGetDope(DeviceObject);
90     if (Dope)
91     {
92         /* Make sure we can flush safely */
93         KeAcquireGuardedMutex(&PopVolumeLock);
94 
95         /* Add this volume into the list of power-manager volumes */
96         if (!Dope->Volume.Flink) InsertTailList(&PopVolumeDevices, &Dope->Volume);
97 
98         /* Allow flushes to go through */
99         KeReleaseGuardedMutex(&PopVolumeLock);
100     }
101 }
102 
103 VOID
104 NTAPI
105 PoRemoveVolumeDevice(IN PDEVICE_OBJECT DeviceObject)
106 {
107     PDEVICE_OBJECT_POWER_EXTENSION Dope;
108     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
109     KIRQL OldIrql;
110     PAGED_CODE();
111 
112     /* If the device already has the dope, return it */
113     DeviceExtension = IoGetDevObjExtension(DeviceObject);
114     if (!DeviceExtension->Dope)
115     {
116         /* no dope */
117         return;
118     }
119 
120     /* Make sure we can flush safely */
121     KeAcquireGuardedMutex(&PopVolumeLock);
122 
123     /* Get dope from device */
124     Dope = (PDEVICE_OBJECT_POWER_EXTENSION)DeviceExtension->Dope;
125 
126     if (Dope->Volume.Flink)
127     {
128         /* Remove from volume from list */
129         RemoveEntryList(&Dope->Volume);
130     }
131 
132     /* Allow flushes to go through */
133     KeReleaseGuardedMutex(&PopVolumeLock);
134 
135     /* Now remove dope from device object */
136     KeAcquireSpinLock(&PopDopeGlobalLock, &OldIrql);
137 
138     /* remove from dev obj */
139     DeviceExtension->Dope = NULL;
140 
141     /* Release lock */
142     KeReleaseSpinLock(&PopDopeGlobalLock, OldIrql);
143 
144     /* Free dope */
145     ExFreePoolWithTag(Dope, TAG_PO_DOPE);
146 }
147 
148 VOID
149 NTAPI
150 PopFlushVolumeWorker(IN PVOID Context)
151 {
152     PPOP_FLUSH_VOLUME FlushContext = (PPOP_FLUSH_VOLUME)Context;
153     PDEVICE_OBJECT_POWER_EXTENSION Dope;
154     PLIST_ENTRY NextEntry;
155     NTSTATUS Status;
156     UCHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + 512];
157     POBJECT_NAME_INFORMATION NameInfo = (PVOID)Buffer;
158     ULONG Length;
159     OBJECT_ATTRIBUTES ObjectAttributes;
160     HANDLE VolumeHandle;
161     IO_STATUS_BLOCK IoStatusBlock;
162 
163     /* Acquire the flush lock since we're messing with the list */
164     KeAcquireGuardedMutex(&PopVolumeLock);
165 
166     /* Loop the flush list */
167     while (!IsListEmpty(&FlushContext->List))
168     {
169         /* Grab the next (ie: current) entry and remove it */
170         NextEntry = FlushContext->List.Flink;
171         RemoveEntryList(NextEntry);
172 
173         /* Add it back on the volume list */
174         InsertTailList(&PopVolumeDevices, NextEntry);
175 
176         /* Done touching the volume list */
177         KeReleaseGuardedMutex(&PopVolumeLock);
178 
179         /* Get the dope from the volume link */
180         Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume);
181 
182         /* Get the name */
183         Status = ObQueryNameString(Dope->DeviceObject,
184                                    NameInfo,
185                                    sizeof(Buffer),
186                                    &Length);
187         if ((NT_SUCCESS(Status)) && (NameInfo->Name.Buffer))
188         {
189             /* Open the volume */
190             DPRINT("Opening: %wZ\n", &NameInfo->Name);
191             InitializeObjectAttributes(&ObjectAttributes,
192                                        &NameInfo->Name,
193                                        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
194                                        0,
195                                        0);
196             Status = ZwCreateFile(&VolumeHandle,
197                                   SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
198                                   &ObjectAttributes,
199                                   &IoStatusBlock,
200                                   NULL,
201                                   GENERIC_READ | GENERIC_WRITE,
202                                   FILE_SHARE_READ | FILE_SHARE_WRITE,
203                                   FILE_OPEN,
204                                   0,
205                                   NULL,
206                                   0);
207             if (NT_SUCCESS(Status))
208             {
209                 /* Flush it and close it */
210                 DPRINT("Sending flush to: %p\n", VolumeHandle);
211                 ZwFlushBuffersFile(VolumeHandle, &IoStatusBlock);
212                 ZwClose(VolumeHandle);
213             }
214         }
215 
216         /* Acquire the flush lock again since we'll touch the list */
217         KeAcquireGuardedMutex(&PopVolumeLock);
218     }
219 
220     /* One more flush completed... if it was the last, signal the caller */
221     if (!--FlushContext->Count) KeSetEvent(&FlushContext->Wait, IO_NO_INCREMENT, FALSE);
222 
223     /* Serialize with flushers */
224     KeReleaseGuardedMutex(&PopVolumeLock);
225 }
226 
227 VOID
228 NTAPI
229 PopFlushVolumes(IN BOOLEAN ShuttingDown)
230 {
231     POP_FLUSH_VOLUME FlushContext = {{0}};
232     ULONG FlushPolicy;
233     UNICODE_STRING RegistryName = RTL_CONSTANT_STRING(L"\\Registry");
234     OBJECT_ATTRIBUTES ObjectAttributes;
235     HANDLE RegistryHandle;
236     PLIST_ENTRY NextEntry;
237     PDEVICE_OBJECT_POWER_EXTENSION  Dope;
238     ULONG VolumeCount = 0;
239     NTSTATUS Status;
240     HANDLE ThreadHandle;
241     ULONG ThreadCount;
242 
243     /* Setup the flush context */
244     InitializeListHead(&FlushContext.List);
245     KeInitializeEvent(&FlushContext.Wait, NotificationEvent, FALSE);
246 
247     /* What to flush */
248     FlushPolicy = ShuttingDown ? 1 | 2 : PopFlushPolicy;
249     if ((FlushPolicy & 1))
250     {
251         /* Registry flush requested, so open it */
252         DPRINT("Opening registry\n");
253         InitializeObjectAttributes(&ObjectAttributes,
254                                    &RegistryName,
255                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
256                                    NULL,
257                                    NULL);
258         Status = ZwOpenKey(&RegistryHandle, KEY_READ, &ObjectAttributes);
259         if (NT_SUCCESS(Status))
260         {
261             /* Flush the registry */
262             DPRINT("Flushing registry\n");
263             ZwFlushKey(RegistryHandle);
264             ZwClose(RegistryHandle);
265         }
266     }
267 
268     /* Serialize with other flushes */
269     KeAcquireGuardedMutex(&PopVolumeLock);
270 
271     /* Scan the volume list */
272     NextEntry = PopVolumeDevices.Flink;
273     while (NextEntry != &PopVolumeDevices)
274     {
275         /* Get the dope from the link */
276         Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume);
277 
278         /* Grab the next entry now, since we'll be modifying the list */
279         NextEntry = NextEntry->Flink;
280 
281         /* Make sure the object is mounted, writable, exists, and is not a floppy */
282         if (!(Dope->DeviceObject->Vpb->Flags & VPB_MOUNTED) ||
283             (Dope->DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) ||
284             (Dope->DeviceObject->Characteristics & FILE_READ_ONLY_DEVICE) ||
285             ((Dope->DeviceObject->Vpb->RealDevice) &&
286              (Dope->DeviceObject->Vpb->RealDevice->Characteristics & FILE_FLOPPY_DISKETTE)))
287         {
288             /* Not flushable */
289             continue;
290         }
291 
292         /* Remove it from the dope and add it to the flush context list */
293         RemoveEntryList(&Dope->Volume);
294         InsertTailList(&FlushContext.List, &Dope->Volume);
295 
296         /* Next */
297         VolumeCount++;
298     }
299 
300     /* Check if we should skip non-removable devices */
301     if (!(FlushPolicy & 2))
302     {
303         /* ReactOS only implements this routine for shutdown, which requires it */
304         UNIMPLEMENTED;
305     }
306 
307     /* Check if there were no volumes at all */
308     if (!VolumeCount)
309     {
310         /* Nothing to do */
311         KeReleaseGuardedMutex(&PopVolumeLock);
312         return;
313     }
314 
315     /* Allocate up to 8 flusher threads */
316     ThreadCount = min(VolumeCount, 8);
317     InitializeObjectAttributes(&ObjectAttributes,
318                                NULL,
319                                OBJ_KERNEL_HANDLE,
320                                NULL,
321                                NULL);
322 
323     /* We will ourselves become a flusher thread */
324     FlushContext.Count = 1;
325     ThreadCount--;
326 
327     /* Look for any extra ones we might need */
328     while (ThreadCount > 0)
329     {
330         /* Create a new one */
331         ThreadCount--;
332         DPRINT("Creating flush thread\n");
333         Status = PsCreateSystemThread(&ThreadHandle,
334                                       THREAD_ALL_ACCESS,
335                                       &ObjectAttributes,
336                                       0L,
337                                       NULL,
338                                       PopFlushVolumeWorker,
339                                       &FlushContext);
340         if (NT_SUCCESS(Status))
341         {
342             /* One more created... */
343             FlushContext.Count++;
344             ZwClose(ThreadHandle);
345         }
346     }
347 
348     /* Allow flushes to go through */
349     KeReleaseGuardedMutex(&PopVolumeLock);
350 
351     /* Enter the flush work */
352     DPRINT("Local flush\n");
353     PopFlushVolumeWorker(&FlushContext);
354 
355     /* Wait for all flushes to be over */
356     DPRINT("Waiting for flushes\n");
357     KeWaitForSingleObject(&FlushContext.Wait, Executive, KernelMode, FALSE, NULL);
358     DPRINT("Flushes have completed\n");
359 }
360 
361 VOID
362 NTAPI
363 PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension)
364 {
365     PEXTENDED_DEVOBJ_EXTENSION DeviceExtension = (PVOID)DeviceObjectExtension;
366     PAGED_CODE();
367 
368     /* Initialize the power flags */
369     DeviceExtension->PowerFlags = PowerSystemUnspecified & 0xF;
370     DeviceExtension->PowerFlags |= ((PowerDeviceUnspecified << 4) & 0xF0);
371 
372     /* The device object is not on drugs yet */
373     DeviceExtension->Dope = NULL;
374 }
375 
376 /* PUBLIC FUNCTIONS **********************************************************/
377 
378