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 '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, '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 PopFlushVolumeWorker(IN PVOID Context) 104 { 105 PPOP_FLUSH_VOLUME FlushContext = (PPOP_FLUSH_VOLUME)Context; 106 PDEVICE_OBJECT_POWER_EXTENSION Dope; 107 PLIST_ENTRY NextEntry; 108 NTSTATUS Status; 109 UCHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + 512]; 110 POBJECT_NAME_INFORMATION NameInfo = (PVOID)Buffer; 111 ULONG Length; 112 OBJECT_ATTRIBUTES ObjectAttributes; 113 HANDLE VolumeHandle; 114 IO_STATUS_BLOCK IoStatusBlock; 115 116 /* Acquire the flush lock since we're messing with the list */ 117 KeAcquireGuardedMutex(&PopVolumeLock); 118 119 /* Loop the flush list */ 120 while (!IsListEmpty(&FlushContext->List)) 121 { 122 /* Grab the next (ie: current) entry and remove it */ 123 NextEntry = FlushContext->List.Flink; 124 RemoveEntryList(NextEntry); 125 126 /* Add it back on the volume list */ 127 InsertTailList(&PopVolumeDevices, NextEntry); 128 129 /* Done touching the volume list */ 130 KeReleaseGuardedMutex(&PopVolumeLock); 131 132 /* Get the dope from the volume link */ 133 Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume); 134 135 /* Get the name */ 136 Status = ObQueryNameString(Dope->DeviceObject, 137 NameInfo, 138 sizeof(Buffer), 139 &Length); 140 if ((NT_SUCCESS(Status)) && (NameInfo->Name.Buffer)) 141 { 142 /* Open the volume */ 143 DPRINT1("Opening: %wZ\n", &NameInfo->Name); 144 InitializeObjectAttributes(&ObjectAttributes, 145 &NameInfo->Name, 146 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 147 0, 148 0); 149 Status = ZwCreateFile(&VolumeHandle, 150 SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, 151 &ObjectAttributes, 152 &IoStatusBlock, 153 NULL, 154 GENERIC_READ | GENERIC_WRITE, 155 FILE_SHARE_READ | FILE_SHARE_WRITE, 156 FILE_OPEN, 157 0, 158 NULL, 159 0); 160 if (NT_SUCCESS(Status)) 161 { 162 /* Flush it and close it */ 163 DPRINT1("Sending flush to: %lx\n", VolumeHandle); 164 ZwFlushBuffersFile(VolumeHandle, &IoStatusBlock); 165 ZwClose(VolumeHandle); 166 } 167 } 168 169 /* Acquire the flush lock again since we'll touch the list */ 170 KeAcquireGuardedMutex(&PopVolumeLock); 171 } 172 173 /* One more flush completed... if it was the last, signal the caller */ 174 if (!--FlushContext->Count) KeSetEvent(&FlushContext->Wait, IO_NO_INCREMENT, FALSE); 175 176 /* Serialize with flushers */ 177 KeReleaseGuardedMutex(&PopVolumeLock); 178 } 179 180 VOID 181 NTAPI 182 PopFlushVolumes(IN BOOLEAN ShuttingDown) 183 { 184 POP_FLUSH_VOLUME FlushContext = {{0}}; 185 ULONG FlushPolicy; 186 UNICODE_STRING RegistryName = RTL_CONSTANT_STRING(L"\\Registry"); 187 OBJECT_ATTRIBUTES ObjectAttributes; 188 HANDLE RegistryHandle; 189 PLIST_ENTRY NextEntry; 190 PDEVICE_OBJECT_POWER_EXTENSION Dope; 191 ULONG VolumeCount = 0; 192 NTSTATUS Status; 193 HANDLE ThreadHandle; 194 ULONG ThreadCount; 195 196 /* Setup the flush context */ 197 InitializeListHead(&FlushContext.List); 198 KeInitializeEvent(&FlushContext.Wait, NotificationEvent, FALSE); 199 200 /* What to flush */ 201 FlushPolicy = ShuttingDown ? 1 | 2 : PopFlushPolicy; 202 if ((FlushPolicy & 1)) 203 { 204 /* Registry flush requested, so open it */ 205 DPRINT1("Opening registry\n"); 206 InitializeObjectAttributes(&ObjectAttributes, 207 &RegistryName, 208 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 209 NULL, 210 NULL); 211 Status = ZwOpenKey(&RegistryHandle, KEY_READ, &ObjectAttributes); 212 if (NT_SUCCESS(Status)) 213 { 214 /* Flush the registry */ 215 DPRINT1("Flushing registry\n"); 216 ZwFlushKey(RegistryHandle); 217 ZwClose(RegistryHandle); 218 } 219 } 220 221 /* Serialize with other flushes */ 222 KeAcquireGuardedMutex(&PopVolumeLock); 223 224 /* Scan the volume list */ 225 NextEntry = PopVolumeDevices.Flink; 226 while (NextEntry != &PopVolumeDevices) 227 { 228 /* Get the dope from the link */ 229 Dope = CONTAINING_RECORD(NextEntry, DEVICE_OBJECT_POWER_EXTENSION, Volume); 230 231 /* Grab the next entry now, since we'll be modifying the list */ 232 NextEntry = NextEntry->Flink; 233 234 /* Make sure the object is mounted, writable, exists, and is not a floppy */ 235 if (!(Dope->DeviceObject->Vpb->Flags & VPB_MOUNTED) || 236 (Dope->DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) || 237 (Dope->DeviceObject->Characteristics & FILE_READ_ONLY_DEVICE) || 238 ((Dope->DeviceObject->Vpb->RealDevice) && 239 (Dope->DeviceObject->Vpb->RealDevice->Characteristics & FILE_FLOPPY_DISKETTE))) 240 { 241 /* Not flushable */ 242 continue; 243 } 244 245 /* Remove it from the dope and add it to the flush context list */ 246 RemoveEntryList(&Dope->Volume); 247 InsertTailList(&FlushContext.List, &Dope->Volume); 248 249 /* Next */ 250 VolumeCount++; 251 } 252 253 /* Check if we should skip non-removable devices */ 254 if (!(FlushPolicy & 2)) 255 { 256 /* ReactOS only implements this routine for shutdown, which requires it */ 257 UNIMPLEMENTED; 258 while (TRUE); 259 } 260 261 /* Check if there were no volumes at all */ 262 if (!VolumeCount) 263 { 264 /* Nothing to do */ 265 KeReleaseGuardedMutex(&PopVolumeLock); 266 return; 267 } 268 269 /* Allocate up to 8 flusher threads */ 270 ThreadCount = min(VolumeCount, 8); 271 InitializeObjectAttributes(&ObjectAttributes, 272 NULL, 273 OBJ_KERNEL_HANDLE, 274 NULL, 275 NULL); 276 277 /* We will ourselves become a flusher thread */ 278 FlushContext.Count = 1; 279 ThreadCount--; 280 281 /* Look for any extra ones we might need */ 282 while (ThreadCount > 0) 283 { 284 /* Create a new one */ 285 ThreadCount--; 286 DPRINT1("Creating flush thread\n"); 287 Status = PsCreateSystemThread(&ThreadHandle, 288 THREAD_ALL_ACCESS, 289 &ObjectAttributes, 290 0L, 291 NULL, 292 PopFlushVolumeWorker, 293 &FlushContext); 294 if (NT_SUCCESS(Status)) 295 { 296 /* One more created... */ 297 FlushContext.Count++; 298 ZwClose(ThreadHandle); 299 } 300 } 301 302 /* Allow flushes to go through */ 303 KeReleaseGuardedMutex(&PopVolumeLock); 304 305 /* Enter the flush work */ 306 DPRINT1("Local flush\n"); 307 PopFlushVolumeWorker(&FlushContext); 308 309 /* Wait for all flushes to be over */ 310 DPRINT1("Waiting for flushes\n"); 311 KeWaitForSingleObject(&FlushContext.Wait, Executive, KernelMode, FALSE, NULL); 312 DPRINT1("Flushes have completed\n"); 313 } 314 315 VOID 316 NTAPI 317 PoInitializeDeviceObject(IN OUT PDEVOBJ_EXTENSION DeviceObjectExtension) 318 { 319 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension = (PVOID)DeviceObjectExtension; 320 PAGED_CODE(); 321 322 /* Initialize the power flags */ 323 DeviceExtension->PowerFlags = PowerSystemUnspecified & 0xF; 324 DeviceExtension->PowerFlags |= ((PowerDeviceUnspecified << 4) & 0xF0); 325 326 /* The device object is not on drugs yet */ 327 DeviceExtension->Dope = NULL; 328 } 329 330 /* PUBLIC FUNCTIONS **********************************************************/ 331 332