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

Side by Side Diff: base/file_util_unittest.cc

Issue 2088006: Give the extension unpacker process a junction/symlink free path to the unpack directory. (Closed)
Patch Set: Rebase for commit. Created 10 years, 6 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_util_posix.cc ('k') | base/file_util_win.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "build/build_config.h" 5 #include "build/build_config.h"
6 6
7 #if defined(OS_WIN) 7 #if defined(OS_WIN)
8 #include <windows.h> 8 #include <windows.h>
9 #include <winioctl.h>
9 #include <shellapi.h> 10 #include <shellapi.h>
10 #include <shlobj.h> 11 #include <shlobj.h>
11 #include <tchar.h> 12 #include <tchar.h>
12 #endif 13 #endif
13 14
14 #include <fstream> 15 #include <fstream>
15 #include <iostream> 16 #include <iostream>
16 #include <set> 17 #include <set>
17 18
18 #include "base/base_paths.h" 19 #include "base/base_paths.h"
19 #include "base/file_path.h" 20 #include "base/file_path.h"
20 #include "base/file_util.h" 21 #include "base/file_util.h"
21 #include "base/logging.h" 22 #include "base/logging.h"
22 #include "base/path_service.h" 23 #include "base/path_service.h"
23 #include "base/platform_thread.h" 24 #include "base/platform_thread.h"
25 #include "base/scoped_handle.h"
24 #include "base/time.h" 26 #include "base/time.h"
25 #include "base/utf_string_conversions.h" 27 #include "base/utf_string_conversions.h"
26 #include "testing/gtest/include/gtest/gtest.h" 28 #include "testing/gtest/include/gtest/gtest.h"
27 #include "testing/platform_test.h" 29 #include "testing/platform_test.h"
28 30
29 // This macro helps avoid wrapped lines in the test structs. 31 // This macro helps avoid wrapped lines in the test structs.
30 #define FPL(x) FILE_PATH_LITERAL(x) 32 #define FPL(x) FILE_PATH_LITERAL(x)
31 33
32 namespace { 34 namespace {
33 35
36 // To test that file_util::Normalize FilePath() deals with NTFS reparse points
37 // correctly, we need functions to create and delete reparse points.
38 #if defined(OS_WIN)
39 typedef struct _REPARSE_DATA_BUFFER {
40 ULONG ReparseTag;
41 USHORT ReparseDataLength;
42 USHORT Reserved;
43 union {
44 struct {
45 USHORT SubstituteNameOffset;
46 USHORT SubstituteNameLength;
47 USHORT PrintNameOffset;
48 USHORT PrintNameLength;
49 ULONG Flags;
50 WCHAR PathBuffer[1];
51 } SymbolicLinkReparseBuffer;
52 struct {
53 USHORT SubstituteNameOffset;
54 USHORT SubstituteNameLength;
55 USHORT PrintNameOffset;
56 USHORT PrintNameLength;
57 WCHAR PathBuffer[1];
58 } MountPointReparseBuffer;
59 struct {
60 UCHAR DataBuffer[1];
61 } GenericReparseBuffer;
62 };
63 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
64
65 // Sets a reparse point. |source| will now point to |target|. Returns true if
66 // the call succeeds, false otherwise.
67 bool SetReparsePoint(HANDLE source, const FilePath& target_path) {
68 std::wstring kPathPrefix = L"\\??\\";
69 std::wstring target_str;
70 // The juction will not work if the target path does not start with \??\ .
71 if (kPathPrefix != target_path.value().substr(0, kPathPrefix.size()))
72 target_str += kPathPrefix;
73 target_str += target_path.value();
74 const wchar_t* target = target_str.c_str();
75 USHORT size_target = static_cast<USHORT>(wcslen(target)) * sizeof(target[0]);
76 char buffer[2000] = {0};
77 DWORD returned;
78
79 REPARSE_DATA_BUFFER* data = reinterpret_cast<REPARSE_DATA_BUFFER*>(buffer);
80
81 data->ReparseTag = 0xa0000003;
82 memcpy(data->MountPointReparseBuffer.PathBuffer, target, size_target + 2);
83
84 data->MountPointReparseBuffer.SubstituteNameLength = size_target;
85 data->MountPointReparseBuffer.PrintNameOffset = size_target + 2;
86 data->ReparseDataLength = size_target + 4 + 8;
87
88 int data_size = data->ReparseDataLength + 8;
89
90 if (!DeviceIoControl(source, FSCTL_SET_REPARSE_POINT, &buffer, data_size,
91 NULL, 0, &returned, NULL)) {
92 return false;
93 }
94 return true;
95 }
96
97 // Delete the reparse point referenced by |source|. Returns true if the call
98 // succeeds, false otherwise.
99 bool DeleteReparsePoint(HANDLE source) {
100 DWORD returned;
101 REPARSE_DATA_BUFFER data = {0};
102 data.ReparseTag = 0xa0000003;
103 if (!DeviceIoControl(source, FSCTL_DELETE_REPARSE_POINT, &data, 8, NULL, 0,
104 &returned, NULL)) {
105 return false;
106 }
107 return true;
108 }
109 #endif
110
34 const wchar_t bogus_content[] = L"I'm cannon fodder."; 111 const wchar_t bogus_content[] = L"I'm cannon fodder.";
35 112
36 const file_util::FileEnumerator::FILE_TYPE FILES_AND_DIRECTORIES = 113 const file_util::FileEnumerator::FILE_TYPE FILES_AND_DIRECTORIES =
37 static_cast<file_util::FileEnumerator::FILE_TYPE>( 114 static_cast<file_util::FileEnumerator::FILE_TYPE>(
38 file_util::FileEnumerator::FILES | 115 file_util::FileEnumerator::FILES |
39 file_util::FileEnumerator::DIRECTORIES); 116 file_util::FileEnumerator::DIRECTORIES);
40 117
41 // file_util winds up using autoreleased objects on the Mac, so this needs 118 // file_util winds up using autoreleased objects on the Mac, so this needs
42 // to be a PlatformTest 119 // to be a PlatformTest
43 class FileUtilTest : public PlatformTest { 120 class FileUtilTest : public PlatformTest {
(...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 FilePath subsubdir_path = subdir_path.Append(FPL("Level3")); 457 FilePath subsubdir_path = subdir_path.Append(FPL("Level3"));
381 file_util::CreateDirectory(subsubdir_path); 458 file_util::CreateDirectory(subsubdir_path);
382 459
383 FilePath file_03 = subsubdir_path.Append(FPL("The file 03.txt")); 460 FilePath file_03 = subsubdir_path.Append(FPL("The file 03.txt"));
384 CreateTextFile(file_03, L"123"); 461 CreateTextFile(file_03, L"123");
385 462
386 int64 computed_size = file_util::ComputeDirectorySize(test_dir_); 463 int64 computed_size = file_util::ComputeDirectorySize(test_dir_);
387 EXPECT_EQ(size_f1 + size_f2 + 3, computed_size); 464 EXPECT_EQ(size_f1 + size_f2 + 3, computed_size);
388 } 465 }
389 466
467 TEST_F(FileUtilTest, NormalizeFilePathBasic) {
468 // Create a directory under the test dir. Because we create it,
469 // we know it is not a link.
470 FilePath file_a_path = test_dir_.Append(FPL("file_a"));
471 FilePath dir_path = test_dir_.Append(FPL("dir"));
472 FilePath file_b_path = dir_path.Append(FPL("file_b"));
473 file_util::CreateDirectory(dir_path);
474
475 FilePath normalized_file_a_path, normalized_file_b_path;
476 ASSERT_FALSE(file_util::PathExists(file_a_path));
477 ASSERT_FALSE(file_util::NormalizeFilePath(file_a_path,
478 &normalized_file_a_path))
479 << "NormalizeFilePath() should fail on nonexistant paths.";
480
481 CreateTextFile(file_a_path, bogus_content);
482 ASSERT_TRUE(file_util::PathExists(file_a_path));
483 ASSERT_TRUE(file_util::NormalizeFilePath(file_a_path,
484 &normalized_file_a_path));
485
486 CreateTextFile(file_b_path, bogus_content);
487 ASSERT_TRUE(file_util::PathExists(file_b_path));
488 ASSERT_TRUE(file_util::NormalizeFilePath(file_b_path,
489 &normalized_file_b_path));
490
491 // Beacuse this test created |dir_path|, we know it is not a link
492 // or junction. So, the real path of the directory holding file a
493 // must be the parent of the path holding file b.
494 ASSERT_TRUE(normalized_file_a_path.DirName()
495 .IsParent(normalized_file_b_path.DirName()));
496 }
497
498 #if defined(OS_WIN)
499
500 TEST_F(FileUtilTest, NormalizeFilePathReparsePoints) {
501 // Build the following directory structure:
502 //
503 // test_dir_
504 // |-> base_a
505 // | |-> sub_a
506 // | |-> file.txt
507 // | |-> long_name___... (Very long name.)
508 // | |-> sub_long
509 // | |-> deep.txt
510 // |-> base_b
511 // |-> to_sub_a (reparse point to test_dir_\base_a\sub_a)
512 // |-> to_base_b (reparse point to test_dir_\base_b)
513 // |-> to_sub_long (reparse point to test_dir_\sub_a\long_name_\sub_long)
514
515 FilePath base_a = test_dir_.Append(FPL("base_a"));
516 ASSERT_TRUE(file_util::CreateDirectory(base_a));
517
518 FilePath sub_a = base_a.Append(FPL("sub_a"));
519 ASSERT_TRUE(file_util::CreateDirectory(sub_a));
520
521 FilePath file_txt = sub_a.Append(FPL("file.txt"));
522 CreateTextFile(file_txt, bogus_content);
523
524 // Want a directory whose name is long enough to make the path to the file
525 // inside just under MAX_PATH chars. This will be used to test that when
526 // a junction expands to a path over MAX_PATH chars in length,
527 // NormalizeFilePath() fails without crashing.
528 FilePath sub_long_rel(FPL("sub_long"));
529 FilePath deep_txt(FPL("deep.txt"));
530
531 int target_length = MAX_PATH;
532 target_length -= (sub_a.value().length() + 1); // +1 for the sepperator '\'.
533 target_length -= (sub_long_rel.Append(deep_txt).value().length() + 1);
534 // Without making the path a bit shorter, CreateDirectory() fails.
535 // the resulting path is still long enough to hit the failing case in
536 // NormalizePath().
537 const int kCreateDirLimit = 4;
538 target_length -= kCreateDirLimit;
539 FilePath::StringType long_name_str = FPL("long_name_");
540 long_name_str.resize(target_length, '_');
541
542 FilePath long_name = sub_a.Append(FilePath(long_name_str));
543 FilePath deep_file = long_name.Append(sub_long_rel).Append(deep_txt);
544 ASSERT_EQ(MAX_PATH - kCreateDirLimit, deep_file.value().length());
545
546 FilePath sub_long = deep_file.DirName();
547 ASSERT_TRUE(file_util::CreateDirectory(sub_long));
548 CreateTextFile(deep_file, bogus_content);
549
550 FilePath base_b = test_dir_.Append(FPL("base_b"));
551 ASSERT_TRUE(file_util::CreateDirectory(base_b));
552
553 FilePath to_sub_a = base_b.Append(FPL("to_sub_a"));
554 ASSERT_TRUE(file_util::CreateDirectory(to_sub_a));
555 ScopedHandle reparse_to_sub_a(
556 ::CreateFile(to_sub_a.value().c_str(),
557 FILE_ALL_ACCESS,
558 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
559 NULL,
560 OPEN_EXISTING,
561 FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory.
562 NULL));
563 ASSERT_NE(INVALID_HANDLE_VALUE, reparse_to_sub_a.Get());
564 ASSERT_TRUE(SetReparsePoint(reparse_to_sub_a, sub_a));
565
566 FilePath to_base_b = base_b.Append(FPL("to_base_b"));
567 ASSERT_TRUE(file_util::CreateDirectory(to_base_b));
568 ScopedHandle reparse_to_base_b(
569 ::CreateFile(to_base_b.value().c_str(),
570 FILE_ALL_ACCESS,
571 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
572 NULL,
573 OPEN_EXISTING,
574 FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory.
575 NULL));
576 ASSERT_NE(INVALID_HANDLE_VALUE, reparse_to_base_b.Get());
577 ASSERT_TRUE(SetReparsePoint(reparse_to_base_b, base_b));
578
579 FilePath to_sub_long = base_b.Append(FPL("to_sub_long"));
580 ASSERT_TRUE(file_util::CreateDirectory(to_sub_long));
581 ScopedHandle reparse_to_sub_long(
582 ::CreateFile(to_sub_long.value().c_str(),
583 FILE_ALL_ACCESS,
584 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
585 NULL,
586 OPEN_EXISTING,
587 FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory.
588 NULL));
589 ASSERT_NE(INVALID_HANDLE_VALUE, reparse_to_sub_long.Get());
590 ASSERT_TRUE(SetReparsePoint(reparse_to_sub_long, sub_long));
591
592 // Normalize a junction free path: base_a\sub_a\file.txt .
593 FilePath normalized_path;
594 ASSERT_TRUE(file_util::NormalizeFilePath(file_txt, &normalized_path));
595 ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
596
597 // Check that the path base_b\to_sub_a\file.txt can be normalized to exclude
598 // the junction to_sub_a.
599 ASSERT_TRUE(file_util::NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
600 &normalized_path));
601 ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
602
603 // Check that the path base_b\to_base_b\to_base_b\to_sub_a\file.txt can be
604 // normalized to exclude junctions to_base_b and to_sub_a .
605 ASSERT_TRUE(file_util::NormalizeFilePath(base_b.Append(FPL("to_base_b"))
606 .Append(FPL("to_base_b"))
607 .Append(FPL("to_sub_a"))
608 .Append(FPL("file.txt")),
609 &normalized_path));
610 ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
611
612 // A long enough path will cause NormalizeFilePath() to fail. Make a long
613 // path using to_base_b many times, and check that paths long enough to fail
614 // do not cause a crash.
615 FilePath long_path = base_b;
616 const int kLengthLimit = MAX_PATH + 200;
617 while (long_path.value().length() <= kLengthLimit) {
618 long_path = long_path.Append(FPL("to_base_b"));
619 }
620 long_path = long_path.Append(FPL("to_sub_a"))
621 .Append(FPL("file.txt"));
622
623 ASSERT_FALSE(file_util::NormalizeFilePath(long_path, &normalized_path));
624
625 // Normalizing the junction to deep.txt should fail, because the expanded
626 // path to deep.txt is longer than MAX_PATH.
627 ASSERT_FALSE(file_util::NormalizeFilePath(to_sub_long.Append(deep_txt),
628 &normalized_path));
629
630 // Delete the reparse points, and see that NormalizeFilePath() fails
631 // to traverse them.
632 ASSERT_TRUE(DeleteReparsePoint(reparse_to_sub_a));
633 ASSERT_TRUE(DeleteReparsePoint(reparse_to_base_b));
634 ASSERT_TRUE(DeleteReparsePoint(reparse_to_sub_long));
635
636 ASSERT_FALSE(file_util::NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
637 &normalized_path));
638 }
639
640 #endif // defined(OS_WIN)
641
642 // The following test of NormalizeFilePath() require that we create a symlink.
643 // This can not be done on windows before vista. On vista, creating a symlink
644 // requires privilege "SeCreateSymbolicLinkPrivilege".
645 // TODO(skerner): Investigate the possibility of giving base_unittests the
646 // privileges required to create a symlink.
390 #if defined(OS_POSIX) 647 #if defined(OS_POSIX)
391 TEST_F(FileUtilTest, RealPath) { 648
392 // Get the real test directory, in case some future change to the 649 bool MakeSymlink(const FilePath& link_to, const FilePath& link_from) {
393 // test setup makes the path to test_dir_ include a symlink. 650 return (symlink(link_to.value().c_str(), link_from.value().c_str()) == 0);
394 FilePath real_test_dir; 651 }
395 ASSERT_TRUE(file_util::RealPath(test_dir_, &real_test_dir)); 652
396 653 TEST_F(FileUtilTest, NormalizeFilePathSymlinks) {
397 FilePath real_path; 654 FilePath normalized_path;
398 ASSERT_TRUE(file_util::RealPath(real_test_dir, &real_path));
399 ASSERT_TRUE(real_test_dir == real_path);
400 655
401 // Link one file to another. 656 // Link one file to another.
402 FilePath link_from = real_test_dir.Append(FPL("from_file")); 657 FilePath link_from = test_dir_.Append(FPL("from_file"));
403 FilePath link_to = real_test_dir.Append(FPL("to_file")); 658 FilePath link_to = test_dir_.Append(FPL("to_file"));
404 CreateTextFile(link_to, bogus_content); 659 CreateTextFile(link_to, bogus_content);
405 660
406 ASSERT_EQ(0, symlink(link_to.value().c_str(), link_from.value().c_str())) 661 ASSERT_TRUE(MakeSymlink(link_to, link_from))
407 << "Failed to create file symlink."; 662 << "Failed to create file symlink.";
408 663
409 // Check that RealPath sees the link. 664 // Check that NormalizeFilePath sees the link.
410 ASSERT_TRUE(file_util::RealPath(link_from, &real_path)); 665 ASSERT_TRUE(file_util::NormalizeFilePath(link_from, &normalized_path));
411 ASSERT_TRUE(link_to != link_from); 666 ASSERT_TRUE(link_to != link_from);
412 ASSERT_TRUE(link_to == real_path); 667 ASSERT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value());
413 668 ASSERT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value());
414 669
415 // Link to a directory. 670 // Link to a directory.
416 link_from = real_test_dir.Append(FPL("from_dir")); 671 link_from = test_dir_.Append(FPL("from_dir"));
417 link_to = real_test_dir.Append(FPL("to_dir")); 672 link_to = test_dir_.Append(FPL("to_dir"));
418 file_util::CreateDirectory(link_to); 673 file_util::CreateDirectory(link_to);
419 674
420 ASSERT_EQ(0, symlink(link_to.value().c_str(), link_from.value().c_str())) 675 ASSERT_TRUE(MakeSymlink(link_to, link_from))
421 << "Failed to create directory symlink."; 676 << "Failed to create directory symlink.";
422 677
423 ASSERT_TRUE(file_util::RealPath(link_from, &real_path)); 678 ASSERT_FALSE(file_util::NormalizeFilePath(link_from, &normalized_path))
424 ASSERT_TRUE(link_to != link_from); 679 << "Links to directories should return false.";
425 ASSERT_TRUE(link_to == real_path); 680
426 681 // Test that a loop in the links causes NormalizeFilePath() to return false.
427 682 link_from = test_dir_.Append(FPL("link_a"));
428 // Test that a loop in the links causes RealPath() to return false. 683 link_to = test_dir_.Append(FPL("link_b"));
429 link_from = real_test_dir.Append(FPL("link_a")); 684 ASSERT_TRUE(MakeSymlink(link_to, link_from))
430 link_to = real_test_dir.Append(FPL("link_b"));
431 ASSERT_EQ(0, symlink(link_to.value().c_str(), link_from.value().c_str()))
432 << "Failed to create loop symlink a."; 685 << "Failed to create loop symlink a.";
433 ASSERT_EQ(0, symlink(link_from.value().c_str(), link_to.value().c_str())) 686 ASSERT_TRUE(MakeSymlink(link_from, link_to))
434 << "Failed to create loop symlink b."; 687 << "Failed to create loop symlink b.";
435 688
436 // Infinite loop! 689 // Infinite loop!
437 ASSERT_FALSE(file_util::RealPath(link_from, &real_path)); 690 ASSERT_FALSE(file_util::NormalizeFilePath(link_from, &normalized_path));
438 } 691 }
439 #endif // defined(OS_POSIX) 692 #endif // defined(OS_POSIX)
440 693
441 TEST_F(FileUtilTest, DeleteNonExistent) { 694 TEST_F(FileUtilTest, DeleteNonExistent) {
442 FilePath non_existent = test_dir_.AppendASCII("bogus_file_dne.foobar"); 695 FilePath non_existent = test_dir_.AppendASCII("bogus_file_dne.foobar");
443 ASSERT_FALSE(file_util::PathExists(non_existent)); 696 ASSERT_FALSE(file_util::PathExists(non_existent));
444 697
445 EXPECT_TRUE(file_util::Delete(non_existent, false)); 698 EXPECT_TRUE(file_util::Delete(non_existent, false));
446 ASSERT_FALSE(file_util::PathExists(non_existent)); 699 ASSERT_FALSE(file_util::PathExists(non_existent));
447 EXPECT_TRUE(file_util::Delete(non_existent, true)); 700 EXPECT_TRUE(file_util::Delete(non_existent, true));
(...skipping 1140 matching lines...) Expand 10 before | Expand all | Expand 10 after
1588 EXPECT_TRUE(file_util::IsDirectoryEmpty(empty_dir)); 1841 EXPECT_TRUE(file_util::IsDirectoryEmpty(empty_dir));
1589 1842
1590 FilePath foo(empty_dir.Append(FILE_PATH_LITERAL("foo.txt"))); 1843 FilePath foo(empty_dir.Append(FILE_PATH_LITERAL("foo.txt")));
1591 std::string bar("baz"); 1844 std::string bar("baz");
1592 ASSERT_TRUE(file_util::WriteFile(foo, bar.c_str(), bar.length())); 1845 ASSERT_TRUE(file_util::WriteFile(foo, bar.c_str(), bar.length()));
1593 1846
1594 EXPECT_FALSE(file_util::IsDirectoryEmpty(empty_dir)); 1847 EXPECT_FALSE(file_util::IsDirectoryEmpty(empty_dir));
1595 } 1848 }
1596 1849
1597 } // namespace 1850 } // namespace
OLDNEW
« no previous file with comments | « base/file_util_posix.cc ('k') | base/file_util_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698