Beispiel #1
0
        private static string?GetProcessPath()
        {
            var builder = new ValueStringBuilder(stackalloc char[Interop.Kernel32.MAX_PATH]);

            uint length;

            while ((length = Interop.Kernel32.GetModuleFileName(IntPtr.Zero, ref builder.GetPinnableReference(), (uint)builder.Capacity)) >= builder.Capacity)
            {
                builder.EnsureCapacity((int)length);
            }

            if (length == 0)
            {
                throw Win32Marshal.GetExceptionForLastWin32Error();
            }

            builder.Length = (int)length;
            return(builder.ToString());
        }
Beispiel #2
0
        private static OpenExistingResult OpenExistingWorker(string name, out Semaphore result)
        {
#if PLATFORM_WINDOWS
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }
            if (name.Length == 0)
            {
                throw new ArgumentException(SR.Argument_EmptyName, nameof(name));
            }

            //Pass false to OpenSemaphore to prevent inheritedHandles
            SafeWaitHandle myHandle = Win32Native.OpenSemaphore(AccessRights, false, name);

            if (myHandle.IsInvalid)
            {
                result = null;

                int errorCode = Marshal.GetLastWin32Error();

                if (Interop.Errors.ERROR_FILE_NOT_FOUND == errorCode || Interop.Errors.ERROR_INVALID_NAME == errorCode)
                {
                    return(OpenExistingResult.NameNotFound);
                }
                if (Interop.Errors.ERROR_PATH_NOT_FOUND == errorCode)
                {
                    return(OpenExistingResult.PathNotFound);
                }
                if (null != name && 0 != name.Length && Interop.Errors.ERROR_INVALID_HANDLE == errorCode)
                {
                    return(OpenExistingResult.NameInvalid);
                }
                //this is for passed through NativeMethods Errors
                throw Win32Marshal.GetExceptionForLastWin32Error();
            }

            result = new Semaphore(myHandle);
            return(OpenExistingResult.Success);
#else
            throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives);
#endif
        }
        // Allocate a specified number of bytes, commit them and free them. This should enlarge
        // page file if necessary and possible.
        private static void GrowPageFileIfNecessaryAndPossible(UIntPtr numBytes)
        {
            unsafe
            {
#if ENABLE_WINRT
                void *pMemory = Interop.mincore.VirtualAllocFromApp(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE);
#else
                void *pMemory = Interop.Kernel32.VirtualAlloc(null, numBytes, Interop.Kernel32.MEM_COMMIT, Interop.Kernel32.PAGE_READWRITE);
#endif
                if (pMemory != null)
                {
                    bool r = Interop.Kernel32.VirtualFree(pMemory, UIntPtr.Zero, Interop.Kernel32.MEM_RELEASE);
                    if (!r)
                    {
                        throw Win32Marshal.GetExceptionForLastWin32Error();
                    }
                }
            }
        }
