[NTOSKRNL] Implement ObSetDirectoryDeviceMap
[reactos.git] / ntoskrnl / po / povolume.c
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