Bug 1861963 [wpt PR 42839] - Fix accessing update_properties for product, a=testonly
[gecko.git] / xpcom / io / FileUtilsWin.h
blob548aed6dd72ab26b412e8eb711c26cb88afdd60e
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 "nsString.h"
14 namespace mozilla {
16 inline bool EnsureLongPath(nsAString& aDosPath) {
17 nsAutoString inputPath(aDosPath);
18 while (true) {
19 DWORD requiredLength = GetLongPathNameW(
20 inputPath.get(), reinterpret_cast<wchar_t*>(aDosPath.BeginWriting()),
21 aDosPath.Length());
22 if (!requiredLength) {
23 return false;
25 if (requiredLength < aDosPath.Length()) {
26 // When GetLongPathNameW deems the last argument too small,
27 // it returns a value, but when you pass that value back, it's
28 // satisfied and returns a number that's one smaller. If the above
29 // check was == instead of <, the loop would go on forever with
30 // GetLongPathNameW returning oscillating values!
31 aDosPath.Truncate(requiredLength);
32 return true;
34 aDosPath.SetLength(requiredLength);
38 inline bool NtPathToDosPath(const nsAString& aNtPath, nsAString& aDosPath) {
39 aDosPath.Truncate();
40 if (aNtPath.IsEmpty()) {
41 return true;
43 constexpr auto symLinkPrefix = u"\\??\\"_ns;
44 uint32_t ntPathLen = aNtPath.Length();
45 uint32_t symLinkPrefixLen = symLinkPrefix.Length();
46 if (ntPathLen >= 6 && aNtPath.CharAt(5) == L':' &&
47 ntPathLen >= symLinkPrefixLen &&
48 Substring(aNtPath, 0, symLinkPrefixLen).Equals(symLinkPrefix)) {
49 // Symbolic link for DOS device. Just strip off the prefix.
50 aDosPath = aNtPath;
51 aDosPath.Cut(0, 4);
52 return true;
54 nsAutoString logicalDrives;
55 while (true) {
56 DWORD requiredLength = GetLogicalDriveStringsW(
57 logicalDrives.Length(),
58 reinterpret_cast<wchar_t*>(logicalDrives.BeginWriting()));
59 if (!requiredLength) {
60 return false;
62 if (requiredLength < logicalDrives.Length()) {
63 // When GetLogicalDriveStringsW deems the first argument too small,
64 // it returns a value, but when you pass that value back, it's
65 // satisfied and returns a number that's one smaller. If the above
66 // check was == instead of <, the loop would go on forever with
67 // GetLogicalDriveStringsW returning oscillating values!
68 logicalDrives.Truncate(requiredLength);
69 // logicalDrives now has the format "C:\\\0D:\\\0Z:\\\0". That is,
70 // the sequence drive letter, colon, backslash, U+0000 repeats.
71 break;
73 logicalDrives.SetLength(requiredLength);
76 const char16_t* cur = logicalDrives.BeginReading();
77 const char16_t* end = logicalDrives.EndReading();
78 nsString targetPath;
79 targetPath.SetLength(MAX_PATH);
80 wchar_t driveTemplate[] = L" :";
81 while (cur < end) {
82 // Unfortunately QueryDosDevice doesn't support the idiom for querying the
83 // output buffer size, so it may require retries.
84 driveTemplate[0] = *cur;
85 DWORD targetPathLen = 0;
86 SetLastError(ERROR_SUCCESS);
87 while (true) {
88 targetPathLen = QueryDosDeviceW(
89 driveTemplate, reinterpret_cast<wchar_t*>(targetPath.BeginWriting()),
90 targetPath.Length());
91 if (targetPathLen || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
92 break;
94 targetPath.SetLength(targetPath.Length() * 2);
96 if (targetPathLen) {
97 // Need to use wcslen here because targetPath contains embedded NULL chars
98 size_t firstTargetPathLen = wcslen(targetPath.get());
99 const char16_t* pathComponent =
100 aNtPath.BeginReading() + firstTargetPathLen;
101 bool found = _wcsnicmp(char16ptr_t(aNtPath.BeginReading()),
102 targetPath.get(), firstTargetPathLen) == 0 &&
103 *pathComponent == L'\\';
104 if (found) {
105 aDosPath = driveTemplate;
106 aDosPath += pathComponent;
107 return EnsureLongPath(aDosPath);
110 // Find the next U+0000 within the logical string
111 while (*cur) {
112 // This loop skips the drive letter, the colon
113 // and the backslash.
114 cur++;
116 // Skip over the U+0000 that ends a drive entry
117 // within the logical string
118 cur++;
120 // Try to handle UNC paths. NB: This must happen after we've checked drive
121 // mappings in case a UNC path is mapped to a drive!
122 constexpr auto uncPrefix = u"\\\\"_ns;
123 constexpr auto deviceMupPrefix = u"\\Device\\Mup\\"_ns;
124 if (StringBeginsWith(aNtPath, deviceMupPrefix)) {
125 aDosPath = uncPrefix;
126 aDosPath += Substring(aNtPath, deviceMupPrefix.Length());
127 return true;
129 constexpr auto deviceLanmanRedirectorPrefix =
130 u"\\Device\\LanmanRedirector\\"_ns;
131 if (StringBeginsWith(aNtPath, deviceLanmanRedirectorPrefix)) {
132 aDosPath = uncPrefix;
133 aDosPath += Substring(aNtPath, deviceLanmanRedirectorPrefix.Length());
134 return true;
136 return false;
139 bool HandleToFilename(HANDLE aHandle, const LARGE_INTEGER& aOffset,
140 nsAString& aFilename);
142 uint32_t GetExecutableArchitecture(const wchar_t* aPath);
144 } // namespace mozilla
146 #endif // mozilla_FileUtilsWin_h