1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "mozilla/gtest/MozAssertions.h"
10 #include "nsComponentManagerUtils.h"
12 #include "nsILocalFileWin.h"
17 #include "gtest/gtest.h"
19 static void CanInitWith(const char* aPath
, bool aShouldWork
) {
20 nsCOMPtr
<nsIFile
> file
= do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
);
21 nsresult rv
= file
->InitWithNativePath(nsDependentCString(aPath
));
22 bool success
= aShouldWork
? NS_SUCCEEDED(rv
) : NS_FAILED(rv
);
23 EXPECT_TRUE(success
) << "'" << aPath
<< "' rv=" << std::hex
27 static void CanAppend(const char* aRoot
, const char* aPath
, bool aShouldWork
) {
28 nsCOMPtr
<nsIFile
> file
= do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
);
29 file
->InitWithNativePath(nsDependentCString(aRoot
));
30 nsAutoCString basePath
;
31 file
->GetNativeTarget(basePath
);
33 nsresult rv
= file
->AppendNative(nsDependentCString(aPath
));
34 bool success
= aShouldWork
? NS_SUCCEEDED(rv
) : NS_FAILED(rv
);
35 EXPECT_TRUE(success
) << "'" << basePath
.get() << "' + '" << aPath
36 << "' rv=" << std::hex
<< (unsigned int)rv
;
39 static void CanSetLeafName(const char* aRoot
, const char* aPath
,
41 nsCOMPtr
<nsIFile
> file
= do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
);
42 file
->InitWithNativePath(nsDependentCString(aRoot
));
43 nsAutoCString basePath
;
44 file
->GetNativeTarget(basePath
);
47 file
->SetLeafName(NS_ConvertUTF8toUTF16(nsDependentCString(aPath
)));
48 bool success
= aShouldWork
? NS_SUCCEEDED(rv
) : NS_FAILED(rv
);
49 EXPECT_TRUE(success
) << "'" << basePath
.get() << "' set leaf to '" << aPath
50 << "' rv=" << std::hex
<< (unsigned int)rv
;
53 TEST(TestFileNTFSSpecialPaths
, PlainPaths
)
55 CanInitWith("C:\\", true);
56 CanInitWith("C:\\foo", true);
57 CanInitWith("C:\\bar\\foo", true);
58 CanInitWith("C:\\bar\\foo\\", true);
60 CanAppend("C:\\", "foo", true);
61 CanAppend("C:\\", "bar", true);
62 CanAppend("C:\\bar", "foo", true);
64 CanSetLeafName("C:\\a", "foo", true);
65 CanSetLeafName("C:\\a", "bar", true);
68 TEST(TestFileNTFSSpecialPaths
, AllowedSpecialChars
)
70 CanInitWith("C:\\$foo", true);
71 CanInitWith("C:\\bar\\$foo", true);
72 CanInitWith("C:\\foo:Zone.Identifier", true);
73 CanInitWith("C:\\$foo:Zone.Identifier", true);
74 CanInitWith("C:\\bar\\$foo:Zone.Identifier", true);
76 CanAppend("C:\\", "$foo", true);
77 CanAppend("C:\\bar\\", "$foo", true);
78 CanAppend("C:\\", "foo:Zone.Identifier", true);
79 CanAppend("C:\\", "$foo:Zone.Identifier", true);
80 CanAppend("C:\\bar\\", "$foo:Zone.Identifier", true);
82 CanSetLeafName("C:\\a", "$foo", true);
83 CanSetLeafName("C:\\a", "foo:Zone.Identifier", true);
84 CanSetLeafName("C:\\a", "$foo:Zone.Identifier", true);
87 TEST(TestFileNTFSSpecialPaths
, ForbiddenAttributes
)
89 CanInitWith("C:\\:$MFT", false);
90 CanInitWith("C:\\:$mft", false);
91 CanInitWith("C:\\:$foo", false);
92 // nsLocalFileWin strips the trailing slash so this should also fail:
93 CanInitWith("C:\\:$MFT\\", false);
94 CanInitWith("C:\\:$mft\\", false);
95 CanInitWith("C:\\:$foo\\", false);
97 // We just block these everywhere, not just at the root:
98 CanInitWith("C:\\bar\\:$mft", false);
99 CanInitWith("C:\\bar\\:$mft\\", false);
100 CanInitWith("C:\\bar\\:$foo", false);
101 CanInitWith("C:\\bar\\:$foo\\", false);
103 // Now do the same for appending.
104 CanAppend("C:\\", ":$MFT", false);
105 CanAppend("C:\\", ":$mft", false);
106 CanAppend("C:\\", ":$foo", false);
107 // nsLocalFileWin strips the trailing slash so this should also fail:
108 CanAppend("C:\\", ":$MFT\\", false);
109 CanAppend("C:\\", ":$mft\\", false);
110 CanAppend("C:\\", ":$foo\\", false);
112 // We just block these everywhere, not just at the root:
113 CanAppend("C:\\bar\\", ":$mft", false);
114 CanAppend("C:\\bar\\", ":$mft\\", false);
115 CanAppend("C:\\bar\\", ":$foo", false);
116 CanAppend("C:\\bar\\", ":$foo\\", false);
118 // And the same thing for leaf names:
119 CanSetLeafName("C:\\a", ":$MFT", false);
120 CanSetLeafName("C:\\a", ":$mft", false);
121 CanSetLeafName("C:\\a", ":$foo", false);
123 CanSetLeafName("C:\\a", ":$MFT\\", false);
124 CanSetLeafName("C:\\a", ":$mft\\", false);
125 CanSetLeafName("C:\\a", ":$foo\\", false);
127 CanSetLeafName("C:\\bar\\foo", ":$mft", false);
128 CanSetLeafName("C:\\bar\\foo", ":$mft\\", false);
129 CanSetLeafName("C:\\bar\\foo", ":$foo", false);
130 CanSetLeafName("C:\\bar\\foo", ":$foo\\", false);
133 TEST(TestFileNTFSSpecialPaths
, ForbiddenMetaFiles
)
135 CanInitWith("C:\\$MFT", false);
136 CanInitWith("C:\\$mft", false);
137 CanInitWith("C:\\$bitmap", false);
139 CanAppend("C:\\", "$MFT", false);
140 CanAppend("C:\\", "$mft", false);
141 CanAppend("C:\\", "$bitmap", false);
143 CanSetLeafName("C:\\a", "$MFT", false);
144 CanSetLeafName("C:\\a", "$mft", false);
145 CanSetLeafName("C:\\a", "$bitmap", false);
147 // nsLocalFileWin strips the trailing slash so this should also fail:
148 CanInitWith("C:\\$MFT\\", false);
149 CanInitWith("C:\\$mft\\", false);
150 CanInitWith("C:\\$bitmap\\", false);
152 CanAppend("C:\\", "$MFT\\", false);
153 CanAppend("C:\\", "$mft\\", false);
154 CanAppend("C:\\", "$bitmap\\", false);
156 CanSetLeafName("C:\\a", "$MFT\\", false);
157 CanSetLeafName("C:\\a", "$mft\\", false);
158 CanSetLeafName("C:\\a", "$bitmap\\", false);
160 // Shouldn't be able to bypass this by asking for ADS stuff:
161 CanInitWith("C:\\$MFT:Zone.Identifier", false);
162 CanInitWith("C:\\$mft:Zone.Identifier", false);
163 CanInitWith("C:\\$bitmap:Zone.Identifier", false);
165 CanAppend("C:\\", "$MFT:Zone.Identifier", false);
166 CanAppend("C:\\", "$mft:Zone.Identifier", false);
167 CanAppend("C:\\", "$bitmap:Zone.Identifier", false);
169 CanSetLeafName("C:\\a", "$MFT:Zone.Identifier", false);
170 CanSetLeafName("C:\\a", "$mft:Zone.Identifier", false);
171 CanSetLeafName("C:\\a", "$bitmap:Zone.Identifier", false);
174 TEST(TestFileNTFSSpecialPaths
, ForbiddenMetaFilesOtherRoots
)
176 // Should still block them for UNC and volume roots
177 CanInitWith("\\\\LOCALHOST\\C$\\$MFT", false);
178 CanInitWith("\\\\?\\Volume{1234567}\\$MFT", false);
180 CanAppend("\\\\LOCALHOST\\", "C$\\$MFT", false);
181 CanAppend("\\\\LOCALHOST\\C$\\", "$MFT", false);
182 CanAppend("\\\\?\\Volume{1234567}\\", "$MFT", false);
183 CanAppend("\\\\Blah\\", "Volume{1234567}\\$MFT", false);
185 CanSetLeafName("\\\\LOCALHOST\\C$", "C$\\$MFT", false);
186 CanSetLeafName("\\\\LOCALHOST\\C$\\foo", "$MFT", false);
187 CanSetLeafName("\\\\?\\Volume{1234567}\\foo", "$MFT", false);
188 CanSetLeafName("\\\\Blah\\foo", "Volume{1234567}\\$MFT", false);
190 // Root detection should cope with un-normalized paths:
191 CanInitWith("C:\\foo\\..\\$MFT", false);
192 CanInitWith("C:\\foo\\..\\$mft\\", false);
193 CanInitWith("\\\\LOCALHOST\\C$\\blah\\..\\$MFT", false);
194 CanInitWith("\\\\?\\Volume{13455635}\\blah\\..\\$MFT", false);
195 // As well as different or duplicated separators:
196 CanInitWith("C:\\foo\\..\\\\$MFT\\", false);
197 CanInitWith("\\\\?\\Volume{1234567}/$MFT", false);
198 CanInitWith("\\\\LOCALHOST\\C$/blah//../$MFT", false);
200 // There are no "append" equivalents for the preceding set of tests,
201 // because append does not allow '..' to be used as a relative path
202 // component, nor does it allow forward slashes:
203 CanAppend("C:\\foo", "..\\", false);
204 CanAppend("C:\\foo", "bar/baz", false);
206 // But this is (strangely) allowed for SetLeafName. Yes, really.
207 CanSetLeafName("C:\\foo\\bar", "..\\$MFT", false);
208 CanSetLeafName("C:\\foo\\bar", "..\\$mft\\", false);
209 CanSetLeafName("\\\\LOCALHOST\\C$\\bl", "ah\\..\\$MFT", false);
210 CanSetLeafName("\\\\?\\Volume{13455635}\\bla", "ah\\..\\$MFT", false);
212 CanSetLeafName("C:\\foo\\bar", "..\\\\$MFT\\", false);
213 CanSetLeafName("\\\\?\\Volume{1234567}\\bar", "/$MFT", false);
214 CanSetLeafName("\\\\LOCALHOST\\C$/blah/", "\\../$MFT", false);
217 TEST(TestFileNTFSSpecialPaths
, NotQuiteMetaFiles
)
219 // These files should not be blocked away from the root:
220 CanInitWith("C:\\bar\\$bitmap", true);
221 CanInitWith("C:\\bar\\$mft", true);
224 CanAppend("C:\\bar\\", "$bitmap", true);
225 CanAppend("C:\\bar\\", "$mft", true);
228 CanSetLeafName("C:\\bar\\foo", "$bitmap", true);
229 CanSetLeafName("C:\\bar\\foo", "$mft", true);
231 // And we shouldn't block on substring matches:
232 CanInitWith("C:\\$MFT stocks", true);
233 CanAppend("C:\\", "$MFT stocks", true);
234 CanSetLeafName("C:\\", "$MFT stocks", true);
237 TEST(TestFileNTFSSpecialPaths
, Normalization
)
239 // First determine the working directory:
240 wchar_t workingDir
[MAX_PATH
];
241 if (nullptr == _wgetcwd(workingDir
, MAX_PATH
- 1)) {
242 EXPECT_FALSE(true) << "Getting working directory failed.";
246 nsString
normalizedPath(workingDir
);
247 // Need at least 3 chars for the root, at least 2 more to get another subdir
248 // in there. This test will fail if cwd is the root of a drive.
249 if (normalizedPath
.Length() < 5 ||
250 !mozilla::IsAsciiAlpha(normalizedPath
.First()) ||
251 normalizedPath
.CharAt(1) != L
':' || normalizedPath
.CharAt(2) != L
'\\') {
252 EXPECT_FALSE(true) << "Working directory not long enough?!";
256 // Copy the drive and colon, but NOT the backslash.
257 nsAutoString
startingFilePath(Substring(normalizedPath
, 0, 2));
258 normalizedPath
.Cut(0, 3);
260 // Then determine the number of path components in cwd:
261 nsAString::const_iterator begin
, end
;
262 normalizedPath
.BeginReading(begin
);
263 normalizedPath
.EndReading(end
);
264 if (!FindCharInReadable(L
'\\', begin
, end
)) {
265 EXPECT_FALSE(true) << "Working directory was at a root";
268 auto numberOfComponentsAboveRoot
= 1;
269 while (FindCharInReadable(L
'\\', begin
, end
)) {
271 numberOfComponentsAboveRoot
++;
274 // Then set up a file with that many `..\` components:
275 startingFilePath
.SetCapacity(3 + numberOfComponentsAboveRoot
* 3 + 9);
276 while (numberOfComponentsAboveRoot
--) {
277 startingFilePath
.AppendLiteral(u
"..\\");
279 startingFilePath
.AppendLiteral(u
"$mft");
281 nsCOMPtr
<nsIFile
> file
= do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
);
282 // This should fail immediately, rather than waiting for a call to
283 // nsIFile::Normalize, because normalization doesn't happen reliably,
284 // and where it does happen consumers often don't check for errors.
285 nsresult rv
= file
->InitWithPath(startingFilePath
);
286 EXPECT_NS_FAILED(rv
) << " from normalizing '"
287 << NS_ConvertUTF16toUTF8(startingFilePath
).get()
288 << "' rv=" << std::hex
<< (unsigned int)rv
;