xref: /reactos/drivers/filesystems/cdfs/write.c (revision 53221834)
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