Windows: File Attribute Reporting Consistency
[openafs.git] / src / WINNT / afsrdr / kernel / lib / AFSIoSupport.cpp
1 /*
2  * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
3  * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistributions of source code must retain the above copyright notice,
11  *   this list of conditions and the following disclaimer.
12  * - Redistributions in binary form must reproduce the above copyright
13  *   notice,
14  *   this list of conditions and the following disclaimer in the
15  *   documentation
16  *   and/or other materials provided with the distribution.
17  * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
18  *   nor the names of their contributors may be used to endorse or promote
19  *   products derived from this software without specific prior written
20  *   permission from Kernel Drivers, LLC and Your File System, Inc.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
26  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 //
36 // File: AFSIoSupport.cpp
37 //
38
39 #include "AFSCommon.h"
40
41 //
42 // Called in the paging or non cached read and write paths to get the
43 // first and last extent in a span.  We also the count of how many
44 // discontiguous extents the are in the cache file.
45 //
46
47 NTSTATUS
48 AFSGetExtents( IN AFSFcb *Fcb,
49                IN PLARGE_INTEGER Offset,
50                IN ULONG Length,
51                OUT AFSExtent *From,
52                OUT ULONG *ExtentCount,
53                OUT ULONG *RunCount)
54 {
55     UNREFERENCED_PARAMETER(Fcb);
56     NTSTATUS           ntStatus = STATUS_SUCCESS;
57     ULONG              extentsCount = 0, runCount = 0;
58     AFSExtent         *pEndExtent = NULL;
59     LARGE_INTEGER      liLastCacheOffset;
60
61     *ExtentCount = 0;
62
63     __Enter
64     {
65         ASSERT(AFSExtentContains(From, Offset));
66
67         pEndExtent = From;
68         extentsCount = 1;
69         runCount = 1;
70         liLastCacheOffset.QuadPart = pEndExtent->CacheOffset.QuadPart + pEndExtent->Size;
71
72         while ((Offset->QuadPart + Length) >
73                pEndExtent->FileOffset.QuadPart + pEndExtent->Size) {
74
75             pEndExtent = NextExtent(pEndExtent, AFS_EXTENTS_LIST);
76
77             if (liLastCacheOffset.QuadPart != pEndExtent->CacheOffset.QuadPart) {
78                 //
79                 // Discontiguous (in the cache)
80                 //
81                 runCount += 1;
82
83             }
84
85             extentsCount+= 1;
86
87             liLastCacheOffset.QuadPart = pEndExtent->CacheOffset.QuadPart + pEndExtent->Size;
88         }
89
90         *ExtentCount = extentsCount;
91         *RunCount = runCount;
92         ntStatus = STATUS_SUCCESS;
93     }
94     return ntStatus;
95 }
96
97
98 NTSTATUS
99 AFSSetupIoRun( IN PDEVICE_OBJECT CacheDevice,
100                IN PIRP           MasterIrp,
101                IN PVOID          SystemBuffer,
102                IN OUT AFSIoRun  *IoRuns,
103                IN PLARGE_INTEGER Start,
104                IN ULONG          Length,
105                IN AFSExtent     *From,
106                IN OUT ULONG     *RunCount)
107 {
108     UNREFERENCED_PARAMETER(MasterIrp);
109     //
110     // Do all the work which we can prior to firing off the IRP
111     // (allocate them, calculate offsets and so on)
112     //
113     LARGE_INTEGER  liCacheOffset;
114     LARGE_INTEGER  liFileOffset = *Start;
115     ULONG          ulCurrLength;
116     ULONG          ulReadOffset;
117     ULONG          ulCurrRun = 0;
118     AFSExtent     *pExtent = From;
119     AFSExtent     *pNextExtent = NULL;
120     BOOLEAN        done = FALSE;
121     NTSTATUS       ntStatus = STATUS_SUCCESS;
122
123     __Enter
124     {
125
126         while( !done)
127         {
128             ASSERT( liFileOffset.QuadPart >= pExtent->FileOffset.QuadPart );
129
130             liCacheOffset = pExtent->CacheOffset;
131             liCacheOffset.QuadPart += (liFileOffset.QuadPart -
132                                        pExtent->FileOffset.QuadPart);
133
134             ulCurrLength = 0;
135
136             //
137             // While there is still data to process, loop
138             //
139             while ( (pExtent->FileOffset.QuadPart + pExtent->Size) <
140                     (Start->QuadPart + Length) )
141             {
142                 //
143                 // Collapse the read if we can
144                 //
145                 pNextExtent = NextExtent( pExtent, AFS_EXTENTS_LIST);
146
147                 if (pNextExtent->CacheOffset.QuadPart !=
148                     (pExtent->CacheOffset.QuadPart + pExtent->Size))
149                 {
150                     //
151                     // This extent and the next extent are not contiguous
152                     //
153                     break;
154                 }
155                 pExtent = pNextExtent;
156             }
157
158             //
159             // So, this run goes from liCacheOffset to the end of pExtent
160             //
161
162             if ((pExtent->FileOffset.QuadPart + pExtent->Size) >= (Start->QuadPart + Length))
163             {
164                 //
165                 // The last extent overruns the area we care
166                 // about, so trim it back.
167                 //
168                 ulCurrLength = (ULONG) (Start->QuadPart + Length -
169                                         liFileOffset.QuadPart);
170                 //
171                 // But we are done
172                 //
173                 done = TRUE;
174             }
175             else
176             {
177                 //
178                 // Length is from the LiFileOffset to the end of this
179                 // extent.
180                 //
181                 ulCurrLength = (ULONG) (pExtent->FileOffset.QuadPart + pExtent->Size -
182                                         liFileOffset.QuadPart);
183                 done = FALSE;
184             }
185
186             //
187             // Calculate the offset into the buffer
188             //
189             ulReadOffset = (ULONG) (liFileOffset.QuadPart - Start->QuadPart);
190
191             IoRuns[ulCurrRun].CacheOffset = liCacheOffset;
192             IoRuns[ulCurrRun].ByteCount = ulCurrLength;
193
194             IoRuns[ulCurrRun].ChildIrp = IoAllocateIrp( CacheDevice->StackSize + 1,
195                                                         FALSE);
196
197             if (NULL == IoRuns[ulCurrRun].ChildIrp)
198             {
199                 try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES );
200             }
201
202             IoRuns[ulCurrRun].ChildIrp->UserBuffer = ((PCHAR) SystemBuffer) + ulReadOffset;
203             IoRuns[ulCurrRun].ChildIrp->Tail.Overlay.Thread = PsGetCurrentThread();
204             IoRuns[ulCurrRun].ChildIrp->RequestorMode = KernelMode;
205             IoRuns[ulCurrRun].ChildIrp->MdlAddress = NULL;
206
207             ulCurrRun ++;
208             if (!done)
209             {
210                 ASSERT(pNextExtent != NULL && pNextExtent != pExtent);
211                 //
212                 // Move cursors forward
213                 //
214                 pExtent = pNextExtent;
215                 liFileOffset = pExtent->FileOffset;
216             }
217         }
218         ASSERT(ulCurrRun <= *RunCount);
219 try_exit:
220         if (!NT_SUCCESS(ntStatus))
221         {
222             for (ulCurrRun = 0 ; ulCurrRun < *RunCount; ulCurrRun++)
223             {
224                 if (IoRuns[ulCurrRun].ChildIrp) {
225                     IoFreeIrp( IoRuns[ulCurrRun].ChildIrp );
226                     IoRuns[ulCurrRun].ChildIrp = NULL;
227                 }
228             }
229         }
230         else
231         {
232             *RunCount = ulCurrRun;
233         }
234     }
235     return ntStatus;
236 }
237
238 //
239 // We manage our own scatter / gather.  This completion
240 // function calls into our generic function.
241 //
242
243 static
244 NTSTATUS
245 CompletionFunction(IN PDEVICE_OBJECT DeviceObject,
246                    PIRP Irp,
247                    PVOID Context)
248 {
249     UNREFERENCED_PARAMETER(DeviceObject);
250     AFSGatherIo *pGather = (AFSGatherIo *) Context;
251
252     AFSCompleteIo( pGather, Irp->IoStatus.Status);
253
254     if (Irp->MdlAddress) {
255         if (Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED) {
256             MmUnlockPages(Irp->MdlAddress);
257         }
258         IoFreeMdl(Irp->MdlAddress);
259         Irp->MdlAddress = NULL;
260     }
261     IoFreeIrp(Irp);
262
263     //
264     // Tell io manager that we own the Irp
265     //
266     return STATUS_MORE_PROCESSING_REQUIRED;
267 }
268
269 NTSTATUS
270 AFSStartIos( IN PFILE_OBJECT     CacheFileObject,
271              IN UCHAR            FunctionCode,
272              IN ULONG            IrpFlags,
273              IN AFSIoRun        *IoRun,
274              IN ULONG            Count,
275              IN OUT AFSGatherIo *Gather)
276 {
277
278     PIO_STACK_LOCATION  pIoStackLocation = NULL;
279     PIRP                pIrp;
280     NTSTATUS            ntStatus = STATUS_SUCCESS;
281     PDEVICE_OBJECT      pDeviceObject = NULL;
282     LONG                lCount;
283
284     __Enter
285     {
286
287         pDeviceObject = IoGetRelatedDeviceObject( CacheFileObject);
288
289         for (ULONG i = 0; i < Count; i++)
290         {
291
292             pIrp = IoRun[i].ChildIrp;
293
294             pIoStackLocation = IoGetNextIrpStackLocation( pIrp);
295
296             pIrp->Flags = IrpFlags;
297             pIoStackLocation->MajorFunction = FunctionCode;
298             pIoStackLocation->DeviceObject = pDeviceObject;
299             pIoStackLocation->FileObject = CacheFileObject;
300             pIoStackLocation->Parameters.Write.Length = IoRun[i].ByteCount;
301             pIoStackLocation->Parameters.Write.ByteOffset = IoRun[i].CacheOffset;
302
303             //
304             // Bump the count *before* we start the IO, that way if it
305             // completes real fast it will still not set the event
306             //
307             lCount = InterlockedIncrement(&Gather->Count);
308
309             //
310             // Set the completion routine.
311             //
312
313             IoSetCompletionRoutine( pIrp,
314                                     CompletionFunction,
315                                     Gather,
316                                     TRUE,
317                                     TRUE,
318                                     TRUE);
319
320             //
321             // Send it to the Cache
322             //
323
324             ntStatus = IoCallDriver( pDeviceObject,
325                                      pIrp);
326
327             if( !NT_SUCCESS( ntStatus))
328             {
329                 break;
330             }
331         }
332     }
333
334     return ntStatus;
335 }
336
337 VOID
338 AFSCompleteIo( IN AFSGatherIo *Gather,
339                IN NTSTATUS Status)
340 {
341     LONG lCount;
342
343     if (!NT_SUCCESS(Status)) {
344
345         Gather->Status = Status;
346     }
347
348     lCount = InterlockedDecrement( &Gather->Count );
349
350     if (0 == lCount) {
351         //
352         // Last outstanding IO.  Complete the parent and
353         // do whatever it takes to clean up
354         //
355         if (!NT_SUCCESS(Gather->Status))
356         {
357             Gather->MasterIrp->IoStatus.Status = Gather->Status;
358             Gather->MasterIrp->IoStatus.Information = 0;
359         }
360
361         if( Gather->CompleteMasterIrp)
362         {
363
364             AFSDbgLogMsg( AFS_SUBSYSTEM_IO_PROCESSING,
365                           AFS_TRACE_LEVEL_VERBOSE,
366                           "AFSCompleteIo Completing Irp %p Status %08lX\n",
367                           Gather->MasterIrp,
368                           Gather->Status);
369
370             AFSCompleteRequest(Gather->MasterIrp, Gather->Status);
371         }
372
373         if (Gather->Synchronous)
374         {
375             //
376             // Someone was waiting for us tell them.  They also own
377             // the data structure so we don't free it
378             //
379             KeSetEvent( &Gather->Event,
380                         0,
381                         FALSE);
382         }
383         else
384         {
385
386             AFSExFreePoolWithTag( Gather, AFS_GATHER_TAG);
387         }
388     }
389 }
390
391 NTSTATUS
392 AFSProcessExtentRun( IN PVOID          SystemBuffer,
393                      IN PLARGE_INTEGER Start,
394                      IN ULONG          Length,
395                      IN AFSExtent     *From,
396                      IN BOOLEAN        WriteRequest)
397 {
398
399     LARGE_INTEGER  liCacheOffset;
400     LARGE_INTEGER  liFileOffset = *Start;
401     ULONG          ulCurrLength;
402     ULONG          ulOffset = 0;
403     ULONG          ulCurrRun = 0;
404     AFSExtent     *pExtent = From;
405     AFSExtent     *pNextExtent = NULL;
406     BOOLEAN        done = FALSE;
407     NTSTATUS       ntStatus = STATUS_SUCCESS;
408
409     __Enter
410     {
411
412         while( !done)
413         {
414             ASSERT( liFileOffset.QuadPart >= pExtent->FileOffset.QuadPart );
415
416             liCacheOffset = pExtent->CacheOffset;
417             liCacheOffset.QuadPart += (liFileOffset.QuadPart -
418                                        pExtent->FileOffset.QuadPart);
419
420             ulCurrLength = 0;
421
422             //
423             // While there is still data to process, loop
424             //
425             while ( (pExtent->FileOffset.QuadPart + pExtent->Size) <
426                     (Start->QuadPart + Length) )
427             {
428                 //
429                 // Collapse the read if we can
430                 //
431                 pNextExtent = NextExtent( pExtent, AFS_EXTENTS_LIST);
432
433                 if (pNextExtent->CacheOffset.QuadPart !=
434                     (pExtent->CacheOffset.QuadPart + pExtent->Size))
435                 {
436                     //
437                     // This extent and the next extent are not contiguous
438                     //
439                     break;
440                 }
441                 pExtent = pNextExtent;
442             }
443
444             //
445             // So, this run goes from liCacheOffset to the end of pExtent
446             //
447
448             if ((pExtent->FileOffset.QuadPart + pExtent->Size) >= (Start->QuadPart + Length))
449             {
450                 //
451                 // The last extent overruns the area we care
452                 // about, so trim it back.
453                 //
454                 ulCurrLength = (ULONG) (Start->QuadPart + Length -
455                                         liFileOffset.QuadPart);
456                 //
457                 // But we are done
458                 //
459                 done = TRUE;
460             }
461             else
462             {
463                 //
464                 // Length is from the LiFileOffset to the end of this
465                 // extent.
466                 //
467                 ulCurrLength = (ULONG) (pExtent->FileOffset.QuadPart + pExtent->Size -
468                                         liFileOffset.QuadPart);
469                 done = FALSE;
470             }
471
472             //
473             // Calculate the offset into the buffer
474             //
475             ulOffset = (ULONG) (liFileOffset.QuadPart - Start->QuadPart);
476
477             if( WriteRequest)
478             {
479
480 #ifdef AMD64
481                 RtlCopyMemory( ((char *)AFSLibCacheBaseAddress + liCacheOffset.QuadPart),
482                                ((char *)SystemBuffer + ulOffset),
483                                ulCurrLength);
484 #else
485                 ASSERT( liCacheOffset.HighPart == 0);
486                 RtlCopyMemory( ((char *)AFSLibCacheBaseAddress + liCacheOffset.LowPart),
487                                ((char *)SystemBuffer + ulOffset),
488                                ulCurrLength);
489 #endif
490             }
491             else
492             {
493
494 #ifdef AMD64
495                 RtlCopyMemory( ((char *)SystemBuffer + ulOffset),
496                                ((char *)AFSLibCacheBaseAddress + liCacheOffset.QuadPart),
497                                ulCurrLength);
498 #else
499                 ASSERT( liCacheOffset.HighPart == 0);
500                 RtlCopyMemory( ((char *)SystemBuffer + ulOffset),
501                                ((char *)AFSLibCacheBaseAddress + liCacheOffset.LowPart),
502                                ulCurrLength);
503 #endif
504             }
505
506             ulCurrRun ++;
507             if (!done)
508             {
509                 ASSERT(pNextExtent != NULL && pNextExtent != pExtent);
510                 //
511                 // Move cursors forward
512                 //
513                 pExtent = pNextExtent;
514                 liFileOffset = pExtent->FileOffset;
515             }
516         }
517     }
518
519     return ntStatus;
520 }