blob: a0c6cb6addb5a58a10e3b0274ef6b82ac7c9de2d [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]836f1342008-10-01 17:40:1312#include <stdio.h>
[email protected]21dec3872008-09-18 19:15:5413#include <string.h>
[email protected]b2e97292008-09-02 18:20:3414#include <sys/errno.h>
15#include <sys/stat.h>
16#include <time.h>
17
18#include <fstream>
19
20#include "base/basictypes.h"
[email protected]640517f2008-10-30 23:54:0421#include "base/file_path.h"
[email protected]b2e97292008-09-02 18:20:3422#include "base/logging.h"
23#include "base/string_util.h"
24
25namespace file_util {
26
[email protected]392264c2008-11-11 00:01:3827static const char* kTempFileName = "com.google.chrome.XXXXXX";
[email protected]778e8c52008-09-11 17:36:2328
[email protected]b2e97292008-09-02 18:20:3429std::wstring GetDirectoryFromPath(const std::wstring& path) {
30 if (EndsWithSeparator(path)) {
31 std::wstring dir = path;
32 TrimTrailingSeparator(&dir);
33 return dir;
34 } else {
35 char full_path[PATH_MAX];
36 base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path));
37 return UTF8ToWide(dirname(full_path));
38 }
39}
40
[email protected]640517f2008-10-30 23:54:0441bool AbsolutePath(FilePath* path) {
[email protected]b2e97292008-09-02 18:20:3442 char full_path[PATH_MAX];
[email protected]640517f2008-10-30 23:54:0443 if (realpath(path->value().c_str(), full_path) == NULL)
[email protected]b2e97292008-09-02 18:20:3444 return false;
[email protected]640517f2008-10-30 23:54:0445 *path = FilePath(full_path);
[email protected]b2e97292008-09-02 18:20:3446 return true;
47}
48
49// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
50// which works both with and without the recursive flag. I'm not sure we need
51// that functionality. If not, remove from file_util_win.cc, otherwise add it
52// here.
[email protected]640517f2008-10-30 23:54:0453bool Delete(const FilePath& path, bool recursive) {
54 const char* path_str = path.value().c_str();
[email protected]b2e97292008-09-02 18:20:3455 struct stat64 file_info;
[email protected]640517f2008-10-30 23:54:0456 int test = stat64(path_str, &file_info);
[email protected]b2e97292008-09-02 18:20:3457 if (test != 0) {
58 // The Windows version defines this condition as success.
59 bool ret = (errno == ENOENT || errno == ENOTDIR);
60 return ret;
61 }
62 if (!S_ISDIR(file_info.st_mode))
[email protected]640517f2008-10-30 23:54:0463 return (unlink(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:3464 if (!recursive)
[email protected]640517f2008-10-30 23:54:0465 return (rmdir(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:3466
67 bool success = true;
68 int ftsflags = FTS_PHYSICAL | FTS_NOSTAT;
69 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:0470 if (base::strlcpy(top_dir, path_str,
[email protected]21dec3872008-09-18 19:15:5471 arraysize(top_dir)) >= arraysize(top_dir)) {
72 return false;
73 }
[email protected]b2e97292008-09-02 18:20:3474 char* dir_list[2] = { top_dir, NULL };
75 FTS* fts = fts_open(dir_list, ftsflags, NULL);
76 if (fts) {
77 FTSENT* fts_ent = fts_read(fts);
78 while (success && fts_ent != NULL) {
79 switch (fts_ent->fts_info) {
80 case FTS_DNR:
81 case FTS_ERR:
82 // log error
83 success = false;
84 continue;
85 break;
86 case FTS_DP:
87 rmdir(fts_ent->fts_accpath);
88 break;
89 case FTS_D:
90 break;
91 case FTS_NSOK:
92 case FTS_F:
93 case FTS_SL:
94 case FTS_SLNONE:
95 unlink(fts_ent->fts_accpath);
96 break;
97 default:
98 DCHECK(false);
99 break;
100 }
101 fts_ent = fts_read(fts);
102 }
103 fts_close(fts);
104 }
105 return success;
106}
107
[email protected]640517f2008-10-30 23:54:04108bool Move(const FilePath& from_path, const FilePath& to_path) {
109 return (rename(from_path.value().c_str(),
110 to_path.value().c_str()) == 0);
[email protected]b2e97292008-09-02 18:20:34111}
112
[email protected]640517f2008-10-30 23:54:04113bool CopyDirectory(const FilePath& from_path,
114 const FilePath& to_path,
[email protected]21dec3872008-09-18 19:15:54115 bool recursive) {
[email protected]21dec3872008-09-18 19:15:54116 // Some old callers of CopyDirectory want it to support wildcards.
117 // After some discussion, we decided to fix those callers.
118 // Break loudly here if anyone tries to do this.
119 // TODO(evanm): remove this once we're sure it's ok.
[email protected]640517f2008-10-30 23:54:04120 DCHECK(to_path.value().find('*') == std::string::npos);
121 DCHECK(from_path.value().find('*') == std::string::npos);
[email protected]21dec3872008-09-18 19:15:54122
123 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04124 if (base::strlcpy(top_dir, from_path.value().c_str(),
[email protected]21dec3872008-09-18 19:15:54125 arraysize(top_dir)) >= arraysize(top_dir)) {
126 return false;
127 }
128
129 char* dir_list[] = { top_dir, NULL };
130 FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL);
131 if (!fts) {
132 LOG(ERROR) << "fts_open failed: " << strerror(errno);
133 return false;
134 }
135
136 int error = 0;
137 FTSENT* ent;
138 while (!error && (ent = fts_read(fts)) != NULL) {
139 // ent->fts_path is the source path, including from_path, so paste
140 // the suffix after from_path onto to_path to create the target_path.
[email protected]640517f2008-10-30 23:54:04141 const std::string target_path =
142 to_path.value() + &ent->fts_path[from_path.value().size()];
[email protected]21dec3872008-09-18 19:15:54143 switch (ent->fts_info) {
144 case FTS_D: // Preorder directory.
145 // If we encounter a subdirectory in a non-recursive copy, prune it
146 // from the traversal.
147 if (!recursive && ent->fts_level > 0) {
148 if (fts_set(fts, ent, FTS_SKIP) != 0)
149 error = errno;
150 continue;
151 }
152
153 // Try creating the target dir, continuing on it if it exists already.
154 if (mkdir(target_path.c_str(), 0777) != 0) {
155 if (errno != EEXIST)
156 error = errno;
157 }
158 break;
159 case FTS_F: // Regular file.
160 case FTS_NSOK: // File, no stat info requested.
161 // TODO(port): use a native file path rather than all these
162 // conversions.
163 errno = 0;
164 if (!CopyFile(UTF8ToWide(ent->fts_path), UTF8ToWide(target_path)))
165 error = errno ? errno : EINVAL;
166 break;
167 case FTS_DP: // Postorder directory.
168 case FTS_DOT: // "." or ".."
169 // Skip it.
170 continue;
171 case FTS_DC: // Directory causing a cycle.
172 // Skip this branch.
173 if (fts_set(fts, ent, FTS_SKIP) != 0)
174 error = errno;
175 break;
176 case FTS_DNR: // Directory cannot be read.
177 case FTS_ERR: // Error.
178 case FTS_NS: // Stat failed.
179 // Abort with the error.
180 error = ent->fts_errno;
181 break;
182 case FTS_SL: // Symlink.
183 case FTS_SLNONE: // Symlink with broken target.
184 LOG(WARNING) << "CopyDirectory() skipping symbolic link.";
185 continue;
186 case FTS_DEFAULT: // Some other sort of file.
187 LOG(WARNING) << "CopyDirectory() skipping weird file.";
188 continue;
189 default:
190 NOTREACHED();
191 continue; // Hope for the best!
192 }
193 }
194 // fts_read may have returned NULL and set errno to indicate an error.
195 if (!error && errno != 0)
196 error = errno;
197
198 if (!fts_close(fts)) {
199 // If we already have an error, let's use that error instead of the error
200 // fts_close set.
201 if (!error)
202 error = errno;
203 }
204
205 if (error) {
206 LOG(ERROR) << "CopyDirectory(): " << strerror(error);
207 return false;
208 }
209 return true;
[email protected]b2e97292008-09-02 18:20:34210}
211
[email protected]640517f2008-10-30 23:54:04212bool PathExists(const FilePath& path) {
[email protected]b2e97292008-09-02 18:20:34213 struct stat64 file_info;
[email protected]640517f2008-10-30 23:54:04214 return (stat64(path.value().c_str(), &file_info) == 0);
[email protected]b2e97292008-09-02 18:20:34215}
216
[email protected]640517f2008-10-30 23:54:04217bool DirectoryExists(const FilePath& path) {
[email protected]806b9c62008-09-11 16:09:11218 struct stat64 file_info;
[email protected]640517f2008-10-30 23:54:04219 if (stat64(path.value().c_str(), &file_info) == 0)
[email protected]806b9c62008-09-11 16:09:11220 return S_ISDIR(file_info.st_mode);
221 return false;
222}
223
[email protected]b2e97292008-09-02 18:20:34224// TODO(erikkay): implement
225#if 0
226bool GetFileCreationLocalTimeFromHandle(int fd,
227 LPSYSTEMTIME creation_time) {
228 if (!file_handle)
229 return false;
230
231 FILETIME utc_filetime;
232 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
233 return false;
234
235 FILETIME local_filetime;
236 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
237 return false;
238
239 return !!FileTimeToSystemTime(&local_filetime, creation_time);
240}
241
242bool GetFileCreationLocalTime(const std::string& filename,
243 LPSYSTEMTIME creation_time) {
244 ScopedHandle file_handle(
245 CreateFile(filename.c_str(), GENERIC_READ,
246 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
247 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
248 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
249}
250#endif
251
[email protected]392264c2008-11-11 00:01:38252bool CreateTemporaryFileName(FilePath* path) {
253 if (!GetTempDir(path))
[email protected]b2e97292008-09-02 18:20:34254 return false;
[email protected]392264c2008-11-11 00:01:38255
256 *path = path->Append(kTempFileName);
257 std::string tmpdir_string = path->value();
[email protected]778e8c52008-09-11 17:36:23258 // this should be OK since mkstemp just replaces characters in place
259 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]392264c2008-11-11 00:01:38260
[email protected]778e8c52008-09-11 17:36:23261 int fd = mkstemp(buffer);
[email protected]b2e97292008-09-02 18:20:34262 if (fd < 0)
263 return false;
[email protected]392264c2008-11-11 00:01:38264
265 close(fd);
[email protected]b2e97292008-09-02 18:20:34266 return true;
267}
268
[email protected]9ccbb372008-10-10 18:50:32269bool CreateTemporaryFileNameInDir(const std::wstring& dir,
270 std::wstring* temp_file) {
271 // Not implemented yet.
272 NOTREACHED();
273 return false;
274}
275
[email protected]b2e97292008-09-02 18:20:34276bool CreateNewTempDirectory(const std::wstring& prefix,
277 std::wstring* new_temp_path) {
[email protected]392264c2008-11-11 00:01:38278 FilePath tmpdir;
[email protected]b2e97292008-09-02 18:20:34279 if (!GetTempDir(&tmpdir))
280 return false;
[email protected]392264c2008-11-11 00:01:38281 tmpdir = tmpdir.Append(kTempFileName);
282 std::string tmpdir_string = tmpdir.value();
[email protected]b2e97292008-09-02 18:20:34283 // this should be OK since mkdtemp just replaces characters in place
[email protected]778e8c52008-09-11 17:36:23284 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]b2e97292008-09-02 18:20:34285 char* dtemp = mkdtemp(buffer);
286 if (!dtemp)
287 return false;
288 *new_temp_path = UTF8ToWide(dtemp);
289 return true;
290}
291
[email protected]640517f2008-10-30 23:54:04292bool CreateDirectory(const FilePath& full_path) {
293 std::vector<FilePath> subpaths;
294
295 // Collect a list of all parent directories.
296 FilePath last_path = full_path;
297 subpaths.push_back(full_path);
298 for (FilePath path = full_path.DirName();
299 path.value() != last_path.value(); path = path.DirName()) {
300 subpaths.push_back(path);
301 last_path = path;
302 }
303
304 // Iterate through the parents and create the missing ones.
305 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
306 i != subpaths.rend(); ++i) {
307 if (!DirectoryExists(*i)) {
308 if (mkdir(i->value().c_str(), 0777) != 0)
[email protected]b2e97292008-09-02 18:20:34309 return false;
310 }
311 }
312 return true;
313}
314
[email protected]eac0709a2008-11-04 21:00:46315bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
[email protected]b2e97292008-09-02 18:20:34316 struct stat64 file_info;
[email protected]eac0709a2008-11-04 21:00:46317 if (stat64(file_path.value().c_str(), &file_info) != 0)
[email protected]b2e97292008-09-02 18:20:34318 return false;
[email protected]f5e3da4d2008-09-26 01:04:08319 results->is_directory = S_ISDIR(file_info.st_mode);
320 results->size = file_info.st_size;
[email protected]b2e97292008-09-02 18:20:34321 return true;
322}
323
[email protected]836f1342008-10-01 17:40:13324FILE* OpenFile(const std::string& filename, const char* mode) {
325 return fopen(filename.c_str(), mode);
326}
327
328FILE* OpenFile(const std::wstring& filename, const char* mode) {
329 return fopen(WideToUTF8(filename).c_str(), mode);
330}
331
[email protected]b2e97292008-09-02 18:20:34332int ReadFile(const std::wstring& filename, char* data, int size) {
333 int fd = open(WideToUTF8(filename).c_str(), O_RDONLY);
334 if (fd < 0)
335 return -1;
336
337 int ret_value = read(fd, data, size);
338 close(fd);
339 return ret_value;
340}
341
342int WriteFile(const std::wstring& filename, const char* data, int size) {
[email protected]778e8c52008-09-11 17:36:23343 int fd = creat(WideToUTF8(filename).c_str(), 0666);
[email protected]b2e97292008-09-02 18:20:34344 if (fd < 0)
345 return -1;
[email protected]778e8c52008-09-11 17:36:23346
347 // Allow for partial writes
348 ssize_t bytes_written_total = 0;
349 do {
350 ssize_t bytes_written_partial = write(fd,
351 data + bytes_written_total,
352 size - bytes_written_total);
353 if (bytes_written_partial < 0) {
354 close(fd);
355 return -1;
356 }
357 bytes_written_total += bytes_written_partial;
358 } while (bytes_written_total < size);
359
[email protected]b2e97292008-09-02 18:20:34360 close(fd);
[email protected]778e8c52008-09-11 17:36:23361 return bytes_written_total;
[email protected]b2e97292008-09-02 18:20:34362}
363
364// Gets the current working directory for the process.
[email protected]640517f2008-10-30 23:54:04365bool GetCurrentDirectory(FilePath* dir) {
[email protected]b2e97292008-09-02 18:20:34366 char system_buffer[PATH_MAX] = "";
[email protected]640517f2008-10-30 23:54:04367 if (!getcwd(system_buffer, sizeof(system_buffer))) {
368 NOTREACHED();
369 return false;
370 }
371 *dir = FilePath(system_buffer);
[email protected]b2e97292008-09-02 18:20:34372 return true;
373}
374
375// Sets the current working directory for the process.
376bool SetCurrentDirectory(const std::wstring& current_directory) {
377 int ret = chdir(WideToUTF8(current_directory).c_str());
378 return (ret == 0);
379}
380
381FileEnumerator::FileEnumerator(const std::wstring& root_path,
382 bool recursive,
383 FileEnumerator::FILE_TYPE file_type)
384 : recursive_(recursive),
385 file_type_(file_type),
386 is_in_find_op_(false),
387 fts_(NULL) {
388 pending_paths_.push(root_path);
389}
390
391FileEnumerator::FileEnumerator(const std::wstring& root_path,
392 bool recursive,
393 FileEnumerator::FILE_TYPE file_type,
394 const std::wstring& pattern)
395 : recursive_(recursive),
396 file_type_(file_type),
397 pattern_(root_path),
398 is_in_find_op_(false),
399 fts_(NULL) {
400 // The Windows version of this code only matches against items in the top-most
401 // directory, and we're comparing fnmatch against full paths, so this is the
402 // easiest way to get the right pattern.
403 AppendToPath(&pattern_, pattern);
404 pending_paths_.push(root_path);
405}
406
407FileEnumerator::~FileEnumerator() {
408 if (fts_)
409 fts_close(fts_);
410}
411
412// As it stands, this method calls itself recursively when the next item of
413// the fts enumeration doesn't match (type, pattern, etc.). In the case of
414// large directories with many files this can be quite deep.
415// TODO(erikkay) - get rid of this recursive pattern
416std::wstring FileEnumerator::Next() {
417 if (!is_in_find_op_) {
418 if (pending_paths_.empty())
419 return std::wstring();
420
421 // The last find FindFirstFile operation is done, prepare a new one.
422 root_path_ = pending_paths_.top();
423 TrimTrailingSeparator(&root_path_);
424 pending_paths_.pop();
425
426 // Start a new find operation.
427 int ftsflags = FTS_LOGICAL;
428 char top_dir[PATH_MAX];
429 base::strlcpy(top_dir, WideToUTF8(root_path_).c_str(), sizeof(top_dir));
430 char* dir_list[2] = { top_dir, NULL };
431 fts_ = fts_open(dir_list, ftsflags, NULL);
432 if (!fts_)
433 return Next();
434 is_in_find_op_ = true;
435 }
436
437 FTSENT* fts_ent = fts_read(fts_);
438 if (fts_ent == NULL) {
439 fts_close(fts_);
440 fts_ = NULL;
441 is_in_find_op_ = false;
442 return Next();
443 }
444
445 // Level 0 is the top, which is always skipped.
446 if (fts_ent->fts_level == 0)
447 return Next();
448
449 // Patterns are only matched on the items in the top-most directory.
450 // (see Windows implementation)
451 if (fts_ent->fts_level == 1 && pattern_.length() > 0) {
[email protected]778e8c52008-09-11 17:36:23452 if (fnmatch(WideToUTF8(pattern_).c_str(), fts_ent->fts_path, 0) != 0) {
[email protected]b2e97292008-09-02 18:20:34453 if (fts_ent->fts_info == FTS_D)
454 fts_set(fts_, fts_ent, FTS_SKIP);
455 return Next();
456 }
457 }
458
459 std::wstring cur_file(UTF8ToWide(fts_ent->fts_path));
460 if (fts_ent->fts_info == FTS_D) {
461 // If not recursive, then prune children.
462 if (!recursive_)
463 fts_set(fts_, fts_ent, FTS_SKIP);
464 return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
465 } else if (fts_ent->fts_info == FTS_F) {
466 return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
467 }
468 // TODO(erikkay) - verify that the other fts_info types aren't interesting
469 return Next();
470}
471
472
473} // namespace file_util