Beispiel #4
0
        private static string ExpandEnvironmentVariablesCore(string name)
        {
            var builder = new ValueStringBuilder(stackalloc char[128]);

            uint length;

            while ((length = Interop.Kernel32.ExpandEnvironmentStrings(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity)
            {
                builder.EnsureCapacity((int)length);
            }

            if (length == 0)
            {
                throw Win32Marshal.GetExceptionForLastWin32Error();
            }

            // length includes the null terminator
            builder.Length = (int)length - 1;
            return(builder.ToString());
        }
Beispiel #5
0
        /// <summary>
        /// Creates a symbolic link.
        /// </summary>
        /// <param name="symlinkFileName">The symbolic link to be created.</param>
        /// <param name="targetFileName">The name of the target for the symbolic link to be created.
        /// If it has a device name associated with it, the link is treated as an absolute link; otherwise, the link is treated as a relative link.</param>
        /// <param name="isDirectory"><see langword="true" /> if the link target is a directory; <see langword="false" /> otherwise.</param>
        internal static void CreateSymbolicLink(string symlinkFileName, string targetFileName, bool isDirectory)
        {
            string originalPath = symlinkFileName;

            symlinkFileName = PathInternal.EnsureExtendedPrefixIfNeeded(symlinkFileName);
            targetFileName  = PathInternal.EnsureExtendedPrefixIfNeeded(targetFileName);

            int flags = 0;

            bool isAtLeastWin10Build14972 =
                Environment.OSVersion.Version.Major == 10 && Environment.OSVersion.Version.Build >= 14972 ||
                Environment.OSVersion.Version.Major >= 11;

            if (isAtLeastWin10Build14972)
            {
                flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
            }

            if (isDirectory)
            {
                flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
            }

            bool success = CreateSymbolicLinkPrivate(symlinkFileName, targetFileName, flags);

            int error;

            if (!success)
            {
                throw Win32Marshal.GetExceptionForLastWin32Error(originalPath);
            }
            // In older versions we need to check GetLastWin32Error regardless of the return value of CreateSymbolicLink,
            // e.g: if the user doesn't have enough privileges to create a symlink the method returns success which we can consider as a silent failure.
            else if (!isAtLeastWin10Build14972 && (error = Marshal.GetLastWin32Error()) != 0)
            {
                throw Win32Marshal.GetExceptionForWin32Error(error, originalPath);
            }
        }
Beispiel #6
0
        public static IconExtractor Open(string fileName)
        {
            if (fileName == null)
            {
                throw new ArgumentNullException("fileName");
            }

            if (fileName.Length == 0)
            {
                throw new ArgumentException(null, "fileName");
            }

            fileName = Path.GetFullPath(fileName);
            fileName = Environment.ExpandEnvironmentVariables(fileName);

            SafeModuleHandle moduleHandle = DllImports.LoadLibraryEx(fileName, IntPtr.Zero, LoadLibraryExFlags.LOAD_LIBRARY_AS_DATAFILE);

            if (moduleHandle.IsInvalid)
            {
                throw Win32Marshal.GetExceptionForLastWin32Error(fileName);
            }

            List <ResourceName> iconNames = new List <ResourceName>();

            DllImports.EnumResourceNames(moduleHandle, ResourceTypes.RT_GROUP_ICON, (hModule, lpszType, lpszName, lParam) =>
            {
                if (lpszType == ResourceTypes.RT_GROUP_ICON)
                {
                    iconNames.Add(new ResourceName(lpszName));
                }

                return(true);
            },
                                         IntPtr.Zero);


            return(new IconExtractor(moduleHandle, iconNames));
        }
Beispiel #7
0
        private static OpenExistingResult OpenExistingWorker(string name, SemaphoreRights rights, out Semaphore?result)
        {
            ArgumentNullException.ThrowIfNull(name);

            if (name.Length == 0)
            {
                throw new ArgumentException(SR.Argument_EmptyName, nameof(name));
            }

            result = null;
            SafeWaitHandle handle = Interop.Kernel32.OpenSemaphore((uint)rights, false, name);

            int errorCode = Marshal.GetLastWin32Error();

            if (handle.IsInvalid)
            {
                return(errorCode switch
                {
                    Interop.Errors.ERROR_FILE_NOT_FOUND or Interop.Errors.ERROR_INVALID_NAME => OpenExistingResult.NameNotFound,
                    Interop.Errors.ERROR_PATH_NOT_FOUND => OpenExistingResult.PathNotFound,
                    Interop.Errors.ERROR_INVALID_HANDLE => OpenExistingResult.NameInvalid,
                    _ => throw Win32Marshal.GetExceptionForLastWin32Error()
                });
Beispiel #8
0
        private static OpenExistingResult OpenExistingWorker(string name, out Semaphore?result)
        {
#if TARGET_WINDOWS
            ArgumentException.ThrowIfNullOrEmpty(name);

            // Pass false to OpenSemaphore to prevent inheritedHandles
            SafeWaitHandle myHandle = Interop.Kernel32.OpenSemaphore(AccessRights, false, name);

            if (myHandle.IsInvalid)
            {
                result = null;
                int errorCode = Marshal.GetLastPInvokeError();

                myHandle.Dispose();

                if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND || errorCode == Interop.Errors.ERROR_INVALID_NAME)
                {
                    return(OpenExistingResult.NameNotFound);
                }
                if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND)
                {
                    return(OpenExistingResult.PathNotFound);
                }
                if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE)
                {
                    return(OpenExistingResult.NameInvalid);
                }
                // this is for passed through NativeMethods Errors
                throw Win32Marshal.GetExceptionForLastWin32Error();
            }

            result = new Semaphore(myHandle);
            return(OpenExistingResult.Success);
#else
            throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives);
#endif
        }
