1 /*++ 2 3 Copyright (c) 1989-2000 Microsoft Corporation 4 5 Module Name: 6 7 Write.c 8 9 Abstract: 10 11 This module implements the File Write routine for Write called by the 12 Fsd/Fsp dispatch drivers. 13 14 15 --*/ 16 17 #include "cdprocs.h" 18 19 // 20 // The Bug check file id for this module 21 // 22 23 #define BugCheckFileId (CDFS_BUG_CHECK_WRITE) 24 25 // 26 // VOID 27 // SafeZeroMemory ( 28 // _Out_ PUCHAR At, 29 // _In_ ULONG ByteCount 30 // ); 31 // 32 33 // 34 // This macro just puts a nice little try-except around RtlZeroMemory 35 // 36 37 #ifndef __REACTOS__ 38 #define SafeZeroMemory(IC,AT,BYTE_COUNT) { \ 39 _SEH2_TRY { \ 40 RtlZeroMemory( (AT), (BYTE_COUNT) ); \ 41 __pragma(warning(suppress: 6320)) \ 42 } _SEH2_EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { \ 43 CdRaiseStatus( IC, STATUS_INVALID_USER_BUFFER ); \ 44 } _SEH2_END; \ 45 } 46 #else 47 #define SafeZeroMemory(IC,AT,BYTE_COUNT) { \ 48 _SEH2_TRY { \ 49 RtlZeroMemory( (AT), (BYTE_COUNT) ); \ 50 } _SEH2_EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { \ 51 CdRaiseStatus( IC, STATUS_INVALID_USER_BUFFER ); \ 52 } _SEH2_END; \ 53 } 54 #endif 55 56 #ifdef ALLOC_PRAGMA 57 #pragma alloc_text(PAGE, CdCommonWrite) 58 #endif 59 60 61 _Requires_lock_held_(_Global_critical_region_) 62 NTSTATUS 63 CdCommonWrite ( 64 _Inout_ PIRP_CONTEXT IrpContext, 65 _Inout_ PIRP Irp 66 ) 67 68 /*++ 69 70 Routine Description: 71 72 This is the common entry point for NtWriteFile calls. For synchronous requests, 73 CommonWrite will complete the request in the current thread. If not 74 synchronous the request will be passed to the Fsp if there is a need to 75 block. 76 77 Arguments: 78 79 Irp - Supplies the Irp to process 80 81 Return Value: 82 83 NTSTATUS - The result of this operation. 84 85 --*/ 86 87 { 88 NTSTATUS Status = STATUS_SUCCESS; 89 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); 90 91 TYPE_OF_OPEN TypeOfOpen; 92 PFCB Fcb; 93 PCCB Ccb; 94 95 BOOLEAN Wait; 96 ULONG SynchronousIo; 97 PVOID UserBuffer; 98 99 LONGLONG StartingOffset; 100 LONGLONG ByteRange; 101 ULONG ByteCount; 102 ULONG WriteByteCount; 103 ULONG OriginalByteCount; 104 105 BOOLEAN ReleaseFile = TRUE; 106 107 CD_IO_CONTEXT LocalIoContext; 108 109 PAGED_CODE(); 110 111 // 112 // If this is a zero length write then return SUCCESS immediately. 113 // 114 115 if (IrpSp->Parameters.Write.Length == 0) { 116 117 CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); 118 return STATUS_SUCCESS; 119 } 120 121 // 122 // Decode the file object and verify we support write on this. It 123 // must be a volume file. 124 // 125 126 TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); 127 128 // Internal lock object is acquired if return status is STATUS_PENDING 129 _Analysis_suppress_lock_checking_(Fcb->Resource); 130 131 if (TypeOfOpen != UserVolumeOpen) { 132 133 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); 134 return STATUS_INVALID_DEVICE_REQUEST; 135 } 136 137 // 138 // Examine our input parameters to determine if this is noncached and/or 139 // a paging io operation. 140 // 141 142 Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); 143 SynchronousIo = FlagOn( IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO ); 144 145 146 // 147 // Extract the range of the Io. 148 // 149 150 StartingOffset = IrpSp->Parameters.Write.ByteOffset.QuadPart; 151 OriginalByteCount = ByteCount = IrpSp->Parameters.Write.Length; 152 153 ByteRange = StartingOffset + ByteCount; 154 155 // 156 // Acquire the file shared to perform the write. 157 // 158 159 CdAcquireFileShared( IrpContext, Fcb ); 160 161 // 162 // Use a try-finally to facilitate cleanup. 163 // 164 165 _SEH2_TRY { 166 167 // 168 // Verify the Fcb. Allow writes if this is a DASD handle that is 169 // dismounting the volume. 170 // 171 172 if (!FlagOn( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE )) { 173 174 CdVerifyFcbOperation( IrpContext, Fcb ); 175 } 176 177 if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) { 178 179 // 180 // Complete the request if it begins beyond the end of file. 181 // 182 183 if (StartingOffset >= Fcb->FileSize.QuadPart) { 184 185 try_return( Status = STATUS_END_OF_FILE ); 186 } 187 188 // 189 // Truncate the write if it extends beyond the end of the file. 190 // 191 192 if (ByteRange > Fcb->FileSize.QuadPart) { 193 194 ByteCount = (ULONG) (Fcb->FileSize.QuadPart - StartingOffset); 195 ByteRange = Fcb->FileSize.QuadPart; 196 } 197 } 198 199 // 200 // If we have an unaligned transfer then post this request if 201 // we can't wait. Unaligned means that the starting offset 202 // is not on a sector boundary or the write is not integral 203 // sectors. 204 // 205 206 WriteByteCount = BlockAlign( Fcb->Vcb, ByteCount ); 207 208 if (SectorOffset( StartingOffset ) || 209 SectorOffset( WriteByteCount ) || 210 (WriteByteCount > OriginalByteCount)) { 211 212 if (!Wait) { 213 214 CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); 215 } 216 217 // 218 // Make sure we don't overwrite the buffer. 219 // 220 221 WriteByteCount = ByteCount; 222 } 223 224 // 225 // Initialize the IoContext for the write. 226 // If there is a context pointer, we need to make sure it was 227 // allocated and not a stale stack pointer. 228 // 229 230 if (IrpContext->IoContext == NULL || 231 !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) { 232 233 // 234 // If we can wait, use the context on the stack. Otherwise 235 // we need to allocate one. 236 // 237 238 if (Wait) { 239 240 IrpContext->IoContext = &LocalIoContext; 241 ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); 242 243 } else { 244 245 IrpContext->IoContext = CdAllocateIoContext(); 246 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); 247 } 248 } 249 250 RtlZeroMemory( IrpContext->IoContext, sizeof( CD_IO_CONTEXT ) ); 251 252 // 253 // Store whether we allocated this context structure in the structure 254 // itself. 255 // 256 257 IrpContext->IoContext->AllocatedContext = 258 BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); 259 260 if (Wait) { 261 262 KeInitializeEvent( &IrpContext->IoContext->SyncEvent, 263 NotificationEvent, 264 FALSE ); 265 266 } else { 267 268 IrpContext->IoContext->ResourceThreadId = ExGetCurrentResourceThread(); 269 IrpContext->IoContext->Resource = Fcb->Resource; 270 IrpContext->IoContext->RequestedByteCount = ByteCount; 271 } 272 273 Irp->IoStatus.Information = WriteByteCount; 274 275 // 276 // Set the FO_MODIFIED flag here to trigger a verify when this 277 // handle is closed. Note that we can err on the conservative 278 // side with no problem, i.e. if we accidently do an extra 279 // verify there is no problem. 280 // 281 282 SetFlag( IrpSp->FileObject->Flags, FO_FILE_MODIFIED ); 283 284 // 285 // Dasd access is always non-cached. Call the Dasd write routine to 286 // perform the actual write. 287 // 288 289 Status = CdVolumeDasdWrite( IrpContext, Fcb, StartingOffset, WriteByteCount ); 290 291 // 292 // Don't complete this request now if STATUS_PENDING was returned. 293 // 294 295 if (Status == STATUS_PENDING) { 296 297 Irp = NULL; 298 ReleaseFile = FALSE; 299 300 // 301 // Test is we should zero part of the buffer or update the 302 // synchronous file position. 303 // 304 305 } else { 306 307 // 308 // Convert any unknown error code to IO_ERROR. 309 // 310 311 if (!NT_SUCCESS( Status )) { 312 313 // 314 // Set the information field to zero. 315 // 316 317 Irp->IoStatus.Information = 0; 318 319 // 320 // Raise if this is a user induced error. 321 // 322 323 if (IoIsErrorUserInduced( Status )) { 324 325 CdRaiseStatus( IrpContext, Status ); 326 } 327 328 Status = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR ); 329 330 // 331 // Check if there is any portion of the user's buffer to zero. 332 // 333 334 } else if (WriteByteCount != ByteCount) { 335 336 CdMapUserBuffer( IrpContext, &UserBuffer ); 337 338 SafeZeroMemory( IrpContext, 339 Add2Ptr( UserBuffer, 340 ByteCount, 341 PVOID ), 342 WriteByteCount - ByteCount ); 343 344 Irp->IoStatus.Information = ByteCount; 345 } 346 347 // 348 // Update the file position if this is a synchronous request. 349 // 350 351 if (SynchronousIo && NT_SUCCESS( Status )) { 352 353 IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange; 354 } 355 } 356 357 try_exit: NOTHING; 358 } _SEH2_FINALLY { 359 360 // 361 // Release the Fcb. 362 // 363 364 if (ReleaseFile) { 365 366 CdReleaseFile( IrpContext, Fcb ); 367 } 368 } _SEH2_END; 369 370 // 371 // Post the request if we got CANT_WAIT. 372 // 373 374 if (Status == STATUS_CANT_WAIT) { 375 376 Status = CdFsdPostRequest( IrpContext, Irp ); 377 378 // 379 // Otherwise complete the request. 380 // 381 382 } else { 383 384 CdCompleteRequest( IrpContext, Irp, Status ); 385 } 386 387 return Status; 388 } 389 390 391