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 file_util {
     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 base::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 base::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 base::FilePath& path,
     97                            void* info, size_t length) {
     98   if (!info || !length)
     99     return false;
    100 
    101   PermissionInfo* perm = reinterpret_cast<PermissionInfo*>(info);
    102 
    103   DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
    104                                   SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
    105                                   NULL, NULL, &perm->dacl, NULL);
    106   LocalFree(perm->security_descriptor);
    107 
    108   char* char_array = reinterpret_cast<char*>(info);
    109   delete [] char_array;
    110 
    111   return rc == ERROR_SUCCESS;
    112 }
    113 
    114 }  // namespace
    115 
    116 bool DieFileDie(const base::FilePath& file, bool recurse) {
    117   // It turns out that to not induce flakiness a long timeout is needed.
    118   const int kIterations = 25;
    119   const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(10) /
    120                                    kIterations;
    121 
    122   if (!base::PathExists(file))
    123     return true;
    124 
    125   // Sometimes Delete fails, so try a few more times. Divide the timeout
    126   // into short chunks, so that if a try succeeds, we won't delay the test
    127   // for too long.
    128   for (int i = 0; i < kIterations; ++i) {
    129     if (base::DeleteFile(file, recurse))
    130       return true;
    131     base::PlatformThread::Sleep(kTimeout);
    132   }
    133   return false;
    134 }
    135 
    136 bool EvictFileFromSystemCache(const base::FilePath& file) {
    137   // Request exclusive access to the file and overwrite it with no buffering.
    138   base::win::ScopedHandle file_handle(
    139       CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
    140                  OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL));
    141   if (!file_handle)
    142     return false;
    143 
    144   // Get some attributes to restore later.
    145   BY_HANDLE_FILE_INFORMATION bhi = {0};
    146   CHECK(::GetFileInformationByHandle(file_handle, &bhi));
    147 
    148   // Execute in chunks. It could be optimized. We want to do few of these since
    149   // these operations will be slow without the cache.
    150 
    151   // Allocate a buffer for the reads and the writes.
    152   char* buffer = reinterpret_cast<char*>(VirtualAlloc(NULL,
    153                                                       kOneMB,
    154                                                       MEM_COMMIT | MEM_RESERVE,
    155                                                       PAGE_READWRITE));
    156 
    157   // If the file size isn't a multiple of kOneMB, we'll need special
    158   // processing.
    159   bool file_is_aligned = true;
    160   int total_bytes = 0;
    161   DWORD bytes_read, bytes_written;
    162   for (;;) {
    163     bytes_read = 0;
    164     ::ReadFile(file_handle, buffer, kOneMB, &bytes_read, NULL);
    165     if (bytes_read == 0)
    166       break;
    167 
    168     if (bytes_read < kOneMB) {
    169       // Zero out the remaining part of the buffer.
    170       // WriteFile will fail if we provide a buffer size that isn't a
    171       // sector multiple, so we'll have to write the entire buffer with
    172       // padded zeros and then use SetEndOfFile to truncate the file.
    173       ZeroMemory(buffer + bytes_read, kOneMB - bytes_read);
    174       file_is_aligned = false;
    175     }
    176 
    177     // Move back to the position we just read from.
    178     // Note that SetFilePointer will also fail if total_bytes isn't sector
    179     // aligned, but that shouldn't happen here.
    180     DCHECK((total_bytes % kOneMB) == 0);
    181     SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN);
    182     if (!::WriteFile(file_handle, buffer, kOneMB, &bytes_written, NULL) ||
    183         bytes_written != kOneMB) {
    184       BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
    185       DCHECK(freed);
    186       NOTREACHED();
    187       return false;
    188     }
    189 
    190     total_bytes += bytes_read;
    191 
    192     // If this is false, then we just processed the last portion of the file.
    193     if (!file_is_aligned)
    194       break;
    195   }
    196 
    197   BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
    198   DCHECK(freed);
    199 
    200   if (!file_is_aligned) {
    201     // The size of the file isn't a multiple of 1 MB, so we'll have
    202     // to open the file again, this time without the FILE_FLAG_NO_BUFFERING
    203     // flag and use SetEndOfFile to mark EOF.
    204     file_handle.Set(NULL);
    205     file_handle.Set(CreateFile(file.value().c_str(), GENERIC_WRITE, 0, NULL,
    206                                OPEN_EXISTING, 0, NULL));
    207     CHECK_NE(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN),
    208              INVALID_SET_FILE_POINTER);
    209     CHECK(::SetEndOfFile(file_handle));
    210   }
    211 
    212   // Restore the file attributes.
    213   CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime,
    214                       &bhi.ftLastWriteTime));
    215 
    216   return true;
    217 }
    218 
    219 // Checks if the volume supports Alternate Data Streams. This is required for
    220 // the Zone Identifier implementation.
    221 bool VolumeSupportsADS(const base::FilePath& path) {
    222   wchar_t drive[MAX_PATH] = {0};
    223   wcscpy_s(drive, MAX_PATH, path.value().c_str());
    224 
    225   if (!PathStripToRootW(drive))
    226     return false;
    227 
    228   DWORD fs_flags = 0;
    229   if (!GetVolumeInformationW(drive, NULL, 0, 0, NULL, &fs_flags, NULL, 0))
    230     return false;
    231 
    232   if (fs_flags & FILE_NAMED_STREAMS)
    233     return true;
    234 
    235   return false;
    236 }
    237 
    238 // Return whether the ZoneIdentifier is correctly set to "Internet" (3)
    239 // Only returns a valid result when called from same process as the
    240 // one that (was supposed to have) set the zone identifier.
    241 bool HasInternetZoneIdentifier(const base::FilePath& full_path) {
    242   base::FilePath zone_path(full_path.value() + L":Zone.Identifier");
    243   std::string zone_path_contents;
    244   if (!file_util::ReadFileToString(zone_path, &zone_path_contents))
    245     return false;
    246 
    247   std::vector<std::string> lines;
    248   // This call also trims whitespaces, including carriage-returns (\r).
    249   base::SplitString(zone_path_contents, '\n', &lines);
    250 
    251   switch (lines.size()) {
    252     case 3:
    253       // optional empty line at end of file:
    254       if (lines[2] != "")
    255         return false;
    256       // fall through:
    257     case 2:
    258       return lines[0] == "[ZoneTransfer]" && lines[1] == "ZoneId=3";
    259     default:
    260       return false;
    261   }
    262 }
    263 
    264 std::wstring FilePathAsWString(const base::FilePath& path) {
    265   return path.value();
    266 }
    267 base::FilePath WStringAsFilePath(const std::wstring& path) {
    268   return base::FilePath(path);
    269 }
    270 
    271 bool MakeFileUnreadable(const base::FilePath& path) {
    272   return DenyFilePermission(path, GENERIC_READ);
    273 }
    274 
    275 bool MakeFileUnwritable(const base::FilePath& path) {
    276   return DenyFilePermission(path, GENERIC_WRITE);
    277 }
    278 
    279 PermissionRestorer::PermissionRestorer(const base::FilePath& path)
    280     : path_(path), info_(NULL), length_(0) {
    281   info_ = GetPermissionInfo(path_, &length_);
    282   DCHECK(info_ != NULL);
    283   DCHECK_NE(0u, length_);
    284 }
    285 
    286 PermissionRestorer::~PermissionRestorer() {
    287   if (!RestorePermissionInfo(path_, info_, length_))
    288     NOTREACHED();
    289 }
    290 
    291 }  // namespace file_util
    292