xref: /aosp_15_r20/external/cronet/base/test/test_file_util_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/test/test_file_util.h"
6 
7 #include <windows.h>
8 
9 #include <aclapi.h>
10 #include <stddef.h>
11 #include <wchar.h>
12 
13 #include <memory>
14 
15 #include "base/check_op.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/memory/ptr_util.h"
19 #include "base/notreached.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "base/threading/platform_thread.h"
23 #include "base/win/scoped_handle.h"
24 #include "base/win/shlwapi.h"
25 
26 namespace base {
27 
28 namespace {
29 
30 struct PermissionInfo {
31   PSECURITY_DESCRIPTOR security_descriptor;
32   ACL dacl;
33 };
34 
35 // Gets a blob indicating the permission information for |path|.
36 // |length| is the length of the blob.  Zero on failure.
37 // Returns the blob pointer, or NULL on failure.
GetPermissionInfo(const FilePath & path,size_t * length)38 void* GetPermissionInfo(const FilePath& path, size_t* length) {
39   DCHECK(length);
40   *length = 0;
41   PACL dacl = nullptr;
42   PSECURITY_DESCRIPTOR security_descriptor;
43   if (GetNamedSecurityInfo(path.value().c_str(), SE_FILE_OBJECT,
44                            DACL_SECURITY_INFORMATION, nullptr, nullptr, &dacl,
45                            nullptr, &security_descriptor) != ERROR_SUCCESS) {
46     return nullptr;
47   }
48   DCHECK(dacl);
49 
50   *length = sizeof(PSECURITY_DESCRIPTOR) + dacl->AclSize;
51   PermissionInfo* info = reinterpret_cast<PermissionInfo*>(new char[*length]);
52   info->security_descriptor = security_descriptor;
53   memcpy(&info->dacl, dacl, dacl->AclSize);
54 
55   return info;
56 }
57 
58 // Restores the permission information for |path|, given the blob retrieved
59 // using |GetPermissionInfo()|.
60 // |info| is the pointer to the blob.
61 // |length| is the length of the blob.
62 // Either |info| or |length| may be NULL/0, in which case nothing happens.
RestorePermissionInfo(const FilePath & path,void * info,size_t length)63 bool RestorePermissionInfo(const FilePath& path, void* info, size_t length) {
64   if (!info || !length)
65     return false;
66 
67   PermissionInfo* perm = reinterpret_cast<PermissionInfo*>(info);
68 
69   DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
70                                   SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
71                                   nullptr, nullptr, &perm->dacl, nullptr);
72   LocalFree(perm->security_descriptor);
73 
74   char* char_array = reinterpret_cast<char*>(info);
75   delete [] char_array;
76 
77   return rc == ERROR_SUCCESS;
78 }
79 
ToCStr(const std::basic_string<wchar_t> & str)80 std::unique_ptr<wchar_t[]> ToCStr(const std::basic_string<wchar_t>& str) {
81   size_t size = str.size() + 1;
82   std::unique_ptr<wchar_t[]> ptr = std::make_unique<wchar_t[]>(size);
83   wcsncpy(ptr.get(), str.c_str(), size);
84   return ptr;
85 }
86 
87 }  // namespace
88 
DieFileDie(const FilePath & file,bool recurse)89 bool DieFileDie(const FilePath& file, bool recurse) {
90   // It turns out that to not induce flakiness a long timeout is needed.
91   const int kIterations = 25;
92   const TimeDelta kTimeout = Seconds(10) / kIterations;
93 
94   if (!PathExists(file))
95     return true;
96 
97   // Sometimes Delete fails, so try a few more times. Divide the timeout
98   // into short chunks, so that if a try succeeds, we won't delay the test
99   // for too long.
100   for (int i = 0; i < kIterations; ++i) {
101     bool success;
102     if (recurse)
103       success = DeletePathRecursively(file);
104     else
105       success = DeleteFile(file);
106     if (success)
107       return true;
108     PlatformThread::Sleep(kTimeout);
109   }
110   return false;
111 }
112 
SyncPageCacheToDisk()113 void SyncPageCacheToDisk() {
114   // Approximating this with noop. The proper implementation would require
115   // administrator privilege:
116   // https://docs.microsoft.com/en-us/windows/desktop/api/FileAPI/nf-fileapi-flushfilebuffers
117 }
118 
EvictFileFromSystemCache(const FilePath & file)119 bool EvictFileFromSystemCache(const FilePath& file) {
120   FilePath::StringType file_value = file.value();
121   if (file_value.length() >= MAX_PATH && file.IsAbsolute()) {
122     file_value.insert(0, L"\\\\?\\");
123   }
124   win::ScopedHandle file_handle(
125       CreateFile(file_value.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr,
126                  OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, nullptr));
127   if (!file_handle.is_valid())
128     return false;
129 
130   // Re-write the file time information to trigger cache eviction for the file.
131   // This function previously overwrote the entire file without buffering, but
132   // local experimentation validates this simplified and *much* faster approach:
133   // [1] Sysinternals RamMap no longer lists these files as cached afterwards.
134   // [2] Telemetry performance test startup.cold.blank_page reports sane values.
135   BY_HANDLE_FILE_INFORMATION bhi = {0};
136   CHECK(::GetFileInformationByHandle(file_handle.get(), &bhi));
137   CHECK(::SetFileTime(file_handle.get(), &bhi.ftCreationTime,
138                       &bhi.ftLastAccessTime, &bhi.ftLastWriteTime));
139   return true;
140 }
141 
142 // Deny |permission| on the file |path|, for the current user.
DenyFilePermission(const FilePath & path,DWORD permission)143 bool DenyFilePermission(const FilePath& path, DWORD permission) {
144   PACL old_dacl;
145   PSECURITY_DESCRIPTOR security_descriptor;
146 
147   std::unique_ptr<TCHAR[]> path_ptr = ToCStr(path.value().c_str());
148   if (GetNamedSecurityInfo(path_ptr.get(), SE_FILE_OBJECT,
149                            DACL_SECURITY_INFORMATION, nullptr, nullptr,
150                            &old_dacl, nullptr,
151                            &security_descriptor) != ERROR_SUCCESS) {
152     return false;
153   }
154 
155   std::unique_ptr<TCHAR[]> current_user = ToCStr(std::wstring(L"CURRENT_USER"));
156   EXPLICIT_ACCESS new_access = {
157       permission,
158       DENY_ACCESS,
159       0,
160       {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_USER,
161        current_user.get()}};
162 
163   PACL new_dacl;
164   if (SetEntriesInAcl(1, &new_access, old_dacl, &new_dacl) != ERROR_SUCCESS) {
165     LocalFree(security_descriptor);
166     return false;
167   }
168 
169   DWORD rc = SetNamedSecurityInfo(path_ptr.get(), SE_FILE_OBJECT,
170                                   DACL_SECURITY_INFORMATION, nullptr, nullptr,
171                                   new_dacl, nullptr);
172   LocalFree(security_descriptor);
173   LocalFree(new_dacl);
174 
175   return rc == ERROR_SUCCESS;
176 }
177 
MakeFileUnreadable(const FilePath & path)178 bool MakeFileUnreadable(const FilePath& path) {
179   return DenyFilePermission(path, GENERIC_READ);
180 }
181 
MakeFileUnwritable(const FilePath & path)182 bool MakeFileUnwritable(const FilePath& path) {
183   return DenyFilePermission(path, GENERIC_WRITE);
184 }
185 
FilePermissionRestorer(const FilePath & path)186 FilePermissionRestorer::FilePermissionRestorer(const FilePath& path)
187     : path_(path), info_(nullptr), length_(0) {
188   info_ = GetPermissionInfo(path_, &length_);
189   DCHECK(info_);
190   DCHECK_NE(0u, length_);
191 }
192 
~FilePermissionRestorer()193 FilePermissionRestorer::~FilePermissionRestorer() {
194   if (!RestorePermissionInfo(path_, info_, length_))
195     NOTREACHED();
196 }
197 
198 }  // namespace base
199