1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using Microsoft.Win32;
6 using Microsoft.Win32.SafeHandles;
7 using System.Runtime.InteropServices;
8 
9 namespace System.IO
10 {
11     internal static partial class InternalFile
12     {
InternalExists(String path)13         internal static bool InternalExists(String path)
14         {
15             Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA();
16             int errorCode = FillAttributeInfo(path, ref data, false, true);
17 
18             return (errorCode == 0) && (data.fileAttributes != -1)
19                     && ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0);
20         }
21 
22         // Returns 0 on success, otherwise a Win32 error code.  Note that
23         // classes should use -1 as the uninitialized state for dataInitialized.
FillAttributeInfo(String path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound)24         internal static int FillAttributeInfo(String path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound)
25         {
26             int errorCode = 0;
27             if (tryagain) // someone has a handle to the file open, or other error
28             {
29                 Interop.Kernel32.WIN32_FIND_DATA findData;
30                 findData = new Interop.Kernel32.WIN32_FIND_DATA();
31 
32                 // Remove trailing slash since this can cause grief to FindFirstFile. You will get an invalid argument error
33                 String tempPath = path.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar });
34 
35                 // For floppy drives, normally the OS will pop up a dialog saying
36                 // there is no disk in drive A:, please insert one.  We don't want that.
37                 // SetThreadErrorMode will let us disable this, but we should set the error
38                 // mode back, since this may have wide-ranging effects.
39                 uint oldMode;
40                 bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode);
41                 try
42                 {
43                     bool error = false;
44                     SafeFindHandle handle = Interop.Kernel32.FindFirstFile(tempPath, ref findData);
45                     try
46                     {
47                         if (handle.IsInvalid)
48                         {
49                             error = true;
50                             errorCode = Marshal.GetLastWin32Error();
51 
52                             if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND ||
53                                 errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND ||
54                                 errorCode == Interop.Errors.ERROR_NOT_READY)  // floppy device not ready
55                             {
56                                 if (!returnErrorOnNotFound)
57                                 {
58                                     // Return default value for backward compatibility
59                                     errorCode = 0;
60                                     data.fileAttributes = -1;
61                                 }
62                             }
63                             return errorCode;
64                         }
65                     }
66                     finally
67                     {
68                         // Close the Win32 handle
69                         try
70                         {
71                             handle.Dispose();
72                         }
73                         catch
74                         {
75                             // if we're already returning an error, don't throw another one.
76                             if (!error)
77                             {
78                                 throw Win32Marshal.GetExceptionForLastWin32Error();
79                             }
80                         }
81                     }
82                 }
83                 finally
84                 {
85                     if (setThreadErrorModeSuccess)
86                         Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode);
87                 }
88 
89                 // Copy the information to data
90                 data.PopulateFrom(ref findData);
91             }
92             else
93             {
94                 // For floppy drives, normally the OS will pop up a dialog saying
95                 // there is no disk in drive A:, please insert one.  We don't want that.
96                 // SetThreadErrorMode will let us disable this, but we should set the error
97                 // mode back, since this may have wide-ranging effects.
98                 bool success = false;
99                 uint oldMode;
100                 bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode);
101                 try
102                 {
103                     success = Interop.Kernel32.GetFileAttributesEx(path, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data);
104                 }
105                 finally
106                 {
107                     if (setThreadErrorModeSuccess)
108                         Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode);
109                 }
110 
111                 if (!success)
112                 {
113                     errorCode = Marshal.GetLastWin32Error();
114                     if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND &&
115                         errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND &&
116                         errorCode != Interop.Errors.ERROR_NOT_READY)  // floppy device not ready
117                     {
118                         // In case someone latched onto the file. Take the perf hit only for failure
119                         return FillAttributeInfo(path, ref data, true, returnErrorOnNotFound);
120                     }
121                     else
122                     {
123                         if (!returnErrorOnNotFound)
124                         {
125                             // Return default value for backward compatibility
126                             errorCode = 0;
127                             data.fileAttributes = -1;
128                         }
129                     }
130                 }
131             }
132 
133             return errorCode;
134         }
135     }
136 }
137