GetExceptionForWin32Error() static private méthode

Converts the specified Win32 error into a corresponding Exception object.
static private GetExceptionForWin32Error ( int errorCode ) : Exception
errorCode int
Résultat System.Exception
Exemple #1
0
        private static unsafe void WriteSyncUsingAsyncHandle(SafeFileHandle handle, ReadOnlySpan <byte> buffer, long fileOffset)
        {
            if (buffer.IsEmpty)
            {
                return;
            }

            handle.EnsureThreadPoolBindingInitialized();

            CallbackResetEvent resetEvent = new CallbackResetEvent(handle.ThreadPoolBinding !);
            NativeOverlapped * overlapped = null;

            try
            {
                overlapped = GetNativeOverlappedForAsyncHandle(handle.ThreadPoolBinding !, fileOffset, resetEvent);

                fixed(byte *pinned = &MemoryMarshal.GetReference(buffer))
                {
                    Interop.Kernel32.WriteFile(handle, pinned, buffer.Length, IntPtr.Zero, overlapped);

                    int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle);

                    if (errorCode == Interop.Errors.ERROR_IO_PENDING)
                    {
                        resetEvent.WaitOne();
                        errorCode = Interop.Errors.ERROR_SUCCESS;
                    }

                    if (errorCode == Interop.Errors.ERROR_SUCCESS)
                    {
                        int result = 0;
                        if (Interop.Kernel32.GetOverlappedResult(handle, overlapped, ref result, bWait: false))
                        {
                            Debug.Assert(result == buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request");
                            return;
                        }

                        errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle);
                    }

                    switch (errorCode)
                    {
                    case Interop.Errors.ERROR_NO_DATA:
                        // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing.
                        return;

                    case Interop.Errors.ERROR_INVALID_PARAMETER:
                        // ERROR_INVALID_PARAMETER may be returned for writes
                        // where the position is too large or for synchronous writes
                        // to a handle opened asynchronously.
                        throw new IOException(SR.IO_FileTooLong);

                    default:
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path);
                    }
                }
            }
            finally
            {
                if (overlapped != null)
                {
                    resetEvent.FreeNativeOverlapped(overlapped);
                }

                resetEvent.Dispose();
            }
        }
Exemple #2
0
        private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options)
        {
            Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);

            int fAccess =
                ((_access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) |
                ((_access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0);

            // Our Inheritable bit was stolen from Windows, but should be set in
            // the security attributes class.  Don't leave this bit set.
            share &= ~FileShare.Inheritable;

            // Must use a valid Win32 constant here...
            if (mode == FileMode.Append)
            {
                mode = FileMode.OpenOrCreate;
            }

            int flagsAndAttributes = (int)options;

            // For mitigating local elevation of privilege attack through named pipes
            // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
            // named pipe server can't impersonate a high privileged client security context
            flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS);

            // Don't pop up a dialog for reading from an empty floppy drive
            uint oldMode = Interop.Kernel32.SetErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS);

            try
            {
                SafeFileHandle fileHandle = Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero);
                fileHandle.IsAsync = _useAsyncIO;

                if (fileHandle.IsInvalid)
                {
                    // Return a meaningful exception with the full path.

                    // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
                    // we usually get ERROR_PATH_NOT_FOUND from the OS.  We should
                    // probably be consistent w/ every other directory.
                    int errorCode = Marshal.GetLastWin32Error();

                    if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path))
                    {
                        errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
                    }

                    throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                }

                int fileType = Interop.Kernel32.GetFileType(fileHandle);
                if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK)
                {
                    fileHandle.Dispose();
                    throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles);
                }

                return(fileHandle);
            }
            finally
            {
                Interop.Kernel32.SetErrorMode(oldMode);
            }
        }
Exemple #3
0
        public static void CreateDirectory(string fullPath)
        {
            // We can save a bunch of work if the directory we want to create already exists.  This also
            // saves us in the case where sub paths are inaccessible (due to ERROR_ACCESS_DENIED) but the
            // final path is accessible and the directory already exists.  For example, consider trying
            // to create c:\Foo\Bar\Baz, where everything already exists but ACLS prevent access to c:\Foo
            // and c:\Foo\Bar.  In that case, this code will think it needs to create c:\Foo, and c:\Foo\Bar
            // and fail to due so, causing an exception to be thrown.  This is not what we want.
            if (DirectoryExists(fullPath))
            {
                return;
            }

            List <string> stackDir = new List <string>();

            // Attempt to figure out which directories don't exist, and only
            // create the ones we need.  Note that FileExists may fail due
            // to Win32 ACL's preventing us from seeing a directory, and this
            // isn't threadsafe.

            bool somepathexists = false;

            int length = fullPath.Length;

            // We need to trim the trailing slash or the code will try to create 2 directories of the same name.
            if (length >= 2 && PathInternal.EndsInDirectorySeparator(fullPath))
            {
                length--;
            }

            int lengthRoot = PathInternal.GetRootLength(fullPath);

            if (length > lengthRoot)
            {
                // Special case root (fullpath = X:\\)
                int i = length - 1;
                while (i >= lengthRoot && !somepathexists)
                {
                    string dir = fullPath.Substring(0, i + 1);

                    if (!DirectoryExists(dir)) // Create only the ones missing
                    {
                        stackDir.Add(dir);
                    }
                    else
                    {
                        somepathexists = true;
                    }

                    while (i > lengthRoot && !PathInternal.IsDirectorySeparator(fullPath[i]))
                    {
                        i--;
                    }
                    i--;
                }
            }

            int count = stackDir.Count;

            // If we were passed a DirectorySecurity, convert it to a security
            // descriptor and set it in he call to CreateDirectory.
            Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default;

            bool   r           = true;
            int    firstError  = 0;
            string errorString = fullPath;

            // If all the security checks succeeded create all the directories
            while (stackDir.Count > 0)
            {
                string name = stackDir[stackDir.Count - 1];
                stackDir.RemoveAt(stackDir.Count - 1);

                r = Interop.Kernel32.CreateDirectory(name, ref secAttrs);
                if (!r && (firstError == 0))
                {
                    int currentError = Marshal.GetLastWin32Error();
                    // While we tried to avoid creating directories that don't
                    // exist above, there are at least two cases that will
                    // cause us to see ERROR_ALREADY_EXISTS here.  FileExists
                    // can fail because we didn't have permission to the
                    // directory.  Secondly, another thread or process could
                    // create the directory between the time we check and the
                    // time we try using the directory.  Thirdly, it could
                    // fail because the target does exist, but is a file.
                    if (currentError != Interop.Errors.ERROR_ALREADY_EXISTS)
                    {
                        firstError = currentError;
                    }
                    else
                    {
                        // If there's a file in this directory's place, or if we have ERROR_ACCESS_DENIED when checking if the directory already exists throw.
                        if (FileExists(name) || (!DirectoryExists(name, out currentError) && currentError == Interop.Errors.ERROR_ACCESS_DENIED))
                        {
                            firstError  = currentError;
                            errorString = name;
                        }
                    }
                }
            }

            // We need this check to mask OS differences
            // Handle CreateDirectory("X:\\") when X: doesn't exist. Similarly for n/w paths.
            if ((count == 0) && !somepathexists)
            {
                string root = Directory.InternalGetDirectoryRoot(fullPath);
                if (!DirectoryExists(root))
                {
                    throw Win32Marshal.GetExceptionForWin32Error(Interop.Errors.ERROR_PATH_NOT_FOUND, root);
                }
                return;
            }

            // Only throw an exception if creating the exact directory we
            // wanted failed to work correctly.
            if (!r && (firstError != 0))
            {
                throw Win32Marshal.GetExceptionForWin32Error(firstError, errorString);
            }
        }
