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