blob: 65dbfcbb96acb660902b5889c063eca33924bb8a [file] [log] [blame]
[email protected]b2e97292008-09-02 18:20:341// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/file_util.h"
6
[email protected]21dec3872008-09-18 19:15:547#include <errno.h>
[email protected]b2e97292008-09-02 18:20:348#include <fcntl.h>
9#include <fnmatch.h>
10#include <fts.h>
11#include <libgen.h>
[email protected]21dec3872008-09-18 19:15:5412#include <string.h>
[email protected]b2e97292008-09-02 18:20:3413#include <sys/errno.h>
14#include <sys/stat.h>
15#include <time.h>
16
17#include <fstream>
18
19#include "base/basictypes.h"
20#include "base/logging.h"
21#include "base/string_util.h"
22
23namespace file_util {
24
[email protected]778e8c52008-09-11 17:36:2325static const wchar_t* kTempFileName = L"com.google.chrome.XXXXXX";
26
[email protected]b2e97292008-09-02 18:20:3427std::wstring GetDirectoryFromPath(const std::wstring& path) {
28 if (EndsWithSeparator(path)) {
29 std::wstring dir = path;
30 TrimTrailingSeparator(&dir);
31 return dir;
32 } else {
33 char full_path[PATH_MAX];
34 base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path));
35 return UTF8ToWide(dirname(full_path));
36 }
37}
38
39bool AbsolutePath(std::wstring* path) {
40 char full_path[PATH_MAX];
41 if (realpath(WideToUTF8(*path).c_str(), full_path) == NULL)
42 return false;
43 *path = UTF8ToWide(full_path);
44 return true;
45}
46
47// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
48// which works both with and without the recursive flag. I'm not sure we need
49// that functionality. If not, remove from file_util_win.cc, otherwise add it
50// here.
51bool Delete(const std::wstring& path, bool recursive) {
[email protected]778e8c52008-09-11 17:36:2352 std::string utf8_path_string = WideToUTF8(path);
53 const char* utf8_path = utf8_path_string.c_str();
[email protected]b2e97292008-09-02 18:20:3454 struct stat64 file_info;
55 int test = stat64(utf8_path, &file_info);
56 if (test != 0) {
57 // The Windows version defines this condition as success.
58 bool ret = (errno == ENOENT || errno == ENOTDIR);
59 return ret;
60 }
61 if (!S_ISDIR(file_info.st_mode))
62 return (unlink(utf8_path) == 0);
63 if (!recursive)
64 return (rmdir(utf8_path) == 0);
65
66 bool success = true;
67 int ftsflags = FTS_PHYSICAL | FTS_NOSTAT;
68 char top_dir[PATH_MAX];
[email protected]21dec3872008-09-18 19:15:5469 if (base::strlcpy(top_dir, utf8_path,
70 arraysize(top_dir)) >= arraysize(top_dir)) {
71 return false;
72 }
[email protected]b2e97292008-09-02 18:20:3473 char* dir_list[2] = { top_dir, NULL };
74 FTS* fts = fts_open(dir_list, ftsflags, NULL);
75 if (fts) {
76 FTSENT* fts_ent = fts_read(fts);
77 while (success && fts_ent != NULL) {
78 switch (fts_ent->fts_info) {
79 case FTS_DNR:
80 case FTS_ERR:
81 // log error
82 success = false;
83 continue;
84 break;
85 case FTS_DP:
86 rmdir(fts_ent->fts_accpath);
87 break;
88 case FTS_D:
89 break;
90 case FTS_NSOK:
91 case FTS_F:
92 case FTS_SL:
93 case FTS_SLNONE:
94 unlink(fts_ent->fts_accpath);
95 break;
96 default:
97 DCHECK(false);
98 break;
99 }
100 fts_ent = fts_read(fts);
101 }
102 fts_close(fts);
103 }
104 return success;
105}
106
107bool Move(const std::wstring& from_path, const std::wstring& to_path) {
108 return (rename(WideToUTF8(from_path).c_str(),
109 WideToUTF8(to_path).c_str()) == 0);
110}
111
[email protected]21dec3872008-09-18 19:15:54112bool CopyDirectory(const std::wstring& from_path_wide,
113 const std::wstring& to_path_wide,
114 bool recursive) {
115 const std::string to_path = WideToUTF8(to_path_wide);
116 const std::string from_path = WideToUTF8(from_path_wide);
117
118 // Some old callers of CopyDirectory want it to support wildcards.
119 // After some discussion, we decided to fix those callers.
120 // Break loudly here if anyone tries to do this.
121 // TODO(evanm): remove this once we're sure it's ok.
122 DCHECK(to_path.find('*') == std::string::npos);
123 DCHECK(from_path.find('*') == std::string::npos);
124
125 char top_dir[PATH_MAX];
126 if (base::strlcpy(top_dir, from_path.c_str(),
127 arraysize(top_dir)) >= arraysize(top_dir)) {
128 return false;
129 }
130
131 char* dir_list[] = { top_dir, NULL };
132 FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL);
133 if (!fts) {
134 LOG(ERROR) << "fts_open failed: " << strerror(errno);
135 return false;
136 }
137
138 int error = 0;
139 FTSENT* ent;
140 while (!error && (ent = fts_read(fts)) != NULL) {
141 // ent->fts_path is the source path, including from_path, so paste
142 // the suffix after from_path onto to_path to create the target_path.
143 const std::string target_path = to_path + &ent->fts_path[from_path.size()];
144 switch (ent->fts_info) {
145 case FTS_D: // Preorder directory.
146 // If we encounter a subdirectory in a non-recursive copy, prune it
147 // from the traversal.
148 if (!recursive && ent->fts_level > 0) {
149 if (fts_set(fts, ent, FTS_SKIP) != 0)
150 error = errno;
151 continue;
152 }
153
154 // Try creating the target dir, continuing on it if it exists already.
155 if (mkdir(target_path.c_str(), 0777) != 0) {
156 if (errno != EEXIST)
157 error = errno;
158 }
159 break;
160 case FTS_F: // Regular file.
161 case FTS_NSOK: // File, no stat info requested.
162 // TODO(port): use a native file path rather than all these
163 // conversions.
164 errno = 0;
165 if (!CopyFile(UTF8ToWide(ent->fts_path), UTF8ToWide(target_path)))
166 error = errno ? errno : EINVAL;
167 break;
168 case FTS_DP: // Postorder directory.
169 case FTS_DOT: // "." or ".."
170 // Skip it.
171 continue;
172 case FTS_DC: // Directory causing a cycle.
173 // Skip this branch.
174 if (fts_set(fts, ent, FTS_SKIP) != 0)
175 error = errno;
176 break;
177 case FTS_DNR: // Directory cannot be read.
178 case FTS_ERR: // Error.
179 case FTS_NS: // Stat failed.
180 // Abort with the error.
181 error = ent->fts_errno;
182 break;
183 case FTS_SL: // Symlink.
184 case FTS_SLNONE: // Symlink with broken target.
185 LOG(WARNING) << "CopyDirectory() skipping symbolic link.";
186 continue;
187 case FTS_DEFAULT: // Some other sort of file.
188 LOG(WARNING) << "CopyDirectory() skipping weird file.";
189 continue;
190 default:
191 NOTREACHED();
192 continue; // Hope for the best!
193 }
194 }
195 // fts_read may have returned NULL and set errno to indicate an error.
196 if (!error && errno != 0)
197 error = errno;
198
199 if (!fts_close(fts)) {
200 // If we already have an error, let's use that error instead of the error
201 // fts_close set.
202 if (!error)
203 error = errno;
204 }
205
206 if (error) {
207 LOG(ERROR) << "CopyDirectory(): " << strerror(error);
208 return false;
209 }
210 return true;
[email protected]b2e97292008-09-02 18:20:34211}
212
213bool PathExists(const std::wstring& path) {
214 struct stat64 file_info;
215 return (stat64(WideToUTF8(path).c_str(), &file_info) == 0);
216}
217
[email protected]806b9c62008-09-11 16:09:11218bool DirectoryExists(const std::wstring& path) {
219 struct stat64 file_info;
220 if (stat64(WideToUTF8(path).c_str(), &file_info) == 0)
221 return S_ISDIR(file_info.st_mode);
222 return false;
223}
224
[email protected]b2e97292008-09-02 18:20:34225// TODO(erikkay): implement
226#if 0
227bool GetFileCreationLocalTimeFromHandle(int fd,
228 LPSYSTEMTIME creation_time) {
229 if (!file_handle)
230 return false;
231
232 FILETIME utc_filetime;
233 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
234 return false;
235
236 FILETIME local_filetime;
237 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
238 return false;
239
240 return !!FileTimeToSystemTime(&local_filetime, creation_time);
241}
242
243bool GetFileCreationLocalTime(const std::string& filename,
244 LPSYSTEMTIME creation_time) {
245 ScopedHandle file_handle(
246 CreateFile(filename.c_str(), GENERIC_READ,
247 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
248 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
249 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
250}
251#endif
252
253bool CreateTemporaryFileName(std::wstring* temp_file) {
254 std::wstring tmpdir;
255 if (!GetTempDir(&tmpdir))
256 return false;
[email protected]778e8c52008-09-11 17:36:23257 AppendToPath(&tmpdir, kTempFileName);
258 std::string tmpdir_string = WideToUTF8(tmpdir);
259 // this should be OK since mkstemp just replaces characters in place
260 char* buffer = const_cast<char*>(tmpdir_string.c_str());
261 int fd = mkstemp(buffer);
[email protected]b2e97292008-09-02 18:20:34262 if (fd < 0)
263 return false;
[email protected]778e8c52008-09-11 17:36:23264 *temp_file = UTF8ToWide(buffer);
265 close(fd);
[email protected]b2e97292008-09-02 18:20:34266 return true;
267}
268
269bool CreateNewTempDirectory(const std::wstring& prefix,
270 std::wstring* new_temp_path) {
271 std::wstring tmpdir;
272 if (!GetTempDir(&tmpdir))
273 return false;
[email protected]778e8c52008-09-11 17:36:23274 AppendToPath(&tmpdir, kTempFileName);
275 std::string tmpdir_string = WideToUTF8(tmpdir);
[email protected]b2e97292008-09-02 18:20:34276 // this should be OK since mkdtemp just replaces characters in place
[email protected]778e8c52008-09-11 17:36:23277 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]b2e97292008-09-02 18:20:34278 char* dtemp = mkdtemp(buffer);
279 if (!dtemp)
280 return false;
281 *new_temp_path = UTF8ToWide(dtemp);
282 return true;
283}
284
285bool CreateDirectory(const std::wstring& full_path) {
286 std::vector<std::wstring> components;
287 PathComponents(full_path, &components);
288 std::wstring path;
289 std::vector<std::wstring>::iterator i = components.begin();
290 for (; i != components.end(); ++i) {
291 if (path.length() == 0)
292 path = *i;
293 else
294 AppendToPath(&path, *i);
[email protected]806b9c62008-09-11 16:09:11295 if (!DirectoryExists(path)) {
[email protected]b2e97292008-09-02 18:20:34296 if (mkdir(WideToUTF8(path).c_str(), 0777) != 0)
297 return false;
298 }
299 }
300 return true;
301}
302
[email protected]f5e3da4d2008-09-26 01:04:08303bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
[email protected]b2e97292008-09-02 18:20:34304 struct stat64 file_info;
305 if (stat64(WideToUTF8(file_path).c_str(), &file_info) != 0)
306 return false;
[email protected]f5e3da4d2008-09-26 01:04:08307 results->is_directory = S_ISDIR(file_info.st_mode);
308 results->size = file_info.st_size;
[email protected]b2e97292008-09-02 18:20:34309 return true;
310}
311
312int ReadFile(const std::wstring& filename, char* data, int size) {
313 int fd = open(WideToUTF8(filename).c_str(), O_RDONLY);
314 if (fd < 0)
315 return -1;
316
317 int ret_value = read(fd, data, size);
318 close(fd);
319 return ret_value;
320}
321
322int WriteFile(const std::wstring& filename, const char* data, int size) {
[email protected]778e8c52008-09-11 17:36:23323 int fd = creat(WideToUTF8(filename).c_str(), 0666);
[email protected]b2e97292008-09-02 18:20:34324 if (fd < 0)
325 return -1;
[email protected]778e8c52008-09-11 17:36:23326
327 // Allow for partial writes
328 ssize_t bytes_written_total = 0;
329 do {
330 ssize_t bytes_written_partial = write(fd,
331 data + bytes_written_total,
332 size - bytes_written_total);
333 if (bytes_written_partial < 0) {
334 close(fd);
335 return -1;
336 }
337 bytes_written_total += bytes_written_partial;
338 } while (bytes_written_total < size);
339
[email protected]b2e97292008-09-02 18:20:34340 close(fd);
[email protected]778e8c52008-09-11 17:36:23341 return bytes_written_total;
[email protected]b2e97292008-09-02 18:20:34342}
343
344// Gets the current working directory for the process.
345bool GetCurrentDirectory(std::wstring* dir) {
346 char system_buffer[PATH_MAX] = "";
347 getcwd(system_buffer, sizeof(system_buffer));
348 *dir = UTF8ToWide(system_buffer);
349 return true;
350}
351
352// Sets the current working directory for the process.
353bool SetCurrentDirectory(const std::wstring& current_directory) {
354 int ret = chdir(WideToUTF8(current_directory).c_str());
355 return (ret == 0);
356}
357
358FileEnumerator::FileEnumerator(const std::wstring& root_path,
359 bool recursive,
360 FileEnumerator::FILE_TYPE file_type)
361 : recursive_(recursive),
362 file_type_(file_type),
363 is_in_find_op_(false),
364 fts_(NULL) {
365 pending_paths_.push(root_path);
366}
367
368FileEnumerator::FileEnumerator(const std::wstring& root_path,
369 bool recursive,
370 FileEnumerator::FILE_TYPE file_type,
371 const std::wstring& pattern)
372 : recursive_(recursive),
373 file_type_(file_type),
374 pattern_(root_path),
375 is_in_find_op_(false),
376 fts_(NULL) {
377 // The Windows version of this code only matches against items in the top-most
378 // directory, and we're comparing fnmatch against full paths, so this is the
379 // easiest way to get the right pattern.
380 AppendToPath(&pattern_, pattern);
381 pending_paths_.push(root_path);
382}
383
384FileEnumerator::~FileEnumerator() {
385 if (fts_)
386 fts_close(fts_);
387}
388
389// As it stands, this method calls itself recursively when the next item of
390// the fts enumeration doesn't match (type, pattern, etc.). In the case of
391// large directories with many files this can be quite deep.
392// TODO(erikkay) - get rid of this recursive pattern
393std::wstring FileEnumerator::Next() {
394 if (!is_in_find_op_) {
395 if (pending_paths_.empty())
396 return std::wstring();
397
398 // The last find FindFirstFile operation is done, prepare a new one.
399 root_path_ = pending_paths_.top();
400 TrimTrailingSeparator(&root_path_);
401 pending_paths_.pop();
402
403 // Start a new find operation.
404 int ftsflags = FTS_LOGICAL;
405 char top_dir[PATH_MAX];
406 base::strlcpy(top_dir, WideToUTF8(root_path_).c_str(), sizeof(top_dir));
407 char* dir_list[2] = { top_dir, NULL };
408 fts_ = fts_open(dir_list, ftsflags, NULL);
409 if (!fts_)
410 return Next();
411 is_in_find_op_ = true;
412 }
413
414 FTSENT* fts_ent = fts_read(fts_);
415 if (fts_ent == NULL) {
416 fts_close(fts_);
417 fts_ = NULL;
418 is_in_find_op_ = false;
419 return Next();
420 }
421
422 // Level 0 is the top, which is always skipped.
423 if (fts_ent->fts_level == 0)
424 return Next();
425
426 // Patterns are only matched on the items in the top-most directory.
427 // (see Windows implementation)
428 if (fts_ent->fts_level == 1 && pattern_.length() > 0) {
[email protected]778e8c52008-09-11 17:36:23429 if (fnmatch(WideToUTF8(pattern_).c_str(), fts_ent->fts_path, 0) != 0) {
[email protected]b2e97292008-09-02 18:20:34430 if (fts_ent->fts_info == FTS_D)
431 fts_set(fts_, fts_ent, FTS_SKIP);
432 return Next();
433 }
434 }
435
436 std::wstring cur_file(UTF8ToWide(fts_ent->fts_path));
437 if (fts_ent->fts_info == FTS_D) {
438 // If not recursive, then prune children.
439 if (!recursive_)
440 fts_set(fts_, fts_ent, FTS_SKIP);
441 return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
442 } else if (fts_ent->fts_info == FTS_F) {
443 return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
444 }
445 // TODO(erikkay) - verify that the other fts_info types aren't interesting
446 return Next();
447}
448
449
450} // namespace file_util