Home | History | Annotate | Download | only in test
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      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 #include <aclapi.h>
      9 #include <shlwapi.h>
     10 
     11 #include <vector>
     12 
     13 #include "base/file_util.h"
     14 #include "base/files/file_path.h"
     15 #include "base/logging.h"
     16 #include "base/strings/string_split.h"
     17 #include "base/threading/platform_thread.h"
     18 #include "base/win/scoped_handle.h"
     19 
     20 namespace base {
     21 
     22 static const ptrdiff_t kOneMB = 1024 * 1024;
     23 
     24 namespace {
     25 
     26 struct PermissionInfo {
     27   PSECURITY_DESCRIPTOR security_descriptor;
     28   ACL dacl;
     29 };
     30 
     31 // Deny |permission| on the file |path|, for the current user.
     32 bool DenyFilePermission(const FilePath& path, DWORD permission) {
     33   PACL old_dacl;
     34   PSECURITY_DESCRIPTOR security_descriptor;
     35   if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
     36                            SE_FILE_OBJECT,
     37                            DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl,
     38                            NULL, &security_descriptor) != ERROR_SUCCESS) {
     39     return false;
     40   }
     41 
     42   EXPLICIT_ACCESS change;
     43   change.grfAccessPermissions = permission;
     44   change.grfAccessMode = DENY_ACCESS;
     45   change.grfInheritance = 0;
     46   change.Trustee.pMultipleTrustee = NULL;
     47   change.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
     48   change.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
     49   change.Trustee.TrusteeType = TRUSTEE_IS_USER;
     50   change.Trustee.ptstrName = L"CURRENT_USER";
     51 
     52   PACL new_dacl;
     53   if (SetEntriesInAcl(1, &change, old_dacl, &new_dacl) != ERROR_SUCCESS) {
     54     LocalFree(security_descriptor);
     55     return false;
     56   }
     57 
     58   DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
     59                                   SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
     60                                   NULL, NULL, new_dacl, NULL);
     61   LocalFree(security_descriptor);
     62   LocalFree(new_dacl);
     63 
     64   return rc == ERROR_SUCCESS;
     65 }
     66 
     67 // Gets a blob indicating the permission information for |path|.
     68 // |length| is the length of the blob.  Zero on failure.
     69 // Returns the blob pointer, or NULL on failure.
     70 void* GetPermissionInfo(const FilePath& path, size_t* length) {
     71   DCHECK(length != NULL);
     72   *length = 0;
     73   PACL dacl = NULL;
     74   PSECURITY_DESCRIPTOR security_descriptor;
     75   if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
     76                            SE_FILE_OBJECT,
     77                            DACL_SECURITY_INFORMATION, NULL, NULL, &dacl,
     78                            NULL, &security_descriptor) != ERROR_SUCCESS) {
     79     return NULL;
     80   }
     81   DCHECK(dacl != NULL);
     82 
     83   *length = sizeof(PSECURITY_DESCRIPTOR) + dacl->AclSize;
     84   PermissionInfo* info = reinterpret_cast<PermissionInfo*>(new char[*length]);
     85   info->security_descriptor = security_descriptor;
     86   memcpy(&info->dacl, dacl, dacl->AclSize);
     87 
     88   return info;
     89 }
     90 
     91 // Restores the permission information for |path|, given the blob retrieved
     92 // using |GetPermissionInfo()|.
     93 // |info| is the pointer to the blob.
     94 // |length| is the length of the blob.
     95 // Either |info| or |length| may be NULL/0, in which case nothing happens.
     96 bool RestorePermissionInfo(const FilePath& path, void* info, size_t length) {
     97   if (!info || !length)
     98     return false;
     99 
    100   PermissionInfo* perm = reinterpret_cast<PermissionInfo*>(info);
    101 
    102   DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
    103                                   SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
    104                                   NULL, NULL, &perm->dacl, NULL);
    105   LocalFree(perm->security_descriptor);
    106 
    107   char* char_array = reinterpret_cast<char*>(info);
    108   delete [] char_array;
    109 
    110   return rc == ERROR_SUCCESS;
    111 }
    112 
    113 }  // namespace
    114 
    115 bool DieFileDie(const FilePath& file, bool recurse) {
    116   // It turns out that to not induce flakiness a long timeout is needed.
    117   const int kIterations = 25;
    118   const TimeDelta kTimeout = TimeDelta::FromSeconds(10) / kIterations;
    119 
    120   if (!PathExists(file))
    121     return true;
    122 
    123   // Sometimes Delete fails, so try a few more times. Divide the timeout
    124   // into short chunks, so that if a try succeeds, we won't delay the test
    125   // for too long.
    126   for (int i = 0; i < kIterations; ++i) {
    127     if (DeleteFile(file, recurse))
    128       return true;
    129     PlatformThread::Sleep(kTimeout);
    130   }
    131   return false;
    132 }
    133 
    134 bool EvictFileFromSystemCache(const FilePath& file) {
    135   // Request exclusive access to the file and overwrite it with no buffering.
    136   base::win::ScopedHandle file_handle(
    137       CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
    138                  OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL));
    139   if (!file_handle)
    140     return false;
    141 
    142   // Get some attributes to restore later.
    143   BY_HANDLE_FILE_INFORMATION bhi = {0};
    144   CHECK(::GetFileInformationByHandle(file_handle, &bhi));
    145 
    146   // Execute in chunks. It could be optimized. We want to do few of these since
    147   // these operations will be slow without the cache.
    148 
    149   // Allocate a buffer for the reads and the writes.
    150   char* buffer = reinterpret_cast<char*>(VirtualAlloc(NULL,
    151                                                       kOneMB,
    152                                                       MEM_COMMIT | MEM_RESERVE,
    153                                                       PAGE_READWRITE));
    154 
    155   // If the file size isn't a multiple of kOneMB, we'll need special
    156   // processing.
    157   bool file_is_aligned = true;
    158   int total_bytes = 0;
    159   DWORD bytes_read, bytes_written;
    160   for (;;) {
    161     bytes_read = 0;
    162     ::ReadFile(file_handle, buffer, kOneMB, &bytes_read, NULL);
    163     if (bytes_read == 0)
    164       break;
    165 
    166     if (bytes_read < kOneMB) {
    167       // Zero out the remaining part of the buffer.
    168       // WriteFile will fail if we provide a buffer size that isn't a
    169       // sector multiple, so we'll have to write the entire buffer with
    170       // padded zeros and then use SetEndOfFile to truncate the file.
    171       ZeroMemory(buffer + bytes_read, kOneMB - bytes_read);
    172       file_is_aligned = false;
    173     }
    174 
    175     // Move back to the position we just read from.
    176     // Note that SetFilePointer will also fail if total_bytes isn't sector
    177     // aligned, but that shouldn't happen here.
    178     DCHECK((total_bytes % kOneMB) == 0);
    179     SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN);
    180     if (!::WriteFile(file_handle, buffer, kOneMB, &bytes_written, NULL) ||
    181         bytes_written != kOneMB) {
    182       BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
    183       DCHECK(freed);
    184       NOTREACHED();
    185       return false;
    186     }
    187 
    188     total_bytes += bytes_read;
    189 
    190     // If this is false, then we just processed the last portion of the file.
    191     if (!file_is_aligned)
    192       break;
    193   }
    194 
    195   BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
    196   DCHECK(freed);
    197 
    198   if (!file_is_aligned) {
    199     // The size of the file isn't a multiple of 1 MB, so we'll have
    200     // to open the file again, this time without the FILE_FLAG_NO_BUFFERING
    201     // flag and use SetEndOfFile to mark EOF.
    202     file_handle.Set(NULL);
    203     file_handle.Set(CreateFile(file.value().c_str(), GENERIC_WRITE, 0, NULL,
    204                                OPEN_EXISTING, 0, NULL));
    205     CHECK_NE(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN),
    206              INVALID_SET_FILE_POINTER);
    207     CHECK(::SetEndOfFile(file_handle));
    208   }
    209 
    210   // Restore the file attributes.
    211   CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime,
    212                       &bhi.ftLastWriteTime));
    213 
    214   return true;
    215 }
    216 
    217 // Checks if the volume supports Alternate Data Streams. This is required for
    218 // the Zone Identifier implementation.
    219 bool VolumeSupportsADS(const FilePath& path) {
    220   wchar_t drive[MAX_PATH] = {0};
    221   wcscpy_s(drive, MAX_PATH, path.value().c_str());
    222 
    223   if (!PathStripToRootW(drive))
    224     return false;
    225 
    226   DWORD fs_flags = 0;
    227   if (!GetVolumeInformationW(drive, NULL, 0, 0, NULL, &fs_flags, NULL, 0))
    228     return false;
    229 
    230   if (fs_flags & FILE_NAMED_STREAMS)
    231     return true;
    232 
    233   return false;
    234 }
    235 
    236 // Return whether the ZoneIdentifier is correctly set to "Internet" (3)
    237 // Only returns a valid result when called from same process as the
    238 // one that (was supposed to have) set the zone identifier.
    239 bool HasInternetZoneIdentifier(const FilePath& full_path) {
    240   FilePath zone_path(full_path.value() + L":Zone.Identifier");
    241   std::string zone_path_contents;
    242   if (!ReadFileToString(zone_path, &zone_path_contents))
    243     return false;
    244 
    245   std::vector<std::string> lines;
    246   // This call also trims whitespaces, including carriage-returns (\r).
    247   SplitString(zone_path_contents, '\n', &lines);
    248 
    249   switch (lines.size()) {
    250     case 3:
    251       // optional empty line at end of file:
    252       if (lines[2] != "")
    253         return false;
    254       // fall through:
    255     case 2:
    256       return lines[0] == "[ZoneTransfer]" && lines[1] == "ZoneId=3";
    257     default:
    258       return false;
    259   }
    260 }
    261 
    262 }  // namespace base
    263 
    264 namespace file_util {
    265 
    266 using base::DenyFilePermission;
    267 using base::GetPermissionInfo;
    268 using base::RestorePermissionInfo;
    269 
    270 std::wstring FilePathAsWString(const base::FilePath& path) {
    271   return path.value();
    272 }
    273 base::FilePath WStringAsFilePath(const std::wstring& path) {
    274   return base::FilePath(path);
    275 }
    276 
    277 bool MakeFileUnreadable(const base::FilePath& path) {
    278   return DenyFilePermission(path, GENERIC_READ);
    279 }
    280 
    281 bool MakeFileUnwritable(const base::FilePath& path) {
    282   return DenyFilePermission(path, GENERIC_WRITE);
    283 }
    284 
    285 PermissionRestorer::PermissionRestorer(const base::FilePath& path)
    286     : path_(path), info_(NULL), length_(0) {
    287   info_ = GetPermissionInfo(path_, &length_);
    288   DCHECK(info_ != NULL);
    289   DCHECK_NE(0u, length_);
    290 }
    291 
    292 PermissionRestorer::~PermissionRestorer() {
    293   if (!RestorePermissionInfo(path_, info_, length_))
    294     NOTREACHED();
    295 }
    296 
    297 }  // namespace file_util
    298