Bug 1852754: part 9) Add tests for dynamically loading <link rel="prefetch"> elements...
[gecko.git] / xpcom / io / FileUtilsWin.h
blob4a59820d083b06d0b808d2736e4d862a537d2b19
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_FileUtilsWin_h
8 #define mozilla_FileUtilsWin_h
10 #include <windows.h>
12 #include "mozilla/Scoped.h"
13 #include "nsString.h"
15 namespace mozilla {
17 inline bool EnsureLongPath(nsAString& aDosPath) {
18 nsAutoString inputPath(aDosPath);
19 while (true) {
20 DWORD requiredLength = GetLongPathNameW(
21 inputPath.get(), reinterpret_cast<wchar_t*>(aDosPath.BeginWriting()),
22 aDosPath.Length());
23 if (!requiredLength) {
24 return false;
26 if (requiredLength < aDosPath.Length()) {
27 // When GetLongPathNameW deems the last argument too small,
28 // it returns a value, but when you pass that value back, it's
29 // satisfied and returns a number that's one smaller. If the above
30 // check was == instead of <, the loop would go on forever with
31 // GetLongPathNameW returning oscillating values!
32 aDosPath.Truncate(requiredLength);
33 return true;
35 aDosPath.SetLength(requiredLength);
39 inline bool NtPathToDosPath(const nsAString& aNtPath, nsAString& aDosPath) {
40 aDosPath.Truncate();
41 if (aNtPath.IsEmpty()) {
42 return true;
44 constexpr auto symLinkPrefix = u"\\??\\"_ns;
45 uint32_t ntPathLen = aNtPath.Length();
46 uint32_t symLinkPrefixLen = symLinkPrefix.Length();
47 if (ntPathLen >= 6 && aNtPath.CharAt(5) == L':' &&
48 ntPathLen >= symLinkPrefixLen &&
49 Substring(aNtPath, 0, symLinkPrefixLen).Equals(symLinkPrefix)) {
50 // Symbolic link for DOS device. Just strip off the prefix.
51 aDosPath = aNtPath;
52 aDosPath.Cut(0, 4);
53 return true;
55 nsAutoString logicalDrives;
56 while (true) {
57 DWORD requiredLength = GetLogicalDriveStringsW(
58 logicalDrives.Length(),
59 reinterpret_cast<wchar_t*>(logicalDrives.BeginWriting()));
60 if (!requiredLength) {
61 return false;
63 if (requiredLength < logicalDrives.Length()) {
64 // When GetLogicalDriveStringsW deems the first argument too small,
65 // it returns a value, but when you pass that value back, it's
66 // satisfied and returns a number that's one smaller. If the above
67 // check was == instead of <, the loop would go on forever with
68 // GetLogicalDriveStringsW returning oscillating values!
69 logicalDrives.Truncate(requiredLength);
70 // logicalDrives now has the format "C:\\\0D:\\\0Z:\\\0". That is,
71 // the sequence drive letter, colon, backslash, U+0000 repeats.
72 break;
74 logicalDrives.SetLength(requiredLength);
77 const char16_t* cur = logicalDrives.BeginReading();
78 const char16_t* end = logicalDrives.EndReading();
79 nsString targetPath;
80 targetPath.SetLength(MAX_PATH);
81 wchar_t driveTemplate[] = L" :";
82 while (cur < end) {
83 // Unfortunately QueryDosDevice doesn't support the idiom for querying the
84 // output buffer size, so it may require retries.
85 driveTemplate[0] = *cur;
86 DWORD targetPathLen = 0;
87 SetLastError(ERROR_SUCCESS);
88 while (true) {
89 targetPathLen = QueryDosDeviceW(
90 driveTemplate, reinterpret_cast<wchar_t*>(targetPath.BeginWriting()),
91 targetPath.Length());
92 if (targetPathLen || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
93 break;
95 targetPath.SetLength(targetPath.Length() * 2);
97 if (targetPathLen) {
98 // Need to use wcslen here because targetPath contains embedded NULL chars
99 size_t firstTargetPathLen = wcslen(targetPath.get());
100 const char16_t* pathComponent =
101 aNtPath.BeginReading() + firstTargetPathLen;
102 bool found = _wcsnicmp(char16ptr_t(aNtPath.BeginReading()),
103 targetPath.get(), firstTargetPathLen) == 0 &&
104 *pathComponent == L'\\';
105 if (found) {
106 aDosPath = driveTemplate;
107 aDosPath += pathComponent;
108 return EnsureLongPath(aDosPath);
111 // Find the next U+0000 within the logical string
112 while (*cur) {
113 // This loop skips the drive letter, the colon
114 // and the backslash.
115 cur++;
117 // Skip over the U+0000 that ends a drive entry
118 // within the logical string
119 cur++;
121 // Try to handle UNC paths. NB: This must happen after we've checked drive
122 // mappings in case a UNC path is mapped to a drive!
123 constexpr auto uncPrefix = u"\\\\"_ns;
124 constexpr auto deviceMupPrefix = u"\\Device\\Mup\\"_ns;
125 if (StringBeginsWith(aNtPath, deviceMupPrefix)) {
126 aDosPath = uncPrefix;
127 aDosPath += Substring(aNtPath, deviceMupPrefix.Length());
128 return true;
130 constexpr auto deviceLanmanRedirectorPrefix =
131 u"\\Device\\LanmanRedirector\\"_ns;
132 if (StringBeginsWith(aNtPath, deviceLanmanRedirectorPrefix)) {
133 aDosPath = uncPrefix;
134 aDosPath += Substring(aNtPath, deviceLanmanRedirectorPrefix.Length());
135 return true;
137 return false;
140 bool HandleToFilename(HANDLE aHandle, const LARGE_INTEGER& aOffset,
141 nsAString& aFilename);
143 uint32_t GetExecutableArchitecture(const wchar_t* aPath);
145 } // namespace mozilla
147 #endif // mozilla_FileUtilsWin_h