private static FileStream CreateSharedBackingObjectUsingFile(Interop.Sys.MemoryMappedProtections protections, long capacity)
        {
            string tempMapsDirectory = s_tempMapsDirectory ?? (s_tempMapsDirectory = PersistedFiles.GetTempFeatureDirectory("maps"));

            Directory.CreateDirectory(tempMapsDirectory);
            string path = Path.Combine(tempMapsDirectory, Guid.NewGuid().ToString("N"));

            FileAccess access =
                (protections & (Interop.Sys.MemoryMappedProtections.PROT_READ | Interop.Sys.MemoryMappedProtections.PROT_WRITE)) != 0 ? FileAccess.ReadWrite :
                (protections & (Interop.Sys.MemoryMappedProtections.PROT_WRITE)) != 0 ? FileAccess.Write :
                FileAccess.Read;

            // Create the backing file, then immediately unlink it so that it'll be cleaned up when no longer in use.
            // Then enlarge it to the requested capacity.
            const int DefaultBufferSize = 0x1000;
            var       fs = new FileStream(path, FileMode.CreateNew, TranslateProtectionsToFileAccess(protections), FileShare.ReadWrite, DefaultBufferSize);

            try
            {
                Interop.CheckIo(Interop.Sys.Unlink(path));
                fs.SetLength(capacity);
            }
            catch
            {
                fs.Dispose();
                throw;
            }
            return(fs);
        }
 private static FileAccess TranslateProtectionsToFileAccess(Interop.Sys.MemoryMappedProtections protections)
 {
     return
         ((protections & (Interop.Sys.MemoryMappedProtections.PROT_READ | Interop.Sys.MemoryMappedProtections.PROT_WRITE)) != 0 ? FileAccess.ReadWrite :
          (protections & (Interop.Sys.MemoryMappedProtections.PROT_WRITE)) != 0 ? FileAccess.Write :
          FileAccess.Read);
 }
Esempio n. 3
0
        private static FileStream CreateSharedBackingObjectUsingFile(Interop.Sys.MemoryMappedProtections protections, long capacity, HandleInheritability inheritability)
        {
            // We create a temporary backing file in TMPDIR.  We don't bother putting it into subdirectories as the file exists
            // extremely briefly: it's opened/created and then immediately unlinked.
            string path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));

            FileShare share = inheritability == HandleInheritability.None ?
                              FileShare.ReadWrite :
                              FileShare.ReadWrite | FileShare.Inheritable;

            // Create the backing file, then immediately unlink it so that it'll be cleaned up when no longer in use.
            // Then enlarge it to the requested capacity.
            const int DefaultBufferSize = 0x1000;
            var       fs = new FileStream(path, FileMode.CreateNew, TranslateProtectionsToFileAccess(protections), share, DefaultBufferSize);

            try
            {
                Interop.CheckIo(Interop.Sys.Unlink(path));
                fs.SetLength(capacity);
            }
            catch
            {
                fs.Dispose();
                throw;
            }
            return(fs);
        }