Exemple #4
0
        private async Task <FileStreamBase> OpenAsync(string fullPath, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
        {
            // Win32 CreateFile returns ERROR_PATH_NOT_FOUND when given a path that ends with '\'
            if (PathHelpers.EndsInDirectorySeparator(fullPath))
            {
                throw Win32Marshal.GetExceptionForWin32Error(Interop.ERROR_PATH_NOT_FOUND, fullPath);
            }

            StorageFile file = null;

            // FileMode
            if (mode == FileMode.Open || mode == FileMode.Truncate)
            {
                file = await StorageFile.GetFileFromPathAsync(fullPath).TranslateWinRTTask(fullPath);
            }
            else
            {
                CreationCollisionOption collisionOptions;

                switch (mode)
                {
                case FileMode.Create:
                    collisionOptions = CreationCollisionOption.ReplaceExisting;
                    break;

                case FileMode.CreateNew:
                    collisionOptions = CreationCollisionOption.FailIfExists;
                    break;

                case FileMode.Append:
                case FileMode.OpenOrCreate:
                default:
                    collisionOptions = CreationCollisionOption.OpenIfExists;
                    break;
                }

                string directoryPath, fileName;
                PathHelpers.SplitDirectoryFile(fullPath, out directoryPath, out fileName);

                StorageFolder directory = await StorageFolder.GetFolderFromPathAsync(directoryPath).TranslateWinRTTask(directoryPath, isDirectory: true);

                file = await directory.CreateFileAsync(fileName, collisionOptions).TranslateWinRTTask(fullPath);
            }

            // FileAccess: WinRT doesn't support FileAccessMode.Write so we upgrade to ReadWrite
            FileAccessMode accessMode = ((access & FileAccess.Write) != 0) ? FileAccessMode.ReadWrite : FileAccessMode.Read;

            // FileShare: cannot translate StorageFile uses a different sharing model (oplocks) that is controlled via FileAccessMode

            // FileOptions: ignore most values of FileOptions as they are hints and are not supported by WinRT.
            // FileOptions.Encrypted is not a hint, and not supported by WinRT, but there is precedent for ignoring this (FAT).
            // FileOptions.DeleteOnClose should result in an UnauthorizedAccessException when
            //   opening a file that can only be read, but we cannot safely reproduce that behavior
            //   in WinRT without actually deleting the file.
            //   Instead the failure will occur in the finalizer for WinRTFileStream and be ignored.

            // open our stream
            Stream stream = (await file.OpenAsync(accessMode).TranslateWinRTTask(fullPath)).AsStream(bufferSize);

            if (mode == FileMode.Append)
            {
                // seek to end.
                stream.Seek(0, SeekOrigin.End);
            }
            else if (mode == FileMode.Truncate)
            {
                // truncate stream to 0
                stream.SetLength(0);
            }

            return(new WinRTFileStream(stream, file, access, options));
        }
        /// <summary>
        /// Gets reparse point information associated to <paramref name="linkPath"/>.
        /// </summary>
        /// <returns>The immediate link target, absolute or relative or null if the file is not a supported link.</returns>
        internal static unsafe string?GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnError, bool returnFullPath)
        {
            using SafeFileHandle handle = OpenSafeFileHandle(linkPath,
                                                             Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS |
                                                             Interop.Kernel32.FileOperations.FILE_FLAG_OPEN_REPARSE_POINT);

            if (handle.IsInvalid)
            {
                if (!throwOnError)
                {
                    return(null);
                }

                int error = Marshal.GetLastWin32Error();
                // File not found doesn't make much sense coming from a directory.
                if (isDirectory && error == Interop.Errors.ERROR_FILE_NOT_FOUND)
                {
                    error = Interop.Errors.ERROR_PATH_NOT_FOUND;
                }

                throw Win32Marshal.GetExceptionForWin32Error(error, linkPath);
            }

            byte[] buffer = ArrayPool <byte> .Shared.Rent(Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE);

            try
            {
                bool success = Interop.Kernel32.DeviceIoControl(
                    handle,
                    dwIoControlCode: Interop.Kernel32.FSCTL_GET_REPARSE_POINT,
                    lpInBuffer: IntPtr.Zero,
                    nInBufferSize: 0,
                    lpOutBuffer: buffer,
                    nOutBufferSize: Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
                    out _,
                    IntPtr.Zero);

                if (!success)
                {
                    if (!throwOnError)
                    {
                        return(null);
                    }

                    int error = Marshal.GetLastWin32Error();
                    // The file or directory is not a reparse point.
                    if (error == Interop.Errors.ERROR_NOT_A_REPARSE_POINT)
                    {
                        return(null);
                    }

                    throw Win32Marshal.GetExceptionForWin32Error(error, linkPath);
                }

                Span <byte> bufferSpan = new(buffer);
                success = MemoryMarshal.TryRead(bufferSpan, out Interop.Kernel32.SymbolicLinkReparseBuffer rbSymlink);
                Debug.Assert(success);

                // We always use SubstituteName(Offset|Length) instead of PrintName(Offset|Length),
                // the latter is just the display name of the reparse point and it can show something completely unrelated to the target.

                if (rbSymlink.ReparseTag == Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_SYMLINK)
                {
                    int offset = sizeof(Interop.Kernel32.SymbolicLinkReparseBuffer) + rbSymlink.SubstituteNameOffset;
                    int length = rbSymlink.SubstituteNameLength;

                    Span <char> targetPath = MemoryMarshal.Cast <byte, char>(bufferSpan.Slice(offset, length));

                    bool isRelative = (rbSymlink.Flags & Interop.Kernel32.SYMLINK_FLAG_RELATIVE) != 0;
                    if (!isRelative)
                    {
                        // Absolute target is in NT format and we need to clean it up before return it to the user.
                        if (targetPath.StartsWith(PathInternal.UncNTPathPrefix.AsSpan()))
                        {
                            // We need to prepend the Win32 equivalent of UNC NT prefix.
                            return(Path.Join(PathInternal.UncPathPrefix.AsSpan(), targetPath.Slice(PathInternal.UncNTPathPrefix.Length)));
                        }

                        return(GetTargetPathWithoutNTPrefix(targetPath));
                    }
                    else if (returnFullPath)
                    {
                        return(Path.Join(Path.GetDirectoryName(linkPath.AsSpan()), targetPath));
                    }
                    else
                    {
                        return(targetPath.ToString());
                    }
                }
                else if (rbSymlink.ReparseTag == Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_MOUNT_POINT)
                {
                    success = MemoryMarshal.TryRead(bufferSpan, out Interop.Kernel32.MountPointReparseBuffer rbMountPoint);
                    Debug.Assert(success);

                    int offset = sizeof(Interop.Kernel32.MountPointReparseBuffer) + rbMountPoint.SubstituteNameOffset;
                    int length = rbMountPoint.SubstituteNameLength;

                    Span <char> targetPath = MemoryMarshal.Cast <byte, char>(bufferSpan.Slice(offset, length));

                    // Unlike symbolic links, mount point paths cannot be relative.
                    Debug.Assert(!PathInternal.IsPartiallyQualified(targetPath));
                    // Mount points cannot point to a remote location.
                    Debug.Assert(!targetPath.StartsWith(PathInternal.UncNTPathPrefix.AsSpan()));
                    return(GetTargetPathWithoutNTPrefix(targetPath));
                }

                return(null);
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(buffer);
            }
Exemple #6
0
        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);
        }
