@@ -43,6 +43,11 @@ public sealed class SqlFileStream : Stream
4343 /// </remarks>
4444 private const int DefaultBufferSize = 1 ;
4545
46+ private const FileOptions AllowedOptions = FileOptions . WriteThrough |
47+ FileOptions . Asynchronous |
48+ FileOptions . RandomAccess |
49+ FileOptions . SequentialScan ;
50+
4651 private const ushort IoControlCodeFunctionCode = 2392 ;
4752
4853 /// <summary>
@@ -719,12 +724,14 @@ private static FileStream OpenFileStream(SafeFileHandle fileHandle, FileAccess a
719724 sp . Assert ( ) ;
720725 bRevertAssert = true ;
721726
722- return new FileStream ( fileHandle , access , DefaultBufferSize , ( options & FileOptions . Asynchronous ) != 0 ) ;
727+ return new FileStream ( fileHandle , access , DefaultBufferSize , ( options & FileOptions . Asynchronous ) != 0 ) ;
723728 }
724729 finally
725730 {
726731 if ( bRevertAssert )
727- SecurityPermission . RevertAssert ( ) ;
732+ {
733+ CodeAccessPermission . RevertAssert ( ) ;
734+ }
728735 }
729736 #else
730737 return new FileStream ( fileHandle , access , DefaultBufferSize , ( options & FileOptions . Asynchronous ) != 0 ) ;
@@ -738,132 +745,129 @@ private void OpenSqlFileStream(
738745 FileOptions options ,
739746 long allocationSize )
740747 {
741- //-----------------------------------------------------------------
742- // precondition validation
743-
744- // these should be checked by any caller of this method
745- // ensure we have validated and normalized the path before
746- Debug . Assert ( path != null ) ;
747- Debug . Assert ( transactionContext != null ) ;
748-
749- if ( access != FileAccess . Read && access != FileAccess . Write && access != FileAccess . ReadWrite )
748+ if ( access is not ( FileAccess . Read or FileAccess . Write or FileAccess . ReadWrite ) )
749+ {
750750 throw ADP . ArgumentOutOfRange ( "access" ) ;
751+ }
751752
752- // FileOptions is a set of flags, so AND the given value against the set of values we do not support
753- if ( ( options & ~ ( FileOptions . WriteThrough | FileOptions . Asynchronous | FileOptions . RandomAccess | FileOptions . SequentialScan ) ) != 0 )
753+ if ( ( options & ~ AllowedOptions ) != 0 )
754+ {
754755 throw ADP . ArgumentOutOfRange ( "options" ) ;
755-
756- // normalize the provided path
757- path = GetFullPathInternal ( path ) ;
756+ }
758757
759758 #if NETFRAMEWORK
760759 // Ensure the running code has permission to read/write the file
761760 DemandAccessPermission ( path , access ) ;
762761 #endif
763762
764- SafeFileHandle hFile = null ;
765- Interop . NtDll . DesiredAccess nDesiredAccess = Interop . NtDll . DesiredAccess . FILE_READ_ATTRIBUTES | Interop . NtDll . DesiredAccess . SYNCHRONIZE ;
766- Interop . NtDll . CreateOptions dwCreateOptions = 0 ;
767- Interop . NtDll . CreateDisposition dwCreateDisposition = 0 ;
768- FileShare shareAccess = FileShare . None ;
763+ Interop . NtDll . CreateOptions createOptions = 0 ;
764+ Interop . NtDll . CreateDisposition createDisposition = 0 ;
765+ Interop . NtDll . DesiredAccess desiredAccess = Interop . NtDll . DesiredAccess . FILE_READ_ATTRIBUTES |
766+ Interop . NtDll . DesiredAccess . SYNCHRONIZE ;
767+ FileShare shareAccess = 0 ;
769768
770769 switch ( access )
771770 {
772771 case FileAccess . Read :
773- nDesiredAccess |= Interop . NtDll . DesiredAccess . FILE_READ_DATA ;
772+ desiredAccess |= Interop . NtDll . DesiredAccess . FILE_READ_DATA ;
774773 shareAccess = FileShare . Delete | FileShare . ReadWrite ;
775- dwCreateDisposition = Interop . NtDll . CreateDisposition . FILE_OPEN ;
774+ createDisposition = Interop . NtDll . CreateDisposition . FILE_OPEN ;
776775 break ;
777776
778777 case FileAccess . Write :
779- nDesiredAccess |= Interop . NtDll . DesiredAccess . FILE_WRITE_DATA ;
778+ desiredAccess |= Interop . NtDll . DesiredAccess . FILE_WRITE_DATA ;
780779 shareAccess = FileShare . Delete | FileShare . Read ;
781- dwCreateDisposition = Interop . NtDll . CreateDisposition . FILE_OVERWRITE ;
780+ createDisposition = Interop . NtDll . CreateDisposition . FILE_OVERWRITE ;
782781 break ;
783782
784783 case FileAccess . ReadWrite :
785- default :
786- // we validate the value of 'access' parameter in the beginning of this method
787- Debug . Assert ( access == FileAccess . ReadWrite ) ;
788-
789- nDesiredAccess |= Interop . NtDll . DesiredAccess . FILE_READ_DATA | Interop . NtDll . DesiredAccess . FILE_WRITE_DATA ;
784+ desiredAccess |= Interop . NtDll . DesiredAccess . FILE_READ_DATA |
785+ Interop . NtDll . DesiredAccess . FILE_WRITE_DATA ;
790786 shareAccess = FileShare . Delete | FileShare . Read ;
791- dwCreateDisposition = Interop . NtDll . CreateDisposition . FILE_OVERWRITE ;
787+ createDisposition = Interop . NtDll . CreateDisposition . FILE_OVERWRITE ;
792788 break ;
789+
790+ // Note: default case is heuristically unreachable due to check above.
793791 }
794792
795793 if ( ( options & FileOptions . WriteThrough ) != 0 )
796794 {
797- dwCreateOptions |= Interop . NtDll . CreateOptions . FILE_WRITE_THROUGH ;
795+ createOptions |= Interop . NtDll . CreateOptions . FILE_WRITE_THROUGH ;
798796 }
799797
800798 if ( ( options & FileOptions . Asynchronous ) == 0 )
801799 {
802- dwCreateOptions |= Interop . NtDll . CreateOptions . FILE_SYNCHRONOUS_IO_NONALERT ;
800+ createOptions |= Interop . NtDll . CreateOptions . FILE_SYNCHRONOUS_IO_NONALERT ;
803801 }
804802
805803 if ( ( options & FileOptions . SequentialScan ) != 0 )
806804 {
807- dwCreateOptions |= Interop . NtDll . CreateOptions . FILE_SEQUENTIAL_ONLY ;
805+ createOptions |= Interop . NtDll . CreateOptions . FILE_SEQUENTIAL_ONLY ;
808806 }
809807
810808 if ( ( options & FileOptions . RandomAccess ) != 0 )
811809 {
812- dwCreateOptions |= Interop . NtDll . CreateOptions . FILE_RANDOM_ACCESS ;
810+ createOptions |= Interop . NtDll . CreateOptions . FILE_RANDOM_ACCESS ;
813811 }
814812
813+ SafeFileHandle fileHandle = null ;
815814 try
816815 {
817816 // NOTE: the Name property is intended to reveal the publicly available moniker for the
818817 // FILESTREAM attributed column data. We will not surface the internal processing that
819818 // takes place to create the mappedPath.
820819 string mappedPath = InitializeNtPath ( path ) ;
821- int retval = 0 ;
822- IntPtr handle ;
823820
824821 Interop . Kernel32 . SetThreadErrorMode ( Interop . Kernel32 . SEM_FAILCRITICALERRORS , out uint oldMode ) ;
825822
823+ // Make the interop call to open the file
824+ int retval ;
826825 try
827826 {
828827 if ( transactionContext . Length >= ushort . MaxValue )
828+ {
829829 throw ADP . ArgumentOutOfRange ( "transactionContext" ) ;
830+ }
831+
832+ IntPtr handle ;
830833
831834 #if NETFRAMEWORK
832- string traceEventMessage = "<sc.SqlFileStream.OpenSqlFileStream|ADV> {0}, desiredAccess=0x{1}, allocationSize={2}, fileAttributes=0x00, shareAccess=0x{3}, dwCreateDisposition=0x{4}, createOptions=0x{5}" ;
835+ const string traceEventMessage = "<sc.SqlFileStream.OpenSqlFileStream|ADV> {0}, desiredAccess=0x{1}, allocationSize={2}, fileAttributes=0x00, shareAccess=0x{3}, dwCreateDisposition=0x{4}, createOptions=0x{5}" ;
833836 ( retval , handle ) = Interop . NtDll . CreateFile (
834837 path : mappedPath ,
835838 eaName : EaNameString ,
836839 eaValue : transactionContext ,
837- desiredAccess : nDesiredAccess ,
840+ desiredAccess : desiredAccess ,
838841 fileAttributes : 0 ,
839842 shareAccess : FileShare . None ,
840- createDisposition : dwCreateDisposition ,
841- createOptions : dwCreateOptions ,
843+ createDisposition : createDisposition ,
844+ createOptions : createOptions ,
842845 impersonationLevel : Interop . ImpersonationLevel . SecurityAnonymous ,
843846 isDynamicTracking : false ,
844847 isEffectiveOnly : false ) ;
845848 #else
846- string traceEventMessage = "SqlFileStream.OpenSqlFileStream | ADV | Object Id {0}, Desired Access 0x{1}, Allocation Size {2}, File Attributes 0, Share Access 0x{3}, Create Disposition 0x{4}, Create Options 0x{5}" ;
849+ const string traceEventMessage = "SqlFileStream.OpenSqlFileStream | ADV | Object Id {0}, Desired Access 0x{1}, Allocation Size {2}, File Attributes 0, Share Access 0x{3}, Create Disposition 0x{4}, Create Options 0x{5}" ;
847850 ( retval , handle ) = Interop . NtDll . CreateFile (
848851 path : mappedPath ,
849852 eaName : EaNameString ,
850853 eaValue : transactionContext ,
851- desiredAccess : nDesiredAccess ,
854+ desiredAccess : desiredAccess ,
852855 fileAttributes : 0 ,
853856 shareAccess : FileShare . None ,
854- createDisposition : dwCreateDisposition ,
855- createOptions : dwCreateOptions ) ;
857+ createDisposition : createDisposition ,
858+ createOptions : createOptions ) ;
856859 #endif
857860
858- SqlClientEventSource . Log . TryAdvancedTraceEvent ( traceEventMessage , _objectId , ( int ) nDesiredAccess , allocationSize , ( int ) shareAccess , dwCreateDisposition , dwCreateOptions ) ;
861+ SqlClientEventSource . Log . TryAdvancedTraceEvent ( traceEventMessage , _objectId , ( int ) desiredAccess , allocationSize , ( int ) shareAccess , createDisposition , createOptions ) ;
859862
860- hFile = new SafeFileHandle ( handle , true ) ;
863+ fileHandle = new SafeFileHandle ( handle , true ) ;
861864 }
862865 finally
863866 {
864867 Interop . Kernel32 . SetThreadErrorMode ( oldMode , out oldMode ) ;
865868 }
866869
870+ // Handle error codes from the interop call
867871 switch ( retval )
868872 {
869873 case 0 :
@@ -876,65 +880,67 @@ private void OpenSqlFileStream(
876880 throw ADP . Argument ( StringsHelper . GetString ( Strings . SqlFileStream_InvalidParameter ) ) ;
877881
878882 case Interop . Errors . ERROR_FILE_NOT_FOUND :
879- {
880- DirectoryNotFoundException e = new DirectoryNotFoundException ( ) ;
881- ADP . TraceExceptionAsReturnValue ( e ) ;
882- throw e ;
883- }
883+ DirectoryNotFoundException dirNotFoundException = new DirectoryNotFoundException ( ) ;
884+ ADP . TraceExceptionAsReturnValue ( dirNotFoundException ) ;
885+ throw dirNotFoundException ;
886+
884887 default :
888+ uint error = Interop . NtDll . RtlNtStatusToDosError ( retval ) ;
889+ if ( error == Interop . NtDll . ERROR_MR_MID_NOT_FOUND )
885890 {
886- uint error = Interop . NtDll . RtlNtStatusToDosError ( retval ) ;
887- if ( error == Interop . NtDll . ERROR_MR_MID_NOT_FOUND )
888- {
889- // status code could not be mapped to a Win32 error code
890- error = ( uint ) retval ;
891- }
892-
893- Win32Exception e = new Win32Exception ( unchecked ( ( int ) error ) ) ;
894- ADP . TraceExceptionAsReturnValue ( e ) ;
895- throw e ;
891+ // status code could not be mapped to a Win32 error code
892+ error = ( uint ) retval ;
896893 }
894+
895+ Win32Exception win32Exception = new Win32Exception ( unchecked ( ( int ) error ) ) ;
896+ ADP . TraceExceptionAsReturnValue ( win32Exception ) ;
897+ throw win32Exception ;
897898 }
898899
899- if ( hFile . IsInvalid )
900+ // Make sure the file handle is usable for us
901+ if ( fileHandle . IsInvalid )
900902 {
901903 Win32Exception e = new Win32Exception ( Interop . Errors . ERROR_INVALID_HANDLE ) ;
902904 ADP . TraceExceptionAsReturnValue ( e ) ;
903905 throw e ;
904906 }
905907
906- if ( Interop . Kernel32 . GetFileType ( hFile ) != Interop . Kernel32 . FileTypes . FILE_TYPE_DISK )
908+ if ( Interop . Kernel32 . GetFileType ( fileHandle ) != Interop . Kernel32 . FileTypes . FILE_TYPE_DISK )
907909 {
908- hFile . Dispose ( ) ;
910+ fileHandle . Dispose ( ) ;
909911 throw ADP . Argument ( StringsHelper . GetString ( Strings . SqlFileStream_PathNotValidDiskResource ) ) ;
910912 }
911913
912914 // If the user is opening the SQL FileStream in read/write mode, we assume that
913915 // they want to scan through current data and then append new data to the end, so
914916 // we need to tell SQL Server to preserve the existing file contents.
915- if ( access == FileAccess . ReadWrite )
917+ if ( access is FileAccess . ReadWrite )
916918 {
917- uint ioControlCode = Interop . Kernel32 . CTL_CODE ( Interop . Kernel32 . FILE_DEVICE_FILE_SYSTEM ,
918- IoControlCodeFunctionCode , ( byte ) Interop . Kernel32 . IoControlTransferType . METHOD_BUFFERED ,
919+ uint ioControlCode = Interop . Kernel32 . CTL_CODE (
920+ Interop . Kernel32 . FILE_DEVICE_FILE_SYSTEM ,
921+ IoControlCodeFunctionCode ,
922+ ( byte ) Interop . Kernel32 . IoControlTransferType . METHOD_BUFFERED ,
919923 ( byte ) Interop . Kernel32 . IoControlCodeAccess . FILE_ANY_ACCESS ) ;
920924
921- if ( ! Interop . Kernel32 . DeviceIoControl ( hFile , ioControlCode , IntPtr . Zero , 0 , IntPtr . Zero , 0 , out uint cbBytesReturned , IntPtr . Zero ) )
925+ if ( ! Interop . Kernel32 . DeviceIoControl ( fileHandle , ioControlCode , IntPtr . Zero , 0 , IntPtr . Zero , 0 , out _ , IntPtr . Zero ) )
922926 {
923927 Win32Exception e = new Win32Exception ( Marshal . GetLastWin32Error ( ) ) ;
924928 ADP . TraceExceptionAsReturnValue ( e ) ;
925929 throw e ;
926930 }
927931 }
928932
929- // now that we've successfully opened a handle on the path and verified that it is a file,
930- // use the SafeFileHandle to initialize our internal FileStream instance
931- Debug . Assert ( _fileStream == null ) ;
932- _fileStream = OpenFileStream ( hFile , access , options ) ;
933+ // Now that we've successfully opened a handle on the path and verified that it is
934+ // a file, use the SafeFileHandle to initialize our internal FileStream instance.
935+ Debug . Assert ( _fileStream is null ) ;
936+ _fileStream = OpenFileStream ( fileHandle , access , options ) ;
933937 }
934938 catch
935939 {
936- if ( hFile != null && ! hFile . IsInvalid )
937- hFile . Dispose ( ) ;
940+ if ( fileHandle is not null && ! fileHandle . IsInvalid )
941+ {
942+ fileHandle . Dispose ( ) ;
943+ }
938944
939945 throw ;
940946 }
0 commit comments