[CDFS_NEW] Accept STATUS_NOT_IMPLEMENTED for IOCTL_CDROM_READ_TOC_EX fallback to...
[reactos.git] / drivers / filesystems / cdfs_new / write.c
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 \f
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