Exemple #7
0
        [System.Security.SecurityCritical]  // auto-generated
        private unsafe static string NormalizePath(string path, bool fullCheck, int maxPathLength, bool expandShortPaths)
        {
            Contract.Requires(path != null, "path can't be null");

            // If the path is in extended syntax, we don't need to normalize, but we still do some basic validity checks
            if (PathInternal.IsExtended(path))
            {
                if (!ValidateExtendedPath(path, fullCheck))
                {
                    throw new ArgumentException(SR.Arg_PathIllegal);
                }

                // \\?\GLOBALROOT gives access to devices out of the scope of the current user, we
                // don't want to allow this for security reasons.
                // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#nt_namespaces
                if (path.StartsWith(@"\\?\globalroot", StringComparison.OrdinalIgnoreCase))
                {
                    throw new ArgumentException(SR.Arg_PathGlobalRoot);
                }

                return(path);
            }

            // If we're doing a full path check, trim whitespace and look for
            // illegal path characters.
            if (fullCheck)
            {
                // Trim whitespace off the end of the string.
                // Win32 normalization trims only U+0020.
                path = path.TrimEnd(TrimEndChars);

                // Look for illegal path characters.
                PathInternal.CheckInvalidPathChars(path);
            }

            int index = 0;

            // We prefer to allocate on the stack for workingset/perf gain. If the
            // starting path is less than MaxPath then we can stackalloc; otherwise we'll
            // use a StringBuilder (PathHelper does this under the hood). The latter may
            // happen in 2 cases:
            // 1. Starting path is greater than MaxPath but it normalizes down to MaxPath.
            // This is relevant for paths containing escape sequences. In this case, we
            // attempt to normalize down to MaxPath, but the caller pays a perf penalty
            // since StringBuilder is used.
            // 2. IsolatedStorage, which supports paths longer than MaxPath (value given
            // by maxPathLength.
            PathHelper newBuffer = null;

            if (path.Length + 1 <= MaxPath)
            {
                char *m_arrayPtr = stackalloc char[MaxPath];
                newBuffer = new PathHelper(m_arrayPtr, MaxPath);
            }
            else
            {
                newBuffer = new PathHelper(path.Length + MaxPath, maxPathLength);
            }

            uint numSpaces = 0;
            uint numDots   = 0;
            bool fixupDirectorySeparator = false;
            // Number of significant chars other than potentially suppressible
            // dots and spaces since the last directory or volume separator char
            uint numSigChars = 0;
            int  lastSigChar = -1; // Index of last significant character.
            // Whether this segment of the path (not the complete path) started
            // with a volume separator char.  Reject "c:...".
            bool startedWithVolumeSeparator = false;
            bool firstSegment = true;
            int  lastDirectorySeparatorPos = 0;

            bool mightBeShortFileName = false;

            // LEGACY: This code is here for backwards compatibility reasons. It
            // ensures that \\foo.cs\bar.cs stays \\foo.cs\bar.cs instead of being
            // turned into \foo.cs\bar.cs.
            if (path.Length > 0 && PathInternal.IsDirectorySeparator(path[0]))
            {
                newBuffer.Append('\\');
                index++;
                lastSigChar = 0;
            }

            // Normalize the string, stripping out redundant dots, spaces, and
            // slashes.
            while (index < path.Length)
            {
                char currentChar = path[index];

                // We handle both directory separators and dots specially.  For
                // directory separators, we consume consecutive appearances.
                // For dots, we consume all dots beyond the second in
                // succession.  All other characters are added as is.  In
                // addition we consume all spaces after the last other char
                // in a directory name up until the directory separator.

                if (PathInternal.IsDirectorySeparator(currentChar))
                {
                    // If we have a path like "123.../foo", remove the trailing dots.
                    // However, if we found "c:\temp\..\bar" or "c:\temp\...\bar", don't.
                    // Also remove trailing spaces from both files & directory names.
                    // This was agreed on with the OS team to fix undeletable directory
                    // names ending in spaces.

                    // If we saw a '\' as the previous last significant character and
                    // are simply going to write out dots, suppress them.
                    // If we only contain dots and slashes though, only allow
                    // a string like [dot]+ [space]*.  Ignore everything else.
                    // Legal: "\.. \", "\...\", "\. \"
                    // Illegal: "\.. .\", "\. .\", "\ .\"
                    if (numSigChars == 0)
                    {
                        // Dot and space handling
                        if (numDots > 0)
                        {
                            // Look for ".[space]*" or "..[space]*"
                            int start = lastSigChar + 1;
                            if (path[start] != '.')
                            {
                                throw new ArgumentException(SR.Arg_PathIllegal);
                            }

                            // Only allow "[dot]+[space]*", and normalize the
                            // legal ones to "." or ".."
                            if (numDots >= 2)
                            {
                                // Reject "C:..."
                                if (startedWithVolumeSeparator && numDots > 2)
                                {
                                    throw new ArgumentException(SR.Arg_PathIllegal);
                                }

                                if (path[start + 1] == '.')
                                {
                                    // Search for a space in the middle of the
                                    // dots and throw
                                    for (int i = start + 2; i < start + numDots; i++)
                                    {
                                        if (path[i] != '.')
                                        {
                                            throw new ArgumentException(SR.Arg_PathIllegal);
                                        }
                                    }

                                    numDots = 2;
                                }
                                else
                                {
                                    if (numDots > 1)
                                    {
                                        throw new ArgumentException(SR.Arg_PathIllegal);
                                    }
                                    numDots = 1;
                                }
                            }

                            if (numDots == 2)
                            {
                                newBuffer.Append('.');
                            }

                            newBuffer.Append('.');
                            fixupDirectorySeparator = false;
                            // Continue in this case, potentially writing out '\'.
                        }

                        if (numSpaces > 0 && firstSegment)
                        {
                            // Handle strings like " \\server\share".
                            if (index + 1 < path.Length && PathInternal.IsDirectorySeparator(path[index + 1]))
                            {
                                newBuffer.Append(DirectorySeparatorChar);
                            }
                        }
                    }
                    numDots   = 0;
                    numSpaces = 0;  // Suppress trailing spaces

                    if (!fixupDirectorySeparator)
                    {
                        fixupDirectorySeparator = true;
                        newBuffer.Append(DirectorySeparatorChar);
                    }
                    numSigChars = 0;
                    lastSigChar = index;
                    startedWithVolumeSeparator = false;
                    firstSegment = false;

                    // For short file names, we must try to expand each of them as
                    // soon as possible.  We need to allow people to specify a file
                    // name that doesn't exist using a path with short file names
                    // in it, such as this for a temp file we're trying to create:
                    // C:\DOCUME~1\USERNA~1.RED\LOCALS~1\Temp\bg3ylpzp
                    // We could try doing this afterwards piece by piece, but it's
                    // probably a lot simpler to do it here.
                    if (mightBeShortFileName)
                    {
                        newBuffer.TryExpandShortFileName();
                        mightBeShortFileName = false;
                    }

                    int thisPos = newBuffer.Length - 1;
                    if (thisPos - lastDirectorySeparatorPos > MaxComponentLength)
                    {
                        throw new PathTooLongException(SR.IO_PathTooLong);
                    }
                    lastDirectorySeparatorPos = thisPos;
                } // if (Found directory separator)
                else if (currentChar == '.')
                {
                    // Reduce only multiple .'s only after slash to 2 dots. For
                    // instance a...b is a valid file name.
                    numDots++;
                    // Don't flush out non-terminal spaces here, because they may in
                    // the end not be significant.  Turn "c:\ . .\foo" -> "c:\foo"
                    // which is the conclusion of removing trailing dots & spaces,
                    // as well as folding multiple '\' characters.
                }
                else if (currentChar == ' ')
                {
                    numSpaces++;
                }
                else
                {  // Normal character logic
                    if (currentChar == '~' && expandShortPaths)
                    {
                        mightBeShortFileName = true;
                    }

                    fixupDirectorySeparator = false;

                    // To reject strings like "C:...\foo" and "C  :\foo"
                    if (firstSegment && currentChar == VolumeSeparatorChar)
                    {
                        // Only accept "C:", not "c :" or ":"
                        // Get a drive letter or ' ' if index is 0.
                        char driveLetter = (index > 0) ? path[index - 1] : ' ';
                        bool validPath   = ((numDots == 0) && (numSigChars >= 1) && (driveLetter != ' '));
                        if (!validPath)
                        {
                            throw new ArgumentException(SR.Arg_PathIllegal);
                        }

                        startedWithVolumeSeparator = true;
                        // We need special logic to make " c:" work, we should not fix paths like "  foo::$DATA"
                        if (numSigChars > 1)
                        {                       // Common case, simply do nothing
                            int spaceCount = 0; // How many spaces did we write out, numSpaces has already been reset.
                            while ((spaceCount < newBuffer.Length) && newBuffer[spaceCount] == ' ')
                            {
                                spaceCount++;
                            }
                            if (numSigChars - spaceCount == 1)
                            {
                                //Safe to update stack ptr directly
                                newBuffer.Length = 0;
                                newBuffer.Append(driveLetter); // Overwrite spaces, we need a special case to not break "  foo" as a relative path.
                            }
                        }
                        numSigChars = 0;
                    }
                    else
                    {
                        numSigChars += 1 + numDots + numSpaces;
                    }

                    // Copy any spaces & dots since the last significant character
                    // to here.  Note we only counted the number of dots & spaces,
                    // and don't know what order they're in.  Hence the copy.
                    if (numDots > 0 || numSpaces > 0)
                    {
                        int numCharsToCopy = (lastSigChar >= 0) ? index - lastSigChar - 1 : index;
                        if (numCharsToCopy > 0)
                        {
                            for (int i = 0; i < numCharsToCopy; i++)
                            {
                                newBuffer.Append(path[lastSigChar + 1 + i]);
                            }
                        }
                        numDots   = 0;
                        numSpaces = 0;
                    }

                    newBuffer.Append(currentChar);
                    lastSigChar = index;
                }

                index++;
            } // end while

            if (newBuffer.Length - 1 - lastDirectorySeparatorPos > MaxComponentLength)
            {
                throw new PathTooLongException(SR.IO_PathTooLong);
            }

            // Drop any trailing dots and spaces from file & directory names, EXCEPT
            // we MUST make sure that "C:\foo\.." is correctly handled.
            // Also handle "C:\foo\." -> "C:\foo", while "C:\." -> "C:\"
            if (numSigChars == 0)
            {
                if (numDots > 0)
                {
                    // Look for ".[space]*" or "..[space]*"
                    int start = lastSigChar + 1;
                    if (path[start] != '.')
                    {
                        throw new ArgumentException(SR.Arg_PathIllegal);
                    }

                    // Only allow "[dot]+[space]*", and normalize the
                    // legal ones to "." or ".."
                    if (numDots >= 2)
                    {
                        // Reject "C:..."
                        if (startedWithVolumeSeparator && numDots > 2)
                        {
                            throw new ArgumentException(SR.Arg_PathIllegal);
                        }

                        if (path[start + 1] == '.')
                        {
                            // Search for a space in the middle of the
                            // dots and throw
                            for (int i = start + 2; i < start + numDots; i++)
                            {
                                if (path[i] != '.')
                                {
                                    throw new ArgumentException(SR.Arg_PathIllegal);
                                }
                            }

                            numDots = 2;
                        }
                        else
                        {
                            if (numDots > 1)
                            {
                                throw new ArgumentException(SR.Arg_PathIllegal);
                            }
                            numDots = 1;
                        }
                    }

                    if (numDots == 2)
                    {
                        newBuffer.Append('.');
                    }

                    newBuffer.Append('.');
                }
            } // if (numSigChars == 0)

            // If we ended up eating all the characters, bail out.
            if (newBuffer.Length == 0)
            {
                throw new ArgumentException(SR.Arg_PathIllegal);
            }

            // Disallow URL's here.  Some of our other Win32 API calls will reject
            // them later, so we might be better off rejecting them here.
            // Note we've probably turned them into "file:\D:\foo.tmp" by now.
            // But for compatibility, ensure that callers that aren't doing a
            // full check aren't rejected here.
            if (fullCheck)
            {
                if (newBuffer.OrdinalStartsWith("http:", false) ||
                    newBuffer.OrdinalStartsWith("file:", false))
                {
                    throw new ArgumentException(SR.Argument_PathUriFormatNotSupported);
                }
            }

            // If the last part of the path (file or directory name) had a tilde,
            // expand that too.
            if (mightBeShortFileName)
            {
                newBuffer.TryExpandShortFileName();
            }

            // Call the Win32 API to do the final canonicalization step.
            int result = 1;

            if (fullCheck)
            {
                // NOTE: Win32 GetFullPathName requires the input buffer to be big enough to fit the initial
                // path which is a concat of CWD and the relative path, this can be of an arbitrary
                // size and could be > MaxPath (which becomes an artificial limit at this point),
                // even though the final normalized path after fixing up the relative path syntax
                // might be well within the MaxPath restriction. For ex,
                // "c:\SomeReallyLongDirName(thinkGreaterThan_MAXPATH)\..\foo.txt" which actually requires a
                // buffer well with in the MaxPath as the normalized path is just "c:\foo.txt"
                // This buffer requirement seems wrong, it could be a bug or a perf optimization
                // like returning required buffer length quickly or avoid stratch buffer etc.
                // Either way we need to workaround it here...

                // Ideally we would get the required buffer length first by calling GetFullPathName
                // once without the buffer and use that in the later call but this doesn't always work
                // due to Win32 GetFullPathName bug. For instance, in Win2k, when the path we are trying to
                // fully qualify is a single letter name (such as "a", "1", ",") GetFullPathName
                // fails to return the right buffer size (i.e, resulting in insufficient buffer).
                // To workaround this bug we will start with MaxPath buffer and grow it once if the
                // return value is > MaxPath.

                result = newBuffer.GetFullPathName();

                // If we called GetFullPathName with something like "foo" and our
                // command window was in short file name mode (ie, by running edlin or
                // DOS versions of grep, etc), we might have gotten back a short file
                // name.  So, check to see if we need to expand it.
                mightBeShortFileName = false;
                for (int i = 0; i < newBuffer.Length && !mightBeShortFileName; i++)
                {
                    if (newBuffer[i] == '~' && expandShortPaths)
                    {
                        mightBeShortFileName = true;
                    }
                }

                if (mightBeShortFileName)
                {
                    bool r = newBuffer.TryExpandShortFileName();
                    // Consider how the path "Doesn'tExist" would expand.  If
                    // we add in the current directory, it too will need to be
                    // fully expanded, which doesn't happen if we use a file
                    // name that doesn't exist.
                    if (!r)
                    {
                        int lastSlash = -1;

                        for (int i = newBuffer.Length - 1; i >= 0; i--)
                        {
                            if (newBuffer[i] == DirectorySeparatorChar)
                            {
                                lastSlash = i;
                                break;
                            }
                        }

                        if (lastSlash >= 0)
                        {
                            // This bounds check is for safe memcpy but we should never get this far
                            if (newBuffer.Length >= maxPathLength)
                            {
                                throw new PathTooLongException(SR.IO_PathTooLong);
                            }

                            int lenSavedName = newBuffer.Length - lastSlash - 1;
                            Debug.Assert(lastSlash < newBuffer.Length, "path unexpectedly ended in a '\'");

                            newBuffer.Fixup(lenSavedName, lastSlash);
                        }
                    }
                }
            }

            if (result != 0)
            {
                /* Throw an ArgumentException for paths like \\, \\server, \\server\
                 * This check can only be properly done after normalizing, so
                 \\foo\.. will be properly rejected. */
                if (newBuffer.Length > 1 && newBuffer[0] == '\\' && newBuffer[1] == '\\')
                {
                    int startIndex = 2;
                    while (startIndex < result)
                    {
                        if (newBuffer[startIndex] == '\\')
                        {
                            startIndex++;
                            break;
                        }
                        else
                        {
                            startIndex++;
                        }
                    }
                    if (startIndex == result)
                    {
                        throw new ArgumentException(SR.Arg_PathIllegalUNC);
                    }
                }
            }

            // Check our result and form the managed string as necessary.
            if (newBuffer.Length >= maxPathLength)
            {
                throw new PathTooLongException(SR.IO_PathTooLong);
            }

            if (result == 0)
            {
                int errorCode = Marshal.GetLastWin32Error();
                if (errorCode == 0)
                {
                    errorCode = Interop.mincore.Errors.ERROR_BAD_PATHNAME;
                }
                throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
            }

            string returnVal = newBuffer.ToString();

            if (string.Equals(returnVal, path, StringComparison.Ordinal))
            {
                returnVal = path;
            }
            return(returnVal);
        }
