public static FileAttributes GetAttributes(string fullPath, bool backupMode = false) { int flags = backupMode ? Win32Api.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS : 0; Win32Api.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Api.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true); if (errorCode != 0) { throw PalWin32FileStream.GetExceptionForWin32Error(errorCode, fullPath); } return((FileAttributes)data.dwFileAttributes); }
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); }