blob: 58454cfac5385862f956da2051e79db4e557b3e1 [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]4b7743de2009-04-21 01:50:397#include <dirent.h>
[email protected]21dec3872008-09-18 19:15:548#include <errno.h>
[email protected]b2e97292008-09-02 18:20:349#include <fcntl.h>
10#include <fnmatch.h>
11#include <fts.h>
12#include <libgen.h>
[email protected]836f1342008-10-01 17:40:1313#include <stdio.h>
[email protected]21dec3872008-09-18 19:15:5414#include <string.h>
[email protected]b2e97292008-09-02 18:20:3415#include <sys/errno.h>
[email protected]7856bb82008-12-12 23:43:0316#include <sys/mman.h>
[email protected]b2e97292008-09-02 18:20:3417#include <sys/stat.h>
[email protected]4b7743de2009-04-21 01:50:3918#include <sys/types.h>
[email protected]b2e97292008-09-02 18:20:3419#include <time.h>
[email protected]4b7743de2009-04-21 01:50:3920#include <unistd.h>
[email protected]b2e97292008-09-02 18:20:3421
22#include <fstream>
23
24#include "base/basictypes.h"
[email protected]157c61b2009-05-01 21:37:3125#include "base/eintr_wrapper.h"
[email protected]640517f2008-10-30 23:54:0426#include "base/file_path.h"
[email protected]807bc042009-07-15 01:32:0227#include "base/lock.h"
[email protected]b2e97292008-09-02 18:20:3428#include "base/logging.h"
[email protected]807bc042009-07-15 01:32:0229#include "base/scoped_ptr.h"
30#include "base/singleton.h"
[email protected]b2e97292008-09-02 18:20:3431#include "base/string_util.h"
[email protected]807bc042009-07-15 01:32:0232#include "base/sys_string_conversions.h"
[email protected]4b7743de2009-04-21 01:50:3933#include "base/time.h"
[email protected]807bc042009-07-15 01:32:0234#include "unicode/coll.h"
[email protected]b2e97292008-09-02 18:20:3435
[email protected]172c5502009-06-24 03:29:2636namespace {
37
38bool IsDirectory(const FTSENT* file) {
39 switch (file->fts_info) {
40 case FTS_D:
41 case FTS_DC:
42 case FTS_DNR:
43 case FTS_DOT:
44 case FTS_DP:
45 return true;
46 default:
47 return false;
48 }
49}
50
[email protected]807bc042009-07-15 01:32:0251class LocaleAwareComparator {
52 public:
53 LocaleAwareComparator() {
54 UErrorCode error_code = U_ZERO_ERROR;
55 // Use the default collator. The default locale should have been properly
56 // set by the time this constructor is called.
57 collator_.reset(Collator::createInstance(error_code));
58 DCHECK(U_SUCCESS(error_code));
59 // Make it case-sensitive.
60 collator_->setStrength(Collator::TERTIARY);
61 // Note: We do not set UCOL_NORMALIZATION_MODE attribute. In other words, we
62 // do not pay performance penalty to guarantee sort order correctness for
63 // non-FCD (http://unicode.org/notes/tn5/#FCD) file names. This should be a
64 // reasonable tradeoff because such file names should be rare and the sort
65 // order doesn't change much anyway.
66 }
67
68 // Note: A similar function is available in l10n_util.
69 // We cannot use it because base should not depend on l10n_util.
70 // TODO(yuzo): Move some of l10n_util to base.
71 int Compare(const string16& a, const string16& b) {
72 // We are not sure if Collator::compare is thread-safe.
73 // Use an AutoLock just in case.
74 AutoLock auto_lock(lock_);
75
76 UErrorCode error_code = U_ZERO_ERROR;
77 UCollationResult result = collator_->compare(
78 static_cast<const UChar*>(a.c_str()),
79 static_cast<int>(a.length()),
80 static_cast<const UChar*>(b.c_str()),
81 static_cast<int>(b.length()),
82 error_code);
83 DCHECK(U_SUCCESS(error_code));
84 return result;
85 }
86
87 private:
88 scoped_ptr<Collator> collator_;
89 Lock lock_;
90 friend struct DefaultSingletonTraits<LocaleAwareComparator>;
91
92 DISALLOW_COPY_AND_ASSIGN(LocaleAwareComparator);
93};
94
95int CompareFiles(const FTSENT** a, const FTSENT** b) {
96 // Order lexicographically with directories before other files.
97 const bool a_is_dir = IsDirectory(*a);
98 const bool b_is_dir = IsDirectory(*b);
99 if (a_is_dir != b_is_dir)
100 return a_is_dir ? -1 : 1;
101
102 // On linux, the file system encoding is not defined. We assume
103 // SysNativeMBToWide takes care of it.
104 //
105 // ICU's collator can take strings in OS native encoding. But we convert the
106 // strings to UTF-16 ourselves to ensure conversion consistency.
107 // TODO(yuzo): Perhaps we should define SysNativeMBToUTF16?
108 return Singleton<LocaleAwareComparator>()->Compare(
109 WideToUTF16(base::SysNativeMBToWide((*a)->fts_name)),
110 WideToUTF16(base::SysNativeMBToWide((*b)->fts_name)));
111}
112
[email protected]172c5502009-06-24 03:29:26113} // namespace
114
[email protected]b2e97292008-09-02 18:20:34115namespace file_util {
116
[email protected]22a087f2009-03-17 19:17:43117#if defined(GOOGLE_CHROME_BUILD)
[email protected]392264c2008-11-11 00:01:38118static const char* kTempFileName = "com.google.chrome.XXXXXX";
[email protected]22a087f2009-03-17 19:17:43119#else
120static const char* kTempFileName = "org.chromium.XXXXXX";
121#endif
[email protected]778e8c52008-09-11 17:36:23122
[email protected]b2e97292008-09-02 18:20:34123std::wstring GetDirectoryFromPath(const std::wstring& path) {
124 if (EndsWithSeparator(path)) {
[email protected]a97f2b42009-04-21 23:15:45125 std::wstring dir = path;
126 TrimTrailingSeparator(&dir);
127 return dir;
[email protected]b2e97292008-09-02 18:20:34128 } else {
129 char full_path[PATH_MAX];
130 base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path));
131 return UTF8ToWide(dirname(full_path));
132 }
133}
[email protected]9e51af92009-02-04 00:58:39134
[email protected]640517f2008-10-30 23:54:04135bool AbsolutePath(FilePath* path) {
[email protected]b2e97292008-09-02 18:20:34136 char full_path[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04137 if (realpath(path->value().c_str(), full_path) == NULL)
[email protected]b2e97292008-09-02 18:20:34138 return false;
[email protected]640517f2008-10-30 23:54:04139 *path = FilePath(full_path);
[email protected]b2e97292008-09-02 18:20:34140 return true;
141}
142
[email protected]4b7743de2009-04-21 01:50:39143int CountFilesCreatedAfter(const FilePath& path,
144 const base::Time& comparison_time) {
145 int file_count = 0;
146
147 DIR* dir = opendir(path.value().c_str());
148 if (dir) {
[email protected]2126577b2009-04-23 15:05:19149 struct dirent ent_buf;
[email protected]4b7743de2009-04-21 01:50:39150 struct dirent* ent;
[email protected]2126577b2009-04-23 15:05:19151 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
[email protected]4b7743de2009-04-21 01:50:39152 if ((strcmp(ent->d_name, ".") == 0) ||
153 (strcmp(ent->d_name, "..") == 0))
154 continue;
155
156 struct stat64 st;
157 int test = stat64(path.Append(ent->d_name).value().c_str(), &st);
158 if (test != 0) {
159 LOG(ERROR) << "stat64 failed: " << strerror(errno);
160 continue;
161 }
[email protected]2126577b2009-04-23 15:05:19162 // Here, we use Time::TimeT(), which discards microseconds. This
163 // means that files which are newer than |comparison_time| may
164 // be considered older. If we don't discard microseconds, it
165 // introduces another issue. Suppose the following case:
166 //
167 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
168 // 2. Create a file and the current time is 10.3 (secs).
169 //
170 // As POSIX doesn't have microsecond precision for |st_ctime|,
171 // the creation time of the file created in the step 2 is 10 and
172 // the file is considered older than |comparison_time|. After
173 // all, we may have to accept either of the two issues: 1. files
174 // which are older than |comparison_time| are considered newer
175 // (current implementation) 2. files newer than
176 // |comparison_time| are considered older.
[email protected]4b7743de2009-04-21 01:50:39177 if (st.st_ctime >= comparison_time.ToTimeT())
178 ++file_count;
179 }
180 closedir(dir);
181 }
182 return file_count;
183}
184
[email protected]b2e97292008-09-02 18:20:34185// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
186// which works both with and without the recursive flag. I'm not sure we need
187// that functionality. If not, remove from file_util_win.cc, otherwise add it
188// here.
[email protected]640517f2008-10-30 23:54:04189bool Delete(const FilePath& path, bool recursive) {
190 const char* path_str = path.value().c_str();
[email protected]b2e97292008-09-02 18:20:34191 struct stat64 file_info;
[email protected]640517f2008-10-30 23:54:04192 int test = stat64(path_str, &file_info);
[email protected]b2e97292008-09-02 18:20:34193 if (test != 0) {
194 // The Windows version defines this condition as success.
[email protected]9e51af92009-02-04 00:58:39195 bool ret = (errno == ENOENT || errno == ENOTDIR);
[email protected]b2e97292008-09-02 18:20:34196 return ret;
197 }
198 if (!S_ISDIR(file_info.st_mode))
[email protected]640517f2008-10-30 23:54:04199 return (unlink(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34200 if (!recursive)
[email protected]640517f2008-10-30 23:54:04201 return (rmdir(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34202
203 bool success = true;
204 int ftsflags = FTS_PHYSICAL | FTS_NOSTAT;
205 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04206 if (base::strlcpy(top_dir, path_str,
[email protected]21dec3872008-09-18 19:15:54207 arraysize(top_dir)) >= arraysize(top_dir)) {
208 return false;
209 }
[email protected]b2e97292008-09-02 18:20:34210 char* dir_list[2] = { top_dir, NULL };
211 FTS* fts = fts_open(dir_list, ftsflags, NULL);
212 if (fts) {
213 FTSENT* fts_ent = fts_read(fts);
214 while (success && fts_ent != NULL) {
215 switch (fts_ent->fts_info) {
216 case FTS_DNR:
217 case FTS_ERR:
218 // log error
219 success = false;
220 continue;
221 break;
222 case FTS_DP:
[email protected]7e1fde6a2008-12-23 20:20:10223 success = (rmdir(fts_ent->fts_accpath) == 0);
[email protected]b2e97292008-09-02 18:20:34224 break;
225 case FTS_D:
226 break;
227 case FTS_NSOK:
228 case FTS_F:
229 case FTS_SL:
230 case FTS_SLNONE:
[email protected]7e1fde6a2008-12-23 20:20:10231 success = (unlink(fts_ent->fts_accpath) == 0);
[email protected]b2e97292008-09-02 18:20:34232 break;
233 default:
234 DCHECK(false);
235 break;
236 }
237 fts_ent = fts_read(fts);
238 }
239 fts_close(fts);
240 }
241 return success;
242}
243
[email protected]640517f2008-10-30 23:54:04244bool Move(const FilePath& from_path, const FilePath& to_path) {
[email protected]cc7948a2009-03-13 20:01:43245 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
246 return true;
247
248 if (!CopyDirectory(from_path, to_path, true))
249 return false;
250
251 Delete(from_path, true);
252 return true;
[email protected]b2e97292008-09-02 18:20:34253}
254
[email protected]c5866dca2009-05-19 17:21:07255bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
256 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
257}
258
[email protected]640517f2008-10-30 23:54:04259bool CopyDirectory(const FilePath& from_path,
260 const FilePath& to_path,
[email protected]21dec3872008-09-18 19:15:54261 bool recursive) {
[email protected]21dec3872008-09-18 19:15:54262 // Some old callers of CopyDirectory want it to support wildcards.
263 // After some discussion, we decided to fix those callers.
264 // Break loudly here if anyone tries to do this.
265 // TODO(evanm): remove this once we're sure it's ok.
[email protected]640517f2008-10-30 23:54:04266 DCHECK(to_path.value().find('*') == std::string::npos);
267 DCHECK(from_path.value().find('*') == std::string::npos);
[email protected]21dec3872008-09-18 19:15:54268
269 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04270 if (base::strlcpy(top_dir, from_path.value().c_str(),
[email protected]21dec3872008-09-18 19:15:54271 arraysize(top_dir)) >= arraysize(top_dir)) {
272 return false;
273 }
274
275 char* dir_list[] = { top_dir, NULL };
276 FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL);
277 if (!fts) {
278 LOG(ERROR) << "fts_open failed: " << strerror(errno);
279 return false;
280 }
281
282 int error = 0;
283 FTSENT* ent;
284 while (!error && (ent = fts_read(fts)) != NULL) {
285 // ent->fts_path is the source path, including from_path, so paste
286 // the suffix after from_path onto to_path to create the target_path.
[email protected]ca0209612009-01-13 18:57:46287 std::string suffix(&ent->fts_path[from_path.value().size()]);
288 // Strip the leading '/' (if any).
289 if (!suffix.empty()) {
[email protected]6ae340f2009-03-06 09:56:28290 DCHECK_EQ('/', suffix[0]);
[email protected]ca0209612009-01-13 18:57:46291 suffix.erase(0, 1);
292 }
293 const FilePath target_path = to_path.Append(suffix);
[email protected]21dec3872008-09-18 19:15:54294 switch (ent->fts_info) {
295 case FTS_D: // Preorder directory.
296 // If we encounter a subdirectory in a non-recursive copy, prune it
297 // from the traversal.
298 if (!recursive && ent->fts_level > 0) {
299 if (fts_set(fts, ent, FTS_SKIP) != 0)
300 error = errno;
301 continue;
302 }
303
304 // Try creating the target dir, continuing on it if it exists already.
[email protected]39a1a062009-05-12 20:12:24305 if (mkdir(target_path.value().c_str(), 0700) != 0) {
[email protected]21dec3872008-09-18 19:15:54306 if (errno != EEXIST)
307 error = errno;
308 }
309 break;
310 case FTS_F: // Regular file.
311 case FTS_NSOK: // File, no stat info requested.
[email protected]21dec3872008-09-18 19:15:54312 errno = 0;
[email protected]ca0209612009-01-13 18:57:46313 if (!CopyFile(FilePath(ent->fts_path), target_path))
[email protected]21dec3872008-09-18 19:15:54314 error = errno ? errno : EINVAL;
315 break;
316 case FTS_DP: // Postorder directory.
317 case FTS_DOT: // "." or ".."
318 // Skip it.
319 continue;
320 case FTS_DC: // Directory causing a cycle.
321 // Skip this branch.
322 if (fts_set(fts, ent, FTS_SKIP) != 0)
323 error = errno;
324 break;
325 case FTS_DNR: // Directory cannot be read.
326 case FTS_ERR: // Error.
327 case FTS_NS: // Stat failed.
328 // Abort with the error.
329 error = ent->fts_errno;
330 break;
331 case FTS_SL: // Symlink.
332 case FTS_SLNONE: // Symlink with broken target.
[email protected]6ae340f2009-03-06 09:56:28333 LOG(WARNING) << "CopyDirectory() skipping symbolic link: " <<
334 ent->fts_path;
[email protected]21dec3872008-09-18 19:15:54335 continue;
336 case FTS_DEFAULT: // Some other sort of file.
[email protected]6ae340f2009-03-06 09:56:28337 LOG(WARNING) << "CopyDirectory() skipping file of unknown type: " <<
338 ent->fts_path;
[email protected]21dec3872008-09-18 19:15:54339 continue;
340 default:
341 NOTREACHED();
342 continue; // Hope for the best!
343 }
344 }
345 // fts_read may have returned NULL and set errno to indicate an error.
346 if (!error && errno != 0)
347 error = errno;
348
349 if (!fts_close(fts)) {
350 // If we already have an error, let's use that error instead of the error
351 // fts_close set.
352 if (!error)
353 error = errno;
354 }
355
356 if (error) {
357 LOG(ERROR) << "CopyDirectory(): " << strerror(error);
358 return false;
359 }
360 return true;
[email protected]b2e97292008-09-02 18:20:34361}
362
[email protected]640517f2008-10-30 23:54:04363bool PathExists(const FilePath& path) {
[email protected]b2e97292008-09-02 18:20:34364 struct stat64 file_info;
[email protected]640517f2008-10-30 23:54:04365 return (stat64(path.value().c_str(), &file_info) == 0);
[email protected]b2e97292008-09-02 18:20:34366}
367
[email protected]7e1fde6a2008-12-23 20:20:10368bool PathIsWritable(const FilePath& path) {
369 FilePath test_path(path);
370 struct stat64 file_info;
371 if (stat64(test_path.value().c_str(), &file_info) != 0) {
372 // If the path doesn't exist, test the parent dir.
373 test_path = test_path.DirName();
374 // If the parent dir doesn't exist, then return false (the path is not
375 // directly writable).
376 if (stat64(test_path.value().c_str(), &file_info) != 0)
377 return false;
378 }
379 if (S_IWOTH & file_info.st_mode)
380 return true;
381 if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
382 return true;
383 if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
384 return true;
385 return false;
386}
387
[email protected]640517f2008-10-30 23:54:04388bool DirectoryExists(const FilePath& path) {
[email protected]806b9c62008-09-11 16:09:11389 struct stat64 file_info;
[email protected]640517f2008-10-30 23:54:04390 if (stat64(path.value().c_str(), &file_info) == 0)
[email protected]806b9c62008-09-11 16:09:11391 return S_ISDIR(file_info.st_mode);
392 return false;
393}
394
[email protected]b2e97292008-09-02 18:20:34395// TODO(erikkay): implement
396#if 0
397bool GetFileCreationLocalTimeFromHandle(int fd,
398 LPSYSTEMTIME creation_time) {
399 if (!file_handle)
400 return false;
[email protected]9e51af92009-02-04 00:58:39401
[email protected]b2e97292008-09-02 18:20:34402 FILETIME utc_filetime;
403 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
404 return false;
[email protected]9e51af92009-02-04 00:58:39405
[email protected]b2e97292008-09-02 18:20:34406 FILETIME local_filetime;
407 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
408 return false;
[email protected]9e51af92009-02-04 00:58:39409
[email protected]b2e97292008-09-02 18:20:34410 return !!FileTimeToSystemTime(&local_filetime, creation_time);
411}
412
413bool GetFileCreationLocalTime(const std::string& filename,
414 LPSYSTEMTIME creation_time) {
415 ScopedHandle file_handle(
[email protected]9e51af92009-02-04 00:58:39416 CreateFile(filename.c_str(), GENERIC_READ,
[email protected]b2e97292008-09-02 18:20:34417 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
418 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
419 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
420}
421#endif
422
[email protected]45301492009-04-23 12:38:08423bool ReadFromFD(int fd, char* buffer, size_t bytes) {
424 size_t total_read = 0;
425 while (total_read < bytes) {
[email protected]157c61b2009-05-01 21:37:31426 ssize_t bytes_read =
427 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
428 if (bytes_read <= 0)
[email protected]45301492009-04-23 12:38:08429 break;
[email protected]157c61b2009-05-01 21:37:31430 total_read += bytes_read;
[email protected]45301492009-04-23 12:38:08431 }
432 return total_read == bytes;
433}
434
[email protected]9e51af92009-02-04 00:58:39435// Creates and opens a temporary file in |directory|, returning the
436// file descriptor. |path| is set to the temporary file path.
437// Note TODO(erikkay) comment in header for BlahFileName() calls; the
438// intent is to rename these files BlahFile() (since they create
439// files, not filenames). This function does NOT unlink() the file.
440int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
441 *path = directory.Append(kTempFileName);
442 const std::string& tmpdir_string = path->value();
[email protected]778e8c52008-09-11 17:36:23443 // this should be OK since mkstemp just replaces characters in place
444 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]392264c2008-11-11 00:01:38445
[email protected]9e51af92009-02-04 00:58:39446 return mkstemp(buffer);
447}
448
449bool CreateTemporaryFileName(FilePath* path) {
450 FilePath directory;
451 if (!GetTempDir(&directory))
452 return false;
453 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
[email protected]b2e97292008-09-02 18:20:34454 if (fd < 0)
455 return false;
[email protected]392264c2008-11-11 00:01:38456 close(fd);
[email protected]b2e97292008-09-02 18:20:34457 return true;
458}
459
[email protected]9e51af92009-02-04 00:58:39460FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
461 FilePath directory;
462 if (!GetShmemTempDir(&directory))
463 return false;
464
[email protected]6faa0e0d2009-04-28 06:50:36465 return CreateAndOpenTemporaryFileInDir(directory, path);
466}
467
468FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
469 int fd = CreateAndOpenFdForTemporaryFile(dir, path);
[email protected]9e51af92009-02-04 00:58:39470 if (fd < 0)
471 return NULL;
472
[email protected]6faa0e0d2009-04-28 06:50:36473 return fdopen(fd, "a+");
[email protected]9e51af92009-02-04 00:58:39474}
475
[email protected]9ccbb372008-10-10 18:50:32476bool CreateTemporaryFileNameInDir(const std::wstring& dir,
477 std::wstring* temp_file) {
478 // Not implemented yet.
479 NOTREACHED();
480 return false;
481}
482
[email protected]7e1fde6a2008-12-23 20:20:10483bool CreateNewTempDirectory(const FilePath::StringType& prefix,
484 FilePath* new_temp_path) {
[email protected]392264c2008-11-11 00:01:38485 FilePath tmpdir;
[email protected]b2e97292008-09-02 18:20:34486 if (!GetTempDir(&tmpdir))
487 return false;
[email protected]392264c2008-11-11 00:01:38488 tmpdir = tmpdir.Append(kTempFileName);
489 std::string tmpdir_string = tmpdir.value();
[email protected]b2e97292008-09-02 18:20:34490 // this should be OK since mkdtemp just replaces characters in place
[email protected]778e8c52008-09-11 17:36:23491 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]b2e97292008-09-02 18:20:34492 char* dtemp = mkdtemp(buffer);
493 if (!dtemp)
494 return false;
[email protected]7e1fde6a2008-12-23 20:20:10495 *new_temp_path = FilePath(dtemp);
[email protected]b2e97292008-09-02 18:20:34496 return true;
497}
498
[email protected]640517f2008-10-30 23:54:04499bool CreateDirectory(const FilePath& full_path) {
500 std::vector<FilePath> subpaths;
501
502 // Collect a list of all parent directories.
503 FilePath last_path = full_path;
504 subpaths.push_back(full_path);
505 for (FilePath path = full_path.DirName();
506 path.value() != last_path.value(); path = path.DirName()) {
507 subpaths.push_back(path);
508 last_path = path;
509 }
510
511 // Iterate through the parents and create the missing ones.
512 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
513 i != subpaths.rend(); ++i) {
514 if (!DirectoryExists(*i)) {
[email protected]39a1a062009-05-12 20:12:24515 if (mkdir(i->value().c_str(), 0700) != 0)
[email protected]b2e97292008-09-02 18:20:34516 return false;
517 }
518 }
519 return true;
520}
521
[email protected]eac0709a2008-11-04 21:00:46522bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
[email protected]b2e97292008-09-02 18:20:34523 struct stat64 file_info;
[email protected]eac0709a2008-11-04 21:00:46524 if (stat64(file_path.value().c_str(), &file_info) != 0)
[email protected]b2e97292008-09-02 18:20:34525 return false;
[email protected]f5e3da4d2008-09-26 01:04:08526 results->is_directory = S_ISDIR(file_info.st_mode);
527 results->size = file_info.st_size;
[email protected]b2e97292008-09-02 18:20:34528 return true;
529}
530
[email protected]825003f2009-05-14 17:49:23531bool GetInode(const FilePath& path, ino_t* inode) {
532 struct stat buffer;
533 int result = stat(path.value().c_str(), &buffer);
534 if (result < 0)
535 return false;
536
537 *inode = buffer.st_ino;
538 return true;
539}
540
[email protected]836f1342008-10-01 17:40:13541FILE* OpenFile(const std::string& filename, const char* mode) {
[email protected]a9cd2a652008-11-17 21:01:19542 return OpenFile(FilePath(filename), mode);
[email protected]836f1342008-10-01 17:40:13543}
544
[email protected]a9cd2a652008-11-17 21:01:19545FILE* OpenFile(const FilePath& filename, const char* mode) {
546 return fopen(filename.value().c_str(), mode);
[email protected]836f1342008-10-01 17:40:13547}
548
[email protected]c870c762009-01-28 05:47:15549int ReadFile(const FilePath& filename, char* data, int size) {
550 int fd = open(filename.value().c_str(), O_RDONLY);
[email protected]b2e97292008-09-02 18:20:34551 if (fd < 0)
552 return -1;
[email protected]a9cd2a652008-11-17 21:01:19553
[email protected]157c61b2009-05-01 21:37:31554 int ret_value = HANDLE_EINTR(read(fd, data, size));
555 HANDLE_EINTR(close(fd));
[email protected]b2e97292008-09-02 18:20:34556 return ret_value;
557}
558
[email protected]c870c762009-01-28 05:47:15559int WriteFile(const FilePath& filename, const char* data, int size) {
560 int fd = creat(filename.value().c_str(), 0666);
[email protected]b2e97292008-09-02 18:20:34561 if (fd < 0)
562 return -1;
[email protected]778e8c52008-09-11 17:36:23563
564 // Allow for partial writes
565 ssize_t bytes_written_total = 0;
566 do {
[email protected]157c61b2009-05-01 21:37:31567 ssize_t bytes_written_partial =
568 HANDLE_EINTR(write(fd, data + bytes_written_total,
569 size - bytes_written_total));
[email protected]778e8c52008-09-11 17:36:23570 if (bytes_written_partial < 0) {
[email protected]157c61b2009-05-01 21:37:31571 HANDLE_EINTR(close(fd));
[email protected]a9cd2a652008-11-17 21:01:19572 return -1;
[email protected]778e8c52008-09-11 17:36:23573 }
574 bytes_written_total += bytes_written_partial;
575 } while (bytes_written_total < size);
576
[email protected]157c61b2009-05-01 21:37:31577 HANDLE_EINTR(close(fd));
[email protected]778e8c52008-09-11 17:36:23578 return bytes_written_total;
[email protected]b2e97292008-09-02 18:20:34579}
580
581// Gets the current working directory for the process.
[email protected]640517f2008-10-30 23:54:04582bool GetCurrentDirectory(FilePath* dir) {
[email protected]b2e97292008-09-02 18:20:34583 char system_buffer[PATH_MAX] = "";
[email protected]640517f2008-10-30 23:54:04584 if (!getcwd(system_buffer, sizeof(system_buffer))) {
585 NOTREACHED();
586 return false;
587 }
588 *dir = FilePath(system_buffer);
[email protected]b2e97292008-09-02 18:20:34589 return true;
590}
591
592// Sets the current working directory for the process.
[email protected]a9cd2a652008-11-17 21:01:19593bool SetCurrentDirectory(const FilePath& path) {
594 int ret = chdir(path.value().c_str());
595 return !ret;
[email protected]b2e97292008-09-02 18:20:34596}
[email protected]a9cd2a652008-11-17 21:01:19597
[email protected]7856bb82008-12-12 23:43:03598///////////////////////////////////////////////
599// FileEnumerator
600
[email protected]0b733222008-12-11 14:55:12601FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34602 bool recursive,
603 FileEnumerator::FILE_TYPE file_type)
604 : recursive_(recursive),
605 file_type_(file_type),
606 is_in_find_op_(false),
607 fts_(NULL) {
[email protected]8199b3a2009-06-09 05:57:38608 // INCLUDE_DOT_DOT must not be specified if recursive.
609 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]b2e97292008-09-02 18:20:34610 pending_paths_.push(root_path);
611}
612
[email protected]0b733222008-12-11 14:55:12613FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34614 bool recursive,
615 FileEnumerator::FILE_TYPE file_type,
[email protected]0b733222008-12-11 14:55:12616 const FilePath::StringType& pattern)
[email protected]b2e97292008-09-02 18:20:34617 : recursive_(recursive),
618 file_type_(file_type),
[email protected]0b733222008-12-11 14:55:12619 pattern_(root_path.value()),
[email protected]b2e97292008-09-02 18:20:34620 is_in_find_op_(false),
621 fts_(NULL) {
[email protected]8199b3a2009-06-09 05:57:38622 // INCLUDE_DOT_DOT must not be specified if recursive.
623 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]b2e97292008-09-02 18:20:34624 // The Windows version of this code only matches against items in the top-most
625 // directory, and we're comparing fnmatch against full paths, so this is the
626 // easiest way to get the right pattern.
[email protected]0b733222008-12-11 14:55:12627 pattern_ = pattern_.Append(pattern);
[email protected]b2e97292008-09-02 18:20:34628 pending_paths_.push(root_path);
629}
[email protected]a9cd2a652008-11-17 21:01:19630
[email protected]b2e97292008-09-02 18:20:34631FileEnumerator::~FileEnumerator() {
632 if (fts_)
633 fts_close(fts_);
634}
635
[email protected]13ef7c02008-11-20 22:30:13636void FileEnumerator::GetFindInfo(FindInfo* info) {
637 DCHECK(info);
638
639 if (!is_in_find_op_)
640 return;
641
642 memcpy(&(info->stat), fts_ent_->fts_statp, sizeof(info->stat));
643 info->filename.assign(fts_ent_->fts_name);
644}
645
[email protected]b2e97292008-09-02 18:20:34646// As it stands, this method calls itself recursively when the next item of
647// the fts enumeration doesn't match (type, pattern, etc.). In the case of
648// large directories with many files this can be quite deep.
649// TODO(erikkay) - get rid of this recursive pattern
[email protected]0b733222008-12-11 14:55:12650FilePath FileEnumerator::Next() {
[email protected]b2e97292008-09-02 18:20:34651 if (!is_in_find_op_) {
652 if (pending_paths_.empty())
[email protected]0b733222008-12-11 14:55:12653 return FilePath();
[email protected]13ef7c02008-11-20 22:30:13654
[email protected]b2e97292008-09-02 18:20:34655 // The last find FindFirstFile operation is done, prepare a new one.
656 root_path_ = pending_paths_.top();
[email protected]0b733222008-12-11 14:55:12657 root_path_ = root_path_.StripTrailingSeparators();
[email protected]b2e97292008-09-02 18:20:34658 pending_paths_.pop();
[email protected]13ef7c02008-11-20 22:30:13659
[email protected]b2e97292008-09-02 18:20:34660 // Start a new find operation.
[email protected]8199b3a2009-06-09 05:57:38661 int ftsflags = FTS_LOGICAL | FTS_SEEDOT;
[email protected]b2e97292008-09-02 18:20:34662 char top_dir[PATH_MAX];
[email protected]e15c8e02009-01-02 12:57:53663 base::strlcpy(top_dir, root_path_.value().c_str(), arraysize(top_dir));
[email protected]b2e97292008-09-02 18:20:34664 char* dir_list[2] = { top_dir, NULL };
[email protected]8199b3a2009-06-09 05:57:38665 fts_ = fts_open(dir_list, ftsflags, CompareFiles);
[email protected]b2e97292008-09-02 18:20:34666 if (!fts_)
667 return Next();
668 is_in_find_op_ = true;
669 }
[email protected]13ef7c02008-11-20 22:30:13670
671 fts_ent_ = fts_read(fts_);
672 if (fts_ent_ == NULL) {
[email protected]b2e97292008-09-02 18:20:34673 fts_close(fts_);
674 fts_ = NULL;
675 is_in_find_op_ = false;
676 return Next();
677 }
[email protected]13ef7c02008-11-20 22:30:13678
[email protected]b2e97292008-09-02 18:20:34679 // Level 0 is the top, which is always skipped.
[email protected]13ef7c02008-11-20 22:30:13680 if (fts_ent_->fts_level == 0)
[email protected]b2e97292008-09-02 18:20:34681 return Next();
[email protected]13ef7c02008-11-20 22:30:13682
[email protected]b2e97292008-09-02 18:20:34683 // Patterns are only matched on the items in the top-most directory.
684 // (see Windows implementation)
[email protected]0b733222008-12-11 14:55:12685 if (fts_ent_->fts_level == 1 && pattern_.value().length() > 0) {
686 if (fnmatch(pattern_.value().c_str(), fts_ent_->fts_path, 0) != 0) {
[email protected]13ef7c02008-11-20 22:30:13687 if (fts_ent_->fts_info == FTS_D)
688 fts_set(fts_, fts_ent_, FTS_SKIP);
[email protected]b2e97292008-09-02 18:20:34689 return Next();
690 }
691 }
[email protected]13ef7c02008-11-20 22:30:13692
[email protected]0b733222008-12-11 14:55:12693 FilePath cur_file(fts_ent_->fts_path);
[email protected]8199b3a2009-06-09 05:57:38694 if (ShouldSkip(cur_file))
695 return Next();
696
[email protected]13ef7c02008-11-20 22:30:13697 if (fts_ent_->fts_info == FTS_D) {
[email protected]b2e97292008-09-02 18:20:34698 // If not recursive, then prune children.
699 if (!recursive_)
[email protected]13ef7c02008-11-20 22:30:13700 fts_set(fts_, fts_ent_, FTS_SKIP);
[email protected]b2e97292008-09-02 18:20:34701 return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
[email protected]13ef7c02008-11-20 22:30:13702 } else if (fts_ent_->fts_info == FTS_F) {
[email protected]b2e97292008-09-02 18:20:34703 return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
[email protected]8199b3a2009-06-09 05:57:38704 } else if (fts_ent_->fts_info == FTS_DOT) {
705 if ((file_type_ & FileEnumerator::DIRECTORIES) && IsDotDot(cur_file)) {
706 return cur_file;
707 }
708 return Next();
[email protected]b2e97292008-09-02 18:20:34709 }
710 // TODO(erikkay) - verify that the other fts_info types aren't interesting
711 return Next();
712}
[email protected]13ef7c02008-11-20 22:30:13713
[email protected]7856bb82008-12-12 23:43:03714///////////////////////////////////////////////
715// MemoryMappedFile
716
717MemoryMappedFile::MemoryMappedFile()
718 : file_(-1),
719 data_(NULL),
720 length_(0) {
721}
722
723bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
[email protected]cc8f1462009-06-12 17:36:55724 file_ = open(file_name.value().c_str(), O_RDONLY);
725
[email protected]4883a4e2009-06-06 19:59:36726 if (file_ == -1) {
727 LOG(ERROR) << "Couldn't open " << file_name.value();
[email protected]7856bb82008-12-12 23:43:03728 return false;
[email protected]4883a4e2009-06-06 19:59:36729 }
[email protected]7856bb82008-12-12 23:43:03730
731 struct stat file_stat;
[email protected]4883a4e2009-06-06 19:59:36732 if (fstat(file_, &file_stat) == -1) {
733 LOG(ERROR) << "Couldn't fstat " << file_name.value() << ", errno " << errno;
[email protected]7856bb82008-12-12 23:43:03734 return false;
[email protected]4883a4e2009-06-06 19:59:36735 }
[email protected]7856bb82008-12-12 23:43:03736 length_ = file_stat.st_size;
737
738 data_ = static_cast<uint8*>(
739 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
740 if (data_ == MAP_FAILED)
[email protected]4883a4e2009-06-06 19:59:36741 LOG(ERROR) << "Couldn't mmap " << file_name.value() << ", errno " << errno;
742
743 return data_ != MAP_FAILED;
[email protected]7856bb82008-12-12 23:43:03744}
745
746void MemoryMappedFile::CloseHandles() {
747 if (data_ != NULL)
748 munmap(data_, length_);
749 if (file_ != -1)
750 close(file_);
751
752 data_ = NULL;
753 length_ = 0;
754 file_ = -1;
755}
[email protected]13ef7c02008-11-20 22:30:13756
[email protected]b2e97292008-09-02 18:20:34757} // namespace file_util