test_file_util_win.cc revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
1// Copyright (c) 2008 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
9#include <vector>
10
11#include "base/file_path.h"
12#include "base/file_util.h"
13#include "base/logging.h"
14#include "base/platform_thread.h"
15#include "base/scoped_handle.h"
16
17namespace file_util {
18
19static const ptrdiff_t kOneMB = 1024 * 1024;
20
21bool DieFileDie(const FilePath& file, bool recurse) {
22  // It turns out that to not induce flakiness a long timeout is needed.
23  const int kTimeoutMs = 10000;
24
25  if (!file_util::PathExists(file))
26    return true;
27
28  // Sometimes Delete fails, so try a few more times. Divide the timeout
29  // into short chunks, so that if a try succeeds, we won't delay the test
30  // for too long.
31  for (int i = 0; i < 25; ++i) {
32    if (file_util::Delete(file, recurse))
33      return true;
34    PlatformThread::Sleep(kTimeoutMs / 25);
35  }
36  return false;
37}
38
39bool EvictFileFromSystemCache(const FilePath& file) {
40  // Request exclusive access to the file and overwrite it with no buffering.
41  ScopedHandle file_handle(
42      CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
43                 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL));
44  if (!file_handle)
45    return false;
46
47  // Get some attributes to restore later.
48  BY_HANDLE_FILE_INFORMATION bhi = {0};
49  CHECK(::GetFileInformationByHandle(file_handle, &bhi));
50
51  // Execute in chunks. It could be optimized. We want to do few of these since
52  // these operations will be slow without the cache.
53
54  // Allocate a buffer for the reads and the writes.
55  char* buffer = reinterpret_cast<char*>(VirtualAlloc(NULL,
56                                                      kOneMB,
57                                                      MEM_COMMIT | MEM_RESERVE,
58                                                      PAGE_READWRITE));
59
60  // If the file size isn't a multiple of kOneMB, we'll need special
61  // processing.
62  bool file_is_aligned = true;
63  int total_bytes = 0;
64  DWORD bytes_read, bytes_written;
65  for (;;) {
66    bytes_read = 0;
67    ReadFile(file_handle, buffer, kOneMB, &bytes_read, NULL);
68    if (bytes_read == 0)
69      break;
70
71    if (bytes_read < kOneMB) {
72      // Zero out the remaining part of the buffer.
73      // WriteFile will fail if we provide a buffer size that isn't a
74      // sector multiple, so we'll have to write the entire buffer with
75      // padded zeros and then use SetEndOfFile to truncate the file.
76      ZeroMemory(buffer + bytes_read, kOneMB - bytes_read);
77      file_is_aligned = false;
78    }
79
80    // Move back to the position we just read from.
81    // Note that SetFilePointer will also fail if total_bytes isn't sector
82    // aligned, but that shouldn't happen here.
83    DCHECK((total_bytes % kOneMB) == 0);
84    SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN);
85    if (!WriteFile(file_handle, buffer, kOneMB, &bytes_written, NULL) ||
86        bytes_written != kOneMB) {
87      BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
88      DCHECK(freed);
89      NOTREACHED();
90      return false;
91    }
92
93    total_bytes += bytes_read;
94
95    // If this is false, then we just processed the last portion of the file.
96    if (!file_is_aligned)
97      break;
98  }
99
100  BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
101  DCHECK(freed);
102
103  if (!file_is_aligned) {
104    // The size of the file isn't a multiple of 1 MB, so we'll have
105    // to open the file again, this time without the FILE_FLAG_NO_BUFFERING
106    // flag and use SetEndOfFile to mark EOF.
107    file_handle.Set(NULL);
108    file_handle.Set(CreateFile(file.value().c_str(), GENERIC_WRITE, 0, NULL,
109                               OPEN_EXISTING, 0, NULL));
110    CHECK(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN) !=
111          INVALID_SET_FILE_POINTER);
112    CHECK(::SetEndOfFile(file_handle));
113  }
114
115  // Restore the file attributes.
116  CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime,
117                      &bhi.ftLastWriteTime));
118
119  return true;
120}
121
122// Like CopyFileNoCache but recursively copies all files and subdirectories
123// in the given input directory to the output directory.
124bool CopyRecursiveDirNoCache(const FilePath& source_dir,
125                             const FilePath& dest_dir) {
126  // Try to create the directory if it doesn't already exist.
127  if (!CreateDirectory(dest_dir)) {
128    if (GetLastError() != ERROR_ALREADY_EXISTS)
129      return false;
130  }
131
132  std::vector<std::wstring> files_copied;
133
134  std::wstring src(source_dir.value());
135  file_util::AppendToPath(&src, L"*");
136
137  WIN32_FIND_DATA fd;
138  HANDLE fh = FindFirstFile(src.c_str(), &fd);
139  if (fh == INVALID_HANDLE_VALUE)
140    return false;
141
142  do {
143    std::wstring cur_file(fd.cFileName);
144    if (cur_file == L"." || cur_file == L"..")
145      continue;  // Skip these special entries.
146
147    FilePath cur_source_path = source_dir.Append(cur_file);
148    FilePath cur_dest_path = dest_dir.Append(cur_file);
149
150    if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
151      // Recursively copy a subdirectory. We stripped "." and ".." already.
152      if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) {
153        FindClose(fh);
154        return false;
155      }
156    } else {
157      // Copy the file.
158      if (!::CopyFile(cur_source_path.value().c_str(),
159                      cur_dest_path.value().c_str(), false)) {
160        FindClose(fh);
161        return false;
162      }
163
164      // We don't check for errors from this function, often, we are copying
165      // files that are in the repository, and they will have read-only set.
166      // This will prevent us from evicting from the cache, but these don't
167      // matter anyway.
168      EvictFileFromSystemCache(cur_dest_path);
169    }
170  } while (FindNextFile(fh, &fd));
171
172  FindClose(fh);
173  return true;
174}
175
176}  // namespace file_util
177