Esempio n. 4
0
            private unsafe void ChangeProtection(Interop.Sys.MemoryMappedProtections prots)
            {
                byte *ptr = null;

                try
                {
                    AcquirePointer(ref ptr);
                    if (Interop.Sys.MProtect((IntPtr)ptr, ByteLength, prots) != 0)
                    {
                        throw CreateExceptionFromErrno();
                    }
                }
                finally
                {
                    if (ptr != null)
                    {
                        ReleasePointer();
                    }
                }
            }
        public 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(nameof(MemoryMappedFile));
            }

            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));
        }
        private static unsafe SafeMemoryMappedFileHandle CreateCore(
            FileStream fileStream, string mapName,
            HandleInheritability inheritability, MemoryMappedFileAccess access,
            MemoryMappedFileOptions options, long capacity)
        {
            if (mapName != null)
            {
                // Named maps are not supported in our Unix implementation.  We could support named maps on Linux using
                // shared memory segments (shmget/shmat/shmdt/shmctl/etc.), but that doesn't work on OSX by default due
                // to very low default limits on OSX for the size of such objects; it also doesn't support behaviors
                // like copy-on-write or the ability to control handle inheritability, and reliably cleaning them up
                // relies on some non-conforming behaviors around shared memory IDs remaining valid even after they've
                // been marked for deletion (IPC_RMID).  We could also support named maps using the current implementation
                // by not unlinking after creating the backing store, but then the backing stores would remain around
                // and accessible even after process exit, with no good way to appropriately clean them up.
                // (File-backed maps may still be used for cross-process communication.)
                throw CreateNamedMapsNotSupportedException();
            }

            bool ownsFileStream = false;

            if (fileStream != null)
            {
                // This map is backed by a file.  Make sure the file's size is increased to be
                // at least as big as the requested capacity of the map.
                if (fileStream.Length < capacity)
                {
                    try
                    {
                        fileStream.SetLength(capacity);
                    }
                    catch (ArgumentException exc)
                    {
                        // If the capacity is too large, we'll get an ArgumentException from SetLength,
                        // but on Windows this same condition is represented by an IOException.
                        throw new IOException(exc.Message, exc);
                    }
                }
            }
            else
            {
                // This map is backed by memory-only.  With files, multiple views over the same map
                // will end up being able to share data through the same file-based backing store;
                // for anonymous maps, we need a similar backing store, or else multiple views would logically
                // each be their own map and wouldn't share any data.  To achieve this, we create a backing object
                // (either memory or on disk, depending on the system) and use its file descriptor as the file handle.
                // However, we only do this when the permission is more than read-only.  We can't change the size
                // of an object that has read-only permissions, but we also don't need to worry about sharing
                // views over a read-only, anonymous, memory-backed map, because the data will never change, so all views
                // will always see zero and can't change that.  In that case, we just use the built-in anonymous support of
                // the map by leaving fileStream as null.
                Interop.Sys.MemoryMappedProtections protections = MemoryMappedView.GetProtections(access, forVerification: false);
                if ((protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0 && capacity > 0)
                {
                    ownsFileStream = true;
                    fileStream     = CreateSharedBackingObject(protections, capacity);

                    // If the MMF handle should not be inherited, mark the backing object fd as O_CLOEXEC.
                    if (inheritability == HandleInheritability.None)
                    {
                        Interop.CheckIo(Interop.Sys.Fcntl.SetCloseOnExec(fileStream.SafeFileHandle));
                    }
                }
            }

            return(new SafeMemoryMappedFileHandle(fileStream, ownsFileStream, inheritability, access, options, capacity));
        }
        // -----------------------------
        // ---- PAL layer ends here ----
        // -----------------------------

        private static FileStream CreateSharedBackingObjectUsingMemory(
            Interop.Sys.MemoryMappedProtections protections, long capacity)
        {
            // The POSIX shared memory object name must begin with '/'.  After that we just want something short and unique.
            string mapName = "/corefx_map_" + Guid.NewGuid().ToString("N");

            // Determine the flags to use when creating the shared memory object
            Interop.Sys.OpenFlags flags = (protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0 ?
                                          Interop.Sys.OpenFlags.O_RDWR :
                                          Interop.Sys.OpenFlags.O_RDONLY;
            flags |= Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL; // CreateNew

            // Determine the permissions with which to create the file
            Interop.Sys.Permissions perms = default(Interop.Sys.Permissions);
            if ((protections & Interop.Sys.MemoryMappedProtections.PROT_READ) != 0)
            {
                perms |= Interop.Sys.Permissions.S_IRUSR;
            }
            if ((protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0)
            {
                perms |= Interop.Sys.Permissions.S_IWUSR;
            }
            if ((protections & Interop.Sys.MemoryMappedProtections.PROT_EXEC) != 0)
            {
                perms |= Interop.Sys.Permissions.S_IXUSR;
            }

            // Create the shared memory object.
            SafeFileHandle fd = Interop.Sys.ShmOpen(mapName, flags, (int)perms);

            if (fd.IsInvalid)
            {
                Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
                if (errorInfo.Error == Interop.Error.ENOTSUP)
                {
                    // If ShmOpen is not supported, fall back to file backing object.
                    // Note that the System.Native shim will force this failure on platforms where
                    // the result of native shm_open does not work well with our subsequent call
                    // to mmap.
                    return(null);
                }

                throw Interop.GetExceptionForIoErrno(errorInfo);
            }

            try
            {
                // Unlink the shared memory object immediately so that it'll go away once all handles
                // to it are closed (as with opened then unlinked files, it'll remain usable via
                // the open handles even though it's unlinked and can't be opened anew via its name).
                Interop.CheckIo(Interop.Sys.ShmUnlink(mapName));

                // Give it the right capacity.  We do this directly with ftruncate rather
                // than via FileStream.SetLength after the FileStream is created because, on some systems,
                // lseek fails on shared memory objects, causing the FileStream to think it's unseekable,
                // causing it to preemptively throw from SetLength.
                Interop.CheckIo(Interop.Sys.FTruncate(fd, capacity));

                // Wrap the file descriptor in a stream and return it.
                return(new FileStream(fd, TranslateProtectionsToFileAccess(protections)));
            }
            catch
            {
                fd.Dispose();
                throw;
            }
        }
 private static FileStream CreateSharedBackingObject(Interop.Sys.MemoryMappedProtections protections, long capacity)
 {
     return(CreateSharedBackingObjectUsingMemory(protections, capacity)
            ?? CreateSharedBackingObjectUsingFile(protections, capacity));
 }
Esempio n. 9
0
        private static FileStream?CreateSharedBackingObjectUsingMemory(
            Interop.Sys.MemoryMappedProtections protections, long capacity, HandleInheritability inheritability)
        {
            // Determine the flags to use when creating the shared memory object
            Interop.Sys.OpenFlags flags = (protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0 ?
                                          Interop.Sys.OpenFlags.O_RDWR :
                                          Interop.Sys.OpenFlags.O_RDONLY;
            flags |= Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL; // CreateNew

            // Determine the permissions with which to create the file
            Interop.Sys.Permissions perms = default(Interop.Sys.Permissions);
            if ((protections & Interop.Sys.MemoryMappedProtections.PROT_READ) != 0)
            {
                perms |= Interop.Sys.Permissions.S_IRUSR;
            }
            if ((protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0)
            {
                perms |= Interop.Sys.Permissions.S_IWUSR;
            }
            if ((protections & Interop.Sys.MemoryMappedProtections.PROT_EXEC) != 0)
            {
                perms |= Interop.Sys.Permissions.S_IXUSR;
            }

            string         mapName;
            SafeFileHandle fd;

            do
            {
                mapName = GenerateMapName();
                fd      = Interop.Sys.ShmOpen(mapName, flags, (int)perms); // Create the shared memory object.

                if (fd.IsInvalid)
                {
                    Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
                    fd.Dispose();

                    if (errorInfo.Error == Interop.Error.ENOTSUP)
                    {
                        // If ShmOpen is not supported, fall back to file backing object.
                        // Note that the System.Native shim will force this failure on platforms where
                        // the result of native shm_open does not work well with our subsequent call to mmap.
                        return(null);
                    }
                    else if (errorInfo.Error != Interop.Error.EEXIST) // map with same name already existed
                    {
                        throw Interop.GetExceptionForIoErrno(errorInfo);
                    }
                }
            } while (fd.IsInvalid);

            try
            {
                // Unlink the shared memory object immediately so that it'll go away once all handles
                // to it are closed (as with opened then unlinked files, it'll remain usable via
                // the open handles even though it's unlinked and can't be opened anew via its name).
                Interop.CheckIo(Interop.Sys.ShmUnlink(mapName));

                // Give it the right capacity.  We do this directly with ftruncate rather
                // than via FileStream.SetLength after the FileStream is created because, on some systems,
                // lseek fails on shared memory objects, causing the FileStream to think it's unseekable,
                // causing it to preemptively throw from SetLength.
                Interop.CheckIo(Interop.Sys.FTruncate(fd, capacity));

                // shm_open sets CLOEXEC implicitly.  If the inheritability requested is Inheritable, remove CLOEXEC.
                if (inheritability == HandleInheritability.Inheritable &&
                    Interop.Sys.Fcntl.SetFD(fd, 0) == -1)
                {
                    throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo());
                }

                // Wrap the file descriptor in a stream and return it.
                return(new FileStream(fd, TranslateProtectionsToFileAccess(protections)));
            }
            catch
            {
                fd.Dispose();
                throw;
            }