Beispiel #9
0
        // Returns the amount of consecutive free memory available in a block
        // of pages.  If we didn't have enough address space, we still return
        // a positive value < size, to help potentially avoid the overhead of
        // this check if we use a MemoryFailPoint with a smaller size next.
        private static unsafe ulong MemFreeAfterAddress(void *address, ulong size)
        {
            if (size >= s_topOfMemory)
            {
                return(0);
            }

            ulong largestFreeRegion = 0;

            Interop.Kernel32.MEMORY_BASIC_INFORMATION memInfo = default;
            UIntPtr sizeOfMemInfo = (UIntPtr)sizeof(Interop.Kernel32.MEMORY_BASIC_INFORMATION);

            while (((ulong)address) + size < s_topOfMemory)
            {
                UIntPtr r = Interop.Kernel32.VirtualQuery(address, ref memInfo, sizeOfMemInfo);
                if (r == UIntPtr.Zero)
                {
                    throw Win32Marshal.GetExceptionForLastWin32Error();
                }

                ulong regionSize = memInfo.RegionSize.ToUInt64();
                if (memInfo.State == Interop.Kernel32.MemOptions.MEM_FREE)
                {
                    if (regionSize >= size)
                    {
                        return(regionSize);
                    }
                    else
                    {
                        largestFreeRegion = Math.Max(largestFreeRegion, regionSize);
                    }
                }
                address = (void *)((ulong)address + regionSize);
            }
            return(largestFreeRegion);
        }