Exemple #8
0
        [System.Security.SecurityCritical]  // auto-generated
        private static void RemoveDirectoryHelper(string fullPath, bool recursive, bool throwOnTopLevelDirectoryNotFound)
        {
            bool      r;
            int       errorCode;
            Exception ex = null;

            // Do not recursively delete through reparse points.  Perhaps in a
            // future version we will add a new flag to control this behavior,
            // but for now we're much safer if we err on the conservative side.
            // This applies to symbolic links and mount points.
            // Note the logic to check whether fullPath is a reparse point is
            // in Delete(String, String, bool), and will set "recursive" to false.
            // Note that Win32's DeleteFile and RemoveDirectory will just delete
            // the reparse point itself.

            if (recursive)
            {
                Interop.mincore.WIN32_FIND_DATA data = new Interop.mincore.WIN32_FIND_DATA();

                // Open a Find handle
                using (SafeFindHandle hnd = Interop.mincore.FindFirstFile(Directory.EnsureTrailingDirectorySeparator(fullPath) + "*", ref data))
                {
                    if (hnd.IsInvalid)
                    {
                        throw Win32Marshal.GetExceptionForLastWin32Error(fullPath);
                    }

                    do
                    {
                        bool isDir = (0 != (data.dwFileAttributes & Interop.mincore.FileAttributes.FILE_ATTRIBUTE_DIRECTORY));
                        if (isDir)
                        {
                            // Skip ".", "..".
                            if (data.cFileName.Equals(".") || data.cFileName.Equals(".."))
                            {
                                continue;
                            }

                            // Recurse for all directories, unless they are
                            // reparse points.  Do not follow mount points nor
                            // symbolic links, but do delete the reparse point
                            // itself.
                            bool shouldRecurse = (0 == (data.dwFileAttributes & (int)FileAttributes.ReparsePoint));
                            if (shouldRecurse)
                            {
                                string newFullPath = Path.Combine(fullPath, data.cFileName);
                                try
                                {
                                    RemoveDirectoryHelper(newFullPath, recursive, false);
                                }
                                catch (Exception e)
                                {
                                    if (ex == null)
                                    {
                                        ex = e;
                                    }
                                }
                            }
                            else
                            {
                                // Check to see if this is a mount point, and
                                // unmount it.
                                if (data.dwReserved0 == Interop.mincore.IOReparseOptions.IO_REPARSE_TAG_MOUNT_POINT)
                                {
                                    // Use full path plus a trailing '\'
                                    String mountPoint = Path.Combine(fullPath, data.cFileName + PathHelpers.DirectorySeparatorCharAsString);
                                    if (!Interop.mincore.DeleteVolumeMountPoint(mountPoint))
                                    {
                                        errorCode = Marshal.GetLastWin32Error();

                                        if (errorCode != Interop.mincore.Errors.ERROR_SUCCESS &&
                                            errorCode != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND)
                                        {
                                            try
                                            {
                                                throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName);
                                            }
                                            catch (Exception e)
                                            {
                                                if (ex == null)
                                                {
                                                    ex = e;
                                                }
                                            }
                                        }
                                    }
                                }

                                // RemoveDirectory on a symbolic link will
                                // remove the link itself.
                                String reparsePoint = Path.Combine(fullPath, data.cFileName);
                                r = Interop.mincore.RemoveDirectory(reparsePoint);
                                if (!r)
                                {
                                    errorCode = Marshal.GetLastWin32Error();
                                    if (errorCode != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND)
                                    {
                                        try
                                        {
                                            throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName);
                                        }
                                        catch (Exception e)
                                        {
                                            if (ex == null)
                                            {
                                                ex = e;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            String fileName = Path.Combine(fullPath, data.cFileName);
                            r = Interop.mincore.DeleteFile(fileName);
                            if (!r)
                            {
                                errorCode = Marshal.GetLastWin32Error();
                                if (errorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND)
                                {
                                    try
                                    {
                                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName);
                                    }
                                    catch (Exception e)
                                    {
                                        if (ex == null)
                                        {
                                            ex = e;
                                        }
                                    }
                                }
                            }
                        }
                    } while (Interop.mincore.FindNextFile(hnd, ref data));
                    // Make sure we quit with a sensible error.
                    errorCode = Marshal.GetLastWin32Error();
                }

                if (ex != null)
                {
                    throw ex;
                }
                if (errorCode != 0 && errorCode != Interop.mincore.Errors.ERROR_NO_MORE_FILES)
                {
                    throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
                }
            }

            r = Interop.mincore.RemoveDirectory(fullPath);

            if (!r)
            {
                errorCode = Marshal.GetLastWin32Error();
                if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND) // A dubious error code.
                {
                    errorCode = Interop.mincore.Errors.ERROR_PATH_NOT_FOUND;
                }
                // This check was originally put in for Win9x (unfortunately without special casing it to be for Win9x only). We can't change the NT codepath now for backcomp reasons.
                if (errorCode == Interop.mincore.Errors.ERROR_ACCESS_DENIED)
                {
                    throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, fullPath));
                }

                // don't throw the DirectoryNotFoundException since this is a subdir and
                // there could be a race condition between two Directory.Delete callers
                if (errorCode == Interop.mincore.Errors.ERROR_PATH_NOT_FOUND && !throwOnTopLevelDirectoryNotFound)
                {
                    return;
                }

                throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
            }
        }
        private unsafe Task <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default)
        {
            if (!CanRead)
            {
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }

            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");

            // Create and store async stream class library specific data in the async result
            FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, destination);
            NativeOverlapped *         intOverlapped    = completionSource.Overlapped;

            // Calculate position in the file we should be at after the read is done
            if (CanSeek)
            {
                long len = Length;

                // Make sure we are reading from the position that we think we are
                VerifyOSHandlePosition();

                if (destination.Length > len - _filePosition)
                {
                    if (_filePosition <= len)
                    {
                        destination = destination.Slice(0, (int)(len - _filePosition));
                    }
                    else
                    {
                        destination = default;
                    }
                }

                // Now set the position to read from in the NativeOverlapped struct
                // For pipes, we should leave the offset fields set to 0.
                intOverlapped->OffsetLow  = unchecked ((int)_filePosition);
                intOverlapped->OffsetHigh = (int)(_filePosition >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves. This isn't threadsafe.

                // WriteFile should not update the file pointer when writing
                // in overlapped mode, according to MSDN.  But it does update
                // the file pointer when writing to a UNC path!
                // So changed the code below to seek to an absolute
                // location, not a relative one.  ReadFile seems consistent though.
                SeekCore(_fileHandle, destination.Length, SeekOrigin.Current);
            }

            // queue an async ReadFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination.Span, intOverlapped, out int errorCode);

            // ReadFile, the OS version, will return 0 on failure.  But
            // my ReadFileNative wrapper returns -1.  My wrapper will return
            // the following:
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
            // on async requests that completed sequentially, r==0
            // You will NEVER RELIABLY be able to get the number of bytes
            // read back from this call when using overlapped structures!  You must
            // not pass in a non-null lpNumBytesRead to ReadFile when using
            // overlapped structures!  This is by design NT behavior.
            if (r == -1)
            {
                // For pipes, when they hit EOF, they will come here.
                if (errorCode == ERROR_BROKEN_PIPE)
                {
                    // Not an error, but EOF.  AsyncFSCallback will NOT be
                    // called.  Call the user callback here.

                    // We clear the overlapped status bit for this special case.
                    // Failure to do so looks like we are freeing a pending overlapped later.
                    intOverlapped->InternalLow = IntPtr.Zero;
                    completionSource.SetCompletedSynchronously(0);
                }
                else if (errorCode != ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        SeekCore(_fileHandle, 0, SeekOrigin.Current);
                    }

                    completionSource.ReleaseNativeResource();

                    if (errorCode == ERROR_HANDLE_EOF)
                    {
                        ThrowHelper.ThrowEndOfFileException();
                    }
                    else
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                    }
                }
                else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
                {
                    // Only once the IO is pending do we register for cancellation
                    completionSource.RegisterForCancellation(cancellationToken);
                }
            }
            else
            {
                // Due to a workaround for a race condition in NT's ReadFile &
                // WriteFile routines, we will always be returning 0 from ReadFileNative
                // when we do async IO instead of the number of bytes read,
                // irregardless of whether the operation completed
                // synchronously or asynchronously.  We absolutely must not
                // set asyncResult._numBytes here, since will never have correct
                // results.
            }

            return(completionSource.Task);
        }
        private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory <byte> source, CancellationToken cancellationToken)
        {
            if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }

            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");

            // Create and store async stream class library specific data in the async result
            FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, source);
            NativeOverlapped *         intOverlapped    = completionSource.Overlapped;

            if (CanSeek)
            {
                // Make sure we set the length of the file appropriately.
                long len = Length;

                // Make sure we are writing to the position that we think we are
                VerifyOSHandlePosition();

                if (_filePosition + source.Length > len)
                {
                    SetLengthCore(_filePosition + source.Length);
                }

                // Now set the position to read from in the NativeOverlapped struct
                // For pipes, we should leave the offset fields set to 0.
                intOverlapped->OffsetLow  = (int)_filePosition;
                intOverlapped->OffsetHigh = (int)(_filePosition >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves.  This isn't threadsafe.
                SeekCore(_fileHandle, source.Length, SeekOrigin.Current);
            }

            // queue an async WriteFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.WriteFileNative(_fileHandle, source.Span, intOverlapped, out int errorCode);

            // WriteFile, the OS version, will return 0 on failure.  But
            // my WriteFileNative wrapper returns -1.  My wrapper will return
            // the following:
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
            // On async requests that completed sequentially, r==0
            // You will NEVER RELIABLY be able to get the number of bytes
            // written back from this call when using overlapped IO!  You must
            // not pass in a non-null lpNumBytesWritten to WriteFile when using
            // overlapped structures!  This is ByDesign NT behavior.
            if (r == -1)
            {
                // For pipes, when they are closed on the other side, they will come here.
                if (errorCode == ERROR_NO_DATA)
                {
                    // Not an error, but EOF. AsyncFSCallback will NOT be called.
                    // Completing TCS and return cached task allowing the GC to collect TCS.
                    completionSource.SetCompletedSynchronously(0);
                    return(Task.CompletedTask);
                }
                else if (errorCode != ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        SeekCore(_fileHandle, 0, SeekOrigin.Current);
                    }

                    completionSource.ReleaseNativeResource();

                    if (errorCode == ERROR_HANDLE_EOF)
                    {
                        ThrowHelper.ThrowEndOfFileException();
                    }
                    else
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                    }
                }
                else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
                {
                    // Only once the IO is pending do we register for cancellation
                    completionSource.RegisterForCancellation(cancellationToken);
                }
            }
            else
            {
                // Due to a workaround for a race condition in NT's ReadFile &
                // WriteFile routines, we will always be returning 0 from WriteFileNative
                // when we do async IO instead of the number of bytes written,
                // irregardless of whether the operation completed
                // synchronously or asynchronously.  We absolutely must not
                // set asyncResult._numBytes here, since will never have correct
                // results.
            }

            return(completionSource.Task);
        }
