OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 "base/test_file_util.h" | 5 #include "base/test_file_util.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <fts.h> | |
9 #include <sys/types.h> | 8 #include <sys/types.h> |
10 #include <sys/stat.h> | 9 #include <sys/stat.h> |
11 | 10 |
12 #include <string> | 11 #include <string> |
13 | 12 |
14 #include "base/logging.h" | 13 #include "base/logging.h" |
15 #include "base/file_path.h" | 14 #include "base/file_path.h" |
16 #include "base/file_util.h" | 15 #include "base/file_util.h" |
17 #include "base/string_util.h" | 16 #include "base/string_util.h" |
18 | 17 |
19 namespace file_util { | 18 namespace file_util { |
20 | 19 |
21 bool DieFileDie(const FilePath& file, bool recurse) { | 20 bool DieFileDie(const FilePath& file, bool recurse) { |
22 // There is no need to workaround Windows problems on POSIX. | 21 // There is no need to workaround Windows problems on POSIX. |
23 // Just pass-through. | 22 // Just pass-through. |
24 return file_util::Delete(file, recurse); | 23 return file_util::Delete(file, recurse); |
25 } | 24 } |
26 | 25 |
| 26 // Mostly a verbatim copy of CopyDirectory |
27 bool CopyRecursiveDirNoCache(const std::wstring& source_dir, | 27 bool CopyRecursiveDirNoCache(const std::wstring& source_dir, |
28 const std::wstring& dest_dir) { | 28 const std::wstring& dest_dir) { |
29 const FilePath from_path(FilePath::FromWStringHack(source_dir)); | 29 const FilePath from_path(FilePath::FromWStringHack(source_dir)); |
30 const FilePath to_path(FilePath::FromWStringHack(dest_dir)); | 30 const FilePath to_path(FilePath::FromWStringHack(dest_dir)); |
31 | 31 |
32 char top_dir[PATH_MAX]; | 32 char top_dir[PATH_MAX]; |
33 if (base::strlcpy(top_dir, from_path.value().c_str(), | 33 if (base::strlcpy(top_dir, from_path.value().c_str(), |
34 arraysize(top_dir)) >= arraysize(top_dir)) { | 34 arraysize(top_dir)) >= arraysize(top_dir)) { |
35 return false; | 35 return false; |
36 } | 36 } |
37 | 37 |
38 char* dir_list[] = { top_dir, NULL }; | 38 // This function does not properly handle destinations within the source |
39 FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL); | 39 FilePath real_to_path = to_path; |
40 if (!fts) { | 40 if (PathExists(real_to_path)) { |
41 LOG(ERROR) << "fts_open failed: " << strerror(errno); | 41 if (!AbsolutePath(&real_to_path)) |
| 42 return false; |
| 43 } else { |
| 44 real_to_path = real_to_path.DirName(); |
| 45 if (!AbsolutePath(&real_to_path)) |
| 46 return false; |
| 47 } |
| 48 if (real_to_path.value().compare(0, from_path.value().size(), |
| 49 from_path.value()) == 0) |
42 return false; | 50 return false; |
| 51 |
| 52 bool success = true; |
| 53 FileEnumerator::FILE_TYPE traverse_type = |
| 54 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES | |
| 55 FileEnumerator::SHOW_SYM_LINKS | FileEnumerator::DIRECTORIES); |
| 56 FileEnumerator traversal(from_path, true, traverse_type); |
| 57 |
| 58 // to_path may not exist yet, start the loop with to_path |
| 59 FileEnumerator::FindInfo info; |
| 60 FilePath current = from_path; |
| 61 if (stat(from_path.value().c_str(), &info.stat) < 0) { |
| 62 LOG(ERROR) << "CopyRecursiveDirNoCache() couldn't stat source directory: " |
| 63 << from_path.value() << " errno = " << errno; |
| 64 success = false; |
43 } | 65 } |
44 | 66 |
45 int error = 0; | 67 while (success && !current.empty()) { |
46 FTSENT* ent; | 68 // current is the source path, including from_path, so paste |
47 while (!error && (ent = fts_read(fts)) != NULL) { | |
48 // ent->fts_path is the source path, including from_path, so paste | |
49 // the suffix after from_path onto to_path to create the target_path. | 69 // the suffix after from_path onto to_path to create the target_path. |
50 std::string suffix(&ent->fts_path[from_path.value().size()]); | 70 std::string suffix(¤t.value().c_str()[from_path.value().size()]); |
51 // Strip the leading '/' (if any). | 71 // Strip the leading '/' (if any). |
52 if (!suffix.empty()) { | 72 if (!suffix.empty()) { |
53 DCHECK_EQ('/', suffix[0]); | 73 DCHECK_EQ('/', suffix[0]); |
54 suffix.erase(0, 1); | 74 suffix.erase(0, 1); |
55 } | 75 } |
56 const FilePath target_path = to_path.Append(suffix); | 76 const FilePath target_path = to_path.Append(suffix); |
57 switch (ent->fts_info) { | 77 |
58 case FTS_D: // Preorder directory. | 78 if (S_ISDIR(info.stat.st_mode)) { |
59 // Try creating the target dir, continuing on it if it exists already. | 79 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 && |
60 // Rely on the user's umask to produce correct permissions. | 80 errno != EEXIST) { |
61 if (mkdir(target_path.value().c_str(), 0777) != 0) { | 81 LOG(ERROR) << "CopyRecursiveDirNoCache() couldn't create directory: " << |
62 if (errno != EEXIST) | 82 target_path.value() << " errno = " << errno; |
63 error = errno; | 83 success = false; |
64 } | 84 } |
65 break; | 85 } else if (S_ISREG(info.stat.st_mode)) { |
66 case FTS_F: // Regular file. | 86 if (CopyFile(current, target_path)) { |
67 case FTS_NSOK: // File, no stat info requested. | 87 success = EvictFileFromSystemCache(target_path); |
68 { | 88 DCHECK(success); |
69 errno = 0; | 89 } else { |
70 FilePath source_path(ent->fts_path); | 90 LOG(ERROR) << "CopyRecursiveDirNoCache() couldn't create file: " << |
71 if (CopyFile(source_path, target_path)) { | 91 target_path.value(); |
72 bool success = EvictFileFromSystemCache(target_path); | 92 success = false; |
73 DCHECK(success); | 93 } |
74 } else { | 94 } else { |
75 error = errno ? errno : EINVAL; | 95 LOG(WARNING) << "CopyRecursiveDirNoCache() skipping non-regular file: " << |
76 } | 96 current.value(); |
77 } | |
78 break; | |
79 case FTS_DP: // Postorder directory. | |
80 case FTS_DOT: // "." or ".." | |
81 // Skip it. | |
82 continue; | |
83 case FTS_DC: // Directory causing a cycle. | |
84 // Skip this branch. | |
85 if (fts_set(fts, ent, FTS_SKIP) != 0) | |
86 error = errno; | |
87 break; | |
88 case FTS_DNR: // Directory cannot be read. | |
89 case FTS_ERR: // Error. | |
90 case FTS_NS: // Stat failed. | |
91 // Abort with the error. | |
92 error = ent->fts_errno; | |
93 break; | |
94 case FTS_SL: // Symlink. | |
95 case FTS_SLNONE: // Symlink with broken target. | |
96 LOG(WARNING) << "skipping symbolic link: " << ent->fts_path; | |
97 continue; | |
98 case FTS_DEFAULT: // Some other sort of file. | |
99 LOG(WARNING) << "skipping file of unknown type: " << ent->fts_path; | |
100 continue; | |
101 default: | |
102 NOTREACHED(); | |
103 continue; // Hope for the best! | |
104 } | 97 } |
105 } | |
106 // fts_read may have returned NULL and set errno to indicate an error. | |
107 if (!error && errno != 0) | |
108 error = errno; | |
109 | 98 |
110 if (!fts_close(fts)) { | 99 current = traversal.Next(); |
111 // If we already have an error, let's use that error instead of the error | 100 traversal.GetFindInfo(&info); |
112 // fts_close set. | |
113 if (!error) | |
114 error = errno; | |
115 } | 101 } |
116 | 102 |
117 if (error) { | 103 return success; |
118 LOG(ERROR) << strerror(error); | |
119 return false; | |
120 } | |
121 return true; | |
122 } | 104 } |
123 | 105 |
124 } // namespace file_util | 106 } // namespace file_util |
OLD | NEW |