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); } }
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); }