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