Exemple #11
0
 private void HandleError(int errorCode, string path)
 {
     Dispose();
     throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
 }
Exemple #12
0
 private Exception HandleError(int errorCode, string path)
 {
     Dispose();
     return(Win32Marshal.GetExceptionForWin32Error(errorCode, path));
 }
 private void HandleError(int hr, String path)
 {
     Dispose();
     throw Win32Marshal.GetExceptionForWin32Error(hr, path);
 }
Exemple #14
0
        private static unsafe int ReadSyncUsingAsyncHandle(SafeFileHandle handle, Span <byte> buffer, long fileOffset)
        {
            handle.EnsureThreadPoolBindingInitialized();

            CallbackResetEvent resetEvent = new CallbackResetEvent(handle.ThreadPoolBinding !);
            NativeOverlapped * overlapped = null;

            try
            {
                overlapped = GetNativeOverlappedForAsyncHandle(handle, fileOffset, resetEvent);

                fixed(byte *pinned = &MemoryMarshal.GetReference(buffer))
                {
                    Interop.Kernel32.ReadFile(handle, pinned, buffer.Length, IntPtr.Zero, overlapped);

                    int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle);

                    if (errorCode == Interop.Errors.ERROR_IO_PENDING)
                    {
                        resetEvent.WaitOne();
                        errorCode = Interop.Errors.ERROR_SUCCESS;
                    }

                    if (errorCode == Interop.Errors.ERROR_SUCCESS)
                    {
                        int result = 0;
                        if (Interop.Kernel32.GetOverlappedResult(handle, overlapped, ref result, bWait: false))
                        {
                            Debug.Assert(result >= 0 && result <= buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request");
                            return(result);
                        }

                        errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle);
                    }

                    switch (errorCode)
                    {
                    case Interop.Errors.ERROR_HANDLE_EOF:     // logically success with 0 bytes read (read at end of file)
                    case Interop.Errors.ERROR_BROKEN_PIPE:
                    case Interop.Errors.ERROR_INVALID_PARAMETER when IsEndOfFileForNoBuffering(handle, fileOffset):
                        // EOF on a pipe. Callback will not be called.
                        // We clear the overlapped status bit for this special case (failure
                        // to do so looks like we are freeing a pending overlapped later).
                        overlapped->InternalLow = IntPtr.Zero;

                        return(0);

                    default:
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path);
                    }
                }
            }
            finally
            {
                if (overlapped != null)
                {
                    resetEvent.FreeNativeOverlapped(overlapped);
                }

                resetEvent.Dispose();
            }
        }
        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(Directory.EnsureTrailingDirectorySeparator(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 ((findData.dwFileAttributes & (int)FileAttributes.ReparsePoint) == 0)
                        {
                            // Not a reparse point, recurse.
                            try
                            {
                                RemoveDirectoryRecursive(
                                    Path.Combine(fullPath, fileName),
                                    findData: ref findData,
                                    topLevel: false);
                            }
                            catch (Exception e)
                            {
                                if (exception == null)
                                {
                                    exception = e;
                                }
                            }
                        }
                        else
                        {
                            // Reparse point, don't recurse, just remove. (dwReserved0 is documented for this flag)
                            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.Combine(fullPath, fileName + PathHelpers.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);
                }
            }

            RemoveDirectoryInternal(fullPath, topLevel: topLevel);
        }