Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: base/file.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/file.h ('k') | base/file_reader.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2003-2009 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 // ========================================================================
15 // File handling routines
16 //
17 // Possible performance improvement: make a subclass or alternate class
18 // that reads an entire file into memory and fulfills read/write requests
19 // in memory (or equivalently, use a memory mapped file). This can greatly
20 // improve performance if there are files we do a lot of read/write requests on.
21 //
22 // We generally are dealing with files that can be very large and want to
23 // minimize memory usage, so this is not high priority
24 //
25 // this has the beginnings of asynchronous access support
26 //
27 // Unfortunately, doing asynchronous reads with FILE_FLAG_OVERLAPPED buys us
28 // nothing because the system cache manager enforces serial requests.
29 //
30 // Hence, we need to also use FILE_FLAG_NO_BUFFERING. this has a number of
31 // constraints:
32 // - file read/write position must be aligned on multiples of the disk sector
33 // size
34 // - file read/write length must be a multiple of the sector size
35 // - read/write buffer must be aligned on multiples of the disk sector size
36 //
37 // In particular, this means that we cannot write 8 bytes, for example, because
38 // we have to write an entire sector.
39 //
40 // Currently, the implementation only supports enough to do some simple read
41 // tests
42 //
43 // The general idea is code that wants to to a sequence of asynchronous actions
44 // will look like the following, for an example of reading multiple event
45 // records asynchronously:
46 //
47 // uint32 async_id = File::GetNextAsyncId()
48 // while (!done) {
49 // for (everything_to_do, e.g., for each event to read) {
50 // call File::Read to read items needed;
51 // returns TR_E_FILE_ASYNC_PENDING if queued; or returns data if done
52 // process the item (e.g., event) if desired
53 // }
54 // call some routine to process pending completions; initiate delayed action
55 // }
56 // call some cleanup routine
57
58 #include "omaha/base/file.h"
59 #include <algorithm>
60 #include "base/scoped_ptr.h"
61 #include "omaha/base/app_util.h"
62 #include "omaha/base/const_config.h"
63 #include "omaha/base/debug.h"
64 #include "omaha/base/error.h"
65 #include "omaha/base/logging.h"
66 #include "omaha/base/path.h"
67 #include "omaha/base/reg_key.h"
68 #include "omaha/base/scoped_any.h"
69 #include "omaha/base/scoped_ptr_address.h"
70 #include "omaha/base/string.h"
71 #include "omaha/base/system.h"
72 #include "omaha/base/timer.h"
73 #include "omaha/base/utils.h"
74
75 namespace omaha {
76
77 // Constants
78 const uint32 kZeroSize = 4096; // Buffer size used for clearing data in a file.
79
80 // The moves-pending-reboot is a MULTISZ registry key in the HKLM part of the
81 // registry.
82 static const TCHAR* kSessionManagerKey =
83 _T("HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
84 static const TCHAR* kPendingFileRenameOps = _T("PendingFileRenameOperations");
85
86 File::File()
87 : handle_(INVALID_HANDLE_VALUE), read_only_(false), sequence_id_(0) {
88 }
89
90 File::~File() {
91 if (handle_ != INVALID_HANDLE_VALUE) {
92 VERIFY1(SUCCEEDED(Close()));
93 }
94 }
95
96 // open for reading only if write == false, otherwise both reading and writing
97 // allow asynchronous operations if async == true. Use this function when you
98 // need exclusive access to the file.
99 HRESULT File::Open(const TCHAR* file_name, bool write, bool async) {
100 return OpenShareMode(file_name, write, async, 0);
101 }
102
103 // Allows specifying a sharing mode such as FILE_SHARE_READ. Otherwise,
104 // this is identical to File::Open().
105 HRESULT File::OpenShareMode(const TCHAR* file_name,
106 bool write,
107 bool async,
108 DWORD share_mode) {
109 ASSERT1(file_name && *file_name);
110 ASSERT1(handle_ == INVALID_HANDLE_VALUE);
111 VERIFY1(!async);
112
113 file_name_ = file_name;
114
115 // there are restrictions on what we can do if using FILE_FLAG_NO_BUFFERING
116 // if (!buffer) { flags |= FILE_FLAG_NO_BUFFERING; }
117 // FILE_FLAG_WRITE_THROUGH
118 // how efficient is NTFS encryption? FILE_ATTRIBUTE_ENCRYPTED
119 // FILE_ATTRIBUTE_TEMPORARY
120 // FILE_FLAG_RANDOM_ACCESS
121 // FILE_FLAG_SEQUENTIAL_SCAN
122
123 handle_ = ::CreateFile(file_name,
124 write ? (FILE_WRITE_DATA |
125 FILE_WRITE_ATTRIBUTES |
126 FILE_READ_DATA) : FILE_READ_DATA,
127 share_mode,
128 NULL,
129 write ? OPEN_ALWAYS : OPEN_EXISTING,
130 FILE_FLAG_RANDOM_ACCESS,
131 NULL);
132
133 if (handle_ == INVALID_HANDLE_VALUE) {
134 HRESULT hr = HRESULTFromLastError();
135 UTIL_LOG(LEVEL_ERROR,
136 (_T("[File::OpenShareMode - CreateFile failed][%s][%d][%d][0x%x]"),
137 file_name, write, async, hr));
138 return hr;
139 }
140
141 // This attribute is not supported directly by the CreateFile function.
142 if (write &&
143 !::SetFileAttributes(file_name, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) {
144 HRESULT hr = HRESULTFromLastError();
145 UTIL_LOG(LEVEL_ERROR,
146 (_T("[File::OpenShareMode - SetFileAttributes failed][0x%x]"), hr));
147 return hr;
148 }
149
150 read_only_ = !write;
151 pos_ = 0;
152 return S_OK;
153 }
154
155 // The path must not be enclosed in quotes. This is the Windows standard.
156 // ::GetFileAttributesEx() returns ERROR_INVALID_NAME for quoted paths.
157 bool File::Exists(const TCHAR* file_name) {
158 ASSERT1(file_name && *file_name);
159 ASSERT1(lstrlen(file_name) > 0);
160
161 // NOTE: This is the fastest implementation I found. The results were:
162 // CreateFile 1783739 avg ticks/call
163 // FindFirstFile 634148 avg ticks/call
164 // GetFileAttributes 428714 avg ticks/call
165 // GetFileAttributesEx 396324 avg ticks/call
166 WIN32_FILE_ATTRIBUTE_DATA attrs = {0};
167 return 0 != ::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs);
168 }
169
170 bool File::IsDirectory(const TCHAR* file_name) {
171 ASSERT1(file_name && *file_name);
172
173 WIN32_FILE_ATTRIBUTE_DATA attrs;
174 SetZero(attrs);
175 if (!::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs)) {
176 UTIL_LOG(LEVEL_ERROR,
177 (_T("[File::IsDirectory - GetFileAttributesEx failed][%s][0x%x]"),
178 file_name, HRESULTFromLastError()));
179 return false;
180 }
181
182 return (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
183 }
184
185 HRESULT File::GetWildcards(const TCHAR* dir,
186 const TCHAR* wildcard,
187 std::vector<CString>* matching_paths) {
188 ASSERT1(dir && *dir);
189 ASSERT1(wildcard && *wildcard);
190 ASSERT1(matching_paths);
191
192 matching_paths->clear();
193
194 // Make sure directory name ends with "\"
195 CString directory = String_MakeEndWith(dir, _T("\\"), false);
196
197 WIN32_FIND_DATA find_data;
198 SetZero(find_data);
199 scoped_hfind hfind(::FindFirstFile(directory + wildcard, &find_data));
200 if (!hfind) {
201 HRESULT hr = HRESULTFromLastError();
202 UTIL_LOG(L5, (_T("[File::GetWildcards - FindFirstFile failed][0x%x]"), hr));
203 return hr;
204 }
205 do {
206 if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL ||
207 !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
208 CString to_file(directory + find_data.cFileName);
209 matching_paths->push_back(to_file);
210 }
211 } while (::FindNextFile(get(hfind), &find_data));
212
213 HRESULT hr = HRESULTFromLastError();
214 if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) {
215 UTIL_LOG(LEVEL_ERROR,
216 (_T("[File::GetWildcards - FindNextFile failed][0x%x]"), hr));
217 return hr;
218 }
219 return S_OK;
220 }
221
222 // returns error if cannot remove
223 // returns success if removed or already removed
224 HRESULT File::Remove(const TCHAR* file_name) {
225 ASSERT1(file_name && *file_name);
226
227 if (!Exists(file_name)) {
228 return S_OK;
229 }
230
231 if (!::DeleteFile(file_name)) {
232 return HRESULTFromLastError();
233 }
234
235 return S_OK;
236 }
237
238 HRESULT File::CopyWildcards(const TCHAR* from_dir,
239 const TCHAR* to_dir,
240 const TCHAR* wildcard,
241 bool replace_existing_files) {
242 ASSERT1(from_dir && *from_dir);
243 ASSERT1(to_dir && *to_dir);
244 ASSERT1(wildcard && *wildcard);
245
246 // Make sure dir names end with a "\"
247 CString from_directory = String_MakeEndWith(from_dir, _T("\\"), false);
248 CString to_directory = String_MakeEndWith(to_dir, _T("\\"), false);
249
250 // Get full path to source files (which is a wildcard)
251 CString from_files(from_directory + wildcard);
252
253 // Run over all files that match wildcard
254 WIN32_FIND_DATA find_data;
255 SetZero(find_data);
256
257 scoped_hfind hfind(::FindFirstFile(from_files, &find_data));
258 if (!hfind) {
259 HRESULT hr = HRESULTFromLastError();
260 UTIL_LOG(LEVEL_ERROR,
261 (_T("[File::CopyWildcards - FindFirstFile failed][0x%x]"), hr));
262 return hr;
263 }
264 do {
265 // Copy files
266 if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL ||
267 !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
268 CString from_file(from_directory + find_data.cFileName);
269 CString to_file(to_directory + find_data.cFileName);
270
271 if (!replace_existing_files && Exists(to_file)) {
272 // Continue, since the caller has explicitly asked us to not replace an
273 // existing file
274 continue;
275 }
276
277 RET_IF_FAILED(Copy(from_file, to_file, replace_existing_files));
278 }
279 } while (::FindNextFile(get(hfind), &find_data));
280
281 HRESULT hr = HRESULTFromLastError();
282 if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) {
283 UTIL_LOG(LEVEL_ERROR,
284 (_T("[File::CopyWildcards - FindNextFile failed][0x%x]"), hr));
285 return hr;
286 }
287 return S_OK;
288 }
289
290 HRESULT File::CopyTree(const TCHAR* from_dir,
291 const TCHAR* to_dir,
292 bool replace_existing_files) {
293 ASSERT1(from_dir && *from_dir);
294 ASSERT1(to_dir && *to_dir);
295
296 UTIL_LOG(L3, (L"[File::CopyTree][from_dir %s][to_dir %s][replace %d]",
297 from_dir, to_dir, replace_existing_files));
298
299 // Make sure dir names end with a "\"
300 CString from_directory(String_MakeEndWith(from_dir, L"\\", false));
301 CString to_directory(String_MakeEndWith(to_dir, L"\\", false));
302
303 RET_IF_FAILED(CreateDir(to_directory, NULL));
304 RET_IF_FAILED(CopyWildcards(from_directory,
305 to_directory,
306 L"*.*",
307 replace_existing_files));
308
309 // Run over all directories
310 WIN32_FIND_DATA find_data;
311 SetZero(find_data);
312
313 CString from_files(from_directory);
314 from_files += _T("*.*");
315
316 scoped_hfind hfind(::FindFirstFile(from_files, &find_data));
317 if (!hfind) {
318 HRESULT hr = HRESULTFromLastError();
319 UTIL_LOG(LEVEL_ERROR,
320 (_T("[File::CopyTree - FindFirstFile failed][0x%x]"), hr));
321 return hr;
322 }
323 do {
324 // Copy files
325 if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 &&
326 String_StrNCmp(find_data.cFileName, L"..", 2, false) &&
327 String_StrNCmp(find_data.cFileName, L".", 2, false)) {
328 CString from_subdir(from_directory + find_data.cFileName);
329 CString to_subdir(to_directory + find_data.cFileName);
330 RET_IF_FAILED(CopyTree(from_subdir, to_subdir, replace_existing_files));
331 }
332 } while (::FindNextFile(get(hfind), &find_data));
333
334 HRESULT hr = HRESULTFromLastError();
335 if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) {
336 UTIL_LOG(LEVEL_ERROR,
337 (_T("[File::CopyTree - FindNextFile failed][0x%x]"), hr));
338 return hr;
339 }
340
341 return S_OK;
342 }
343
344 HRESULT File::Copy(const TCHAR* from,
345 const TCHAR* to,
346 bool replace_existing_file) {
347 ASSERT1(from && *from);
348 ASSERT1(to && *to);
349
350 if (!replace_existing_file && Exists(to)) {
351 // Return success, since the caller has explicitly asked us to not replace
352 // an existing file
353 return S_OK;
354 }
355
356 if (!::CopyFile(from, to, !replace_existing_file)) {
357 HRESULT hr = HRESULTFromLastError();
358 UTIL_LOG(LEVEL_ERROR, (_T("[File::Copy - CopyFile failed]")
359 _T("[from=%s][to=%s][replace=%u][0x%x]"),
360 from, to, replace_existing_file, hr));
361 return hr;
362 }
363
364 return S_OK;
365 }
366
367 // TODO(omaha): Combine common code in Move/MoveAfterReboot
368 HRESULT File::Move(const TCHAR* from,
369 const TCHAR* to,
370 bool replace_existing_file) {
371 ASSERT1(from && *from);
372 ASSERT1(to && *to);
373
374 DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH;
375 if (replace_existing_file) {
376 flags |= MOVEFILE_REPLACE_EXISTING;
377 }
378
379 if (!::MoveFileEx(from, to, flags)) {
380 HRESULT hr = HRESULTFromLastError();
381 UTIL_LOG(LEVEL_ERROR,
382 (_T("[File::Move - MoveFileEx failed]")
383 _T("[from=%s][to=%s][replace=%u][0x%x]"),
384 from, to, replace_existing_file, hr));
385 return hr;
386 }
387
388 return S_OK;
389 }
390
391 // DeleteAfterReboot tries to delete the files by either moving them to the TEMP
392 // directory and deleting them on reboot, or if that fails, by trying to delete
393 // them in-place on reboot
394 HRESULT File::DeleteAfterReboot(const TCHAR* from) {
395 ASSERT1(from && *from);
396
397 if (File::Exists(from)) {
398 HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
399 CString from_temp;
400
401 // No point in moving into TEMP if we're already there
402 if (!String_StartsWith(from, app_util::GetTempDir(), true)) {
403 // Try to move to the TEMP directory first
404 CString temp_dir(String_MakeEndWith(app_util::GetTempDir(),
405 _T("\\"),
406 false));
407 // Of the form "C:\\Windows\\Temp\\FROM.EXE1f4c0b7f"
408 from_temp.Format(_T("%s%s%x"),
409 temp_dir,
410 GetFileFromPath(from),
411 ::GetTickCount());
412
413 hr = File::Move(from, from_temp, true);
414 UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - move %s to %s][0x%x]"),
415 from, from_temp, hr));
416 }
417
418 if (SUCCEEDED(hr)) {
419 UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - delete %s after reboot]"),
420 from_temp));
421 // Move temp file after reboot
422 if (FAILED(hr = File::MoveAfterReboot(from_temp, NULL))) {
423 UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
424 _T("[failed to delete after reboot %s][0x%x]"),
425 from_temp, hr));
426 }
427 } else {
428 // Move original file after reboot
429 if (FAILED(hr = File::MoveAfterReboot(from, NULL))) {
430 UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
431 _T("[failed to delete after reboot %s][0x%x]"),
432 from, hr));
433 }
434 }
435
436 return hr;
437 }
438
439 return S_OK;
440 }
441
442
443 HRESULT File::MoveAfterReboot(const TCHAR* from, const TCHAR* to) {
444 ASSERT1(from && *from);
445
446 if (!File::Exists(from)) {
447 // File/directory doesn't exist, should this return failure or success?
448 // Decision: Failure. Because the caller can decide if it is really
449 // failure or not in his specific case.
450 UTIL_LOG(LEVEL_WARNING, (_T("[File::MoveAfterReboot]")
451 _T("[file doesn't exist][from %s]"), from));
452 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
453 }
454
455 DWORD flags = MOVEFILE_DELAY_UNTIL_REBOOT;
456 if (!File::IsDirectory(from)) {
457 // This flag valid only for files
458 flags |= MOVEFILE_REPLACE_EXISTING;
459 }
460
461 if (!::MoveFileEx(from, to, flags)) {
462 HRESULT hr = HRESULTFromLastError();
463 UTIL_LOG(LEVEL_ERROR, (_T("[File::MoveAfterReboot]")
464 _T("[failed to MoveFileEx from '%s' to '%s'][0x%x]"),
465 from, to, hr));
466 return hr;
467 }
468
469 return S_OK;
470 }
471
472 // See if we have any moves pending a reboot. Return SUCCESS if we do
473 // not encounter errors (not finding a move is not an error). We need to
474 // also check the value of *found_ptr for whether we actually found a move.
475 // On return, *value_multisz_ptr is the value within
476 // "PendingFileRenameOperations", but with any moves for in_directory removed
477 // from it.
478 // The prefix_match boolean controls whether we do an exact match on
479 // in_directory, or remove all entries with the in_directory prefix.
480 // NOTE: If the only values found were our own keys, the whole
481 // PendingFileRenameOperations MULTISZ needs to be deleted.
482 // This is signified by a returned *value_size_chars_ptr of 0.
483 HRESULT File::GetPendingRenamesValueMinusDir(const TCHAR* in_directory,
484 bool prefix_match,
485 TCHAR** value_multisz_ptr,
486 DWORD* value_size_chars_ptr,
487 bool* found_ptr) {
488 ASSERT1(in_directory && *in_directory);
489
490 // Convert to references for easier-to-read-code:
491 TCHAR*& value_multisz = *value_multisz_ptr;
492 DWORD& value_size_chars = *value_size_chars_ptr;
493 bool& found = *found_ptr;
494
495 // Initialize [out] parameters
496 value_multisz = NULL;
497 value_size_chars = 0;
498 found = false;
499
500 // Locals mirroring the [out] parameters.
501 // We will only set the corresponding [out] parameters when we have something
502 // meaningful to return to the caller
503 scoped_array<TCHAR> value_multisz_local;
504 DWORD value_size_chars_local = 0;
505
506 DWORD value_size_bytes = 0;
507 // Get the current value of the key
508 // If the Key is missing, that's totally acceptable.
509 RET_IF_FALSE(
510 RegKey::HasValue(kSessionManagerKey, kPendingFileRenameOps) &&
511 SUCCEEDED(RegKey::GetValue(kSessionManagerKey,
512 kPendingFileRenameOps,
513 reinterpret_cast<byte**>(&value_multisz_local),
514 &value_size_bytes)),
515 S_OK);
516
517 ASSERT1(value_multisz_local.get() || value_size_bytes == 0);
518 UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
519 _T("[read multisz %d bytes]"),
520 value_size_bytes));
521 RET_IF_FALSE(value_size_bytes > 0, S_OK);
522
523 // The size should always be aligned to a TCHAR boundary, otherwise the key
524 // is corrupted.
525 ASSERT1((value_size_bytes % sizeof(TCHAR)) == 0);
526 RET_IF_FALSE((value_size_bytes % sizeof(TCHAR)) == 0,
527 HRESULT_FROM_WIN32(ERROR_BADKEY));
528 // Valid size, so convert to TCHARs:
529 value_size_chars_local = value_size_bytes / sizeof(TCHAR);
530
531 // Buffer must terminate with two nulls
532 ASSERT(value_size_chars_local >= 2 &&
533 !value_multisz_local[value_size_chars_local - 1] &&
534 !value_multisz_local[value_size_chars_local - 2],
535 (_T("buffer must terminate with two nulls")));
536 RET_IF_FALSE(value_size_chars_local >= 2 &&
537 !value_multisz_local[value_size_chars_local - 1] &&
538 !value_multisz_local[value_size_chars_local - 2],
539 HRESULT_FROM_WIN32(ERROR_BADKEY));
540 // Mark the end of the string.
541 // multisz_end will point at the character past end of buffer:
542 TCHAR* multisz_end = value_multisz_local.get() + value_size_chars_local;
543
544 // We're looking for \??\C:\... The \??\ was
545 // added by the OS to the directory name we specified.
546 CString from_dir(_T("\\??\\"));
547 from_dir += in_directory;
548 DWORD from_dir_len = from_dir.GetLength();
549
550 // A MULTISZ is a list of null terminated strings, terminated by a double
551 // null. We keep two pointers marching along the string in parallel.
552 TCHAR* str_read = value_multisz_local.get();
553 TCHAR* str_write = str_read;
554
555 while ((str_read < multisz_end) && *str_read) {
556 size_t str_len = ::lstrlen(str_read);
557 // A FALSE here indicates a corrupt PendingFileRenameOperations
558 RET_IF_FALSE((str_read + str_len + 1) < multisz_end,
559 HRESULT_FROM_WIN32(ERROR_BADKEY));
560 if (0 == String_StrNCmp(str_read,
561 from_dir,
562 from_dir_len + (prefix_match ? 0 : 1),
563 true)) {
564 // String matches, we want to remove this string, so advance only the
565 // read pointer - past this string and the replacement string.
566 UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
567 _T("[skips past match '%s']"),
568 str_read));
569 str_read += str_len + 1;
570 str_read += ::lstrlen(str_read) + 1;
571 continue;
572 }
573 // String doesn't match, we want to keep it.
574 if (str_read != str_write) {
575 // Here we're not in sync in the buffer, we've got to move two
576 // strings down.
577 UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
578 _T("[copying some other deletion][%s][%s]"),
579 str_read, str_read + ::lstrlen(str_read) + 1));
580 ASSERT1(str_write < str_read);
581 String_StrNCpy(str_write, str_read, str_len+1);
582 str_read += str_len + 1;
583 str_write += str_len + 1;
584 str_len = ::lstrlen(str_read);
585 String_StrNCpy(str_write, str_read, str_len+1);
586 str_read += str_len + 1;
587 str_write += str_len + 1;
588 } else {
589 // We're in sync in the buffer, advance both pointers past two strings
590 UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
591 _T("[skipping past some other deletion][%s][%s]"),
592 str_read, str_read + ::lstrlen(str_read) + 1));
593 str_read += str_len + 1;
594 str_read += ::lstrlen(str_read) + 1;
595 str_write = str_read;
596 }
597 }
598
599 // A FALSE here indicates a corrupt PendingFileRenameOperations
600 RET_IF_FALSE(str_read < multisz_end,
601 HRESULT_FROM_WIN32(ERROR_BADKEY));
602
603 if (str_read != str_write) {
604 // We found some values
605 found = true;
606
607 if (str_write == value_multisz_local.get()) {
608 // The only values were our own keys,
609 // and the whole PendingFileRenameOperations
610 // value needs to be deleted. We do not populate
611 // value_size_chars or value_multisz in this case.
612 ASSERT1(!value_size_chars);
613 ASSERT1(!value_multisz);
614 } else {
615 // The last string should have a NULL terminator:
616 ASSERT1(str_write[-1] == '\0');
617 RET_IF_FALSE(str_write[-1] == '\0',
618 HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
619 // a REG_MULTI_SZ needs to be terminated with an extra NULL.
620 *str_write = '\0';
621 ++str_write;
622
623 // Populate value_size_chars and value_multisz in this case.
624 value_multisz = value_multisz_local.release();
625 value_size_chars = str_write - value_multisz;
626 }
627 }
628
629 return S_OK;
630 }
631
632 // Remove any moves pending a reboot from the PendingFileRenameOperations
633 // in the registry.
634 // The prefix_match boolean controls whether we do an exact match on
635 // in_directory, or remove all entries with the in_directory prefix.
636 HRESULT File::RemoveFromMovesPendingReboot(const TCHAR* in_directory,
637 bool prefix_match) {
638 ASSERT1(in_directory && *in_directory);
639
640 bool found = false;
641 // scoped_array will free the value_multisz buffer on stack unwind:
642 scoped_array<TCHAR> value_multisz;
643 DWORD value_size_chars = 0;
644 HRESULT hr = GetPendingRenamesValueMinusDir(in_directory,
645 prefix_match,
646 address(value_multisz),
647 &value_size_chars,
648 &found);
649 if (SUCCEEDED(hr) && found) {
650 if (value_multisz.get() == NULL) {
651 // There's no point in writing an empty value_multisz.
652 // Let's delete the PendingFileRenameOperations value
653 UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]")
654 _T("[deleting PendingFileRenameOperations value]")));
655 RET_IF_FAILED(RegKey::DeleteValue(kSessionManagerKey,
656 kPendingFileRenameOps));
657 } else {
658 // Let's write the modified value_multisz into the
659 // PendingFileRenameOperations value
660 UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]")
661 _T("[rewriting multisz %d bytes]"),
662 value_size_chars * sizeof(TCHAR)));
663 RET_IF_FAILED(RegKey::SetValueMultiSZ(
664 kSessionManagerKey,
665 kPendingFileRenameOps,
666 reinterpret_cast<byte*>(value_multisz.get()),
667 value_size_chars * sizeof(TCHAR)));
668 }
669 }
670
671 // Failure of GetPendingRenamesValueMinusDir() may indicate something
672 // seriously wrong with the system. Propogate error.
673 return hr;
674 }
675
676 // Did the user try to uninstall a previous install of the same version, and
677 // we couldn't clean up without a reboot?
678 // We check if there are any moves pending a reboot from the
679 // PendingFileRenameOperations in the registry.
680 // The prefix_match boolean controls whether we do an exact match on
681 // in_directory, or check all entries with the in_directory prefix.
682 bool File::AreMovesPendingReboot(const TCHAR* in_directory, bool prefix_match) {
683 ASSERT1(in_directory && *in_directory);
684
685 bool found = false;
686 // scoped_array will free the value_multisz buffer on stack unwind:
687 scoped_array<TCHAR> value_multisz;
688 DWORD value_size_chars = 0;
689
690 if (SUCCEEDED(GetPendingRenamesValueMinusDir(in_directory,
691 prefix_match,
692 address(value_multisz),
693 &value_size_chars,
694 &found)) && found) {
695 return true;
696 }
697
698 return false;
699 }
700
701 HRESULT File::GetFileTime(const TCHAR* file_name,
702 FILETIME* created,
703 FILETIME* accessed,
704 FILETIME* modified) {
705 ASSERT1(file_name && *file_name);
706
707 bool is_dir = IsDirectory(file_name);
708 // To obtain a handle to a directory, call the CreateFile function with
709 // the FILE_FLAG_BACKUP_SEMANTICS flag
710 scoped_hfile file_handle(
711 ::CreateFile(file_name,
712 FILE_READ_DATA,
713 FILE_SHARE_READ,
714 NULL,
715 OPEN_EXISTING,
716 is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL,
717 NULL));
718 HRESULT hr = S_OK;
719
720 if (!file_handle) {
721 hr = HRESULTFromLastError();
722 UTIL_LOG(LE, (_T("[File::GetFileTime]")
723 _T("[failed to open file][%s][0x%x]"), file_name, hr));
724 } else {
725 if (!::GetFileTime(get(file_handle), created, accessed, modified)) {
726 hr = HRESULTFromLastError();
727 UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]")
728 _T("[failed to get file time][%s][0x%x]"),
729 file_name, hr));
730 }
731 }
732
733 return hr;
734 }
735
736 HRESULT File::SetFileTime(const TCHAR* file_name,
737 const FILETIME* created,
738 const FILETIME* accessed,
739 const FILETIME* modified) {
740 ASSERT1(file_name && *file_name);
741
742 bool is_dir = IsDirectory(file_name);
743 // To obtain a handle to a directory, call the CreateFile function with
744 // the FILE_FLAG_BACKUP_SEMANTICS flag
745 scoped_hfile file_handle(
746 ::CreateFile(file_name,
747 FILE_WRITE_ATTRIBUTES,
748 FILE_SHARE_WRITE,
749 NULL,
750 OPEN_EXISTING,
751 is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL,
752 NULL));
753 HRESULT hr = S_OK;
754
755 if (!file_handle) {
756 hr = HRESULTFromLastError();
757 UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]")
758 _T("[failed to open file][%s][0x%x]"),
759 file_name, hr));
760 } else {
761 BOOL res = ::SetFileTime(get(file_handle), created, accessed, modified);
762 if (!res) {
763 hr = HRESULTFromLastError();
764 UTIL_LOG(LEVEL_ERROR, (_T("[File::SetFileTime]")
765 _T("[failed to set file time][%s][0x%x]"),
766 file_name, hr));
767 }
768 }
769
770 return hr;
771 }
772
773 HRESULT File::Sync() {
774 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
775
776 if (!::FlushFileBuffers(handle_)) {
777 HRESULT hr = HRESULTFromLastError();
778 UTIL_LOG(LEVEL_ERROR, (_T("[File::Sync]")
779 _T("[FlushFileBuffers failed][%s][0x%x]"),
780 file_name_, hr));
781 return hr;
782 }
783 return S_OK;
784 }
785
786 HRESULT File::SeekToBegin() {
787 return SeekFromBegin(0);
788 }
789
790 HRESULT File::SeekFromBegin(uint32 n) {
791 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
792
793 if (::SetFilePointer(handle_, n, NULL, FILE_BEGIN) ==
794 INVALID_SET_FILE_POINTER) {
795 HRESULT hr = HRESULTFromLastError();
796 UTIL_LOG(LEVEL_ERROR, (_T("[File::SeekFromBegin]")
797 _T("[SetFilePointer failed][%s][0x%x]"),
798 file_name_, hr));
799 return hr;
800 }
801 pos_ = n;
802 return S_OK;
803 }
804
805 // read nLen bytes starting at position n
806 // returns number of bytes read
807 //
808 // async operations:
809 //
810 // async_id - identifier of a sequence of async operations, 0 for synchronous
811 //
812 // if the async operation has not been initiated, we initiate it
813 // if it is in progress we do nothing
814 // if it has been completed we return the data
815 // does not delete async data entry
816 HRESULT File::ReadAt(const uint32 offset, byte* buf, const uint32 len,
817 const uint32, uint32* bytes_read) {
818 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
819 ASSERT1(buf);
820 ASSERT1(len); // reading 0 bytes is not valid (differs from CRT API)
821
822 RET_IF_FAILED(SeekFromBegin(offset));
823
824 DWORD read = 0;
825 if (!::ReadFile(handle_, buf, len, &read, NULL)) {
826 HRESULT hr = HRESULTFromLastError();
827 UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]")
828 _T("[ReadFile failed][%s][0x%x]"), file_name_, hr));
829 return hr;
830 }
831
832 if (bytes_read) {
833 *bytes_read = read;
834 }
835
836 return (read == len) ? S_OK : E_FAIL;
837 }
838
839
840 // reads up to max_len bytes from the start of the file
841 // not considered an error if there are less than max_len bytes read
842 // returns number of bytes read
843 HRESULT File::ReadFromStartOfFile(const uint32 max_len,
844 byte* buf,
845 uint32* bytes_read) {
846 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
847 ASSERT1(buf);
848 ASSERT1(max_len);
849
850 RET_IF_FAILED(SeekFromBegin(0));
851
852 uint32 file_len = 0;
853 RET_IF_FAILED(GetLength(&file_len));
854
855 if (!file_len) {
856 if (bytes_read) {
857 *bytes_read = 0;
858 }
859 return S_OK;
860 }
861
862 uint32 len = max_len;
863 if (len > file_len) {
864 len = static_cast<uint32>(file_len);
865 }
866
867 return Read(len, buf, bytes_read);
868 }
869
870 // this function handles lines terminated with LF or CRLF
871 // all CR characters are removed from each line, and LF is assumed
872 // to be the end of line and is removed
873 HRESULT File::ReadLineAnsi(uint32 max_len, char* line, uint32* len) {
874 ASSERT1(line);
875 ASSERT1(max_len);
876
877 char c = 0;
878 uint32 len_read = 0;
879 uint32 total_len = 0;
880
881 while (SUCCEEDED(Read(1, reinterpret_cast<byte *>(&c), &len_read)) &&
882 len_read &&
883 c != '\n') {
884 if (total_len < max_len - 1 && c != '\r') {
885 line[total_len++] = c;
886 }
887 }
888
889 ASSERT1(total_len < max_len);
890 line[total_len] = '\0';
891
892 if (len) {
893 *len = total_len;
894 }
895
896 return (len_read || total_len) ? S_OK : E_FAIL;
897 }
898
899 // used by ReadFromStartOfFile and ReadLineAnsi; not reading all requested bytes
900 // is not considered fatal
901 HRESULT File::Read(const uint32 len, byte* buf, uint32* bytes_read) {
902 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
903 ASSERT1(buf);
904 ASSERT1(len);
905
906 DWORD read = 0;
907 if (!::ReadFile(handle_, buf, len, &read, NULL)) {
908 HRESULT hr = HRESULTFromLastError();
909 UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]")
910 _T("[ReadFile failed][%s][0x%x]"),
911 file_name_, hr));
912 return hr;
913 }
914
915 if (bytes_read) {
916 *bytes_read = read;
917 }
918
919 return S_OK;
920 }
921
922
923 // returns number of bytes written
924 HRESULT File::WriteAt(const uint32 offset,
925 const byte* buf,
926 const uint32 len,
927 uint32,
928 uint32* bytes_written) {
929 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
930 ASSERT1(!read_only_);
931 ASSERT1(buf);
932 ASSERT1(len);
933
934 RET_IF_FAILED(SeekFromBegin(offset));
935
936 return Write(buf, len, bytes_written);
937 }
938
939
940 // write buffer n times
941 HRESULT File::WriteN(const byte* buf,
942 const uint32 len,
943 const uint32 n,
944 uint32* bytes_written) {
945 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
946 ASSERT1(!read_only_);
947 ASSERT1(buf);
948 ASSERT1(len);
949 ASSERT1(n);
950
951 HRESULT hr = S_OK;
952
953 uint32 total_wrote = 0;
954
955 byte* temp_buf = const_cast<byte*>(buf);
956
957 scoped_array<byte> encrypt_buf;
958
959 uint32 to_go = n;
960 while (to_go) {
961 uint32 wrote = 0;
962 hr = Write(temp_buf, len, &wrote);
963 if (FAILED(hr)) {
964 if (bytes_written) {
965 *bytes_written = total_wrote;
966 }
967 return hr;
968 }
969
970 total_wrote += wrote;
971 to_go--;
972 }
973
974 if (bytes_written) {
975 *bytes_written = total_wrote;
976 }
977 return hr;
978 }
979
980 // returns number of bytes written
981 HRESULT File::Write(const byte* buf, const uint32 len, uint32* bytes_written) {
982 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
983 ASSERT1(!read_only_);
984 ASSERT1(buf);
985 ASSERT1(len); // writing 0 bytes is not valid (differs from CRT API)
986
987 byte* b = const_cast<byte*>(buf);
988
989 scoped_array<byte> encrypt_buf;
990
991 DWORD wrote = 0;
992 if (!::WriteFile(handle_, b, len, &wrote, NULL)) {
993 HRESULT hr = HRESULTFromLastError();
994 UTIL_LOG(LEVEL_ERROR, (_T("[File::Write]")
995 _T("[WriteFile failed][%s][0x%x]"),
996 file_name_, hr));
997 return hr;
998 }
999
1000 if (bytes_written) {
1001 *bytes_written = wrote;
1002 }
1003 pos_ += wrote;
1004
1005 return (wrote == len) ? S_OK : E_FAIL;
1006 }
1007
1008 HRESULT File::ClearAt(const uint32 offset,
1009 const uint32 len,
1010 uint32* bytes_written) {
1011 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1012 ASSERT1(!read_only_);
1013 ASSERT1(len);
1014
1015 byte zero[kZeroSize] = {0};
1016 uint32 to_go = len;
1017 uint32 written = 0;
1018 uint32 pos = offset;
1019
1020 while (to_go) {
1021 uint32 wrote = 0;
1022 uint32 write_len = std::min(to_go, kZeroSize);
1023 RET_IF_FAILED(WriteAt(pos, zero, write_len, 0, &wrote));
1024
1025 if (wrote != write_len) {
1026 return E_FAIL;
1027 }
1028 pos += wrote;
1029 written += wrote;
1030 to_go -= write_len;
1031 }
1032
1033 if (bytes_written) {
1034 *bytes_written = written;
1035 }
1036 return S_OK;
1037 }
1038
1039 // returns true on failure
1040 // zeros new data if zero_data == true
1041 HRESULT File::SetLength(const uint32 n, bool zero_data) {
1042 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1043 ASSERT1(!read_only_);
1044 ASSERT1(n <= kMaxFileSize);
1045
1046 HRESULT hr = S_OK;
1047
1048 uint32 len = 0;
1049 VERIFY1(SUCCEEDED(GetLength(&len)));
1050
1051 if (len == n) {
1052 return S_OK;
1053 }
1054
1055 // according to the documentation, the
1056 // new space will not be initialized
1057 if (n > len) {
1058 if (zero_data) {
1059 uint32 bytes_written = 0;
1060 RET_IF_FAILED(ClearAt(len, n - len, &bytes_written));
1061 if (bytes_written != n - len) {
1062 return E_FAIL;
1063 }
1064 } else {
1065 byte zero = 0;
1066 uint32 bytes_written = 0;
1067 RET_IF_FAILED(WriteAt(n - 1, &zero, 1, 0, &bytes_written));
1068 if (bytes_written != 1) {
1069 return E_FAIL;
1070 }
1071 }
1072 } else {
1073 SeekFromBegin(n);
1074 SetEndOfFile(handle_);
1075 }
1076
1077 ASSERT1(SUCCEEDED(GetLength(&len)) && len == n);
1078
1079 return S_OK;
1080 }
1081
1082 HRESULT File::ExtendInBlocks(const uint32 block_size, uint32 size_needed,
1083 uint32* new_size, bool clear_new_space) {
1084 ASSERT1(new_size);
1085
1086 *new_size = size_needed;
1087
1088 if (*new_size % block_size) {
1089 *new_size += block_size - (*new_size % block_size);
1090 }
1091
1092 // is zero_data needed? may reduce fragmentation by causing the block to
1093 // be written
1094 return SetLength(*new_size, clear_new_space);
1095 }
1096
1097 // returns S_OK on success
1098 HRESULT File::GetLength(uint32* length) {
1099 ASSERT1(length);
1100 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1101
1102 DWORD len = GetFileSize(handle_, NULL);
1103 if (len == INVALID_FILE_SIZE) {
1104 ASSERT(false, (_T("cannot get file length")));
1105 return E_FAIL;
1106 }
1107 *length = len;
1108 return S_OK;
1109 }
1110
1111 HRESULT File::Touch() {
1112 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1113
1114 FILETIME file_time;
1115 SetZero(file_time);
1116
1117 ::GetSystemTimeAsFileTime(&file_time);
1118
1119 if (!::SetFileTime(handle_, NULL, NULL, &file_time)) {
1120 return HRESULTFromLastError();
1121 }
1122 return S_OK;
1123 }
1124
1125 HRESULT File::Close() {
1126 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1127
1128 HRESULT hr = S_OK;
1129 if (!::CloseHandle(handle_)) {
1130 hr = HRESULTFromLastError();
1131 }
1132
1133 handle_ = INVALID_HANDLE_VALUE;
1134
1135 return hr;
1136 }
1137
1138 // this is just for consistency with other classes; does not do anything
1139 HRESULT File::Reload(uint32* number_errors) {
1140 ASSERT1(number_errors);
1141 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1142
1143 *number_errors = 0;
1144 return S_OK;
1145 }
1146
1147 // this is just for consistency with other classes; does not do anything
1148 HRESULT File::Verify(uint32* number_errors) {
1149 ASSERT1(number_errors);
1150 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1151
1152 *number_errors = 0;
1153 return S_OK;
1154 }
1155
1156 // this is just for consistency with other classes; does not do anything
1157 HRESULT File::Dump() {
1158 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1159 return S_OK;
1160 }
1161
1162 // for consistency with other classes
1163 HRESULT File::GetSizeOnDisk(uint64* size_on_disk) {
1164 ASSERT1(size_on_disk);
1165 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1166
1167 uint32 len = 0;
1168 RET_IF_FAILED(GetLength(&len));
1169
1170 *size_on_disk = len;
1171 return S_OK;
1172 }
1173
1174 // for consistency with other classes
1175 HRESULT File::GetReloadDiskSpaceNeeded(uint64* bytes_needed) {
1176 ASSERT1(bytes_needed);
1177 ASSERT1(handle_ != INVALID_HANDLE_VALUE);
1178
1179 uint32 len = 0;
1180 RET_IF_FAILED(GetLength(&len));
1181
1182 *bytes_needed = len;
1183 return S_OK;
1184 }
1185
1186 // Get the file size
1187 HRESULT File::GetFileSizeUnopen(const TCHAR* filename, uint32* out_size) {
1188 ASSERT1(filename);
1189 ASSERT1(out_size);
1190
1191 WIN32_FILE_ATTRIBUTE_DATA data;
1192 SetZero(data);
1193
1194 if (!::GetFileAttributesEx(filename, ::GetFileExInfoStandard, &data)) {
1195 return HRESULTFromLastError();
1196 }
1197
1198 *out_size = data.nFileSizeLow;
1199
1200 return S_OK;
1201 }
1202
1203 // Get the last time with a file was written to, and the size
1204 HRESULT File::GetLastWriteTimeAndSize(const TCHAR* file_path,
1205 SYSTEMTIME* out_time,
1206 unsigned int* out_size) {
1207 ASSERT1(file_path);
1208
1209 WIN32_FIND_DATA wfd;
1210 SetZero(wfd);
1211
1212 HANDLE find = ::FindFirstFile(file_path, &wfd);
1213 if (find == INVALID_HANDLE_VALUE) {
1214 return HRESULTFromLastError();
1215 }
1216
1217 ::FindClose(find);
1218
1219 if (out_size) {
1220 *out_size = wfd.nFileSizeLow;
1221 }
1222
1223 if (out_time) {
1224 // If created time is newer than write time, then use that instead
1225 // [it tends to be more relevant when copying files around]
1226 FILETIME* latest_time = NULL;
1227 if (::CompareFileTime(&wfd.ftCreationTime, &wfd.ftLastWriteTime) > 0) {
1228 latest_time = &wfd.ftCreationTime;
1229 } else {
1230 latest_time = &wfd.ftLastWriteTime;
1231 }
1232
1233 if (!::FileTimeToSystemTime(latest_time, out_time)) {
1234 return HRESULTFromLastError();
1235 }
1236 }
1237
1238 return S_OK;
1239 }
1240
1241 bool File::AreFilesIdentical(const TCHAR* filename1, const TCHAR* filename2) {
1242 UTIL_LOG(L4, (_T("[File::AreFilesIdentical][%s][%s]"), filename1, filename2));
1243
1244 uint32 file_size1 = 0;
1245 HRESULT hr = File::GetFileSizeUnopen(filename1, &file_size1);
1246 if (FAILED(hr)) {
1247 UTIL_LOG(LE, (_T("[GetFileSizeUnopen failed file_size1][0x%x]"), hr));
1248 return false;
1249 }
1250
1251 uint32 file_size2 = 0;
1252 hr = File::GetFileSizeUnopen(filename2, &file_size2);
1253 if (FAILED(hr)) {
1254 UTIL_LOG(LE, (_T("[GetFileSizeUnopen failed file_size2][0x%x]"), hr));
1255 return false;
1256 }
1257
1258 if (file_size1 != file_size2) {
1259 UTIL_LOG(L3, (_T("[file_size1 != file_size2][%d][%d]"),
1260 file_size1, file_size2));
1261 return false;
1262 }
1263
1264 File file1;
1265 hr = file1.OpenShareMode(filename1, false, false, FILE_SHARE_READ);
1266 if (FAILED(hr)) {
1267 UTIL_LOG(LE, (_T("[file1.OpenShareMode failed][0x%x]"), hr));
1268 return false;
1269 }
1270
1271 File file2;
1272 hr = file2.OpenShareMode(filename2, false, false, FILE_SHARE_READ);
1273 if (FAILED(hr)) {
1274 UTIL_LOG(LE, (_T("[file2.OpenShareMode failed][0x%x]"), hr));
1275 return false;
1276 }
1277
1278 static const uint32 kBufferSize = 0x10000;
1279 std::vector<uint8> buffer1(kBufferSize);
1280 std::vector<uint8> buffer2(kBufferSize);
1281 uint32 bytes_left = file_size1;
1282
1283 while (bytes_left > 0) {
1284 uint32 bytes_to_read = std::min(bytes_left, kBufferSize);
1285 uint32 bytes_read1 = 0;
1286 uint32 bytes_read2 = 0;
1287
1288 hr = file1.Read(bytes_to_read, &buffer1.front(), &bytes_read1);
1289 if (FAILED(hr)) {
1290 UTIL_LOG(LE, (_T("[file1.Read failed][%d][%d][0x%x]"),
1291 bytes_left, bytes_to_read, hr));
1292 return false;
1293 }
1294
1295 hr = file2.Read(bytes_to_read, &buffer2.front(), &bytes_read2);
1296 if (FAILED(hr)) {
1297 UTIL_LOG(LE, (_T("[file2.Read failed][%d][%d][0x%x]"),
1298 bytes_left, bytes_to_read, hr));
1299 return false;
1300 }
1301
1302 if (bytes_to_read != bytes_read1 || bytes_to_read != bytes_read2) {
1303 UTIL_LOG(LE,
1304 (_T("[bytes_to_read != bytes_read1 || bytes_to_read != bytes_read2]")
1305 _T("[%d][%d][%d]"), bytes_to_read, bytes_read1, bytes_read2));
1306 return false;
1307 }
1308
1309 if (memcmp(&buffer1.front(), &buffer2.front(), bytes_read1) != 0) {
1310 UTIL_LOG(L3, (_T("[memcmp failed][%d][%d]"), bytes_left, bytes_read1));
1311 return false;
1312 }
1313
1314 if (bytes_left < bytes_to_read) {
1315 UTIL_LOG(LE, (_T("[bytes_left < bytes_to_read][%d][%d]"),
1316 bytes_left, bytes_to_read));
1317 return false;
1318 }
1319
1320 bytes_left -= bytes_to_read;
1321 }
1322
1323 return true;
1324 }
1325
1326 FileLock::FileLock() {
1327 }
1328
1329 FileLock::~FileLock() {
1330 Unlock();
1331 }
1332
1333 HRESULT FileLock::Lock(const TCHAR* file) {
1334 std::vector<CString> files;
1335 files.push_back(file);
1336 return Lock(files);
1337 }
1338
1339 HRESULT FileLock::Lock(const std::vector<CString>& files) {
1340 ASSERT1(!files.empty());
1341
1342 // Try to lock all files
1343 size_t curr_size = handles_.size();
1344 for (size_t i = 0; i < files.size(); ++i) {
1345 scoped_hfile handle(::CreateFile(files[i],
1346 GENERIC_READ,
1347 FILE_SHARE_READ,
1348 NULL,
1349 OPEN_EXISTING,
1350 FILE_ATTRIBUTE_NORMAL,
1351 NULL));
1352 if (!handle) {
1353 UTIL_LOG(LEVEL_ERROR,
1354 (_T("[FileLock::Lock - failed to lock file][%s][0x%x]"),
1355 files[i], HRESULTFromLastError()));
1356 break;
1357 }
1358 handles_.push_back(release(handle));
1359 }
1360
1361 // Cleanup if we fail to lock all the files
1362 if (curr_size + files.size() < handles_.size()) {
1363 for (size_t i = handles_.size() - 1; i >= curr_size; --i) {
1364 VERIFY(::CloseHandle(handles_[i]), (_T("")));
1365 handles_.pop_back();
1366 }
1367 return E_FAIL;
1368 }
1369
1370 return S_OK;
1371 }
1372
1373 HRESULT FileLock::Unlock() {
1374 for (size_t i = 0; i < handles_.size(); ++i) {
1375 VERIFY(::CloseHandle(handles_[i]), (_T("")));
1376 }
1377 handles_.clear();
1378 return S_OK;
1379 }
1380
1381
1382 // path_name: the directory to watch
1383 // watch_subtree: watch all subdirectory changes or
1384 // only immediate child values
1385 // notify_filter: See the documentation for FindFirstChangeNotification
1386 FileWatcher::FileWatcher(const TCHAR* path_name, bool watch_subtree,
1387 DWORD notify_filter)
1388 : path_name_(path_name),
1389 watch_subtree_(watch_subtree),
1390 notify_filter_(notify_filter) {
1391 ASSERT1(path_name && *path_name);
1392 UTIL_LOG(L3, (_T("[FileWatcher::FileWatcher][%s]"), path_name));
1393 }
1394
1395 // Get the event that is signaled on store changes.
1396 HANDLE FileWatcher::change_event() const {
1397 ASSERT(valid(change_event_), (_T("call FileWatcher::SetupEvent first")));
1398 return get(change_event_);
1399 }
1400
1401
1402 // Called to create/reset the event that gets signaled
1403 // any time the store changes. Access the created
1404 // event using change_event().
1405 HRESULT FileWatcher::EnsureEventSetup() {
1406 UTIL_LOG(L3, (_T("[FileWatcher::EnsureEventSetup]")));
1407 if (!valid(change_event_)) {
1408 reset(change_event_, ::FindFirstChangeNotification(path_name_,
1409 watch_subtree_,
1410 notify_filter_));
1411 if (!valid(change_event_)) {
1412 ASSERT(false, (_T("unable to get file change notification")));
1413 return E_FAIL;
1414 }
1415 // path name was only needed to set-up the event and now that is done....
1416 path_name_.Empty();
1417 return S_OK;
1418 }
1419
1420 // if the event is set-up and no changes have occurred,
1421 // then there is no need to re-setup the event.
1422 if (valid(change_event_) && !HasChangeOccurred()) {
1423 return NOERROR;
1424 }
1425
1426 return ::FindNextChangeNotification(get(change_event_)) ? S_OK : E_FAIL;
1427 }
1428
1429 } // namespace omaha
1430
OLDNEW
« no previous file with comments | « base/file.h ('k') | base/file_reader.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698