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
23namespace 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