public static SafeFileHandle OpenHandle(string fullPath, bool asDirectory, bool writeMode = false, bool backupMode = false, bool asyncMode = true, int additionalFlags = 0)
        {
            string root = fullPath.Substring(0, Win32PathInternal.GetRootLength(fullPath.AsSpan()));

            if (root == fullPath && root[1] == Path.VolumeSeparatorChar)
            {
                // intentionally not fullpath, most upstack public APIs expose this as path.
                throw new ArgumentException("path");
            }

            if (asyncMode)
            {
                additionalFlags |= (int)FileOptions.Asynchronous;
            }

            using (Lfs.EnterDisableMediaInsertionPrompt())
            {
                SafeFileHandle handle = Win32Api.Kernel32.CreateFile(
                    fullPath,
                    writeMode ? Win32Api.Kernel32.GenericOperations.GENERIC_WRITE | Win32Api.Kernel32.GenericOperations.GENERIC_READ : Win32Api.Kernel32.GenericOperations.GENERIC_READ,
                    FileShare.ReadWrite | FileShare.Delete,
                    FileMode.Open,
                    ((asDirectory || backupMode) ? Win32Api.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS : 0) | additionalFlags);

                if (handle.IsInvalid)
                {
                    int errorCode = Marshal.GetLastWin32Error();

                    // NT5 oddity - when trying to open "C:\" as a File,
                    // we usually get ERROR_PATH_NOT_FOUND from the OS.  We should
                    // probably be consistent w/ every other directory.
                    if (!asDirectory && errorCode == Win32Api.Errors.ERROR_PATH_NOT_FOUND && fullPath.Equals(Directory.GetDirectoryRoot(fullPath)))
                    {
                        errorCode = Win32Api.Errors.ERROR_ACCESS_DENIED;
                    }

                    throw PalWin32FileStream.GetExceptionForWin32Error(errorCode, fullPath);
                }

                if (((FileOptions)additionalFlags).Bit(FileOptions.Asynchronous))
                {
                    handle._SetAsync(true);
                    ThreadPool.BindHandle(handle);
                }
                else
                {
                    handle._SetAsync(false);
                }

                return(handle);
            }
        }
Ejemplo n.º 2
0
 internal static ReadOnlySpan <char> TrimEndingDirectorySeparator(ReadOnlySpan <char> path) =>
 Win32PathInternal.EndsInDirectorySeparator(path) ?
 path.Slice(0, path.Length - 1) :
 path;
        public static int FillAttributeInfo(string path, ref Win32Api.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool returnErrorOnNotFound)
        {
            int errorCode = Win32Api.Errors.ERROR_SUCCESS;

            // Neither GetFileAttributes or FindFirstFile like trailing separators
            path = Win32PathInternal.TrimEndingDirectorySeparator(path);

            using (Win32Api.Win32DisableMediaInsertionPrompt.Create())
            {
                if (!Win32Api.Kernel32.GetFileAttributesEx(path, Win32Api.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data))
                {
                    errorCode = Marshal.GetLastWin32Error();
                    if (errorCode != Win32Api.Errors.ERROR_FILE_NOT_FOUND &&
                        errorCode != Win32Api.Errors.ERROR_PATH_NOT_FOUND &&
                        errorCode != Win32Api.Errors.ERROR_NOT_READY &&
                        errorCode != Win32Api.Errors.ERROR_INVALID_NAME &&
                        errorCode != Win32Api.Errors.ERROR_BAD_PATHNAME &&
                        errorCode != Win32Api.Errors.ERROR_BAD_NETPATH &&
                        errorCode != Win32Api.Errors.ERROR_BAD_NET_NAME &&
                        errorCode != Win32Api.Errors.ERROR_INVALID_PARAMETER &&
                        errorCode != Win32Api.Errors.ERROR_NETWORK_UNREACHABLE &&
                        errorCode != Win32Api.Errors.ERROR_NETWORK_ACCESS_DENIED &&
                        errorCode != Win32Api.Errors.ERROR_INVALID_HANDLE     // eg from \\.\CON
                        )
                    {
                        // Assert so we can track down other cases (if any) to add to our test suite
                        Debug.Assert(errorCode == Win32Api.Errors.ERROR_ACCESS_DENIED || errorCode == Win32Api.Errors.ERROR_SHARING_VIOLATION,
                                     $"Unexpected error code getting attributes {errorCode}");

                        // Files that are marked for deletion will not let you GetFileAttributes,
                        // ERROR_ACCESS_DENIED is given back without filling out the data struct.
                        // FindFirstFile, however, will. Historically we always gave back attributes
                        // for marked-for-deletion files.
                        //
                        // Another case where enumeration works is with special system files such as
                        // pagefile.sys that give back ERROR_SHARING_VIOLATION on GetAttributes.
                        //
                        // Ideally we'd only try again for known cases due to the potential performance
                        // hit. The last attempt to do so baked for nearly a year before we found the
                        // pagefile.sys case. As such we're probably stuck filtering out specific
                        // cases that we know we don't want to retry on.

                        var findData = new Win32Api.Kernel32.WIN32_FIND_DATA();
                        using (Win32Api.SafeFindHandle handle = Win32Api.Kernel32.FindFirstFile(path, ref findData))
                        {
                            if (handle.IsInvalid)
                            {
                                errorCode = Marshal.GetLastWin32Error();
                            }
                            else
                            {
                                errorCode = Win32Api.Errors.ERROR_SUCCESS;
                                data.PopulateFrom(ref findData);
                            }
                        }
                    }
                }
            }

            if (errorCode != Win32Api.Errors.ERROR_SUCCESS && !returnErrorOnNotFound)
            {
                switch (errorCode)
                {
                case Win32Api.Errors.ERROR_FILE_NOT_FOUND:
                case Win32Api.Errors.ERROR_PATH_NOT_FOUND:
                case Win32Api.Errors.ERROR_NOT_READY:     // Removable media not ready
                    // Return default value for backward compatibility
                    data.dwFileAttributes = -1;
                    return(Win32Api.Errors.ERROR_SUCCESS);
                }
            }

            return(errorCode);
        }