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 | |
6 | // |
7 | // This header provides general path-related file system services. |
8 | |
9 | #ifndef _clr_fs_Path_h_ |
10 | #define _clr_fs_Path_h_ |
11 | |
12 | #include "clrtypes.h" |
13 | #include "cor.h" // SELECTANY |
14 | |
15 | #include "strsafe.h" |
16 | |
17 | #include "clr/str.h" |
18 | |
19 | #ifndef LONG_FORMAT_PATH_PREFIX |
20 | #define LONG_FORMAT_PATH_PREFIX W("\\\\?\\") |
21 | #endif |
22 | |
23 | namespace clr |
24 | { |
25 | namespace fs |
26 | { |
27 | // This list taken from ndp/clr/src/bcl/system/io/path.cs |
28 | SELECTANY WCHAR const g_rgInvalidPathChars[] = |
29 | { W('"'), W('<'), W('>'), W('|'), W('\0'), (WCHAR)1, (WCHAR)2, (WCHAR)3, (WCHAR)4, (WCHAR)5, (WCHAR)6, |
30 | (WCHAR)7, (WCHAR)8, (WCHAR)9, (WCHAR)10, (WCHAR)11, (WCHAR)12, (WCHAR)13, (WCHAR)14, |
31 | (WCHAR)15, (WCHAR)16, (WCHAR)17, (WCHAR)18, (WCHAR)19, (WCHAR)20, (WCHAR)21, (WCHAR)22, |
32 | (WCHAR)23, (WCHAR)24, (WCHAR)25, (WCHAR)26, (WCHAR)27, (WCHAR)28, (WCHAR)29, (WCHAR)30, |
33 | (WCHAR)31 }; |
34 | |
35 | class Path |
36 | { |
37 | public: |
38 | //----------------------------------------------------------------------------------------- |
39 | static inline bool |
40 | Exists( |
41 | LPCWSTR wzPath) |
42 | { |
43 | DWORD attrs = WszGetFileAttributes(wzPath); |
44 | return (attrs != INVALID_FILE_ATTRIBUTES); |
45 | } |
46 | |
47 | //----------------------------------------------------------------------------------------- |
48 | // Returns true if wzPath represents a long format path (i.e., prefixed with '\\?\'). |
49 | static inline bool |
50 | HasLongFormatPrefix(LPCWSTR wzPath) |
51 | { |
52 | _ASSERTE(!clr::str::IsNullOrEmpty(wzPath)); // Must check this first. |
53 | return wcscmp(wzPath, LONG_FORMAT_PATH_PREFIX) == 0; |
54 | } |
55 | |
56 | //----------------------------------------------------------------------------------------- |
57 | // Returns true if wzPath represents a relative path. |
58 | static inline bool |
59 | IsRelative(LPCWSTR wzPath) |
60 | { |
61 | _ASSERTE(wzPath != nullptr); |
62 | |
63 | // Similar to System.IO.Path.IsRelative() |
64 | #if PLATFORM_UNIX |
65 | if(wzPath[0] == VOLUME_SEPARATOR_CHAR_W) |
66 | { |
67 | return false; |
68 | } |
69 | #else |
70 | // Check for a paths like "C:\..." or "\\...". Additional notes: |
71 | // - "\\?\..." - long format paths are considered as absolute paths due to the "\\" prefix |
72 | // - "\..." - these paths are relative, as they depend on the current drive |
73 | // - "C:..." and not "C:\..." - these paths are relative, as they depend on the current directory for drive C |
74 | if (wzPath[0] != W('\0') && |
75 | wzPath[1] == VOLUME_SEPARATOR_CHAR_W && |
76 | wzPath[2] == DIRECTORY_SEPARATOR_CHAR_W && |
77 | ( |
78 | (wzPath[0] >= W('A') && wzPath[0] <= W('Z')) || |
79 | (wzPath[0] >= W('a') && wzPath[0] <= W('z')) |
80 | )) |
81 | { |
82 | return false; |
83 | } |
84 | if (wzPath[0] == DIRECTORY_SEPARATOR_CHAR_W && wzPath[1] == DIRECTORY_SEPARATOR_CHAR_W) |
85 | { |
86 | return false; |
87 | } |
88 | #endif |
89 | |
90 | return true; |
91 | } |
92 | |
93 | //----------------------------------------------------------------------------------------- |
94 | // Combines two path parts. wzPathLeft must be a directory path and may be either absolute |
95 | // or relative. wzPathRight may be a directory or file path and must be relative. The |
96 | // result is placed in wzBuffer and the number of chars written is placed in pcchBuffer on |
97 | // success; otherwise an error HRESULT is returned. |
98 | static HRESULT |
99 | Combine(LPCWSTR wzPathLeft, LPCWSTR wzPathRight, __in DWORD *pcchBuffer, __out_ecount(*pcchBuffer) LPWSTR wzBuffer) |
100 | { |
101 | STATIC_CONTRACT_NOTHROW; |
102 | |
103 | HRESULT hr = S_OK; |
104 | |
105 | if (clr::str::IsNullOrEmpty(wzPathLeft) || clr::str::IsNullOrEmpty(wzPathRight) || pcchBuffer == nullptr) |
106 | return E_INVALIDARG; |
107 | |
108 | LPWSTR wzBuf = wzBuffer; |
109 | size_t cchBuf = *pcchBuffer; |
110 | |
111 | IfFailRet(StringCchCopyExW(wzBuf, cchBuf, wzPathLeft, &wzBuf, &cchBuf, STRSAFE_NULL_ON_FAILURE)); |
112 | IfFailRet(StringCchCatExW(wzBuf, cchBuf, wzBuf[-1] == DIRECTORY_SEPARATOR_CHAR_W ? W("" ) : DIRECTORY_SEPARATOR_STR_W, &wzBuf, &cchBuf, STRSAFE_NULL_ON_FAILURE)); |
113 | IfFailRet(StringCchCatExW(wzBuf, cchBuf, wzPathRight, &wzBuf, &cchBuf, STRSAFE_NULL_ON_FAILURE)); |
114 | |
115 | return S_OK; |
116 | } |
117 | |
118 | //----------------------------------------------------------------------------------------- |
119 | // Checks if the path provided is valid within the specified constraints. |
120 | // ***NOTE: does not yet check for invalid path characters. |
121 | static bool |
122 | IsValid(LPCWSTR wzPath, DWORD cchPath, bool fAllowLongFormat) |
123 | { |
124 | if (clr::str::IsNullOrEmpty(wzPath)) |
125 | return false; |
126 | |
127 | bool fIsLongFormat = HasLongFormatPrefix(wzPath); |
128 | |
129 | if (fIsLongFormat && !fAllowLongFormat) |
130 | return false; |
131 | |
132 | if (!fIsLongFormat && cchPath > _MAX_PATH) |
133 | return false; |
134 | |
135 | return true; |
136 | } |
137 | }; |
138 | } |
139 | } |
140 | |
141 | #endif // _clr_fs_Path_h_ |
142 | |