Beispiel #10
0
        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 = new Interop.Kernel32.MEMORY_BASIC_INFORMATION();
            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: http://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 = new Interop.Kernel32.MEMORY_BASIC_INFORMATION();
                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 void RemoveDirectoryRecursive(string fullPath, ref Interop.Kernel32.WIN32_FIND_DATA findData, bool topLevel)
        {
            int       errorCode;
            Exception exception = null;

            using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(Path.Join(fullPath, "*"), ref findData))
            {
                if (handle.IsInvalid)
                {
                    throw Win32Marshal.GetExceptionForLastWin32Error(fullPath);
                }

                do
                {
                    if ((findData.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0)
                    {
                        // File
                        string fileName = findData.cFileName.GetStringFromFixedBuffer();
                        if (!Interop.Kernel32.DeleteFile(Path.Combine(fullPath, fileName)) && exception == null)
                        {
                            errorCode = Marshal.GetLastWin32Error();

                            // We don't care if something else deleted the file first
                            if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND)
                            {
                                exception = Win32Marshal.GetExceptionForWin32Error(errorCode, fileName);
                            }
                        }
                    }
                    else
                    {
                        // Directory, skip ".", "..".
                        if (findData.cFileName.FixedBufferEqualsString(".") || findData.cFileName.FixedBufferEqualsString(".."))
                        {
                            continue;
                        }

                        string fileName = findData.cFileName.GetStringFromFixedBuffer();

                        if (!IsNameSurrogateReparsePoint(ref findData))
                        {
                            // Not a reparse point, or the reparse point isn't a name surrogate, recurse.
                            try
                            {
                                RemoveDirectoryRecursive(
                                    Path.Combine(fullPath, fileName),
                                    findData: ref findData,
                                    topLevel: false);
                            }
                            catch (Exception e)
                            {
                                if (exception == null)
                                {
                                    exception = e;
                                }
                            }
                        }
                        else
                        {
                            // Name surrogate reparse point, don't recurse, simply remove the directory.
                            // If a mount point, we have to delete the mount point first.
                            if (findData.dwReserved0 == Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_MOUNT_POINT)
                            {
                                // Mount point. Unmount using full path plus a trailing '\'.
                                // (Note: This doesn't remove the underlying directory)
                                string mountPoint = Path.Join(fullPath, fileName, PathInternal.DirectorySeparatorCharAsString);
                                if (!Interop.Kernel32.DeleteVolumeMountPoint(mountPoint) && exception == null)
                                {
                                    errorCode = Marshal.GetLastWin32Error();
                                    if (errorCode != Interop.Errors.ERROR_SUCCESS &&
                                        errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND)
                                    {
                                        exception = Win32Marshal.GetExceptionForWin32Error(errorCode, fileName);
                                    }
                                }
                            }

                            // Note that RemoveDirectory on a symbolic link will remove the link itself.
                            if (!Interop.Kernel32.RemoveDirectory(Path.Combine(fullPath, fileName)) && exception == null)
                            {
                                errorCode = Marshal.GetLastWin32Error();
                                if (errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND)
                                {
                                    exception = Win32Marshal.GetExceptionForWin32Error(errorCode, fileName);
                                }
                            }
                        }
                    }
                } while (Interop.Kernel32.FindNextFile(handle, ref findData));

                if (exception != null)
                {
                    throw exception;
                }

                errorCode = Marshal.GetLastWin32Error();
                if (errorCode != Interop.Errors.ERROR_SUCCESS && errorCode != Interop.Errors.ERROR_NO_MORE_FILES)
                {
                    throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
                }
            }

            // As we successfully removed all of the files we shouldn't care about the directory itself
            // not being empty. As file deletion is just a marker to remove the file when all handles
            // are closed we could still have contents hanging around.
            RemoveDirectoryInternal(fullPath, topLevel: topLevel, allowDirectoryNotEmpty: true);
        }
        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));
        }
        private static unsafe string?GetFinalLinkTarget(string linkPath, bool isDirectory)
        {
            Interop.Kernel32.WIN32_FIND_DATA data = default;
            GetFindData(linkPath, isDirectory, ignoreAccessDenied: false, ref data);

            // The file or directory is not a reparse point.
            if ((data.dwFileAttributes & (uint)FileAttributes.ReparsePoint) == 0 ||
                // Only symbolic links are supported at the moment.
                (data.dwReserved0 & Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_SYMLINK) == 0)
            {
                return(null);
            }

            // We try to open the final file since they asked for the final target.
            using SafeFileHandle handle = OpenSafeFileHandle(linkPath,
                                                             Interop.Kernel32.FileOperations.OPEN_EXISTING |
                                                             Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS);

            if (handle.IsInvalid)
            {
                // If the handle fails because it is unreachable, is because the link was broken.
                // We need to fallback to manually traverse the links and return the target of the last resolved link.
                int error = Marshal.GetLastWin32Error();
                if (IsPathUnreachableError(error))
                {
                    return(GetFinalLinkTargetSlow(linkPath));
                }

                throw Win32Marshal.GetExceptionForWin32Error(error, linkPath);
            }

            const int InitialBufferSize = 4096;

            char[] buffer = ArrayPool <char> .Shared.Rent(InitialBufferSize);

            try
            {
                uint result = GetFinalPathNameByHandle(handle, buffer);

                // If the function fails because lpszFilePath is too small to hold the string plus the terminating null character,
                // the return value is the required buffer size, in TCHARs. This value includes the size of the terminating null character.
                if (result > buffer.Length)
                {
                    char[] toReturn = buffer;
                    buffer = ArrayPool <char> .Shared.Rent((int)result);

                    ArrayPool <char> .Shared.Return(toReturn);

                    result = GetFinalPathNameByHandle(handle, buffer);
                }

                // If the function fails for any other reason, the return value is zero.
                if (result == 0)
                {
                    throw Win32Marshal.GetExceptionForLastWin32Error(linkPath);
                }

                Debug.Assert(PathInternal.IsExtended(new string(buffer, 0, (int)result).AsSpan()));
                // GetFinalPathNameByHandle always returns with extended DOS prefix even if the link target was created without one.
                // While this does not interfere with correct behavior, it might be unexpected.
                // Hence we trim it if the passed-in path to the link wasn't extended.
                int start = PathInternal.IsExtended(linkPath.AsSpan()) ? 0 : 4;
                return(new string(buffer, start, (int)result - start));
            }
            finally
            {
                ArrayPool <char> .Shared.Return(buffer);
            }

            uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer)
            {
                fixed(char *bufPtr = buffer)
                {
                    return(Interop.Kernel32.GetFinalPathNameByHandle(handle, bufPtr, (uint)buffer.Length, Interop.Kernel32.FILE_NAME_NORMALIZED));
                }
            }

            string?GetFinalLinkTargetSlow(string linkPath)
            {
                // Since all these paths will be passed to CreateFile, which takes a string anyway, it is pointless to use span.
                // I am not sure if it's possible to change CreateFile's param to ROS<char> and avoid all these allocations.

                // We don't throw on error since we already did all the proper validations before.
                string?current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: false, returnFullPath: true);
                string?prev    = null;

                while (current != null)
                {
                    prev    = current;
                    current = GetImmediateLinkTarget(current, isDirectory, throwOnError: false, returnFullPath: true);
                }

                return(prev);
            }
        }
