1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/fsrtl/filtrctx.c 5 * PURPOSE: File Stream Filter Context support for File System Drivers 6 * PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* PRIVATE FUNCTIONS *********************************************************/ 16 17 typedef struct _FILE_OBJECT_FILTER_CONTEXTS 18 { 19 FAST_MUTEX FilterContextsMutex; 20 LIST_ENTRY FilterContexts; 21 } FILE_OBJECT_FILTER_CONTEXTS, *PFILE_OBJECT_FILTER_CONTEXTS; 22 23 /* 24 * @implemented 25 */ 26 VOID 27 NTAPI 28 FsRtlPTeardownPerFileObjectContexts(IN PFILE_OBJECT FileObject) 29 { 30 PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL; 31 32 ASSERT(FileObject); 33 34 if (!(FOContext = IoGetFileObjectFilterContext(FileObject))) 35 { 36 return; 37 } 38 39 ASSERT(IoChangeFileObjectFilterContext(FileObject, FOContext, FALSE) == STATUS_SUCCESS); 40 ASSERT(IsListEmpty(&(FOContext->FilterContexts))); 41 42 ExFreePoolWithTag(FOContext, 'FOCX'); 43 } 44 45 46 /* PUBLIC FUNCTIONS **********************************************************/ 47 48 /*++ 49 * @name FsRtlIsPagingFile 50 * @implemented NT 5.2 51 * 52 * The FsRtlIsPagingFile routine checks if the FileObject is a Paging File. 53 * 54 * @param FileObject 55 * A pointer to the File Object to be tested. 56 * 57 * @return TRUE if the File is a Paging File, FALSE otherwise. 58 * 59 * @remarks None. 60 * 61 *--*/ 62 LOGICAL 63 NTAPI 64 FsRtlIsPagingFile(IN PFILE_OBJECT FileObject) 65 { 66 return MmIsFileObjectAPagingFile(FileObject); 67 } 68 69 /* 70 * @implemented 71 */ 72 PFSRTL_PER_FILEOBJECT_CONTEXT 73 NTAPI 74 FsRtlLookupPerFileObjectContext(IN PFILE_OBJECT FileObject, 75 IN PVOID OwnerId OPTIONAL, 76 IN PVOID InstanceId OPTIONAL) 77 { 78 PLIST_ENTRY NextEntry; 79 PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL; 80 PFSRTL_PER_FILEOBJECT_CONTEXT TmpPerFOContext, PerFOContext = NULL; 81 82 if (!FileObject || !(FOContext = IoGetFileObjectFilterContext(FileObject))) 83 { 84 return NULL; 85 } 86 87 ExAcquireFastMutex(&(FOContext->FilterContextsMutex)); 88 89 /* If list is empty, no need to browse it */ 90 if (!IsListEmpty(&(FOContext->FilterContexts))) 91 { 92 for (NextEntry = FOContext->FilterContexts.Flink; 93 NextEntry != &(FOContext->FilterContexts); 94 NextEntry = NextEntry->Flink) 95 { 96 /* If we don't have any criteria for search, first entry will be enough */ 97 if (!OwnerId && !InstanceId) 98 { 99 PerFOContext = (PFSRTL_PER_FILEOBJECT_CONTEXT)NextEntry; 100 break; 101 } 102 /* Else, we've to find something that matches with the parameters. */ 103 else 104 { 105 TmpPerFOContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_FILEOBJECT_CONTEXT, Links); 106 if ((InstanceId && TmpPerFOContext->InstanceId == InstanceId && TmpPerFOContext->OwnerId == OwnerId) || 107 (OwnerId && TmpPerFOContext->OwnerId == OwnerId)) 108 { 109 PerFOContext = TmpPerFOContext; 110 break; 111 } 112 } 113 } 114 } 115 116 ExReleaseFastMutex(&(FOContext->FilterContextsMutex)); 117 118 return PerFOContext; 119 } 120 121 /* 122 * @implemented 123 */ 124 PFSRTL_PER_STREAM_CONTEXT 125 NTAPI 126 FsRtlLookupPerStreamContextInternal(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader, 127 IN PVOID OwnerId OPTIONAL, 128 IN PVOID InstanceId OPTIONAL) 129 { 130 PLIST_ENTRY NextEntry; 131 PFSRTL_PER_STREAM_CONTEXT TmpPerStreamContext, PerStreamContext = NULL; 132 133 ASSERT(AdvFcbHeader); 134 ASSERT(FlagOn(AdvFcbHeader->Flags2, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS)); 135 136 ExAcquireFastMutex(AdvFcbHeader->FastMutex); 137 138 /* If list is empty, no need to browse it */ 139 if (!IsListEmpty(&(AdvFcbHeader->FilterContexts))) 140 { 141 for (NextEntry = AdvFcbHeader->FilterContexts.Flink; 142 NextEntry != &(AdvFcbHeader->FilterContexts); 143 NextEntry = NextEntry->Flink) 144 { 145 /* If we don't have any criteria for search, first entry will be enough */ 146 if (!OwnerId && !InstanceId) 147 { 148 PerStreamContext = (PFSRTL_PER_STREAM_CONTEXT)NextEntry; 149 break; 150 } 151 /* Else, we've to find something that matches with the parameters. */ 152 else 153 { 154 TmpPerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links); 155 if ((InstanceId && TmpPerStreamContext->InstanceId == InstanceId && TmpPerStreamContext->OwnerId == OwnerId) || 156 (OwnerId && TmpPerStreamContext->OwnerId == OwnerId)) 157 { 158 PerStreamContext = TmpPerStreamContext; 159 break; 160 } 161 } 162 } 163 } 164 165 ExReleaseFastMutex(AdvFcbHeader->FastMutex); 166 167 return PerStreamContext; 168 } 169 170 /* 171 * @implemented 172 */ 173 NTSTATUS 174 NTAPI 175 FsRtlInsertPerFileObjectContext(IN PFILE_OBJECT FileObject, 176 IN PFSRTL_PER_FILEOBJECT_CONTEXT Ptr) 177 { 178 PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL; 179 NTSTATUS Status; 180 181 if (!FileObject) 182 { 183 return STATUS_INVALID_PARAMETER; 184 } 185 186 if (!(FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION)) 187 { 188 return STATUS_INVALID_DEVICE_REQUEST; 189 } 190 191 /* Get filter contexts */ 192 FOContext = IoGetFileObjectFilterContext(FileObject); 193 if (!FOContext) 194 { 195 /* If there's none, allocate new structure */ 196 FOContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(FILE_OBJECT_FILTER_CONTEXTS), 'FOCX'); 197 if (!FOContext) 198 { 199 return STATUS_INSUFFICIENT_RESOURCES; 200 } 201 202 /* Initialize it */ 203 ExInitializeFastMutex(&(FOContext->FilterContextsMutex)); 204 InitializeListHead(&(FOContext->FilterContexts)); 205 206 /* Set it */ 207 Status = IoChangeFileObjectFilterContext(FileObject, FOContext, TRUE); 208 if (!NT_SUCCESS(Status)) 209 { 210 /* If it fails, it means that someone else has set it in the meanwhile */ 211 ExFreePoolWithTag(FOContext, 'FOCX'); 212 213 /* So, we can get it */ 214 FOContext = IoGetFileObjectFilterContext(FileObject); 215 if (!FOContext) 216 { 217 /* If we fall down here, something went very bad. This shouldn't happen */ 218 ASSERT(FALSE); 219 return STATUS_UNSUCCESSFUL; 220 } 221 } 222 } 223 224 /* Finally, insert */ 225 ExAcquireFastMutex(&(FOContext->FilterContextsMutex)); 226 InsertHeadList(&(FOContext->FilterContexts), &(Ptr->Links)); 227 ExReleaseFastMutex(&(FOContext->FilterContextsMutex)); 228 229 return STATUS_SUCCESS; 230 } 231 232 /* 233 * @implemented 234 */ 235 NTSTATUS 236 NTAPI 237 FsRtlInsertPerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader, 238 IN PFSRTL_PER_STREAM_CONTEXT PerStreamContext) 239 { 240 if (!(AdvFcbHeader) || !(AdvFcbHeader->Flags2 & FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS)) 241 { 242 return STATUS_INVALID_DEVICE_REQUEST; 243 } 244 245 ExAcquireFastMutex(AdvFcbHeader->FastMutex); 246 InsertHeadList(&(AdvFcbHeader->FilterContexts), &(PerStreamContext->Links)); 247 ExReleaseFastMutex(AdvFcbHeader->FastMutex); 248 return STATUS_SUCCESS; 249 } 250 251 /* 252 * @implemented 253 */ 254 PFSRTL_PER_FILEOBJECT_CONTEXT 255 NTAPI 256 FsRtlRemovePerFileObjectContext(IN PFILE_OBJECT FileObject, 257 IN PVOID OwnerId OPTIONAL, 258 IN PVOID InstanceId OPTIONAL) 259 { 260 PLIST_ENTRY NextEntry; 261 PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL; 262 PFSRTL_PER_FILEOBJECT_CONTEXT TmpPerFOContext, PerFOContext = NULL; 263 264 if (!FileObject || !(FOContext = IoGetFileObjectFilterContext(FileObject))) 265 { 266 return NULL; 267 } 268 269 ExAcquireFastMutex(&(FOContext->FilterContextsMutex)); 270 271 /* If list is empty, no need to browse it */ 272 if (!IsListEmpty(&(FOContext->FilterContexts))) 273 { 274 for (NextEntry = FOContext->FilterContexts.Flink; 275 NextEntry != &(FOContext->FilterContexts); 276 NextEntry = NextEntry->Flink) 277 { 278 /* If we don't have any criteria for search, first entry will be enough */ 279 if (!OwnerId && !InstanceId) 280 { 281 PerFOContext = (PFSRTL_PER_FILEOBJECT_CONTEXT)NextEntry; 282 break; 283 } 284 /* Else, we've to find something that matches with the parameters. */ 285 else 286 { 287 TmpPerFOContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_FILEOBJECT_CONTEXT, Links); 288 if ((InstanceId && TmpPerFOContext->InstanceId == InstanceId && TmpPerFOContext->OwnerId == OwnerId) || 289 (OwnerId && TmpPerFOContext->OwnerId == OwnerId)) 290 { 291 PerFOContext = TmpPerFOContext; 292 break; 293 } 294 } 295 } 296 297 /* Finally remove entry from list */ 298 if (PerFOContext) 299 { 300 RemoveEntryList(&(PerFOContext->Links)); 301 } 302 } 303 304 ExReleaseFastMutex(&(FOContext->FilterContextsMutex)); 305 306 return PerFOContext; 307 } 308 309 /* 310 * @implemented 311 */ 312 PFSRTL_PER_STREAM_CONTEXT 313 NTAPI 314 FsRtlRemovePerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader, 315 IN PVOID OwnerId OPTIONAL, 316 IN PVOID InstanceId OPTIONAL) 317 { 318 PLIST_ENTRY NextEntry; 319 PFSRTL_PER_STREAM_CONTEXT TmpPerStreamContext, PerStreamContext = NULL; 320 321 if (!(AdvFcbHeader) || !(AdvFcbHeader->Flags2 & FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS)) 322 { 323 return NULL; 324 } 325 326 ExAcquireFastMutex(AdvFcbHeader->FastMutex); 327 /* If list is empty, no need to browse it */ 328 if (!IsListEmpty(&(AdvFcbHeader->FilterContexts))) 329 { 330 for (NextEntry = AdvFcbHeader->FilterContexts.Flink; 331 NextEntry != &(AdvFcbHeader->FilterContexts); 332 NextEntry = NextEntry->Flink) 333 { 334 /* If we don't have any criteria for search, first entry will be enough */ 335 if (!OwnerId && !InstanceId) 336 { 337 PerStreamContext = (PFSRTL_PER_STREAM_CONTEXT)NextEntry; 338 break; 339 } 340 /* Else, we've to find something that matches with the parameters. */ 341 else 342 { 343 TmpPerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links); 344 if ((InstanceId && TmpPerStreamContext->InstanceId == InstanceId && TmpPerStreamContext->OwnerId == OwnerId) || 345 (OwnerId && TmpPerStreamContext->OwnerId == OwnerId)) 346 { 347 PerStreamContext = TmpPerStreamContext; 348 break; 349 } 350 } 351 } 352 353 /* Finally remove entry from list */ 354 if (PerStreamContext) 355 { 356 RemoveEntryList(&(PerStreamContext->Links)); 357 } 358 } 359 ExReleaseFastMutex(AdvFcbHeader->FastMutex); 360 361 return PerStreamContext; 362 363 } 364 365 /* 366 * @implemented 367 */ 368 VOID 369 NTAPI 370 FsRtlTeardownPerStreamContexts(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader) 371 { 372 PLIST_ENTRY NextEntry; 373 volatile BOOLEAN IsMutexLocked = FALSE; 374 PFSRTL_PER_STREAM_CONTEXT PerStreamContext; 375 376 _SEH2_TRY 377 { 378 /* Acquire mutex to deal with the list */ 379 ExAcquireFastMutex(AdvFcbHeader->FastMutex); 380 IsMutexLocked = TRUE; 381 382 /* While there are items... */ 383 while (!IsListEmpty(&(AdvFcbHeader->FilterContexts))) 384 { 385 /* ...remove one */ 386 NextEntry = RemoveHeadList(&(AdvFcbHeader->FilterContexts)); 387 PerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links); 388 389 /* Release mutex before calling callback */ 390 ExReleaseFastMutex(AdvFcbHeader->FastMutex); 391 IsMutexLocked = FALSE; 392 393 /* Call the callback */ 394 ASSERT(PerStreamContext->FreeCallback); 395 (*PerStreamContext->FreeCallback)(PerStreamContext); 396 397 /* Relock the list to continue */ 398 ExAcquireFastMutex(AdvFcbHeader->FastMutex); 399 IsMutexLocked = TRUE; 400 } 401 } 402 _SEH2_FINALLY 403 { 404 /* If mutex was locked, release */ 405 if (IsMutexLocked) 406 { 407 ExReleaseFastMutex(AdvFcbHeader->FastMutex); 408 } 409 } 410 _SEH2_END; 411 } 412