public unsafe static extern void *MapViewOfFile(SafeMemoryMappedFileHandle hMap, uint access, uint offsetHigh, uint ofsfetLow, IntPtr size);
public static MemoryMappedFile CreateFromFile(FileStream fileStream, string?mapName, long capacity, MemoryMappedFileAccess access, HandleInheritability inheritability, bool leaveOpen) { if (fileStream == null) { throw new ArgumentNullException(nameof(fileStream), SR.ArgumentNull_FileStream); } if (mapName != null && mapName.Length == 0) { throw new ArgumentException(SR.Argument_MapNameEmptyString); } if (capacity < 0) { throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_PositiveOrDefaultCapacityRequired); } if (capacity == 0 && fileStream.Length == 0) { throw new ArgumentException(SR.Argument_EmptyFile); } if (access < MemoryMappedFileAccess.ReadWrite || access > MemoryMappedFileAccess.ReadWriteExecute) { throw new ArgumentOutOfRangeException(nameof(access)); } if (access == MemoryMappedFileAccess.Write) { throw new ArgumentException(SR.Argument_NewMMFWriteAccessNotAllowed, nameof(access)); } if (access == MemoryMappedFileAccess.Read && capacity > fileStream.Length) { throw new ArgumentException(SR.Argument_ReadAccessWithLargeCapacity); } if (inheritability < HandleInheritability.None || inheritability > HandleInheritability.Inheritable) { throw new ArgumentOutOfRangeException(nameof(inheritability)); } // flush any bytes written to the FileStream buffer so that we can see them in our MemoryMappedFile fileStream.Flush(); if (capacity == DefaultSize) { capacity = fileStream.Length; } // one can always create a small view if they do not want to map an entire file if (fileStream.Length > capacity) { throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_CapacityGEFileSizeRequired); } SafeMemoryMappedFileHandle handle = CreateCore(fileStream, mapName, inheritability, access, MemoryMappedFileOptions.None, capacity); return(new MemoryMappedFile(handle, fileStream, leaveOpen)); }
public static MemoryMappedFile CreateFromFile(String path, FileMode mode, String mapName, Int64 capacity, MemoryMappedFileAccess access) { if (path == null) { throw new ArgumentNullException("path"); } if (mapName != null && mapName.Length == 0) { throw new ArgumentException(SR.GetString(SR.Argument_MapNameEmptyString)); } if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity", SR.GetString(SR.ArgumentOutOfRange_PositiveOrDefaultCapacityRequired)); } if (access < MemoryMappedFileAccess.ReadWrite || access > MemoryMappedFileAccess.ReadWriteExecute) { throw new ArgumentOutOfRangeException("access"); } if (mode == FileMode.Append) { throw new ArgumentException(SR.GetString(SR.Argument_NewMMFAppendModeNotAllowed), "mode"); } if (access == MemoryMappedFileAccess.Write) { throw new ArgumentException(SR.GetString(SR.Argument_NewMMFWriteAccessNotAllowed), "access"); } bool existed = File.Exists(path); FileStream fileStream = new FileStream(path, mode, GetFileStreamFileSystemRights(access), FileShare.None, 0x1000, FileOptions.None); if (capacity == 0 && fileStream.Length == 0) { CleanupFile(fileStream, existed, path); throw new ArgumentException(SR.GetString(SR.Argument_EmptyFile)); } if (access == MemoryMappedFileAccess.Read && capacity > fileStream.Length) { CleanupFile(fileStream, existed, path); throw new ArgumentException(SR.GetString(SR.Argument_ReadAccessWithLargeCapacity)); } if (capacity == DefaultSize) { capacity = fileStream.Length; } // one can always create a small view if they do not want to map an entire file if (fileStream.Length > capacity) { CleanupFile(fileStream, existed, path); throw new ArgumentOutOfRangeException("capacity", SR.GetString(SR.ArgumentOutOfRange_CapacityGEFileSizeRequired)); } SafeMemoryMappedFileHandle handle = null; try { handle = CreateCore(fileStream.SafeFileHandle, mapName, HandleInheritability.None, null, access, MemoryMappedFileOptions.None, capacity); } catch { CleanupFile(fileStream, existed, path); throw; } Debug.Assert(handle != null && !handle.IsInvalid); return(new MemoryMappedFile(handle, fileStream, false)); }
private static SafeMemoryMappedFileHandle CreateOrOpenCore(SafeFileHandle fileHandle, String mapName, HandleInheritability inheritability, MemoryMappedFileSecurity memoryMappedFileSecurity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, Int64 capacity) { Debug.Assert(access != MemoryMappedFileAccess.Write, "Callers requesting write access shouldn't try to create a mmf"); SafeMemoryMappedFileHandle handle = null; Object pinningHandle; UnsafeNativeMethods.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(inheritability, memoryMappedFileSecurity, out pinningHandle); // split the long into two ints Int32 capacityLow = (Int32)(capacity & 0x00000000FFFFFFFFL); Int32 capacityHigh = (Int32)(capacity >> 32); try { int waitRetries = 14; //((2^13)-1)*10ms == approximately 1.4mins int waitSleep = 0; // keep looping until we've exhausted retries or break as soon we we get valid handle while (waitRetries > 0) { // try to create handle = UnsafeNativeMethods.CreateFileMapping(fileHandle, secAttrs, GetPageAccess(access) | (int)options, capacityHigh, capacityLow, mapName); Int32 createErrorCode = Marshal.GetLastWin32Error(); if (!handle.IsInvalid) { break; } else { if (createErrorCode != UnsafeNativeMethods.ERROR_ACCESS_DENIED) { __Error.WinIOError(createErrorCode, String.Empty); } // the mapname exists but our ACL is preventing us from opening it with CreateFileMapping. // Let's try to open it with OpenFileMapping. handle.SetHandleAsInvalid(); } // try to open handle = UnsafeNativeMethods.OpenFileMapping(GetFileMapAccess(access), (inheritability & HandleInheritability.Inheritable) != 0, mapName); Int32 openErrorCode = Marshal.GetLastWin32Error(); // valid handle if (!handle.IsInvalid) { break; } // didn't get valid handle; have to retry else { if (openErrorCode != UnsafeNativeMethods.ERROR_FILE_NOT_FOUND) { __Error.WinIOError(openErrorCode, String.Empty); } // increase wait time --waitRetries; if (waitSleep == 0) { waitSleep = 10; } else { System.Threading.Thread.Sleep(waitSleep); waitSleep *= 2; } } } // finished retrying but couldn't create or open if (handle == null || handle.IsInvalid) { throw new InvalidOperationException(SR.GetString(SR.InvalidOperation_CantCreateFileMapping)); } } finally { if (pinningHandle != null) { GCHandle pinHandle = (GCHandle)pinningHandle; pinHandle.Free(); } } return(handle); }
internal static extern int GetFileSize(SafeMemoryMappedFileHandle hFile, out int highSize);
internal static partial SafeMemoryMappedViewHandle MapViewOfFile( SafeMemoryMappedFileHandle hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);
internal unsafe static MemoryMappedView CreateView(SafeMemoryMappedFileHandle memMappedFileHandle, MemoryMappedFileAccess access, Int64 offset, Int64 size) { // MapViewOfFile can only create views that start at a multiple of the system memory allocation // granularity. We decided to hide this restriction form the user by creating larger views than the // user requested and hiding the parts that the user did not request. extraMemNeeded is the amount of // extra memory we allocate before the start of the requested view. MapViewOfFile will also round the // capacity of the view to the nearest multiple of the system page size. Once again, we hide this // from the user by preventing them from writing to any memory that they did not request. ulong extraMemNeeded = (ulong)offset % (ulong)MemoryMappedFile.GetSystemPageAllocationGranularity(); // newOffset takes into account the fact that we have some extra memory allocated before the requested view ulong newOffset = (ulong)offset - extraMemNeeded; Debug.Assert(newOffset >= 0, "newOffset = (offset - extraMemNeeded) < 0"); // determine size to pass to MapViewOfFile ulong nativeSize; if (size != MemoryMappedFile.DefaultSize) { nativeSize = (ulong)size + (ulong)extraMemNeeded; } else { nativeSize = 0; } if (IntPtr.Size == 4 && nativeSize > UInt32.MaxValue) { throw new ArgumentOutOfRangeException("size", SR.GetString(SR.ArgumentOutOfRange_CapacityLargerThanLogicalAddressSpaceNotAllowed)); } // if request is >= than total virtual, then MapViewOfFile will fail with meaningless error message // "the parameter is incorrect"; this provides better error message in advance UnsafeNativeMethods.MEMORYSTATUSEX memStatus = new UnsafeNativeMethods.MEMORYSTATUSEX(); bool result = UnsafeNativeMethods.GlobalMemoryStatusEx(memStatus); ulong totalVirtual = memStatus.ullTotalVirtual; if (nativeSize >= totalVirtual) { throw new IOException(SR.GetString(SR.IO_NotEnoughMemory)); } // split the Int64 into two ints uint offsetLow = (uint)(newOffset & 0x00000000FFFFFFFFL); uint offsetHigh = (uint)(newOffset >> 32); // create the view SafeMemoryMappedViewHandle viewHandle = UnsafeNativeMethods.MapViewOfFile(memMappedFileHandle, MemoryMappedFile.GetFileMapAccess(access), offsetHigh, offsetLow, new UIntPtr(nativeSize)); if (viewHandle.IsInvalid) { __Error.WinIOError(Marshal.GetLastWin32Error(), String.Empty); } // Query the view for its size and allocation type UnsafeNativeMethods.MEMORY_BASIC_INFORMATION viewInfo = new UnsafeNativeMethods.MEMORY_BASIC_INFORMATION(); UnsafeNativeMethods.VirtualQuery(viewHandle, ref viewInfo, (IntPtr)Marshal.SizeOf(viewInfo)); ulong viewSize = (ulong)viewInfo.RegionSize; // allocate the pages if we were using the MemoryMappedFileOptions.DelayAllocatePages option if ((viewInfo.State & UnsafeNativeMethods.MEM_RESERVE) != 0) { IntPtr tempHandle = UnsafeNativeMethods.VirtualAlloc(viewHandle, (UIntPtr)viewSize, UnsafeNativeMethods.MEM_COMMIT, MemoryMappedFile.GetPageAccess(access)); int lastError = Marshal.GetLastWin32Error(); if (viewHandle.IsInvalid) { __Error.WinIOError(lastError, String.Empty); } } // if the user specified DefaultSize as the size, we need to get the actual size if (size == MemoryMappedFile.DefaultSize) { size = (Int64)(viewSize - extraMemNeeded); } else { Debug.Assert(viewSize >= (ulong)size, "viewSize < size"); } viewHandle.Initialize((ulong)size + extraMemNeeded); MemoryMappedView mmv = new MemoryMappedView(viewHandle, (long)extraMemNeeded, size, access); return(mmv); }
public static unsafe MemoryMappedView CreateView(SafeMemoryMappedFileHandle memMappedFileHandle, MemoryMappedFileAccess access, long offset, long size) { // MapViewOfFile can only create views that start at a multiple of the system memory allocation // granularity. We decided to hide this restriction from the user by creating larger views than the // user requested and hiding the parts that the user did not request. extraMemNeeded is the amount of // extra memory we allocate before the start of the requested view. MapViewOfFile will also round the // capacity of the view to the nearest multiple of the system page size. Once again, we hide this // from the user by preventing them from writing to any memory that they did not request. ulong nativeSize; long extraMemNeeded, newOffset; ValidateSizeAndOffset( size, offset, GetSystemPageAllocationGranularity(), out nativeSize, out extraMemNeeded, out newOffset); // if request is >= than total virtual, then MapViewOfFile will fail with meaningless error message // "the parameter is incorrect"; this provides better error message in advance Interop.CheckForAvailableVirtualMemory(nativeSize); // create the view SafeMemoryMappedViewHandle viewHandle = Interop.MapViewOfFile(memMappedFileHandle, (int)MemoryMappedFile.GetFileMapAccess(access), newOffset, new UIntPtr(nativeSize)); if (viewHandle.IsInvalid) { viewHandle.Dispose(); throw Win32Marshal.GetExceptionForLastWin32Error(); } // Query the view for its size and allocation type Interop.Kernel32.MEMORY_BASIC_INFORMATION viewInfo = default; Interop.Kernel32.VirtualQuery(viewHandle, ref viewInfo, (UIntPtr)Marshal.SizeOf(viewInfo)); ulong viewSize = (ulong)viewInfo.RegionSize; // Allocate the pages if we were using the MemoryMappedFileOptions.DelayAllocatePages option // OR check if the allocated view size is smaller than the expected native size // If multiple overlapping views are created over the file mapping object, the pages in a given region // could have different attributes(MEM_RESERVE OR MEM_COMMIT) as MapViewOfFile preserves coherence between // views created on a mapping object backed by same file. // In which case, the viewSize will be smaller than nativeSize required and viewState could be MEM_COMMIT // but more pages may need to be committed in the region. // This is because, VirtualQuery function(that internally invokes VirtualQueryEx function) returns the attributes // and size of the region of pages with matching attributes starting from base address. // VirtualQueryEx: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366907(v=vs.85).aspx if (((viewInfo.State & Interop.Kernel32.MemOptions.MEM_RESERVE) != 0) || ((ulong)viewSize < (ulong)nativeSize)) { IntPtr tempHandle = Interop.VirtualAlloc( viewHandle, (UIntPtr)(nativeSize != MemoryMappedFile.DefaultSize ? nativeSize : viewSize), Interop.Kernel32.MemOptions.MEM_COMMIT, MemoryMappedFile.GetPageAccess(access)); int lastError = Marshal.GetLastWin32Error(); if (viewHandle.IsInvalid) { viewHandle.Dispose(); throw Win32Marshal.GetExceptionForWin32Error(lastError); } // again query the view for its new size viewInfo = default; Interop.Kernel32.VirtualQuery(viewHandle, ref viewInfo, (UIntPtr)Marshal.SizeOf(viewInfo)); viewSize = (ulong)viewInfo.RegionSize; } // if the user specified DefaultSize as the size, we need to get the actual size if (size == MemoryMappedFile.DefaultSize) { size = (long)(viewSize - (ulong)extraMemNeeded); } else { Debug.Assert(viewSize >= (ulong)size, "viewSize < size"); } viewHandle.Initialize((ulong)size + (ulong)extraMemNeeded); return(new MemoryMappedView(viewHandle, extraMemNeeded, size, access)); }
private static SafeMemoryMappedFileHandle CreateOrOpenCore( string mapName, HandleInheritability inheritability, MemoryMappedFileAccess access, MemoryMappedFileOptions options, long capacity) { /// Try to open the file if it exists -- this requires a bit more work. Loop until we can /// either create or open a memory mapped file up to a timeout. CreateFileMapping may fail /// if the file exists and we have non-null security attributes, in which case we need to /// use OpenFileMapping. But, there exists a race condition because the memory mapped file /// may have closed inbetween the two calls -- hence the loop. /// /// The retry/timeout logic increases the wait time each pass through the loop and times /// out in approximately 1.4 minutes. If after retrying, a MMF handle still hasn't been opened, /// throw an InvalidOperationException. Debug.Assert(access != MemoryMappedFileAccess.Write, "Callers requesting write access shouldn't try to create a mmf"); SafeMemoryMappedFileHandle handle = null; Interop.mincore.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(inheritability); // split the long into two ints int capacityLow = unchecked ((int)(capacity & 0x00000000FFFFFFFFL)); int capacityHigh = unchecked ((int)(capacity >> 32)); int waitRetries = 14; //((2^13)-1)*10ms == approximately 1.4mins int waitSleep = 0; // keep looping until we've exhausted retries or break as soon we we get valid handle while (waitRetries > 0) { // try to create handle = Interop.mincore.CreateFileMapping(INVALID_HANDLE_VALUE, ref secAttrs, GetPageAccess(access) | (int)options, capacityHigh, capacityLow, mapName); if (!handle.IsInvalid) { break; } else { int createErrorCode = Marshal.GetLastWin32Error(); if (createErrorCode != Interop.mincore.Errors.ERROR_ACCESS_DENIED) { throw Win32Marshal.GetExceptionForWin32Error(createErrorCode); } } // try to open handle = Interop.mincore.OpenFileMapping(GetFileMapAccess(access), (inheritability & HandleInheritability.Inheritable) != 0, mapName); // valid handle if (!handle.IsInvalid) { break; } // didn't get valid handle; have to retry else { int openErrorCode = Marshal.GetLastWin32Error(); if (openErrorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND) { throw Win32Marshal.GetExceptionForWin32Error(openErrorCode); } // increase wait time --waitRetries; if (waitSleep == 0) { waitSleep = 10; } else { ThreadSleep(waitSleep); waitSleep *= 2; } } } // finished retrying but couldn't create or open if (handle == null || handle.IsInvalid) { throw new InvalidOperationException(SR.InvalidOperation_CantCreateFileMapping); } return(handle); }
internal static extern SafeMemoryMappedViewHandle MapViewOfFile( SafeMemoryMappedFileHandle hFileMappingObject, FileMapAccess dwDesiredAccess, UInt32 dwFileOffsetHigh, UInt32 dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);
public unsafe static MemoryMappedView CreateView( SafeMemoryMappedFileHandle memMappedFileHandle, MemoryMappedFileAccess access, long requestedOffset, long requestedSize) { if (requestedOffset > memMappedFileHandle._capacity) { throw new ArgumentOutOfRangeException("offset"); } if (requestedSize > MaxProcessAddressSpace) { throw new IOException(SR.ArgumentOutOfRange_CapacityLargerThanLogicalAddressSpaceNotAllowed); } if (requestedOffset + requestedSize > memMappedFileHandle._capacity) { throw new UnauthorizedAccessException(); } if (memMappedFileHandle.IsClosed) { throw new ObjectDisposedException(typeof(MemoryMappedFile).Name); } if (requestedSize == MemoryMappedFile.DefaultSize) { requestedSize = memMappedFileHandle._capacity - requestedOffset; } // mmap can only create views that start at a multiple of the page size. As on Windows, // we hide this restriction form the user by creating larger views than the user requested and hiding the parts // that the user did not request. extraMemNeeded is the amount of extra memory we allocate before the start of the // requested view. (mmap may round up the actual length such that it is also page-aligned; we hide that by using // the right size and not extending the size to be page-aligned.) ulong nativeSize, extraMemNeeded, nativeOffset; int pageSize = Interop.libc.sysconf(Interop.libc.SysConfNames._SC_PAGESIZE); ValidateSizeAndOffset( requestedSize, requestedOffset, pageSize, out nativeSize, out extraMemNeeded, out nativeOffset); if (nativeSize == 0) { nativeSize = (ulong)pageSize; } bool gotRefOnHandle = false; try { // Determine whether to create the pages as private or as shared; the former is used for copy-on-write. Interop.libc.MemoryMappedFlags flags = (memMappedFileHandle._access == MemoryMappedFileAccess.CopyOnWrite) ? Interop.libc.MemoryMappedFlags.MAP_PRIVATE : Interop.libc.MemoryMappedFlags.MAP_SHARED; // If we have a file handle, get the file descriptor from it. If the handle is null, // we'll use an anonymous backing store for the map. int fd; if (memMappedFileHandle._fileHandle != null) { // Get the file descriptor from the SafeFileHandle memMappedFileHandle._fileHandle.DangerousAddRef(ref gotRefOnHandle); Debug.Assert(gotRefOnHandle); fd = (int)memMappedFileHandle._fileHandle.DangerousGetHandle(); Debug.Assert(fd >= 0); } else { Debug.Assert(!gotRefOnHandle); fd = -1; flags |= Interop.libc.MemoryMappedFlags.MAP_ANONYMOUS; } // Nothing to do for options.DelayAllocatePages, since we're only creating the map // with mmap when creating the view. // Verify that the requested view permissions don't exceed the map's permissions Interop.libc.MemoryMappedProtections viewProtForVerification = GetProtections(access, forVerification: true); Interop.libc.MemoryMappedProtections mapProtForVerification = GetProtections(memMappedFileHandle._access, forVerification: true); if ((viewProtForVerification & mapProtForVerification) != viewProtForVerification) { throw new UnauthorizedAccessException(viewProtForVerification + " <> " + mapProtForVerification); } // Create the map IntPtr addr = Interop.libc.mmap( IntPtr.Zero, // don't specify an address; let the system choose one (IntPtr)nativeSize, // specify the rounded-size we computed so as to page align; size + extraMemNeeded GetProtections(access, forVerification: false), // viewProtections is strictly less than mapProtections, so use viewProtections flags, fd, // mmap adds a ref count to the fd, so there's no need to dup it. (long)nativeOffset); // specify the rounded-offset we computed so as to page align; offset - extraMemNeeded if ((long)addr < 0) { throw Interop.GetExceptionForIoErrno(Marshal.GetLastWin32Error()); } // Based on the HandleInheritability, try to prevent the memory-mapped region // from being inherited by a forked process if (memMappedFileHandle._inheritability == HandleInheritability.None) { int adviseResult = Interop.libc.madvise(addr, (IntPtr)nativeSize, Interop.libc.MemoryMappedAdvice.MADV_DONTFORK); Debug.Assert(adviseResult == 0); // In release, ignore failures from advise; it's just a hint, anyway. } // Create and return the view handle var viewHandle = new SafeMemoryMappedViewHandle(addr, ownsHandle: true); viewHandle.Initialize(nativeSize); return(new MemoryMappedView( viewHandle, (long)extraMemNeeded, // the view points to offset - extraMemNeeded, so we need to shift back by extraMemNeeded requestedSize, // only allow access to the actual size requested access)); } finally { if (gotRefOnHandle) { memMappedFileHandle._fileHandle.DangerousRelease(); } } }
internal MemoryMappedFileSecurity(SafeMemoryMappedFileHandle safeHandle, AccessControlSections includeSections) : base(false, ResourceType.KernelObject, safeHandle, includeSections) { }
internal static extern SafeMemoryMappedViewHandle MapViewOfFile(SafeMemoryMappedFileHandle handle, int dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);
public unsafe static MemoryMappedView CreateView( SafeMemoryMappedFileHandle memMappedFileHandle, MemoryMappedFileAccess access, long requestedOffset, long requestedSize) { if (requestedOffset > memMappedFileHandle._capacity) { throw new ArgumentOutOfRangeException("offset"); } if (requestedSize > MaxProcessAddressSpace) { throw new IOException(SR.ArgumentOutOfRange_CapacityLargerThanLogicalAddressSpaceNotAllowed); } if (requestedOffset + requestedSize > memMappedFileHandle._capacity) { throw new UnauthorizedAccessException(); } if (memMappedFileHandle.IsClosed) { throw new ObjectDisposedException(typeof(MemoryMappedFile).Name); } if (requestedSize == MemoryMappedFile.DefaultSize) { requestedSize = memMappedFileHandle._capacity - requestedOffset; } // mmap can only create views that start at a multiple of the page size. As on Windows, // we hide this restriction form the user by creating larger views than the user requested and hiding the parts // that the user did not request. extraMemNeeded is the amount of extra memory we allocate before the start of the // requested view. (mmap may round up the actual length such that it is also page-aligned; we hide that by using // the right size and not extending the size to be page-aligned.) ulong nativeSize; long extraMemNeeded, nativeOffset; long pageSize = Interop.Sys.SysConf(Interop.Sys.SysConfName._SC_PAGESIZE); Debug.Assert(pageSize > 0); ValidateSizeAndOffset( requestedSize, requestedOffset, pageSize, out nativeSize, out extraMemNeeded, out nativeOffset); // Determine whether to create the pages as private or as shared; the former is used for copy-on-write. Interop.Sys.MemoryMappedFlags flags = (memMappedFileHandle._access == MemoryMappedFileAccess.CopyOnWrite || access == MemoryMappedFileAccess.CopyOnWrite) ? Interop.Sys.MemoryMappedFlags.MAP_PRIVATE : Interop.Sys.MemoryMappedFlags.MAP_SHARED; // If we have a file handle, get the file descriptor from it. If the handle is null, // we'll use an anonymous backing store for the map. SafeFileHandle fd; if (memMappedFileHandle._fileStream != null) { // Get the file descriptor from the SafeFileHandle fd = memMappedFileHandle._fileStream.SafeFileHandle; Debug.Assert(!fd.IsInvalid); } else { fd = new SafeFileHandle(new IntPtr(-1), false); flags |= Interop.Sys.MemoryMappedFlags.MAP_ANONYMOUS; } // Nothing to do for options.DelayAllocatePages, since we're only creating the map // with mmap when creating the view. // Verify that the requested view permissions don't exceed the map's permissions Interop.Sys.MemoryMappedProtections viewProtForVerification = GetProtections(access, forVerification: true); Interop.Sys.MemoryMappedProtections mapProtForVerification = GetProtections(memMappedFileHandle._access, forVerification: true); if ((viewProtForVerification & mapProtForVerification) != viewProtForVerification) { throw new UnauthorizedAccessException(); } // viewProtections is strictly less than mapProtections, so use viewProtections for actually creating the map. Interop.Sys.MemoryMappedProtections viewProtForCreation = GetProtections(access, forVerification: false); // Create the map IntPtr addr = IntPtr.Zero; if (nativeSize > 0) { addr = Interop.Sys.MMap( IntPtr.Zero, // don't specify an address; let the system choose one nativeSize, // specify the rounded-size we computed so as to page align; size + extraMemNeeded viewProtForCreation, flags, fd, // mmap adds a ref count to the fd, so there's no need to dup it. nativeOffset); // specify the rounded-offset we computed so as to page align; offset - extraMemNeeded } else { // There are some corner cases where the .NET API allows the requested size to be zero, e.g. the caller is // creating a map at the end of the capacity. We can't pass 0 to mmap, as that'll fail with EINVAL, nor can // we create a map that extends beyond the end of the underlying file, as that'll fail on some platforms at the // time of the map's creation. Instead, since there's no data to be read/written, it doesn't actually matter // what backs the view, so we just create an anonymous mapping. addr = Interop.Sys.MMap( IntPtr.Zero, 1, // any length that's greater than zero will suffice viewProtForCreation, flags | Interop.Sys.MemoryMappedFlags.MAP_ANONYMOUS, new SafeFileHandle(new IntPtr(-1), false), // ignore the actual fd even if there was one 0); requestedSize = 0; extraMemNeeded = 0; } if (addr == IntPtr.Zero) // note that shim uses null pointer, not non-null MAP_FAILED sentinel { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo()); } // Based on the HandleInheritability, try to prevent the memory-mapped region // from being inherited by a forked process if (memMappedFileHandle._inheritability == HandleInheritability.None) { DisableForkingIfPossible(addr, nativeSize); } // Create and return the view handle var viewHandle = new SafeMemoryMappedViewHandle(addr, ownsHandle: true); viewHandle.Initialize((ulong)nativeSize); return(new MemoryMappedView( viewHandle, extraMemNeeded, // the view points to offset - extraMemNeeded, so we need to shift back by extraMemNeeded requestedSize, // only allow access to the actual size requested access)); }
internal static extern SafeMemoryMappedViewHandle MapViewOfFileFromApp( SafeMemoryMappedFileHandle hFileMappingObject, int DesiredAccess, long FileOffset, UIntPtr NumberOfBytesToMap);
public unsafe static MemoryMappedView CreateView(SafeMemoryMappedFileHandle memMappedFileHandle, MemoryMappedFileAccess access, Int64 offset, Int64 size) { // MapViewOfFile can only create views that start at a multiple of the system memory allocation // granularity. We decided to hide this restriction form the user by creating larger views than the // user requested and hiding the parts that the user did not request. extraMemNeeded is the amount of // extra memory we allocate before the start of the requested view. MapViewOfFile will also round the // capacity of the view to the nearest multiple of the system page size. Once again, we hide this // from the user by preventing them from writing to any memory that they did not request. ulong extraMemNeeded = (ulong)offset % (ulong)GetSystemPageAllocationGranularity(); // newOffset takes into account the fact that we have some extra memory allocated before the requested view ulong newOffset = (ulong)offset - extraMemNeeded; Debug.Assert(newOffset >= 0, "newOffset = (offset - extraMemNeeded) < 0"); // determine size to pass to MapViewOfFile ulong nativeSize = (size != MemoryMappedFile.DefaultSize) ? (ulong)size + (ulong)extraMemNeeded : 0; if (IntPtr.Size == 4 && nativeSize > UInt32.MaxValue) { throw new ArgumentOutOfRangeException("size", SR.ArgumentOutOfRange_CapacityLargerThanLogicalAddressSpaceNotAllowed); } // if request is >= than total virtual, then MapViewOfFile will fail with meaningless error message // "the parameter is incorrect"; this provides better error message in advance Interop.MEMORYSTATUSEX memStatus; memStatus.dwLength = (uint)sizeof(Interop.MEMORYSTATUSEX); Interop.mincore.GlobalMemoryStatusEx(out memStatus); ulong totalVirtual = memStatus.ullTotalVirtual; if (nativeSize >= totalVirtual) { throw new IOException(SR.IO_NotEnoughMemory); } // split the Int64 into two ints int offsetLow = unchecked ((int)(newOffset & 0x00000000FFFFFFFFL)); int offsetHigh = unchecked ((int)(newOffset >> 32)); // create the view SafeMemoryMappedViewHandle viewHandle = Interop.mincore.MapViewOfFile(memMappedFileHandle, (int)MemoryMappedFile.GetFileMapAccess(access), offsetHigh, offsetLow, new UIntPtr(nativeSize)); if (viewHandle.IsInvalid) { throw Win32Marshal.GetExceptionForLastWin32Error(); } // Query the view for its size and allocation type Interop.MEMORY_BASIC_INFORMATION viewInfo = new Interop.MEMORY_BASIC_INFORMATION(); Interop.mincore.VirtualQuery(viewHandle, ref viewInfo, (UIntPtr)Marshal.SizeOf(viewInfo)); ulong viewSize = (ulong)viewInfo.RegionSize; // Allocate the pages if we were using the MemoryMappedFileOptions.DelayAllocatePages option // OR check if the allocated view size is smaller than the expected native size // If multiple overlapping views are created over the file mapping object, the pages in a given region // could have different attributes(MEM_RESERVE OR MEM_COMMIT) as MapViewOfFile preserves coherence between // views created on a mapping object backed by same file. // In which case, the viewSize will be smaller than nativeSize required and viewState could be MEM_COMMIT // but more pages may need to be committed in the region. // This is because, VirtualQuery function(that internally invokes VirtualQueryEx function) returns the attributes // and size of the region of pages with matching attributes starting from base address. // VirtualQueryEx: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366907(v=vs.85).aspx if (((viewInfo.State & Interop.MEM_RESERVE) != 0) || (viewSize < nativeSize)) { IntPtr tempHandle = Interop.mincore.VirtualAlloc(viewHandle, (UIntPtr)nativeSize, Interop.MEM_COMMIT, MemoryMappedFile.GetPageAccess(access)); int lastError = Marshal.GetLastWin32Error(); if (viewHandle.IsInvalid) { throw Win32Marshal.GetExceptionForWin32Error(lastError); } // again query the view for its new size viewInfo = new Interop.MEMORY_BASIC_INFORMATION(); Interop.mincore.VirtualQuery(viewHandle, ref viewInfo, (UIntPtr)Marshal.SizeOf(viewInfo)); viewSize = (ulong)viewInfo.RegionSize; } // if the user specified DefaultSize as the size, we need to get the actual size if (size == MemoryMappedFile.DefaultSize) { size = (Int64)(viewSize - extraMemNeeded); } else { Debug.Assert(viewSize >= (ulong)size, "viewSize < size"); } viewHandle.Initialize((ulong)size + extraMemNeeded); return(new MemoryMappedView(viewHandle, (long)extraMemNeeded, size, access)); }
public static extern SafeMemoryMappedViewHandle MapViewOfFile(SafeMemoryMappedFileHandle hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, IntPtr dwNumberOfBytesToMap);
private MemoryMappedFile(SafeMemoryMappedFileHandle handle) { this._handle = handle; }
public unsafe static MemoryMappedView CreateView( SafeMemoryMappedFileHandle memMappedFileHandle, MemoryMappedFileAccess access, Int64 offset, Int64 size) { throw NotImplemented.ByDesign; // TODO: Implement this }