blob: 27adbfa507ed67a218f18cb8e978bf1a3fc3867d [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>
[email protected]b2e97292008-09-02 18:20:3411#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>
[email protected]7856bb82008-12-12 23:43:0315#include <sys/mman.h>
[email protected]b2e97292008-09-02 18:20:3416#include <sys/stat.h>
[email protected]4b7743de2009-04-21 01:50:3917#include <sys/types.h>
[email protected]b2e97292008-09-02 18:20:3418#include <time.h>
[email protected]4b7743de2009-04-21 01:50:3919#include <unistd.h>
[email protected]b2e97292008-09-02 18:20:3420
[email protected]3224dcd2009-09-16 17:31:2521#if defined(OS_MACOSX)
22#include <AvailabilityMacros.h>
23#endif
24
[email protected]b2e97292008-09-02 18:20:3425#include <fstream>
26
27#include "base/basictypes.h"
[email protected]157c61b2009-05-01 21:37:3128#include "base/eintr_wrapper.h"
[email protected]640517f2008-10-30 23:54:0429#include "base/file_path.h"
[email protected]807bc042009-07-15 01:32:0230#include "base/lock.h"
[email protected]b2e97292008-09-02 18:20:3431#include "base/logging.h"
[email protected]807bc042009-07-15 01:32:0232#include "base/scoped_ptr.h"
33#include "base/singleton.h"
[email protected]b2e97292008-09-02 18:20:3434#include "base/string_util.h"
[email protected]807bc042009-07-15 01:32:0235#include "base/sys_string_conversions.h"
[email protected]4b7743de2009-04-21 01:50:3936#include "base/time.h"
[email protected]047a03f2009-10-07 02:10:2037#include "base/utf_string_conversions.h"
[email protected]807bc042009-07-15 01:32:0238#include "unicode/coll.h"
[email protected]b2e97292008-09-02 18:20:3439
[email protected]9543af052009-09-15 22:42:5940
[email protected]172c5502009-06-24 03:29:2641namespace {
42
[email protected]807bc042009-07-15 01:32:0243class LocaleAwareComparator {
44 public:
45 LocaleAwareComparator() {
46 UErrorCode error_code = U_ZERO_ERROR;
47 // Use the default collator. The default locale should have been properly
48 // set by the time this constructor is called.
[email protected]b5b2385a2009-08-18 05:12:2949 collator_.reset(icu::Collator::createInstance(error_code));
[email protected]807bc042009-07-15 01:32:0250 DCHECK(U_SUCCESS(error_code));
51 // Make it case-sensitive.
[email protected]b5b2385a2009-08-18 05:12:2952 collator_->setStrength(icu::Collator::TERTIARY);
[email protected]807bc042009-07-15 01:32:0253 // Note: We do not set UCOL_NORMALIZATION_MODE attribute. In other words, we
54 // do not pay performance penalty to guarantee sort order correctness for
55 // non-FCD (http://unicode.org/notes/tn5/#FCD) file names. This should be a
56 // reasonable tradeoff because such file names should be rare and the sort
57 // order doesn't change much anyway.
58 }
59
60 // Note: A similar function is available in l10n_util.
61 // We cannot use it because base should not depend on l10n_util.
62 // TODO(yuzo): Move some of l10n_util to base.
63 int Compare(const string16& a, const string16& b) {
64 // We are not sure if Collator::compare is thread-safe.
65 // Use an AutoLock just in case.
66 AutoLock auto_lock(lock_);
67
68 UErrorCode error_code = U_ZERO_ERROR;
69 UCollationResult result = collator_->compare(
70 static_cast<const UChar*>(a.c_str()),
71 static_cast<int>(a.length()),
72 static_cast<const UChar*>(b.c_str()),
73 static_cast<int>(b.length()),
74 error_code);
75 DCHECK(U_SUCCESS(error_code));
76 return result;
77 }
78
79 private:
[email protected]b5b2385a2009-08-18 05:12:2980 scoped_ptr<icu::Collator> collator_;
[email protected]807bc042009-07-15 01:32:0281 Lock lock_;
82 friend struct DefaultSingletonTraits<LocaleAwareComparator>;
83
84 DISALLOW_COPY_AND_ASSIGN(LocaleAwareComparator);
85};
86
[email protected]172c5502009-06-24 03:29:2687} // namespace
88
[email protected]b2e97292008-09-02 18:20:3489namespace file_util {
90
[email protected]3224dcd2009-09-16 17:31:2591#if defined(OS_FREEBSD) || \
92 (defined(OS_MACOSX) && \
93 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
[email protected]fb66f9d2009-09-07 16:39:4694typedef struct stat stat_wrapper_t;
95static int CallStat(const char *path, stat_wrapper_t *sb) {
96 return stat(path, sb);
97}
98#else
99typedef struct stat64 stat_wrapper_t;
100static int CallStat(const char *path, stat_wrapper_t *sb) {
101 return stat64(path, sb);
102}
103#endif
104
105
[email protected]22a087f2009-03-17 19:17:43106#if defined(GOOGLE_CHROME_BUILD)
[email protected]392264c2008-11-11 00:01:38107static const char* kTempFileName = "com.google.chrome.XXXXXX";
[email protected]22a087f2009-03-17 19:17:43108#else
109static const char* kTempFileName = "org.chromium.XXXXXX";
110#endif
[email protected]778e8c52008-09-11 17:36:23111
[email protected]b2e97292008-09-02 18:20:34112std::wstring GetDirectoryFromPath(const std::wstring& path) {
113 if (EndsWithSeparator(path)) {
[email protected]a97f2b42009-04-21 23:15:45114 std::wstring dir = path;
115 TrimTrailingSeparator(&dir);
116 return dir;
[email protected]b2e97292008-09-02 18:20:34117 } else {
118 char full_path[PATH_MAX];
119 base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path));
120 return UTF8ToWide(dirname(full_path));
121 }
122}
[email protected]9e51af92009-02-04 00:58:39123
[email protected]640517f2008-10-30 23:54:04124bool AbsolutePath(FilePath* path) {
[email protected]b2e97292008-09-02 18:20:34125 char full_path[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04126 if (realpath(path->value().c_str(), full_path) == NULL)
[email protected]b2e97292008-09-02 18:20:34127 return false;
[email protected]640517f2008-10-30 23:54:04128 *path = FilePath(full_path);
[email protected]b2e97292008-09-02 18:20:34129 return true;
130}
131
[email protected]4b7743de2009-04-21 01:50:39132int CountFilesCreatedAfter(const FilePath& path,
133 const base::Time& comparison_time) {
134 int file_count = 0;
135
136 DIR* dir = opendir(path.value().c_str());
137 if (dir) {
[email protected]fb66f9d2009-09-07 16:39:46138#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD)
[email protected]49930c3a2009-08-06 21:23:07139 #error Depending on the definition of struct dirent, additional space for \
140 pathname may be needed
141#endif
[email protected]2126577b2009-04-23 15:05:19142 struct dirent ent_buf;
[email protected]4b7743de2009-04-21 01:50:39143 struct dirent* ent;
[email protected]2126577b2009-04-23 15:05:19144 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
[email protected]4b7743de2009-04-21 01:50:39145 if ((strcmp(ent->d_name, ".") == 0) ||
146 (strcmp(ent->d_name, "..") == 0))
147 continue;
148
[email protected]fb66f9d2009-09-07 16:39:46149 stat_wrapper_t st;
150 int test = CallStat(path.Append(ent->d_name).value().c_str(), &st);
[email protected]4b7743de2009-04-21 01:50:39151 if (test != 0) {
152 LOG(ERROR) << "stat64 failed: " << strerror(errno);
153 continue;
154 }
[email protected]2126577b2009-04-23 15:05:19155 // Here, we use Time::TimeT(), which discards microseconds. This
156 // means that files which are newer than |comparison_time| may
157 // be considered older. If we don't discard microseconds, it
158 // introduces another issue. Suppose the following case:
159 //
160 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
161 // 2. Create a file and the current time is 10.3 (secs).
162 //
163 // As POSIX doesn't have microsecond precision for |st_ctime|,
164 // the creation time of the file created in the step 2 is 10 and
165 // the file is considered older than |comparison_time|. After
166 // all, we may have to accept either of the two issues: 1. files
167 // which are older than |comparison_time| are considered newer
168 // (current implementation) 2. files newer than
169 // |comparison_time| are considered older.
[email protected]4b7743de2009-04-21 01:50:39170 if (st.st_ctime >= comparison_time.ToTimeT())
171 ++file_count;
172 }
173 closedir(dir);
174 }
175 return file_count;
176}
177
[email protected]b2e97292008-09-02 18:20:34178// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
179// which works both with and without the recursive flag. I'm not sure we need
180// that functionality. If not, remove from file_util_win.cc, otherwise add it
181// here.
[email protected]640517f2008-10-30 23:54:04182bool Delete(const FilePath& path, bool recursive) {
183 const char* path_str = path.value().c_str();
[email protected]fb66f9d2009-09-07 16:39:46184 stat_wrapper_t file_info;
185 int test = CallStat(path_str, &file_info);
[email protected]b2e97292008-09-02 18:20:34186 if (test != 0) {
187 // The Windows version defines this condition as success.
[email protected]9e51af92009-02-04 00:58:39188 bool ret = (errno == ENOENT || errno == ENOTDIR);
[email protected]b2e97292008-09-02 18:20:34189 return ret;
190 }
191 if (!S_ISDIR(file_info.st_mode))
[email protected]640517f2008-10-30 23:54:04192 return (unlink(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34193 if (!recursive)
[email protected]640517f2008-10-30 23:54:04194 return (rmdir(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34195
196 bool success = true;
[email protected]49930c3a2009-08-06 21:23:07197 std::stack<std::string> directories;
198 directories.push(path.value());
199 FileEnumerator traversal(path, true, static_cast<FileEnumerator::FILE_TYPE>(
200 FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
201 FileEnumerator::SHOW_SYM_LINKS));
202 for (FilePath current = traversal.Next(); success && !current.empty();
203 current = traversal.Next()) {
204 FileEnumerator::FindInfo info;
205 traversal.GetFindInfo(&info);
206
207 if (S_ISDIR(info.stat.st_mode))
208 directories.push(current.value());
209 else
210 success = (unlink(current.value().c_str()) == 0);
[email protected]21dec3872008-09-18 19:15:54211 }
[email protected]49930c3a2009-08-06 21:23:07212
213 while (success && !directories.empty()) {
214 FilePath dir = FilePath(directories.top());
215 directories.pop();
216 success = (rmdir(dir.value().c_str()) == 0);
[email protected]b2e97292008-09-02 18:20:34217 }
[email protected]49930c3a2009-08-06 21:23:07218
[email protected]b2e97292008-09-02 18:20:34219 return success;
220}
221
[email protected]640517f2008-10-30 23:54:04222bool Move(const FilePath& from_path, const FilePath& to_path) {
[email protected]cc7948a2009-03-13 20:01:43223 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
224 return true;
225
226 if (!CopyDirectory(from_path, to_path, true))
227 return false;
228
229 Delete(from_path, true);
230 return true;
[email protected]b2e97292008-09-02 18:20:34231}
232
[email protected]c5866dca2009-05-19 17:21:07233bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
234 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
235}
236
[email protected]640517f2008-10-30 23:54:04237bool CopyDirectory(const FilePath& from_path,
238 const FilePath& to_path,
[email protected]21dec3872008-09-18 19:15:54239 bool recursive) {
[email protected]21dec3872008-09-18 19:15:54240 // Some old callers of CopyDirectory want it to support wildcards.
241 // After some discussion, we decided to fix those callers.
242 // Break loudly here if anyone tries to do this.
243 // TODO(evanm): remove this once we're sure it's ok.
[email protected]640517f2008-10-30 23:54:04244 DCHECK(to_path.value().find('*') == std::string::npos);
245 DCHECK(from_path.value().find('*') == std::string::npos);
[email protected]21dec3872008-09-18 19:15:54246
247 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04248 if (base::strlcpy(top_dir, from_path.value().c_str(),
[email protected]21dec3872008-09-18 19:15:54249 arraysize(top_dir)) >= arraysize(top_dir)) {
250 return false;
251 }
252
[email protected]49930c3a2009-08-06 21:23:07253 // This function does not properly handle destinations within the source
254 FilePath real_to_path = to_path;
255 if (PathExists(real_to_path)) {
256 if (!AbsolutePath(&real_to_path))
257 return false;
258 } else {
259 real_to_path = real_to_path.DirName();
260 if (!AbsolutePath(&real_to_path))
261 return false;
262 }
263 FilePath real_from_path = from_path;
264 if (!AbsolutePath(&real_from_path))
[email protected]21dec3872008-09-18 19:15:54265 return false;
[email protected]49930c3a2009-08-06 21:23:07266 if (real_to_path.value().size() >= real_from_path.value().size() &&
267 real_to_path.value().compare(0, real_from_path.value().size(),
268 real_from_path.value()) == 0)
269 return false;
270
271 bool success = true;
272 FileEnumerator::FILE_TYPE traverse_type =
273 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES |
274 FileEnumerator::SHOW_SYM_LINKS);
275 if (recursive)
276 traverse_type = static_cast<FileEnumerator::FILE_TYPE>(
277 traverse_type | FileEnumerator::DIRECTORIES);
278 FileEnumerator traversal(from_path, recursive, traverse_type);
279
280 // to_path may not exist yet, start the loop with to_path
281 FileEnumerator::FindInfo info;
282 FilePath current = from_path;
283 if (stat(from_path.value().c_str(), &info.stat) < 0) {
284 LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " <<
285 from_path.value() << " errno = " << errno;
286 success = false;
[email protected]21dec3872008-09-18 19:15:54287 }
288
[email protected]49930c3a2009-08-06 21:23:07289 while (success && !current.empty()) {
290 // current is the source path, including from_path, so paste
[email protected]21dec3872008-09-18 19:15:54291 // the suffix after from_path onto to_path to create the target_path.
[email protected]49930c3a2009-08-06 21:23:07292 std::string suffix(&current.value().c_str()[from_path.value().size()]);
[email protected]ca0209612009-01-13 18:57:46293 // Strip the leading '/' (if any).
294 if (!suffix.empty()) {
[email protected]6ae340f2009-03-06 09:56:28295 DCHECK_EQ('/', suffix[0]);
[email protected]ca0209612009-01-13 18:57:46296 suffix.erase(0, 1);
297 }
298 const FilePath target_path = to_path.Append(suffix);
[email protected]21dec3872008-09-18 19:15:54299
[email protected]49930c3a2009-08-06 21:23:07300 if (S_ISDIR(info.stat.st_mode)) {
301 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
302 errno != EEXIST) {
303 LOG(ERROR) << "CopyDirectory() couldn't create directory: " <<
304 target_path.value() << " errno = " << errno;
305 success = false;
306 }
307 } else if (S_ISREG(info.stat.st_mode)) {
308 if (!CopyFile(current, target_path)) {
309 LOG(ERROR) << "CopyDirectory() couldn't create file: " <<
310 target_path.value();
311 success = false;
312 }
313 } else {
314 LOG(WARNING) << "CopyDirectory() skipping non-regular file: " <<
315 current.value();
[email protected]21dec3872008-09-18 19:15:54316 }
[email protected]21dec3872008-09-18 19:15:54317
[email protected]49930c3a2009-08-06 21:23:07318 current = traversal.Next();
319 traversal.GetFindInfo(&info);
[email protected]21dec3872008-09-18 19:15:54320 }
321
[email protected]49930c3a2009-08-06 21:23:07322 return success;
[email protected]b2e97292008-09-02 18:20:34323}
324
[email protected]640517f2008-10-30 23:54:04325bool PathExists(const FilePath& path) {
[email protected]fb66f9d2009-09-07 16:39:46326 stat_wrapper_t file_info;
327 return CallStat(path.value().c_str(), &file_info) == 0;
[email protected]b2e97292008-09-02 18:20:34328}
329
[email protected]7e1fde6a2008-12-23 20:20:10330bool PathIsWritable(const FilePath& path) {
331 FilePath test_path(path);
[email protected]fb66f9d2009-09-07 16:39:46332 stat_wrapper_t file_info;
333 if (CallStat(test_path.value().c_str(), &file_info) != 0) {
[email protected]7e1fde6a2008-12-23 20:20:10334 // If the path doesn't exist, test the parent dir.
335 test_path = test_path.DirName();
336 // If the parent dir doesn't exist, then return false (the path is not
337 // directly writable).
[email protected]fb66f9d2009-09-07 16:39:46338 if (CallStat(test_path.value().c_str(), &file_info) != 0)
[email protected]7e1fde6a2008-12-23 20:20:10339 return false;
340 }
341 if (S_IWOTH & file_info.st_mode)
342 return true;
343 if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
344 return true;
345 if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
346 return true;
347 return false;
348}
349
[email protected]640517f2008-10-30 23:54:04350bool DirectoryExists(const FilePath& path) {
[email protected]fb66f9d2009-09-07 16:39:46351 stat_wrapper_t file_info;
352 if (CallStat(path.value().c_str(), &file_info) == 0)
[email protected]806b9c62008-09-11 16:09:11353 return S_ISDIR(file_info.st_mode);
354 return false;
355}
356
[email protected]b2e97292008-09-02 18:20:34357// TODO(erikkay): implement
358#if 0
359bool GetFileCreationLocalTimeFromHandle(int fd,
360 LPSYSTEMTIME creation_time) {
361 if (!file_handle)
362 return false;
[email protected]9e51af92009-02-04 00:58:39363
[email protected]b2e97292008-09-02 18:20:34364 FILETIME utc_filetime;
365 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
366 return false;
[email protected]9e51af92009-02-04 00:58:39367
[email protected]b2e97292008-09-02 18:20:34368 FILETIME local_filetime;
369 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
370 return false;
[email protected]9e51af92009-02-04 00:58:39371
[email protected]b2e97292008-09-02 18:20:34372 return !!FileTimeToSystemTime(&local_filetime, creation_time);
373}
374
375bool GetFileCreationLocalTime(const std::string& filename,
376 LPSYSTEMTIME creation_time) {
377 ScopedHandle file_handle(
[email protected]9e51af92009-02-04 00:58:39378 CreateFile(filename.c_str(), GENERIC_READ,
[email protected]b2e97292008-09-02 18:20:34379 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
380 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
381 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
382}
383#endif
384
[email protected]45301492009-04-23 12:38:08385bool ReadFromFD(int fd, char* buffer, size_t bytes) {
386 size_t total_read = 0;
387 while (total_read < bytes) {
[email protected]157c61b2009-05-01 21:37:31388 ssize_t bytes_read =
389 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
390 if (bytes_read <= 0)
[email protected]45301492009-04-23 12:38:08391 break;
[email protected]157c61b2009-05-01 21:37:31392 total_read += bytes_read;
[email protected]45301492009-04-23 12:38:08393 }
394 return total_read == bytes;
395}
396
[email protected]9e51af92009-02-04 00:58:39397// Creates and opens a temporary file in |directory|, returning the
[email protected]16eac0a72009-09-11 17:33:50398// file descriptor. |path| is set to the temporary file path.
399// This function does NOT unlink() the file.
[email protected]9e51af92009-02-04 00:58:39400int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
401 *path = directory.Append(kTempFileName);
402 const std::string& tmpdir_string = path->value();
[email protected]778e8c52008-09-11 17:36:23403 // this should be OK since mkstemp just replaces characters in place
404 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]392264c2008-11-11 00:01:38405
[email protected]9e51af92009-02-04 00:58:39406 return mkstemp(buffer);
407}
408
[email protected]33edeab2009-08-18 16:07:55409bool CreateTemporaryFile(FilePath* path) {
[email protected]9e51af92009-02-04 00:58:39410 FilePath directory;
411 if (!GetTempDir(&directory))
412 return false;
413 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
[email protected]b2e97292008-09-02 18:20:34414 if (fd < 0)
415 return false;
[email protected]392264c2008-11-11 00:01:38416 close(fd);
[email protected]b2e97292008-09-02 18:20:34417 return true;
418}
419
[email protected]9e51af92009-02-04 00:58:39420FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
421 FilePath directory;
422 if (!GetShmemTempDir(&directory))
423 return false;
424
[email protected]6faa0e0d2009-04-28 06:50:36425 return CreateAndOpenTemporaryFileInDir(directory, path);
426}
427
428FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
429 int fd = CreateAndOpenFdForTemporaryFile(dir, path);
[email protected]9e51af92009-02-04 00:58:39430 if (fd < 0)
431 return NULL;
432
[email protected]6faa0e0d2009-04-28 06:50:36433 return fdopen(fd, "a+");
[email protected]9e51af92009-02-04 00:58:39434}
[email protected]6445c402009-09-11 20:06:27435
436bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
437 int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
438 return ((fd >= 0) && !close(fd));
[email protected]9ccbb372008-10-10 18:50:32439}
440
[email protected]7e1fde6a2008-12-23 20:20:10441bool CreateNewTempDirectory(const FilePath::StringType& prefix,
442 FilePath* new_temp_path) {
[email protected]392264c2008-11-11 00:01:38443 FilePath tmpdir;
[email protected]b2e97292008-09-02 18:20:34444 if (!GetTempDir(&tmpdir))
445 return false;
[email protected]392264c2008-11-11 00:01:38446 tmpdir = tmpdir.Append(kTempFileName);
447 std::string tmpdir_string = tmpdir.value();
[email protected]b2e97292008-09-02 18:20:34448 // this should be OK since mkdtemp just replaces characters in place
[email protected]778e8c52008-09-11 17:36:23449 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]b2e97292008-09-02 18:20:34450 char* dtemp = mkdtemp(buffer);
451 if (!dtemp)
452 return false;
[email protected]7e1fde6a2008-12-23 20:20:10453 *new_temp_path = FilePath(dtemp);
[email protected]b2e97292008-09-02 18:20:34454 return true;
455}
456
[email protected]640517f2008-10-30 23:54:04457bool CreateDirectory(const FilePath& full_path) {
458 std::vector<FilePath> subpaths;
459
460 // Collect a list of all parent directories.
461 FilePath last_path = full_path;
462 subpaths.push_back(full_path);
463 for (FilePath path = full_path.DirName();
464 path.value() != last_path.value(); path = path.DirName()) {
465 subpaths.push_back(path);
466 last_path = path;
467 }
468
469 // Iterate through the parents and create the missing ones.
470 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
471 i != subpaths.rend(); ++i) {
472 if (!DirectoryExists(*i)) {
[email protected]39a1a062009-05-12 20:12:24473 if (mkdir(i->value().c_str(), 0700) != 0)
[email protected]b2e97292008-09-02 18:20:34474 return false;
475 }
476 }
477 return true;
478}
479
[email protected]eac0709a2008-11-04 21:00:46480bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
[email protected]fb66f9d2009-09-07 16:39:46481 stat_wrapper_t file_info;
482 if (CallStat(file_path.value().c_str(), &file_info) != 0)
[email protected]b2e97292008-09-02 18:20:34483 return false;
[email protected]f5e3da4d2008-09-26 01:04:08484 results->is_directory = S_ISDIR(file_info.st_mode);
485 results->size = file_info.st_size;
[email protected]5ef323892009-07-24 16:13:53486 results->last_modified = base::Time::FromTimeT(file_info.st_mtime);
[email protected]b2e97292008-09-02 18:20:34487 return true;
488}
489
[email protected]825003f2009-05-14 17:49:23490bool GetInode(const FilePath& path, ino_t* inode) {
491 struct stat buffer;
492 int result = stat(path.value().c_str(), &buffer);
493 if (result < 0)
494 return false;
495
496 *inode = buffer.st_ino;
497 return true;
498}
499
[email protected]836f1342008-10-01 17:40:13500FILE* OpenFile(const std::string& filename, const char* mode) {
[email protected]a9cd2a652008-11-17 21:01:19501 return OpenFile(FilePath(filename), mode);
[email protected]836f1342008-10-01 17:40:13502}
503
[email protected]a9cd2a652008-11-17 21:01:19504FILE* OpenFile(const FilePath& filename, const char* mode) {
505 return fopen(filename.value().c_str(), mode);
[email protected]836f1342008-10-01 17:40:13506}
507
[email protected]c870c762009-01-28 05:47:15508int ReadFile(const FilePath& filename, char* data, int size) {
509 int fd = open(filename.value().c_str(), O_RDONLY);
[email protected]b2e97292008-09-02 18:20:34510 if (fd < 0)
511 return -1;
[email protected]a9cd2a652008-11-17 21:01:19512
[email protected]157c61b2009-05-01 21:37:31513 int ret_value = HANDLE_EINTR(read(fd, data, size));
514 HANDLE_EINTR(close(fd));
[email protected]b2e97292008-09-02 18:20:34515 return ret_value;
516}
517
[email protected]c870c762009-01-28 05:47:15518int WriteFile(const FilePath& filename, const char* data, int size) {
519 int fd = creat(filename.value().c_str(), 0666);
[email protected]b2e97292008-09-02 18:20:34520 if (fd < 0)
521 return -1;
[email protected]778e8c52008-09-11 17:36:23522
[email protected]fbea0232009-09-16 00:29:22523 int rv = WriteFileDescriptor(fd, data, size);
[email protected]157c61b2009-05-01 21:37:31524 HANDLE_EINTR(close(fd));
[email protected]fbea0232009-09-16 00:29:22525 return rv;
526}
527
528int WriteFileDescriptor(const int fd, const char* data, int size) {
529 // Allow for partial writes.
530 ssize_t bytes_written_total = 0;
531 for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
532 bytes_written_total += bytes_written_partial) {
533 bytes_written_partial =
534 HANDLE_EINTR(write(fd, data + bytes_written_total,
535 size - bytes_written_total));
536 if (bytes_written_partial < 0)
537 return -1;
538 }
539
[email protected]778e8c52008-09-11 17:36:23540 return bytes_written_total;
[email protected]b2e97292008-09-02 18:20:34541}
542
543// Gets the current working directory for the process.
[email protected]640517f2008-10-30 23:54:04544bool GetCurrentDirectory(FilePath* dir) {
[email protected]b2e97292008-09-02 18:20:34545 char system_buffer[PATH_MAX] = "";
[email protected]640517f2008-10-30 23:54:04546 if (!getcwd(system_buffer, sizeof(system_buffer))) {
547 NOTREACHED();
548 return false;
549 }
550 *dir = FilePath(system_buffer);
[email protected]b2e97292008-09-02 18:20:34551 return true;
552}
553
554// Sets the current working directory for the process.
[email protected]a9cd2a652008-11-17 21:01:19555bool SetCurrentDirectory(const FilePath& path) {
556 int ret = chdir(path.value().c_str());
557 return !ret;
[email protected]b2e97292008-09-02 18:20:34558}
[email protected]a9cd2a652008-11-17 21:01:19559
[email protected]7856bb82008-12-12 23:43:03560///////////////////////////////////////////////
561// FileEnumerator
562
[email protected]0b733222008-12-11 14:55:12563FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34564 bool recursive,
565 FileEnumerator::FILE_TYPE file_type)
[email protected]49930c3a2009-08-06 21:23:07566 : root_path_(root_path),
567 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34568 file_type_(file_type),
569 is_in_find_op_(false),
[email protected]49930c3a2009-08-06 21:23:07570 current_directory_entry_(0) {
[email protected]8199b3a2009-06-09 05:57:38571 // INCLUDE_DOT_DOT must not be specified if recursive.
572 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]b2e97292008-09-02 18:20:34573 pending_paths_.push(root_path);
574}
575
[email protected]0b733222008-12-11 14:55:12576FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34577 bool recursive,
578 FileEnumerator::FILE_TYPE file_type,
[email protected]0b733222008-12-11 14:55:12579 const FilePath::StringType& pattern)
[email protected]49930c3a2009-08-06 21:23:07580 : root_path_(root_path),
581 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34582 file_type_(file_type),
[email protected]49930c3a2009-08-06 21:23:07583 pattern_(root_path.Append(pattern)),
[email protected]b2e97292008-09-02 18:20:34584 is_in_find_op_(false),
[email protected]49930c3a2009-08-06 21:23:07585 current_directory_entry_(0) {
[email protected]8199b3a2009-06-09 05:57:38586 // INCLUDE_DOT_DOT must not be specified if recursive.
587 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]49930c3a2009-08-06 21:23:07588 // The Windows version of this code appends the pattern to the root_path,
589 // potentially only matching against items in the top-most directory.
590 // Do the same here.
591 if (pattern.size() == 0)
592 pattern_ = FilePath();
[email protected]b2e97292008-09-02 18:20:34593 pending_paths_.push(root_path);
594}
[email protected]a9cd2a652008-11-17 21:01:19595
[email protected]b2e97292008-09-02 18:20:34596FileEnumerator::~FileEnumerator() {
[email protected]b2e97292008-09-02 18:20:34597}
598
[email protected]13ef7c02008-11-20 22:30:13599void FileEnumerator::GetFindInfo(FindInfo* info) {
600 DCHECK(info);
601
[email protected]49930c3a2009-08-06 21:23:07602 if (current_directory_entry_ >= directory_entries_.size())
[email protected]13ef7c02008-11-20 22:30:13603 return;
604
[email protected]49930c3a2009-08-06 21:23:07605 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_];
606 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat));
607 info->filename.assign(cur_entry->filename.value());
[email protected]13ef7c02008-11-20 22:30:13608}
609
[email protected]0b733222008-12-11 14:55:12610FilePath FileEnumerator::Next() {
[email protected]49930c3a2009-08-06 21:23:07611 ++current_directory_entry_;
612
613 // While we've exhausted the entries in the current directory, do the next
614 while (current_directory_entry_ >= directory_entries_.size()) {
[email protected]b2e97292008-09-02 18:20:34615 if (pending_paths_.empty())
[email protected]0b733222008-12-11 14:55:12616 return FilePath();
[email protected]13ef7c02008-11-20 22:30:13617
[email protected]b2e97292008-09-02 18:20:34618 root_path_ = pending_paths_.top();
[email protected]0b733222008-12-11 14:55:12619 root_path_ = root_path_.StripTrailingSeparators();
[email protected]b2e97292008-09-02 18:20:34620 pending_paths_.pop();
[email protected]13ef7c02008-11-20 22:30:13621
[email protected]49930c3a2009-08-06 21:23:07622 std::vector<DirectoryEntryInfo> entries;
623 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
624 continue;
[email protected]13ef7c02008-11-20 22:30:13625
[email protected]49930c3a2009-08-06 21:23:07626 // The API says that order is not guaranteed, but order affects UX
627 std::sort(entries.begin(), entries.end(), CompareFiles);
[email protected]13ef7c02008-11-20 22:30:13628
[email protected]49930c3a2009-08-06 21:23:07629 directory_entries_.clear();
630 current_directory_entry_ = 0;
631 for (std::vector<DirectoryEntryInfo>::const_iterator
632 i = entries.begin(); i != entries.end(); ++i) {
633 FilePath full_path = root_path_.Append(i->filename);
634 if (ShouldSkip(full_path))
635 continue;
[email protected]13ef7c02008-11-20 22:30:13636
[email protected]49930c3a2009-08-06 21:23:07637 if (pattern_.value().size() &&
638 fnmatch(pattern_.value().c_str(), full_path.value().c_str(),
639 FNM_NOESCAPE))
640 continue;
641
642 if (recursive_ && S_ISDIR(i->stat.st_mode))
643 pending_paths_.push(full_path);
644
645 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) ||
646 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES)))
647 directory_entries_.push_back(*i);
[email protected]b2e97292008-09-02 18:20:34648 }
649 }
[email protected]13ef7c02008-11-20 22:30:13650
[email protected]49930c3a2009-08-06 21:23:07651 return root_path_.Append(directory_entries_[current_directory_entry_
652 ].filename);
653}
[email protected]8199b3a2009-06-09 05:57:38654
[email protected]49930c3a2009-08-06 21:23:07655bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
656 const FilePath& source, bool show_links) {
657 DIR* dir = opendir(source.value().c_str());
658 if (!dir)
659 return false;
660
[email protected]fb66f9d2009-09-07 16:39:46661#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD)
[email protected]49930c3a2009-08-06 21:23:07662 #error Depending on the definition of struct dirent, additional space for \
663 pathname may be needed
664#endif
665 struct dirent dent_buf;
666 struct dirent* dent;
667 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
668 DirectoryEntryInfo info;
[email protected]49930c3a2009-08-06 21:23:07669 info.filename = FilePath(dent->d_name);
[email protected]2237f5d72009-09-03 22:39:34670
671 FilePath full_name = source.Append(dent->d_name);
672 int ret;
[email protected]49930c3a2009-08-06 21:23:07673 if (show_links)
[email protected]2237f5d72009-09-03 22:39:34674 ret = lstat(full_name.value().c_str(), &info.stat);
[email protected]49930c3a2009-08-06 21:23:07675 else
[email protected]2237f5d72009-09-03 22:39:34676 ret = stat(full_name.value().c_str(), &info.stat);
677 if (ret < 0) {
678 // Print the stat() error message unless it was ENOENT and we're
679 // following symlinks.
680 if (!(ret == ENOENT && !show_links)) {
681 LOG(ERROR) << "Couldn't stat "
682 << source.Append(dent->d_name).value() << ": "
683 << strerror(errno);
684 }
[email protected]49930c3a2009-08-06 21:23:07685 memset(&info.stat, 0, sizeof(info.stat));
[email protected]8199b3a2009-06-09 05:57:38686 }
[email protected]49930c3a2009-08-06 21:23:07687 entries->push_back(info);
[email protected]b2e97292008-09-02 18:20:34688 }
[email protected]49930c3a2009-08-06 21:23:07689
690 closedir(dir);
691 return true;
692}
693
694bool FileEnumerator::CompareFiles(const DirectoryEntryInfo& a,
695 const DirectoryEntryInfo& b) {
696 // Order lexicographically with directories before other files.
697 if (S_ISDIR(a.stat.st_mode) != S_ISDIR(b.stat.st_mode))
698 return S_ISDIR(a.stat.st_mode);
699
700 // On linux, the file system encoding is not defined. We assume
701 // SysNativeMBToWide takes care of it.
702 //
703 // ICU's collator can take strings in OS native encoding. But we convert the
704 // strings to UTF-16 ourselves to ensure conversion consistency.
705 // TODO(yuzo): Perhaps we should define SysNativeMBToUTF16?
706 return Singleton<LocaleAwareComparator>()->Compare(
707 WideToUTF16(base::SysNativeMBToWide(a.filename.value().c_str())),
708 WideToUTF16(base::SysNativeMBToWide(b.filename.value().c_str()))) < 0;
[email protected]b2e97292008-09-02 18:20:34709}
[email protected]13ef7c02008-11-20 22:30:13710
[email protected]7856bb82008-12-12 23:43:03711///////////////////////////////////////////////
712// MemoryMappedFile
713
714MemoryMappedFile::MemoryMappedFile()
715 : file_(-1),
716 data_(NULL),
717 length_(0) {
718}
719
720bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
[email protected]cc8f1462009-06-12 17:36:55721 file_ = open(file_name.value().c_str(), O_RDONLY);
722
[email protected]4883a4e2009-06-06 19:59:36723 if (file_ == -1) {
724 LOG(ERROR) << "Couldn't open " << file_name.value();
[email protected]7856bb82008-12-12 23:43:03725 return false;
[email protected]4883a4e2009-06-06 19:59:36726 }
[email protected]7856bb82008-12-12 23:43:03727
728 struct stat file_stat;
[email protected]4883a4e2009-06-06 19:59:36729 if (fstat(file_, &file_stat) == -1) {
730 LOG(ERROR) << "Couldn't fstat " << file_name.value() << ", errno " << errno;
[email protected]7856bb82008-12-12 23:43:03731 return false;
[email protected]4883a4e2009-06-06 19:59:36732 }
[email protected]7856bb82008-12-12 23:43:03733 length_ = file_stat.st_size;
734
735 data_ = static_cast<uint8*>(
736 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
737 if (data_ == MAP_FAILED)
[email protected]4883a4e2009-06-06 19:59:36738 LOG(ERROR) << "Couldn't mmap " << file_name.value() << ", errno " << errno;
739
740 return data_ != MAP_FAILED;
[email protected]7856bb82008-12-12 23:43:03741}
742
743void MemoryMappedFile::CloseHandles() {
744 if (data_ != NULL)
745 munmap(data_, length_);
746 if (file_ != -1)
747 close(file_);
748
749 data_ = NULL;
750 length_ = 0;
751 file_ = -1;
752}
[email protected]13ef7c02008-11-20 22:30:13753
[email protected]b2e97292008-09-02 18:20:34754} // namespace file_util