Beispiel #14
0
        // We can remove this link demand in a future version - we will
        // have scenarios for this in partial trust in the future, but
        // we're doing this just to restrict this in case the code below
        // is somehow incorrect.
        public MemoryFailPoint(int sizeInMegabytes)
        {
            if (sizeInMegabytes <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(sizeInMegabytes), SR.ArgumentOutOfRange_NeedNonNegNum);
            }

#if !FEATURE_PAL // Remove this when CheckForAvailableMemory is able to provide legitimate estimates
            ulong size = ((ulong)sizeInMegabytes) << 20;
            _reservedMemory = size;

            // Check to see that we both have enough memory on the system
            // and that we have enough room within the user section of the
            // process's address space.  Also, we need to use the GC segment
            // size, not the amount of memory the user wants to allocate.
            // Consider correcting this to reflect free memory within the GC
            // heap, and to check both the normal & large object heaps.
            ulong segmentSize = (ulong)(Math.Ceiling((double)size / GCSegmentSize) * GCSegmentSize);
            if (segmentSize >= TopOfMemory)
            {
                throw new InsufficientMemoryException(SR.InsufficientMemory_MemFailPoint_TooBig);
            }

            ulong requestedSizeRounded = (ulong)(Math.Ceiling((double)sizeInMegabytes / MemoryCheckGranularity) * MemoryCheckGranularity);
            //re-convert into bytes
            requestedSizeRounded <<= 20;

            ulong availPageFile         = 0; // available VM (physical + page file)
            ulong totalAddressSpaceFree = 0; // non-contiguous free address space

            // Check for available memory, with 2 attempts at getting more
            // memory.
            // Stage 0: If we don't have enough, trigger a GC.
            // Stage 1: If we don't have enough, try growing the swap file.
            // Stage 2: Update memory state, then fail or leave loop.
            //
            // (In the future, we could consider adding another stage after
            // Stage 0 to run finalizers.  However, before doing that make sure
            // that we could abort this constructor when we call
            // GC.WaitForPendingFinalizers, noting that this method uses a CER
            // so it can't be aborted, and we have a critical finalizer.  It
            // would probably work, but do some thinking first.)
            for (int stage = 0; stage < 3; stage++)
            {
                CheckForAvailableMemory(out availPageFile, out totalAddressSpaceFree);

                // If we have enough room, then skip some stages.
                // Note that multiple threads can still lead to a race condition for our free chunk
                // of address space, which can't be easily solved.
                ulong reserved         = SharedStatics.MemoryFailPointReservedMemory;
                ulong segPlusReserved  = segmentSize + reserved;
                bool  overflow         = segPlusReserved < segmentSize || segPlusReserved < reserved;
                bool  needPageFile     = availPageFile < (requestedSizeRounded + reserved + LowMemoryFudgeFactor) || overflow;
                bool  needAddressSpace = totalAddressSpaceFree < segPlusReserved || overflow;

                // Ensure our cached amount of free address space is not stale.
                long now = Environment.TickCount;  // Handle wraparound.
                if ((now > LastTimeCheckingAddressSpace + CheckThreshold || now < LastTimeCheckingAddressSpace) ||
                    LastKnownFreeAddressSpace < (long)segmentSize)
                {
                    CheckForFreeAddressSpace(segmentSize, false);
                }
                bool needContiguousVASpace = (ulong)LastKnownFreeAddressSpace < segmentSize;

#if false
                Console.WriteLine($"MemoryFailPoint:" +
                                  $"Checking for {(segmentSize >> 20)} MB, " +
                                  $"for allocation size of {sizeInMegabytes} MB, " +
                                  $"stage {stage}. " +
                                  $"Need page file? {needPageFile} " +
                                  $"Need Address Space? {needAddressSpace} " +
                                  $"Need Contiguous address space? {needContiguousVASpace} " +
                                  $"Avail page file: {(availPageFile >> 20)} MB " +
                                  $"Total free VA space: {totalAddressSpaceFree >> 20} MB " +
                                  $"Contiguous free address space (found): {LastKnownFreeAddressSpace >> 20} MB " +
                                  $"Space reserved via process's MemoryFailPoints: {reserved} MB");
#endif

                if (!needPageFile && !needAddressSpace && !needContiguousVASpace)
                {
                    break;
                }

                switch (stage)
                {
                case 0:
                    // The GC will release empty segments to the OS.  This will
                    // relieve us from having to guess whether there's
                    // enough memory in either GC heap, and whether
                    // internal fragmentation will prevent those
                    // allocations from succeeding.
                    GC.Collect();
                    continue;

                case 1:
                    // Do this step if and only if the page file is too small.
                    if (!needPageFile)
                    {
                        continue;
                    }

                    // Attempt to grow the OS's page file.  Note that we ignore
                    // any allocation routines from the host intentionally.
                    RuntimeHelpers.PrepareConstrainedRegions();

                    // This shouldn't overflow due to the if clauses above.
                    UIntPtr numBytes = new UIntPtr(segmentSize);
                    unsafe
                    {
                        void *pMemory = Win32Native.VirtualAlloc(null, numBytes, Win32Native.MEM_COMMIT, Win32Native.PAGE_READWRITE);
                        if (pMemory != null)
                        {
                            bool r = Win32Native.VirtualFree(pMemory, UIntPtr.Zero, Win32Native.MEM_RELEASE);
                            if (!r)
                            {
                                throw Win32Marshal.GetExceptionForLastWin32Error();
                            }
                        }
                    }

                    continue;

                case 2:
                    // The call to CheckForAvailableMemory above updated our
                    // state.
                    if (needPageFile || needAddressSpace)
                    {
                        InsufficientMemoryException e = new InsufficientMemoryException(SR.InsufficientMemory_MemFailPoint);
#if DEBUG
                        e.Data["MemFailPointState"] = new MemoryFailPointState(sizeInMegabytes, segmentSize,
                                                                               needPageFile, needAddressSpace, needContiguousVASpace,
                                                                               availPageFile >> 20, totalAddressSpaceFree >> 20,
                                                                               LastKnownFreeAddressSpace >> 20, reserved);
#endif
                        throw e;
                    }

                    if (needContiguousVASpace)
                    {
                        InsufficientMemoryException e = new InsufficientMemoryException(SR.InsufficientMemory_MemFailPoint_VAFrag);
#if DEBUG
                        e.Data["MemFailPointState"] = new MemoryFailPointState(sizeInMegabytes, segmentSize,
                                                                               needPageFile, needAddressSpace, needContiguousVASpace,
                                                                               availPageFile >> 20, totalAddressSpaceFree >> 20,
                                                                               LastKnownFreeAddressSpace >> 20, reserved);
#endif
                        throw e;
                    }

                    break;

                default:
                    Debug.Fail("Fell through switch statement!");
                    break;
                }
            }

            // Success - we have enough room the last time we checked.
            // Now update our shared state in a somewhat atomic fashion
            // and handle a simple race condition with other MemoryFailPoint instances.
            AddToLastKnownFreeAddressSpace(-((long)size));
            if (LastKnownFreeAddressSpace < 0)
            {
                CheckForFreeAddressSpace(segmentSize, true);
            }

            RuntimeHelpers.PrepareConstrainedRegions();

            SharedStatics.AddMemoryFailPointReservation((long)size);
            _mustSubtractReservation = true;
#endif
        }