Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / drivers / filesystems / reiserfs / src / write.c
1 /*
2 * COPYRIGHT: GNU GENERAL PUBLIC LICENSE VERSION 2
3 * PROJECT: ReiserFs file system driver for Windows NT/2000/XP/Vista.
4 * FILE: write.c
5 * PURPOSE:
6 * PROGRAMMER: Mark Piper, Matt Wu, Bo Brantén.
7 * HOMEPAGE:
8 * UPDATE HISTORY:
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include "rfsd.h"
14
15 /* GLOBALS ***************************************************************/
16
17 extern PRFSD_GLOBAL RfsdGlobal;
18
19 /* DEFINITIONS *************************************************************/
20
21 typedef struct _RFSD_FLPFLUSH_CONTEXT {
22
23 PRFSD_VCB Vcb;
24 PRFSD_FCB Fcb;
25 PFILE_OBJECT FileObject;
26
27 KDPC Dpc;
28 KTIMER Timer;
29 WORK_QUEUE_ITEM Item;
30
31 } RFSD_FLPFLUSH_CONTEXT, *PRFSD_FLPFLUSH_CONTEXT;
32
33 #ifdef _PREFAST_
34 WORKER_THREAD_ROUTINE RfsdFloppyFlush;
35 #endif // _PREFAST_
36
37 VOID
38 RfsdFloppyFlush(IN PVOID Parameter);
39
40 #ifdef _PREFAST_
41 KDEFERRED_ROUTINE RfsdFloppyFlushDpc;
42 #endif // _PREFAST_
43
44 VOID
45 RfsdFloppyFlushDpc (
46 IN PKDPC Dpc,
47 IN PVOID DeferredContext,
48 IN PVOID SystemArgument1,
49 IN PVOID SystemArgument2);
50
51 NTSTATUS
52 RfsdWriteComplete (IN PRFSD_IRP_CONTEXT IrpContext);
53
54 NTSTATUS
55 RfsdWriteFile (IN PRFSD_IRP_CONTEXT IrpContext);
56
57 NTSTATUS
58 RfsdWriteVolume (IN PRFSD_IRP_CONTEXT IrpContext);
59
60 VOID
61 RfsdDeferWrite(IN PRFSD_IRP_CONTEXT, PIRP Irp);
62
63 #ifdef ALLOC_PRAGMA
64 #if !RFSD_READ_ONLY
65 #pragma alloc_text(PAGE, RfsdFloppyFlush)
66 #pragma alloc_text(PAGE, RfsdStartFloppyFlushDpc)
67 #pragma alloc_text(PAGE, RfsdZeroHoles)
68 #pragma alloc_text(PAGE, RfsdWrite)
69 #pragma alloc_text(PAGE, RfsdWriteVolume)
70 #pragma alloc_text(PAGE, RfsdWriteInode)
71 #pragma alloc_text(PAGE, RfsdWriteFile)
72 #pragma alloc_text(PAGE, RfsdWriteComplete)
73 #endif // !RFSD_READ_ONLY
74 #endif
75
76 /* FUNCTIONS *************************************************************/
77
78 #if !RFSD_READ_ONLY
79
80 VOID
81 RfsdFloppyFlush(IN PVOID Parameter)
82 {
83 PRFSD_FLPFLUSH_CONTEXT Context;
84 PFILE_OBJECT FileObject;
85 PRFSD_FCB Fcb;
86 PRFSD_VCB Vcb;
87
88 PAGED_CODE();
89
90 Context = (PRFSD_FLPFLUSH_CONTEXT) Parameter;
91 FileObject = Context->FileObject;
92 Fcb = Context->Fcb;
93 Vcb = Context->Vcb;
94
95 RfsdPrint((DBG_USER, "RfsdFloppyFlushing ...\n"));
96
97 FsRtlEnterFileSystem();
98
99 IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
100
101 if (Vcb) {
102 ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE);
103 ExReleaseResourceLite(&Vcb->PagingIoResource);
104
105 CcFlushCache(&(Vcb->SectionObject), NULL, 0, NULL);
106 }
107
108 if (FileObject) {
109 ASSERT(Fcb == (PRFSD_FCB)FileObject->FsContext);
110
111 ExAcquireSharedStarveExclusive(&Fcb->PagingIoResource, TRUE);
112 ExReleaseResourceLite(&Fcb->PagingIoResource);
113
114 CcFlushCache(&(Fcb->SectionObject), NULL, 0, NULL);
115
116 ObDereferenceObject(FileObject);
117 }
118
119 IoSetTopLevelIrp(NULL);
120
121 FsRtlExitFileSystem();
122
123 ExFreePool(Parameter);
124 }
125
126 VOID
127 RfsdFloppyFlushDpc (
128 IN PKDPC Dpc,
129 IN PVOID DeferredContext,
130 IN PVOID SystemArgument1,
131 IN PVOID SystemArgument2
132 )
133 {
134 PRFSD_FLPFLUSH_CONTEXT Context;
135
136 Context = (PRFSD_FLPFLUSH_CONTEXT) DeferredContext;
137
138 RfsdPrint((DBG_USER, "RfsdFloppyFlushDpc is to be started...\n"));
139
140 ExInitializeWorkItem( &Context->Item,
141 RfsdFloppyFlush,
142 Context );
143
144 ExQueueWorkItem(&Context->Item, CriticalWorkQueue);
145 }
146
147 VOID
148 RfsdStartFloppyFlushDpc (
149 PRFSD_VCB Vcb,
150 PRFSD_FCB Fcb,
151 PFILE_OBJECT FileObject )
152 {
153 LARGE_INTEGER OneSecond;
154 PRFSD_FLPFLUSH_CONTEXT Context;
155
156 PAGED_CODE();
157
158 ASSERT(IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK));
159
160 Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(PRFSD_FLPFLUSH_CONTEXT), RFSD_POOL_TAG);
161
162 if (!Context) {
163 DbgBreak();
164 return;
165 }
166
167 KeInitializeTimer(&Context->Timer);
168
169 KeInitializeDpc( &Context->Dpc,
170 RfsdFloppyFlushDpc,
171 Context );
172
173 Context->Vcb = Vcb;
174 Context->Fcb = Fcb;
175 Context->FileObject = FileObject;
176
177 if (FileObject) {
178 ObReferenceObject(FileObject);
179 }
180
181 OneSecond.QuadPart = (LONGLONG)-1*1000*1000*10;
182 KeSetTimer( &Context->Timer,
183 OneSecond,
184 &Context->Dpc );
185 }
186
187 BOOLEAN
188 RfsdZeroHoles (
189 IN PRFSD_IRP_CONTEXT IrpContext,
190 IN PRFSD_VCB Vcb,
191 IN PFILE_OBJECT FileObject,
192 IN LONGLONG Offset,
193 IN LONGLONG Count
194 )
195 {
196 LARGE_INTEGER StartAddr = {0,0};
197 LARGE_INTEGER EndAddr = {0,0};
198
199 PAGED_CODE();
200
201 StartAddr.QuadPart = (Offset + (SECTOR_SIZE - 1)) &
202 ~((LONGLONG)SECTOR_SIZE - 1);
203
204 EndAddr.QuadPart = (Offset + Count + (SECTOR_SIZE - 1)) &
205 ~((LONGLONG)SECTOR_SIZE - 1);
206
207 if (StartAddr.QuadPart < EndAddr.QuadPart) {
208 return CcZeroData( FileObject,
209 &StartAddr,
210 &EndAddr,
211 IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
212 }
213
214 return TRUE;
215 }
216
217 VOID
218 RfsdDeferWrite(IN PRFSD_IRP_CONTEXT IrpContext, PIRP Irp)
219 {
220 ASSERT(IrpContext->Irp == Irp);
221
222 RfsdQueueRequest(IrpContext);
223 }
224
225 NTSTATUS
226 RfsdWriteVolume (IN PRFSD_IRP_CONTEXT IrpContext)
227 {
228 NTSTATUS Status = STATUS_UNSUCCESSFUL;
229
230 PRFSD_VCB Vcb;
231 PRFSD_CCB Ccb;
232 PRFSD_FCBVCB FcbOrVcb;
233 PFILE_OBJECT FileObject;
234
235 PDEVICE_OBJECT DeviceObject;
236
237 PIRP Irp;
238 PIO_STACK_LOCATION IoStackLocation;
239
240 ULONG Length;
241 LARGE_INTEGER ByteOffset;
242
243 BOOLEAN PagingIo;
244 BOOLEAN Nocache;
245 BOOLEAN SynchronousIo;
246 BOOLEAN MainResourceAcquired = FALSE;
247 BOOLEAN PagingIoResourceAcquired = FALSE;
248
249 BOOLEAN bDeferred = FALSE;
250
251 PUCHAR Buffer;
252
253 PAGED_CODE();
254
255 _SEH2_TRY {
256
257 ASSERT(IrpContext);
258
259 ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
260 (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
261
262 DeviceObject = IrpContext->DeviceObject;
263
264 Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension;
265
266 ASSERT(Vcb != NULL);
267
268 ASSERT((Vcb->Identifier.Type == RFSDVCB) &&
269 (Vcb->Identifier.Size == sizeof(RFSD_VCB)));
270
271 FileObject = IrpContext->FileObject;
272
273 FcbOrVcb = (PRFSD_FCBVCB) FileObject->FsContext;
274
275 ASSERT(FcbOrVcb);
276
277 if (!(FcbOrVcb->Identifier.Type == RFSDVCB && (PVOID)FcbOrVcb == (PVOID)Vcb)) {
278 Status = STATUS_INVALID_DEVICE_REQUEST;
279 _SEH2_LEAVE;
280 }
281
282 Ccb = (PRFSD_CCB) FileObject->FsContext2;
283
284 Irp = IrpContext->Irp;
285
286 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
287
288 Length = IoStackLocation->Parameters.Write.Length;
289 ByteOffset = IoStackLocation->Parameters.Write.ByteOffset;
290
291 PagingIo = (Irp->Flags & IRP_PAGING_IO ? TRUE : FALSE);
292 Nocache = (Irp->Flags & IRP_NOCACHE ? TRUE : FALSE);
293 SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO ? TRUE : FALSE);
294
295 RfsdPrint((DBG_INFO, "RfsdWriteVolume: Off=%I64xh Len=%xh Paging=%xh Nocache=%xh\n",
296 ByteOffset.QuadPart, Length, PagingIo, Nocache));
297
298 if (Length == 0) {
299 Irp->IoStatus.Information = 0;
300 Status = STATUS_SUCCESS;
301 _SEH2_LEAVE;
302 }
303
304 // For the case of "Direct Access Storage Device", we
305 // need flush/purge the cache
306
307 if (Ccb != NULL) {
308
309 ExAcquireResourceExclusiveLite(&Vcb->MainResource, TRUE);
310 MainResourceAcquired = TRUE;
311
312 Status = RfsdPurgeVolume( Vcb, TRUE);
313
314 ExReleaseResourceLite(&Vcb->MainResource);
315 MainResourceAcquired = FALSE;
316
317 if(!IsFlagOn(Ccb->Flags, CCB_ALLOW_EXTENDED_DASD_IO)) {
318 if (ByteOffset.QuadPart + Length > Vcb->Header.FileSize.QuadPart) {
319 Length = (ULONG)(Vcb->Header.FileSize.QuadPart - ByteOffset.QuadPart);
320 }
321 }
322
323 {
324 RFSD_BDL BlockArray;
325
326 if ((ByteOffset.LowPart & (SECTOR_SIZE - 1)) ||
327 (Length & (SECTOR_SIZE - 1)) ) {
328 Status = STATUS_INVALID_PARAMETER;
329 _SEH2_LEAVE;
330 }
331
332 Status = RfsdLockUserBuffer(
333 IrpContext->Irp,
334 Length,
335 IoReadAccess );
336
337 if (!NT_SUCCESS(Status)) {
338 _SEH2_LEAVE;
339 }
340
341 BlockArray.Irp = NULL;
342 BlockArray.Lba = ByteOffset.QuadPart;;
343 BlockArray.Offset = 0;
344 BlockArray.Length = Length;
345
346 Status = RfsdReadWriteBlocks(IrpContext,
347 Vcb,
348 &BlockArray,
349 Length,
350 1,
351 FALSE );
352 Irp = IrpContext->Irp;
353
354 _SEH2_LEAVE;
355 }
356 }
357
358 if (Nocache &&
359 (ByteOffset.LowPart & (SECTOR_SIZE - 1) ||
360 Length & (SECTOR_SIZE - 1))) {
361 Status = STATUS_INVALID_PARAMETER;
362 _SEH2_LEAVE;
363 }
364
365 if (FlagOn(IrpContext->MinorFunction, IRP_MN_DPC)) {
366 ClearFlag(IrpContext->MinorFunction, IRP_MN_DPC);
367 Status = STATUS_PENDING;
368 _SEH2_LEAVE;
369 }
370
371 if (ByteOffset.QuadPart >=
372 Vcb->PartitionInformation.PartitionLength.QuadPart ) {
373 Irp->IoStatus.Information = 0;
374 Status = STATUS_END_OF_FILE;
375 _SEH2_LEAVE;
376 }
377
378 #if FALSE
379
380 if (!Nocache) {
381
382 BOOLEAN bAgain = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
383 BOOLEAN bWait = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
384 BOOLEAN bQueue = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_REQUEUED);
385
386 if ( !CcCanIWrite(
387 FileObject,
388 Length,
389 (bWait && bQueue),
390 bAgain ) ) {
391
392 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
393
394 CcDeferWrite( FileObject,
395 (PCC_POST_DEFERRED_WRITE)RfsdDeferWrite,
396 IrpContext,
397 Irp,
398 Length,
399 bAgain );
400
401 bDeferred = TRUE;
402
403 DbgBreak();
404
405 Status = STATUS_PENDING;
406
407 _SEH2_LEAVE;
408 }
409 }
410
411 #endif
412
413 if (Nocache && !PagingIo && (Vcb->SectionObject.DataSectionObject != NULL)) {
414
415 ExAcquireResourceExclusiveLite(&Vcb->MainResource, TRUE);
416 MainResourceAcquired = TRUE;
417
418 ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE);
419 ExReleaseResourceLite(&Vcb->PagingIoResource);
420
421 CcFlushCache( &(Vcb->SectionObject),
422 &ByteOffset,
423 Length,
424 &(Irp->IoStatus));
425
426 if (!NT_SUCCESS(Irp->IoStatus.Status)) {
427 Status = Irp->IoStatus.Status;
428 _SEH2_LEAVE;
429 }
430
431 ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE);
432 ExReleaseResourceLite(&Vcb->PagingIoResource);
433
434 CcPurgeCacheSection( &(Vcb->SectionObject),
435 (PLARGE_INTEGER)&(ByteOffset),
436 Length,
437 FALSE );
438
439 ExReleaseResourceLite(&Vcb->MainResource);
440 MainResourceAcquired = FALSE;
441 }
442
443 if (!PagingIo) {
444
445 #pragma prefast( suppress: 28137, "by design" )
446 if (!ExAcquireResourceExclusiveLite(
447 &Vcb->MainResource,
448 IrpContext->IsSynchronous )) {
449 Status = STATUS_PENDING;
450 _SEH2_LEAVE;
451 }
452
453 MainResourceAcquired = TRUE;
454
455 } else {
456
457 /*
458 ULONG ResShCnt, ResExCnt;
459 ResShCnt = ExIsResourceAcquiredSharedLite(&Vcb->PagingIoResource);
460 ResExCnt = ExIsResourceAcquiredExclusiveLite(&Vcb->PagingIoResource);
461
462 RfsdPrint((DBG_USER, "PagingIoRes: %xh:%xh Synchronous=%xh\n", ResShCnt, ResExCnt, IrpContext->IsSynchronous));
463 */
464
465 if (Ccb) {
466
467 if (!ExAcquireResourceSharedLite(
468 &Vcb->PagingIoResource,
469 IrpContext->IsSynchronous )) {
470 Status = STATUS_PENDING;
471 _SEH2_LEAVE;
472 }
473
474 PagingIoResourceAcquired = TRUE;
475 }
476 }
477
478 if (!Nocache) {
479
480 if ( (ByteOffset.QuadPart + Length) >
481 Vcb->PartitionInformation.PartitionLength.QuadPart ){
482 Length = (ULONG) (
483 Vcb->PartitionInformation.PartitionLength.QuadPart -
484 ByteOffset.QuadPart);
485
486 Length &= ~((ULONG)SECTOR_SIZE - 1);
487 }
488
489 if (FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
490
491 CcPrepareMdlWrite (
492 Vcb->StreamObj,
493 &ByteOffset,
494 Length,
495 &Irp->MdlAddress,
496 &Irp->IoStatus );
497
498 Status = Irp->IoStatus.Status;
499
500 } else {
501
502 Buffer = RfsdGetUserBuffer(Irp);
503
504 if (Buffer == NULL) {
505 DbgBreak();
506
507 Status = STATUS_INVALID_USER_BUFFER;
508 _SEH2_LEAVE;
509 }
510
511 if (!CcCopyWrite( Vcb->StreamObj,
512 (PLARGE_INTEGER)(&ByteOffset),
513 Length,
514 TRUE,
515 Buffer )) {
516 Status = STATUS_PENDING;
517 _SEH2_LEAVE;
518 }
519
520 Status = Irp->IoStatus.Status;
521 RfsdAddMcbEntry(Vcb, ByteOffset.QuadPart, (LONGLONG)Length);
522 }
523
524 if (NT_SUCCESS(Status)) {
525 Irp->IoStatus.Information = Length;
526 }
527
528 } else {
529
530 PRFSD_BDL rfsd_bdl = NULL;
531 ULONG Blocks = 0;
532
533 LONGLONG DirtyStart;
534 LONGLONG DirtyLba;
535 LONGLONG DirtyLength;
536 LONGLONG RemainLength;
537
538 if ((ByteOffset.QuadPart + Length) >
539 Vcb->PartitionInformation.PartitionLength.QuadPart ) {
540 Length = (ULONG) (
541 Vcb->PartitionInformation.PartitionLength.QuadPart -
542 ByteOffset.QuadPart);
543
544 Length &= ~((ULONG)SECTOR_SIZE - 1);
545 }
546
547 Status = RfsdLockUserBuffer(
548 IrpContext->Irp,
549 Length,
550 IoReadAccess );
551
552 if (!NT_SUCCESS(Status)) {
553 _SEH2_LEAVE;
554 }
555
556 rfsd_bdl = ExAllocatePoolWithTag(PagedPool,
557 (Length / Vcb->BlockSize) *
558 sizeof(RFSD_BDL), RFSD_POOL_TAG);
559
560 if (!rfsd_bdl) {
561 Status = STATUS_INSUFFICIENT_RESOURCES;
562 _SEH2_LEAVE;
563 }
564
565 DirtyLba = ByteOffset.QuadPart;
566 RemainLength = (LONGLONG) Length;
567
568 while (RemainLength > 0) {
569
570 DirtyStart = DirtyLba;
571
572 if (RfsdLookupMcbEntry( Vcb,
573 DirtyStart,
574 &DirtyLba,
575 &DirtyLength,
576 (PLONGLONG)NULL,
577 (PLONGLONG)NULL,
578 (PULONG)NULL) ) {
579
580 if (DirtyLba == -1) {
581 DirtyLba = DirtyStart + DirtyLength;
582
583 RemainLength = ByteOffset.QuadPart +
584 (LONGLONG)Length -
585 DirtyLba;
586 continue;
587 }
588
589 rfsd_bdl[Blocks].Irp = NULL;
590 rfsd_bdl[Blocks].Lba = DirtyLba;
591 rfsd_bdl[Blocks].Offset = (ULONG)( (LONGLONG)Length +
592 DirtyStart -
593 RemainLength -
594 DirtyLba );
595
596 if (DirtyLba + DirtyLength > DirtyStart + RemainLength) {
597 rfsd_bdl[Blocks].Length = (ULONG)( DirtyStart +
598 RemainLength -
599 DirtyLba );
600 RemainLength = 0;
601 } else {
602 rfsd_bdl[Blocks].Length = (ULONG)DirtyLength;
603 RemainLength = (DirtyStart + RemainLength) -
604 (DirtyLba + DirtyLength);
605 }
606
607 DirtyLba = DirtyStart + DirtyLength;
608 Blocks++;
609
610 } else {
611
612 if (Blocks == 0) {
613
614 if (rfsd_bdl)
615 ExFreePool(rfsd_bdl);
616
617 //
618 // Lookup fails at the first time, ie.
619 // no dirty blocks in the run
620 //
621
622 DbgBreak();
623
624 if (RemainLength == (LONGLONG)Length)
625 Status = STATUS_SUCCESS;
626 else
627 Status = STATUS_UNSUCCESSFUL;
628
629 _SEH2_LEAVE;
630
631 } else {
632 break;
633 }
634 }
635 }
636
637 if (Blocks > 0) {
638
639 Status = RfsdReadWriteBlocks(IrpContext,
640 Vcb,
641 rfsd_bdl,
642 Length,
643 Blocks,
644 FALSE );
645 Irp = IrpContext->Irp;
646
647 if (NT_SUCCESS(Status)) {
648 ULONG i;
649
650 for (i=0; i<Blocks;i++) {
651 RfsdRemoveMcbEntry( Vcb,
652 rfsd_bdl[i].Lba,
653 rfsd_bdl[i].Length );
654 }
655 }
656
657 if (rfsd_bdl)
658 ExFreePool(rfsd_bdl);
659
660 if (!Irp)
661 _SEH2_LEAVE;
662
663 } else {
664
665 if (rfsd_bdl)
666 ExFreePool(rfsd_bdl);
667
668 Irp->IoStatus.Information = Length;
669
670 Status = STATUS_SUCCESS;
671 _SEH2_LEAVE;
672 }
673 }
674 } _SEH2_FINALLY {
675
676 if (PagingIoResourceAcquired) {
677 ExReleaseResourceForThreadLite(
678 &Vcb->PagingIoResource,
679 ExGetCurrentResourceThread());
680 }
681
682 if (MainResourceAcquired) {
683 ExReleaseResourceForThreadLite(
684 &Vcb->MainResource,
685 ExGetCurrentResourceThread());
686 }
687
688 if (!IrpContext->ExceptionInProgress) {
689 if (Irp) {
690 if (Status == STATUS_PENDING) {
691 if(!bDeferred) {
692 Status = RfsdLockUserBuffer(
693 IrpContext->Irp,
694 Length,
695 IoReadAccess );
696
697 if (NT_SUCCESS(Status)) {
698 Status = RfsdQueueRequest(IrpContext);
699 } else {
700 RfsdCompleteIrpContext(IrpContext, Status);
701 }
702 }
703
704 } else {
705
706 if (NT_SUCCESS(Status)) {
707 if (SynchronousIo && !PagingIo) {
708 FileObject->CurrentByteOffset.QuadPart =
709 ByteOffset.QuadPart + Irp->IoStatus.Information;
710 }
711
712 if (!PagingIo) {
713 SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
714 }
715 }
716
717 RfsdCompleteIrpContext(IrpContext, Status);
718 }
719 } else {
720 RfsdFreeIrpContext(IrpContext);
721 }
722 }
723 } _SEH2_END;
724
725 return Status;
726 }
727
728 NTSTATUS
729 RfsdWriteInode (
730 IN PRFSD_IRP_CONTEXT IrpContext,
731 IN PRFSD_VCB Vcb,
732 IN ULONG InodeNo,
733 IN PRFSD_INODE Inode,
734 IN ULONGLONG Offset,
735 IN PVOID Buffer,
736 IN ULONG Size,
737 IN BOOLEAN bWriteToDisk,
738 OUT PULONG dwRet
739 )
740 {
741 PRFSD_BDL rfsd_bdl = NULL;
742 ULONG blocks, i;
743 NTSTATUS Status = STATUS_UNSUCCESSFUL;
744
745 ULONGLONG FileSize;
746 ULONGLONG AllocSize;
747
748 BOOLEAN bAlloc = FALSE;
749
750 PAGED_CODE();
751 #if 0
752 if (dwRet) {
753 *dwRet = 0;
754 }
755
756 //
757 // For file/non pagingio, we support the allocation on writing.
758 //
759
760 if (S_ISREG(Inode->i_mode)) {
761
762 if (!(IrpContext->Irp->Flags & IRP_PAGING_IO)) {
763 bAlloc = TRUE;
764 }
765 }
766
767 //
768 // Initialize the FileSize / AllocationSize ...
769 //
770
771 FileSize = (ULONGLONG) Inode->i_size;
772 if (S_ISREG(Inode->i_mode))
773 FileSize |= ((ULONGLONG)(Inode->i_size_high) << 32);
774 AllocSize = CEILING_ALIGNED(FileSize, (ULONGLONG)Vcb->BlockSize);
775
776
777 //
778 // Check the inputed parameters ...
779 //
780
781 if (!bAlloc) {
782
783 if (Offset >= AllocSize) {
784 RfsdPrint((DBG_ERROR, "RfsdWritenode: beyond the file range.\n"));
785 return STATUS_SUCCESS;
786 }
787
788 if (Offset + Size > AllocSize) {
789 Size = (ULONG)(AllocSize - Offset);
790 }
791 }
792
793 Status = RfsdBuildBDL (
794 IrpContext,
795 Vcb,
796 InodeNo,
797 Inode,
798 Offset,
799 Size,
800 bAlloc,
801 &rfsd_bdl,
802 &blocks
803 );
804
805 if (blocks <= 0) {
806 Status = STATUS_SUCCESS;
807 goto errorout;
808 }
809
810 if (bWriteToDisk) {
811
812 //
813 // We assume the offset is aligned.
814 //
815
816 Status = RfsdReadWriteBlocks(
817 IrpContext,
818 Vcb,
819 rfsd_bdl,
820 Size,
821 blocks,
822 FALSE
823 );
824
825 } else {
826
827 for(i = 0; i < blocks; i++) {
828
829 if( !RfsdSaveBuffer(
830 IrpContext,
831 Vcb,
832 rfsd_bdl[i].Lba,
833 rfsd_bdl[i].Length,
834 (PVOID)((PUCHAR)Buffer + rfsd_bdl[i].Offset)
835 )) {
836 goto errorout;
837 }
838 }
839
840 if (IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) {
841
842 RfsdPrint((DBG_USER, "RfsdWriteInode is starting FlushingDpc...\n"));
843 RfsdStartFloppyFlushDpc(Vcb, NULL, NULL);
844 }
845
846 Status = STATUS_SUCCESS;
847 }
848
849 errorout:
850
851 if (rfsd_bdl)
852 ExFreePool(rfsd_bdl);
853
854 if (NT_SUCCESS(Status)) {
855 if (dwRet) *dwRet = Size;
856 }
857 #endif // 0
858 return Status;
859 }
860
861 NTSTATUS
862 RfsdWriteFile(IN PRFSD_IRP_CONTEXT IrpContext)
863 {
864 NTSTATUS Status = STATUS_UNSUCCESSFUL;
865
866 PRFSD_VCB Vcb;
867 PRFSD_FCB Fcb;
868 PRFSD_CCB Ccb;
869 PFILE_OBJECT FileObject;
870 PFILE_OBJECT CacheObject;
871
872 PDEVICE_OBJECT DeviceObject;
873
874 PIRP Irp;
875 PIO_STACK_LOCATION IoStackLocation;
876
877 ULONG Length;
878 ULONG ReturnedLength;
879 LARGE_INTEGER ByteOffset;
880
881 BOOLEAN PagingIo;
882 BOOLEAN Nocache;
883 BOOLEAN SynchronousIo;
884 BOOLEAN MainResourceAcquired = FALSE;
885 BOOLEAN PagingIoResourceAcquired = FALSE;
886
887 BOOLEAN bNeedExtending = FALSE;
888 BOOLEAN bAppendFile = FALSE;
889
890 BOOLEAN bDeferred = FALSE;
891
892 PUCHAR Buffer;
893
894 PAGED_CODE();
895 #if 0
896 _SEH2_TRY {
897
898 ASSERT(IrpContext);
899
900 ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
901 (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
902
903 DeviceObject = IrpContext->DeviceObject;
904
905 Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension;
906
907 ASSERT(Vcb != NULL);
908
909 ASSERT((Vcb->Identifier.Type == RFSDVCB) &&
910 (Vcb->Identifier.Size == sizeof(RFSD_VCB)));
911
912 FileObject = IrpContext->FileObject;
913
914 Fcb = (PRFSD_FCB) FileObject->FsContext;
915
916 ASSERT(Fcb);
917
918 ASSERT((Fcb->Identifier.Type == RFSDFCB) &&
919 (Fcb->Identifier.Size == sizeof(RFSD_FCB)));
920
921 Ccb = (PRFSD_CCB) FileObject->FsContext2;
922
923 Irp = IrpContext->Irp;
924
925 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
926
927 Length = IoStackLocation->Parameters.Write.Length;
928 ByteOffset = IoStackLocation->Parameters.Write.ByteOffset;
929
930 PagingIo = (Irp->Flags & IRP_PAGING_IO ? TRUE : FALSE);
931 Nocache = (Irp->Flags & IRP_NOCACHE ? TRUE : FALSE);
932 SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO ? TRUE : FALSE);
933
934 RfsdPrint((DBG_INFO, "RfsdWriteFile: Off=%I64xh Len=%xh Paging=%xh Nocache=%xh\n",
935 ByteOffset.QuadPart, Length, PagingIo, Nocache));
936
937 /*
938 if (IsFlagOn(Fcb->Flags, FCB_FILE_DELETED))
939 {
940 Status = STATUS_FILE_DELETED;
941 _SEH2_LEAVE;
942 }
943
944 if (IsFlagOn(Fcb->Flags, FCB_DELETE_PENDING))
945 {
946 Status = STATUS_DELETE_PENDING;
947 _SEH2_LEAVE;
948 }
949 */
950 if (Length == 0) {
951 Irp->IoStatus.Information = 0;
952 Status = STATUS_SUCCESS;
953 _SEH2_LEAVE;
954 }
955
956 if (Nocache &&
957 (ByteOffset.LowPart & (SECTOR_SIZE - 1) ||
958 Length & (SECTOR_SIZE - 1))) {
959 Status = STATUS_INVALID_PARAMETER;
960 _SEH2_LEAVE;
961 }
962
963 if (FlagOn(IrpContext->MinorFunction, IRP_MN_DPC)) {
964 ClearFlag(IrpContext->MinorFunction, IRP_MN_DPC);
965 Status = STATUS_PENDING;
966 _SEH2_LEAVE;
967 }
968
969 #if FALSE
970 if (!Nocache) {
971
972 BOOLEAN bAgain = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
973 BOOLEAN bWait = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
974 BOOLEAN bQueue = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_REQUEUED);
975
976 if ( !CcCanIWrite(
977 FileObject,
978 Length,
979 (bWait && bQueue),
980 bAgain ) ) {
981 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
982
983 CcDeferWrite( FileObject,
984 (PCC_POST_DEFERRED_WRITE)RfsdDeferWrite,
985 IrpContext,
986 Irp,
987 Length,
988 bAgain );
989
990 bDeferred = TRUE;
991
992 DbgBreak();
993
994 Status = STATUS_PENDING;
995 _SEH2_LEAVE;
996 }
997 }
998
999 #endif
1000
1001 if (IsEndOfFile(ByteOffset)) {
1002 bAppendFile = TRUE;
1003 ByteOffset.QuadPart = Fcb->Header.FileSize.QuadPart;
1004 }
1005
1006 if ( FlagOn(Fcb->RfsdMcb->FileAttr, FILE_ATTRIBUTE_DIRECTORY) && !PagingIo) {
1007 Status = STATUS_INVALID_DEVICE_REQUEST;
1008 _SEH2_LEAVE;
1009 }
1010
1011 //
1012 // Do flushing for such cases
1013 //
1014 if (Nocache && !PagingIo && (Fcb->SectionObject.DataSectionObject != NULL)) {
1015
1016 ExAcquireResourceExclusiveLite( &Fcb->MainResource,
1017 IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
1018
1019 MainResourceAcquired = TRUE;
1020
1021 ExAcquireSharedStarveExclusive( &Fcb->PagingIoResource, TRUE);
1022 ExReleaseResourceLite(&Fcb->PagingIoResource);
1023
1024 CcFlushCache( &(Fcb->SectionObject),
1025 &ByteOffset,
1026 Length,
1027 &(Irp->IoStatus));
1028 ClearFlag(Fcb->Flags, FCB_FILE_MODIFIED);
1029
1030 if (!NT_SUCCESS(Irp->IoStatus.Status))
1031 {
1032 Status = Irp->IoStatus.Status;
1033 _SEH2_LEAVE;
1034 }
1035
1036 ExAcquireSharedStarveExclusive( &Fcb->PagingIoResource, TRUE);
1037 ExReleaseResourceLite(&Fcb->PagingIoResource);
1038
1039 CcPurgeCacheSection( &(Fcb->SectionObject),
1040 (PLARGE_INTEGER)&(ByteOffset),
1041 Length,
1042 FALSE );
1043
1044 ExReleaseResourceLite(&Fcb->MainResource);
1045 MainResourceAcquired = FALSE;
1046 }
1047
1048 if (!PagingIo) {
1049
1050 if (!ExAcquireResourceExclusiveLite(
1051 &Fcb->MainResource,
1052 IrpContext->IsSynchronous )) {
1053 Status = STATUS_PENDING;
1054 _SEH2_LEAVE;
1055 }
1056
1057 MainResourceAcquired = TRUE;
1058
1059 } else {
1060
1061 /*
1062 ULONG ResShCnt, ResExCnt;
1063 ResShCnt = ExIsResourceAcquiredSharedLite(&Fcb->PagingIoResource);
1064 ResExCnt = ExIsResourceAcquiredExclusiveLite(&Fcb->PagingIoResource);
1065
1066 RfsdPrint((DBG_USER, "RfsdWriteFile: Inode=%xh %S PagingIo: %xh:%xh Synchronous=%xh\n",
1067 Fcb->RfsdMcb->Inode, Fcb->RfsdMcb->ShortName.Buffer, ResShCnt, ResExCnt, IrpContext->IsSynchronous));
1068 */
1069 if (!ExAcquireResourceSharedLite(
1070 &Fcb->PagingIoResource,
1071 IrpContext->IsSynchronous )) {
1072 Status = STATUS_PENDING;
1073 _SEH2_LEAVE;
1074 }
1075
1076 PagingIoResourceAcquired = TRUE;
1077 }
1078
1079 if (!PagingIo) {
1080 if (!FsRtlCheckLockForWriteAccess(
1081 &Fcb->FileLockAnchor,
1082 Irp )) {
1083 Status = STATUS_FILE_LOCK_CONFLICT;
1084 _SEH2_LEAVE;
1085 }
1086 }
1087
1088 if (Nocache) {
1089
1090 if ( (ByteOffset.QuadPart + Length) >
1091 Fcb->Header.AllocationSize.QuadPart) {
1092
1093 if ( ByteOffset.QuadPart >=
1094 Fcb->Header.AllocationSize.QuadPart) {
1095
1096 Status = STATUS_SUCCESS;
1097 Irp->IoStatus.Information = 0;
1098 _SEH2_LEAVE;
1099
1100 } else {
1101
1102 if (Length > (ULONG)(Fcb->Header.AllocationSize.QuadPart
1103 - ByteOffset.QuadPart)) {
1104 Length = (ULONG)(Fcb->Header.AllocationSize.QuadPart
1105 - ByteOffset.QuadPart);
1106 }
1107 }
1108 }
1109 }
1110
1111 if (!Nocache) {
1112
1113 if (FlagOn(Fcb->RfsdMcb->FileAttr, FILE_ATTRIBUTE_DIRECTORY)) {
1114 _SEH2_LEAVE;
1115 }
1116
1117 if (FileObject->PrivateCacheMap == NULL) {
1118
1119 CcInitializeCacheMap(
1120 FileObject,
1121 (PCC_FILE_SIZES)(&Fcb->Header.AllocationSize),
1122 FALSE,
1123 &RfsdGlobal->CacheManagerCallbacks,
1124 Fcb );
1125
1126 CcSetReadAheadGranularity(
1127 FileObject,
1128 READ_AHEAD_GRANULARITY );
1129
1130 CcSetFileSizes(
1131 FileObject,
1132 (PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
1133 }
1134
1135 CacheObject = FileObject;
1136
1137 //
1138 // Need extending the size of inode ?
1139 //
1140 if ( (bAppendFile) || ((ByteOffset.QuadPart + Length) >
1141 (Fcb->Header.FileSize.QuadPart)) ) {
1142
1143 LARGE_INTEGER ExtendSize;
1144 LARGE_INTEGER FileSize;
1145
1146 bNeedExtending = TRUE;
1147 FileSize = Fcb->Header.FileSize;
1148 ExtendSize.QuadPart = (LONGLONG)(ByteOffset.QuadPart + Length);
1149
1150 if (ExtendSize.QuadPart > Fcb->Header.AllocationSize.QuadPart) {
1151 Status = RfsdExpandFile(IrpContext, Vcb, Fcb, &ExtendSize);
1152 if (!NT_SUCCESS(Status)) {
1153 _SEH2_LEAVE;
1154 }
1155 }
1156
1157 {
1158 Fcb->Header.FileSize.QuadPart = ExtendSize.QuadPart;
1159 Fcb->Inode->i_size = ExtendSize.LowPart;
1160 Fcb->Inode->i_size_high = (ULONG) ExtendSize.HighPart;
1161 }
1162
1163 if (FileObject->PrivateCacheMap) {
1164
1165 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
1166
1167 if (ByteOffset.QuadPart > FileSize.QuadPart) {
1168 RfsdZeroHoles( IrpContext, Vcb, FileObject, FileSize.QuadPart,
1169 ByteOffset.QuadPart - FileSize.QuadPart);
1170 }
1171
1172 if (Fcb->Header.AllocationSize.QuadPart > ExtendSize.QuadPart) {
1173 RfsdZeroHoles(IrpContext, Vcb, FileObject, ExtendSize.QuadPart,
1174 Fcb->Header.AllocationSize.QuadPart - ExtendSize.QuadPart);
1175 }
1176 }
1177
1178 if (RfsdSaveInode(IrpContext, Vcb, Fcb->RfsdMcb->Inode, Fcb->Inode)) {
1179 Status = STATUS_SUCCESS;
1180 }
1181
1182 RfsdNotifyReportChange(
1183 IrpContext,
1184 Vcb,
1185 Fcb,
1186 FILE_NOTIFY_CHANGE_SIZE,
1187 FILE_ACTION_MODIFIED );
1188 }
1189
1190 if (FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
1191 CcPrepareMdlWrite(
1192 CacheObject,
1193 (&ByteOffset),
1194 Length,
1195 &Irp->MdlAddress,
1196 &Irp->IoStatus );
1197
1198 Status = Irp->IoStatus.Status;
1199
1200 } else {
1201
1202 Buffer = RfsdGetUserBuffer(Irp);
1203
1204 if (Buffer == NULL) {
1205 DbgBreak();
1206 Status = STATUS_INVALID_USER_BUFFER;
1207 _SEH2_LEAVE;
1208 }
1209
1210 if (!CcCopyWrite(
1211 CacheObject,
1212 (PLARGE_INTEGER)&ByteOffset,
1213 Length,
1214 IrpContext->IsSynchronous,
1215 Buffer )) {
1216 Status = STATUS_PENDING;
1217 _SEH2_LEAVE;
1218 }
1219
1220 Status = Irp->IoStatus.Status;
1221 }
1222
1223 if (NT_SUCCESS(Status)) {
1224
1225 Irp->IoStatus.Information = Length;
1226
1227 if (IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) {
1228 RfsdPrint((DBG_USER, "RfsdWriteFile is starting FlushingDpc...\n"));
1229 RfsdStartFloppyFlushDpc(Vcb, Fcb, FileObject);
1230 }
1231 }
1232
1233 } else {
1234
1235 ReturnedLength = Length;
1236
1237 Status = RfsdLockUserBuffer(
1238 IrpContext->Irp,
1239 Length,
1240 IoReadAccess );
1241
1242 if (!NT_SUCCESS(Status)) {
1243 _SEH2_LEAVE;
1244 }
1245
1246 Irp->IoStatus.Status = STATUS_SUCCESS;
1247 Irp->IoStatus.Information = Length;
1248
1249 Status = RfsdWriteInode(
1250 IrpContext,
1251 Vcb,
1252 Fcb->RfsdMcb->Inode,
1253 Fcb->Inode,
1254 (ULONGLONG)(ByteOffset.QuadPart),
1255 NULL,
1256 Length,
1257 TRUE,
1258 &ReturnedLength
1259 );
1260
1261 Irp = IrpContext->Irp;
1262
1263 }
1264
1265 } _SEH2_FINALLY {
1266
1267 if (PagingIoResourceAcquired) {
1268 ExReleaseResourceForThreadLite(
1269 &Fcb->PagingIoResource,
1270 ExGetCurrentResourceThread());
1271 }
1272
1273 if (MainResourceAcquired) {
1274 ExReleaseResourceForThreadLite(
1275 &Fcb->MainResource,
1276 ExGetCurrentResourceThread());
1277 }
1278
1279 if (!IrpContext->ExceptionInProgress) {
1280 if (Irp) {
1281 if (Status == STATUS_PENDING) {
1282 if (!bDeferred) {
1283 Status = RfsdLockUserBuffer(
1284 IrpContext->Irp,
1285 Length,
1286 IoReadAccess );
1287
1288 if (NT_SUCCESS(Status)) {
1289 Status = RfsdQueueRequest(IrpContext);
1290 } else {
1291 RfsdCompleteIrpContext(IrpContext, Status);
1292 }
1293 }
1294 } else {
1295 if (NT_SUCCESS(Status)) {
1296 if (SynchronousIo && !PagingIo) {
1297 FileObject->CurrentByteOffset.QuadPart =
1298 ByteOffset.QuadPart + Irp->IoStatus.Information;
1299 }
1300
1301 if (!PagingIo)
1302 {
1303 SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
1304 SetFlag(Fcb->Flags, FCB_FILE_MODIFIED);
1305 }
1306 }
1307
1308 RfsdCompleteIrpContext(IrpContext, Status);
1309 }
1310 } else {
1311 RfsdFreeIrpContext(IrpContext);
1312 }
1313 }
1314 } _SEH2_END;
1315 #endif // 0
1316 return Status;
1317 }
1318
1319 NTSTATUS
1320 RfsdWriteComplete (IN PRFSD_IRP_CONTEXT IrpContext)
1321 {
1322 NTSTATUS Status = STATUS_UNSUCCESSFUL;
1323 PFILE_OBJECT FileObject;
1324 PIRP Irp;
1325 PIO_STACK_LOCATION IrpSp;
1326
1327 PAGED_CODE();
1328
1329 _SEH2_TRY {
1330
1331 ASSERT(IrpContext);
1332
1333 ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
1334 (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
1335
1336 FileObject = IrpContext->FileObject;
1337
1338 Irp = IrpContext->Irp;
1339 IrpSp = IoGetCurrentIrpStackLocation(Irp);
1340
1341 CcMdlWriteComplete(FileObject, &(IrpSp->Parameters.Write.ByteOffset), Irp->MdlAddress);
1342
1343 Irp->MdlAddress = NULL;
1344
1345 Status = STATUS_SUCCESS;
1346
1347 } _SEH2_FINALLY {
1348
1349 if (!IrpContext->ExceptionInProgress) {
1350 RfsdCompleteIrpContext(IrpContext, Status);
1351 }
1352 } _SEH2_END;
1353
1354 return Status;
1355 }
1356
1357 NTSTATUS
1358 RfsdWrite (IN PRFSD_IRP_CONTEXT IrpContext)
1359 {
1360 NTSTATUS Status;
1361 PRFSD_FCBVCB FcbOrVcb;
1362 PDEVICE_OBJECT DeviceObject;
1363 PFILE_OBJECT FileObject;
1364 PRFSD_VCB Vcb;
1365 BOOLEAN bCompleteRequest = TRUE;
1366
1367 PAGED_CODE();
1368
1369 ASSERT(IrpContext);
1370
1371 ASSERT((IrpContext->Identifier.Type == RFSDICX) &&
1372 (IrpContext->Identifier.Size == sizeof(RFSD_IRP_CONTEXT)));
1373
1374 _SEH2_TRY {
1375
1376 if (FlagOn(IrpContext->MinorFunction, IRP_MN_COMPLETE)) {
1377
1378 Status = RfsdWriteComplete(IrpContext);
1379 bCompleteRequest = FALSE;
1380
1381 } else {
1382
1383 DeviceObject = IrpContext->DeviceObject;
1384
1385 if (DeviceObject == RfsdGlobal->DeviceObject) {
1386 Status = STATUS_INVALID_DEVICE_REQUEST;
1387 _SEH2_LEAVE;
1388 }
1389
1390 Vcb = (PRFSD_VCB) DeviceObject->DeviceExtension;
1391
1392 if (Vcb->Identifier.Type != RFSDVCB ||
1393 Vcb->Identifier.Size != sizeof(RFSD_VCB) ) {
1394 Status = STATUS_INVALID_PARAMETER;
1395 _SEH2_LEAVE;
1396 }
1397
1398 ASSERT(IsMounted(Vcb));
1399
1400 if (IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) {
1401 Status = STATUS_TOO_LATE;
1402 _SEH2_LEAVE;
1403 }
1404
1405 if (IsFlagOn(Vcb->Flags, VCB_READ_ONLY)) {
1406 Status = STATUS_MEDIA_WRITE_PROTECTED;
1407 _SEH2_LEAVE;
1408 }
1409
1410 FileObject = IrpContext->FileObject;
1411
1412 FcbOrVcb = (PRFSD_FCBVCB) FileObject->FsContext;
1413
1414 if (FcbOrVcb->Identifier.Type == RFSDVCB) {
1415
1416 Status = RfsdWriteVolume(IrpContext);
1417
1418 if (!NT_SUCCESS(Status)) {
1419 DbgBreak();
1420 }
1421
1422 bCompleteRequest = FALSE;
1423 } else if (FcbOrVcb->Identifier.Type == RFSDFCB) {
1424 Status = RfsdWriteFile(IrpContext);
1425
1426 if (!NT_SUCCESS(Status)) {
1427 DbgBreak();
1428 }
1429
1430 bCompleteRequest = FALSE;
1431 } else {
1432 Status = STATUS_INVALID_PARAMETER;
1433 }
1434 }
1435
1436 } _SEH2_FINALLY {
1437
1438 if (bCompleteRequest) {
1439 RfsdCompleteIrpContext(IrpContext, Status);
1440 }
1441 } _SEH2_END;
1442
1443 return Status;
1444 }
1445
1446 #endif // !RFSD_READ_ONLY