OLD | NEW |
| (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 | |
OLD | NEW |