blob: 251d030f725ceaf50cc0cb1503937299f452447b [file] [log] [blame]
[email protected]1010f7d2008-08-06 16:29:441// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "base/file_util.h"
31
32#include <windows.h>
33#include <shellapi.h>
34#include <shlobj.h>
35#include <time.h>
36#include <string>
37
38#include "base/logging.h"
39#include "base/scoped_handle.h"
40#include "base/string_util.h"
41#include "base/win_util.h"
42
43namespace file_util {
44
[email protected]5af2edb92008-08-08 20:16:0845std::wstring GetDirectoryFromPath(const std::wstring& path) {
46 wchar_t path_buffer[MAX_PATH];
47 wchar_t* file_ptr = NULL;
48 if (GetFullPathName(path.c_str(), MAX_PATH, path_buffer, &file_ptr) == 0)
49 return L"";
50
51 std::wstring::size_type length =
52 file_ptr ? file_ptr - path_buffer : path.length();
53 std::wstring directory(path, 0, length);
54 TrimTrailingSeparator(&directory);
55 return directory;
56}
57
[email protected]1010f7d2008-08-06 16:29:4458int CountFilesCreatedAfter(const std::wstring& path,
59 const FILETIME& comparison_time) {
60 int file_count = 0;
61
62 WIN32_FIND_DATA find_file_data;
63 std::wstring filename_spec = path + L"\\*"; // All files in given dir
64 HANDLE find_handle = FindFirstFile(filename_spec.c_str(), &find_file_data);
65 if (find_handle != INVALID_HANDLE_VALUE) {
66 do {
67 // Don't count current or parent directories.
68 if ((wcscmp(find_file_data.cFileName, L"..") == 0) ||
69 (wcscmp(find_file_data.cFileName, L".") == 0))
70 continue;
71
72 long result = CompareFileTime(&find_file_data.ftCreationTime,
73 &comparison_time);
74 // File was created after or on comparison time
75 if ((result == 1) || (result == 0))
76 ++file_count;
77 } while (FindNextFile(find_handle, &find_file_data));
78 FindClose(find_handle);
79 }
80
81 return file_count;
82}
83
84bool Delete(const std::wstring& path, bool recursive) {
85 if (path.length() >= MAX_PATH)
86 return false;
87
88 // If we're not recursing use DeleteFile; it should be faster. DeleteFile
89 // fails if passed a directory though, which is why we fall through on
90 // failure to the SHFileOperation.
91 if (!recursive && DeleteFile(path.c_str()) != 0)
92 return true;
93
94 // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
95 // so we have to use wcscpy because wcscpy_s writes non-NULLs
96 // into the rest of the buffer.
97 wchar_t double_terminated_path[MAX_PATH + 1] = {0};
98#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
99 wcscpy(double_terminated_path, path.c_str());
100
101 SHFILEOPSTRUCT file_operation = {0};
102 file_operation.wFunc = FO_DELETE;
103 file_operation.pFrom = double_terminated_path;
104 file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
105 if (!recursive)
106 file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
107 return (SHFileOperation(&file_operation) == 0);
108}
109
110bool Move(const std::wstring& from_path, const std::wstring& to_path) {
111 // NOTE: I suspect we could support longer paths, but that would involve
112 // analyzing all our usage of files.
113 if (from_path.length() >= MAX_PATH || to_path.length() >= MAX_PATH)
114 return false;
115 return (MoveFileEx(from_path.c_str(), to_path.c_str(),
116 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0);
117}
118
119bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
120 // NOTE: I suspect we could support longer paths, but that would involve
121 // analyzing all our usage of files.
122 if (from_path.length() >= MAX_PATH || to_path.length() >= MAX_PATH)
123 return false;
124 return (::CopyFile(from_path.c_str(), to_path.c_str(), false) != 0);
125}
126
127bool ShellCopy(const std::wstring& from_path, const std::wstring& to_path,
128 bool recursive) {
129 // NOTE: I suspect we could support longer paths, but that would involve
130 // analyzing all our usage of files.
131 if (from_path.length() >= MAX_PATH || to_path.length() >= MAX_PATH)
132 return false;
133
134 // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
135 // so we have to use wcscpy because wcscpy_s writes non-NULLs
136 // into the rest of the buffer.
137 wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
138 wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
139#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
140 wcscpy(double_terminated_path_from, from_path.c_str());
141#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
142 wcscpy(double_terminated_path_to, to_path.c_str());
143
144 SHFILEOPSTRUCT file_operation = {0};
145 file_operation.wFunc = FO_COPY;
146 file_operation.pFrom = double_terminated_path_from;
147 file_operation.pTo = double_terminated_path_to;
148 file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION |
149 FOF_NOCONFIRMMKDIR;
150 if (!recursive)
151 file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
152
153 return (SHFileOperation(&file_operation) == 0);
154}
155
156bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
157 bool recursive) {
158 if (recursive)
159 return ShellCopy(from_path, to_path, true);
160
161 // Instead of creating a new directory, we copy the old one to include the
162 // security information of the folder as part of the copy.
163 if (!PathExists(to_path)) {
164 // Except that Vista fails to do that, and instead do a recursive copy if
165 // the target directory doesn't exist.
166 if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA)
167 CreateDirectory(to_path);
168 else
169 ShellCopy(from_path, to_path, false);
170 }
171
172 std::wstring directory(from_path);
173 AppendToPath(&directory, L"*.*");
174 return ShellCopy(directory, to_path, false);
175}
176
177bool PathExists(const std::wstring& path) {
178 return (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES);
179}
180
181bool PathIsWritable(const std::wstring& path) {
182 HANDLE dir =
183 CreateFile(path.c_str(), FILE_ADD_FILE,
184 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
185 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
186
187 if (dir == INVALID_HANDLE_VALUE)
188 return false;
189
190 CloseHandle(dir);
191 return true;
192}
193
194bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle,
195 LPSYSTEMTIME creation_time) {
196 if (!file_handle)
197 return false;
198
199 FILETIME utc_filetime;
200 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
201 return false;
202
203 FILETIME local_filetime;
204 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
205 return false;
206
207 return !!FileTimeToSystemTime(&local_filetime, creation_time);
208}
209
210bool GetFileCreationLocalTime(const std::wstring& filename,
211 LPSYSTEMTIME creation_time) {
212 ScopedHandle file_handle(
213 CreateFile(filename.c_str(), GENERIC_READ,
214 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
215 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
216 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
217}
218
219bool ResolveShortcut(std::wstring* path) {
220 HRESULT result;
221 IShellLink *shell = NULL;
222 bool is_resolved = false;
223
224 // Get pointer to the IShellLink interface
225 result = CoCreateInstance(CLSID_ShellLink, NULL,
226 CLSCTX_INPROC_SERVER, IID_IShellLink,
227 reinterpret_cast<LPVOID*>(&shell));
228 if (SUCCEEDED(result)) {
229 IPersistFile *persist = NULL;
230 // Query IShellLink for the IPersistFile interface
231 result = shell->QueryInterface(IID_IPersistFile,
232 reinterpret_cast<LPVOID*>(&persist));
233 if (SUCCEEDED(result)) {
234 WCHAR temp_path[MAX_PATH];
235 // Load the shell link
236 result = persist->Load(path->c_str(), STGM_READ);
237 if (SUCCEEDED(result)) {
238 // Try to find the target of a shortcut
239 result = shell->Resolve(0, SLR_NO_UI);
240 if (SUCCEEDED(result)) {
241 result = shell->GetPath(temp_path, MAX_PATH,
242 NULL, SLGP_UNCPRIORITY);
243 *path = temp_path;
244 is_resolved = true;
245 }
246 }
247 }
248 if (persist)
249 persist->Release();
250 }
251 if (shell)
252 shell->Release();
253
254 return is_resolved;
255}
256
257bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination,
258 const wchar_t *working_dir, const wchar_t *arguments,
259 const wchar_t *description, const wchar_t *icon,
260 int icon_index) {
261 IShellLink *i_shell_link = NULL;
262 IPersistFile *i_persist_file = NULL;
263
264 // Get pointer to the IShellLink interface
265 HRESULT result = CoCreateInstance(CLSID_ShellLink, NULL,
266 CLSCTX_INPROC_SERVER, IID_IShellLink,
267 reinterpret_cast<LPVOID*>(&i_shell_link));
268 if (FAILED(result))
269 return false;
270
271 // Query IShellLink for the IPersistFile interface
272 result = i_shell_link->QueryInterface(IID_IPersistFile,
273 reinterpret_cast<LPVOID*>(&i_persist_file));
274 if (FAILED(result)) {
275 i_shell_link->Release();
276 return false;
277 }
278
279 if (FAILED(i_shell_link->SetPath(source))) {
280 i_persist_file->Release();
281 i_shell_link->Release();
282 return false;
283 }
284
285 if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir))) {
286 i_persist_file->Release();
287 i_shell_link->Release();
288 return false;
289 }
290
291 if (arguments && FAILED(i_shell_link->SetArguments(arguments))) {
292 i_persist_file->Release();
293 i_shell_link->Release();
294 return false;
295 }
296
297 if (description && FAILED(i_shell_link->SetDescription(description))) {
298 i_persist_file->Release();
299 i_shell_link->Release();
300 return false;
301 }
302
303 if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index))) {
304 i_persist_file->Release();
305 i_shell_link->Release();
306 return false;
307 }
308
309 result = i_persist_file->Save(destination, TRUE);
310 i_persist_file->Release();
311 i_shell_link->Release();
312 return SUCCEEDED(result);
313}
314
315
316bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
317 const wchar_t *working_dir, const wchar_t *arguments,
318 const wchar_t *description, const wchar_t *icon,
319 int icon_index) {
320 // Get pointer to the IPersistFile interface and load existing link
321 IShellLink *i_shell_link = NULL;
322 if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL,
323 CLSCTX_INPROC_SERVER, IID_IShellLink,
324 reinterpret_cast<LPVOID*>(&i_shell_link))))
325 return false;
326
327 IPersistFile *i_persist_file = NULL;
328 if (FAILED(i_shell_link->QueryInterface(
329 IID_IPersistFile, reinterpret_cast<LPVOID*>(&i_persist_file)))) {
330 i_shell_link->Release();
331 return false;
332 }
333
334 if (FAILED(i_persist_file->Load(destination, 0))) {
335 i_persist_file->Release();
336 i_shell_link->Release();
337 return false;
338 }
339
340 if (source && FAILED(i_shell_link->SetPath(source))) {
341 i_persist_file->Release();
342 i_shell_link->Release();
343 return false;
344 }
345
346 if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir))) {
347 i_persist_file->Release();
348 i_shell_link->Release();
349 return false;
350 }
351
352 if (arguments && FAILED(i_shell_link->SetArguments(arguments))) {
353 i_persist_file->Release();
354 i_shell_link->Release();
355 return false;
356 }
357
358 if (description && FAILED(i_shell_link->SetDescription(description))) {
359 i_persist_file->Release();
360 i_shell_link->Release();
361 return false;
362 }
363
364 if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index))) {
365 i_persist_file->Release();
366 i_shell_link->Release();
367 return false;
368 }
369
370 HRESULT result = i_persist_file->Save(destination, TRUE);
371 i_persist_file->Release();
372 i_shell_link->Release();
373 return SUCCEEDED(result);
374}
375
376bool GetTempDir(std::wstring* path) {
377 wchar_t temp_path[MAX_PATH + 1];
378 DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
379 if (path_len >= MAX_PATH || path_len <= 0)
380 return false;
381 path->assign(temp_path);
382 TrimTrailingSeparator(path);
383 return true;
384}
385
386bool CreateTemporaryFileName(std::wstring* temp_file) {
387 wchar_t temp_name[MAX_PATH + 1];
388 std::wstring temp_path;
389
390 if (!GetTempDir(&temp_path))
391 return false;
392
393 if (!GetTempFileName(temp_path.c_str(), L"", 0, temp_name))
394 return false; // fail!
395
396 DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH);
397 if (path_len > MAX_PATH + 1 || path_len == 0)
398 return false; // fail!
399
400 temp_file->assign(temp_name, path_len);
401 return true;
402}
403
404bool CreateNewTempDirectory(const std::wstring& prefix,
405 std::wstring* new_temp_path) {
406 std::wstring system_temp_dir;
407 if (!GetTempDir(&system_temp_dir))
408 return false;
409
410 std::wstring path_to_create;
411 srand(static_cast<uint32>(time(NULL)));
412
413 int count = 0;
414 while (count < 50) {
415 // Try create a new temporary directory with random generated name. If
416 // the one exists, keep trying another path name until we reach some limit.
417 path_to_create.assign(system_temp_dir);
418 std::wstring new_dir_name;
419 new_dir_name.assign(prefix);
420 new_dir_name.append(IntToWString(rand() % kint16max));
421 file_util::AppendToPath(&path_to_create, new_dir_name);
422
423 if (::CreateDirectory(path_to_create.c_str(), NULL))
424 break;
425 count++;
426 }
427
428 if (count == 50) {
429 return false;
430 }
431
432 new_temp_path->assign(path_to_create);
433 return true;
434}
435
436bool CreateDirectory(const std::wstring& full_path) {
437 int err = SHCreateDirectoryEx(NULL, full_path.c_str(), NULL);
438 return err == ERROR_SUCCESS;
439}
440
441bool GetFileSize(const std::wstring& file_path, int64* file_size) {
442 ScopedHandle file_handle(
443 CreateFile(file_path.c_str(), GENERIC_READ,
444 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
445 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
446
447 LARGE_INTEGER win32_file_size = {0};
448 if (!GetFileSizeEx(file_handle, &win32_file_size)) {
449 return false;
450 }
451
452 *file_size = win32_file_size.QuadPart;
453 return true;
454}
455
456int ReadFile(const std::wstring& filename, char* data, int size) {
457 ScopedHandle file(CreateFile(filename.c_str(),
458 GENERIC_READ,
459 FILE_SHARE_READ | FILE_SHARE_WRITE,
460 NULL,
461 OPEN_EXISTING,
462 FILE_FLAG_SEQUENTIAL_SCAN,
463 NULL));
464 if (file == INVALID_HANDLE_VALUE)
465 return -1;
466
467 int ret_value;
468 DWORD read;
469 if (::ReadFile(file, data, size, &read, NULL) && read == size) {
470 ret_value = static_cast<int>(read);
471 } else {
472 ret_value = -1;
473 }
474
475 return ret_value;
476}
477
478int WriteFile(const std::wstring& filename, const char* data, int size) {
479 ScopedHandle file(CreateFile(filename.c_str(),
480 GENERIC_WRITE,
481 0,
482 NULL,
483 CREATE_ALWAYS,
484 0,
485 NULL));
486 if (file == INVALID_HANDLE_VALUE)
487 return -1;
488
489 int ret_value;
490 DWORD written;
491 if (::WriteFile(file, data, size, &written, NULL) && written == size) {
492 ret_value = static_cast<int>(written);
493 } else {
494 ret_value = -1;
495 }
496
497 return ret_value;
498}
499
500bool RenameFileAndResetSecurityDescriptor(
501 const std::wstring& source_file_path,
502 const std::wstring& target_file_path) {
503 // The MoveFile API does not reset the security descriptor on the target
504 // file. To ensure that the target file gets the correct security descriptor
505 // we create the target file initially in the target path, read its security
506 // descriptor and stamp this descriptor on the target file after the MoveFile
507 // API completes.
508 ScopedHandle temp_file_handle_for_security_desc(
509 CreateFileW(target_file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
510 FILE_SHARE_READ, NULL, OPEN_ALWAYS,
511 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
512 NULL));
513 if (!temp_file_handle_for_security_desc.IsValid())
514 return false;
515
516 // Check how much we should allocate for the security descriptor.
517 unsigned long security_descriptor_size_in_bytes = 0;
518 GetFileSecurity(target_file_path.c_str(), DACL_SECURITY_INFORMATION, NULL, 0,
519 &security_descriptor_size_in_bytes);
520 if (ERROR_INSUFFICIENT_BUFFER != GetLastError() ||
521 security_descriptor_size_in_bytes == 0)
522 return false;
523
524 scoped_array<char> security_descriptor(
525 new char[security_descriptor_size_in_bytes]);
526
527 if (!GetFileSecurity(target_file_path.c_str(), DACL_SECURITY_INFORMATION,
528 security_descriptor.get(),
529 security_descriptor_size_in_bytes,
530 &security_descriptor_size_in_bytes)) {
531 return false;
532 }
533
534 temp_file_handle_for_security_desc.Set(INVALID_HANDLE_VALUE);
535
536 if (!MoveFileEx(source_file_path.c_str(), target_file_path.c_str(),
537 MOVEFILE_COPY_ALLOWED)) {
538 return false;
539 }
540
541 return !!SetFileSecurity(target_file_path.c_str(),
542 DACL_SECURITY_INFORMATION,
543 security_descriptor.get());
544}
545
546// Gets the current working directory for the process.
547bool GetCurrentDirectory(std::wstring* dir) {
548 wchar_t system_buffer[MAX_PATH];
549 system_buffer[0] = 0;
550 DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
551 if (len == 0 || len > MAX_PATH)
552 return false;
553 *dir = system_buffer;
554 file_util::TrimTrailingSeparator(dir);
555 return true;
556}
557
558// Sets the current working directory for the process.
559bool SetCurrentDirectory(const std::wstring& current_directory) {
560 BOOL ret = ::SetCurrentDirectory(current_directory.c_str());
561 return (ret ? true : false);
562}
563
564///////////////////////////////////////////////
565
566
567FileEnumerator::FileEnumerator(const std::wstring& root_path,
568 bool recursive,
569 FileEnumerator::FILE_TYPE file_type)
570 : recursive_(recursive),
571 file_type_(file_type),
572 is_in_find_op_(false),
573 find_handle_(INVALID_HANDLE_VALUE) {
574 pending_paths_.push(root_path);
575}
576
577FileEnumerator::FileEnumerator(const std::wstring& root_path,
578 bool recursive,
579 FileEnumerator::FILE_TYPE file_type,
580 const std::wstring& pattern)
581 : recursive_(recursive),
582 file_type_(file_type),
583 is_in_find_op_(false),
584 pattern_(pattern),
585 find_handle_(INVALID_HANDLE_VALUE) {
586 pending_paths_.push(root_path);
587}
588
589FileEnumerator::~FileEnumerator() {
590 if (find_handle_ != INVALID_HANDLE_VALUE)
591 FindClose(find_handle_);
592}
593
594std::wstring FileEnumerator::Next() {
595 if (!is_in_find_op_) {
596 if (pending_paths_.empty())
597 return std::wstring();
598
599 // The last find FindFirstFile operation is done, prepare a new one.
600 // root_path_ must have the trailing directory character.
601 root_path_ = pending_paths_.top();
602 file_util::AppendToPath(&root_path_, std::wstring());
603 pending_paths_.pop();
604
605 // Start a new find operation.
606 std::wstring src(root_path_);
607
608 if (pattern_.empty())
609 file_util::AppendToPath(&src, L"*"); // No pattern = match everything.
610 else
611 file_util::AppendToPath(&src, pattern_);
612
613 find_handle_ = FindFirstFile(src.c_str(), &find_data_);
614 is_in_find_op_ = true;
615
616 } else {
617 // Search for the next file/directory.
618 if (!FindNextFile(find_handle_, &find_data_)) {
619 FindClose(find_handle_);
620 find_handle_ = INVALID_HANDLE_VALUE;
621 }
622 }
623
624 if (INVALID_HANDLE_VALUE == find_handle_) {
625 is_in_find_op_ = false;
626
627 // This is reached when we have finished a directory and are advancing to
628 // the next one in the queue. We applied the pattern (if any) to the files
629 // in the root search directory, but for those directories which were
630 // matched, we want to enumerate all files inside them. This will happen
631 // when the handle is empty.
632 pattern_.clear();
633
634 return Next();
635 }
636
637 std::wstring cur_file(find_data_.cFileName);
638 // Skip over . and ..
639 if (L"." == cur_file || L".." == cur_file)
640 return Next();
641
642 // Construct the absolute filename.
643 cur_file.insert(0, root_path_);
644
645 if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
646 if (recursive_) {
647 // If |cur_file| is a directory, and we are doing recursive searching, add
648 // it to pending_paths_ so we scan it after we finish scanning this
649 // directory.
650 pending_paths_.push(cur_file);
651 return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
652 }
653
654 if ((file_type_ & FileEnumerator::DIRECTORIES) == 0)
655 return Next();
656 }
657 return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
658}
659
[email protected]5af2edb92008-08-08 20:16:08660} // namespace file_util