blob: b02df8e81ab0c2814c4c396b43c8eecc30be1fb8 [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
21#include <fstream>
22
23#include "base/basictypes.h"
[email protected]157c61b2009-05-01 21:37:3124#include "base/eintr_wrapper.h"
[email protected]640517f2008-10-30 23:54:0425#include "base/file_path.h"
[email protected]807bc042009-07-15 01:32:0226#include "base/lock.h"
[email protected]b2e97292008-09-02 18:20:3427#include "base/logging.h"
[email protected]807bc042009-07-15 01:32:0228#include "base/scoped_ptr.h"
29#include "base/singleton.h"
[email protected]b2e97292008-09-02 18:20:3430#include "base/string_util.h"
[email protected]807bc042009-07-15 01:32:0231#include "base/sys_string_conversions.h"
[email protected]4b7743de2009-04-21 01:50:3932#include "base/time.h"
[email protected]807bc042009-07-15 01:32:0233#include "unicode/coll.h"
[email protected]b2e97292008-09-02 18:20:3434
[email protected]172c5502009-06-24 03:29:2635namespace {
36
[email protected]807bc042009-07-15 01:32:0237class LocaleAwareComparator {
38 public:
39 LocaleAwareComparator() {
40 UErrorCode error_code = U_ZERO_ERROR;
41 // Use the default collator. The default locale should have been properly
42 // set by the time this constructor is called.
[email protected]b5b2385a2009-08-18 05:12:2943 collator_.reset(icu::Collator::createInstance(error_code));
[email protected]807bc042009-07-15 01:32:0244 DCHECK(U_SUCCESS(error_code));
45 // Make it case-sensitive.
[email protected]b5b2385a2009-08-18 05:12:2946 collator_->setStrength(icu::Collator::TERTIARY);
[email protected]807bc042009-07-15 01:32:0247 // Note: We do not set UCOL_NORMALIZATION_MODE attribute. In other words, we
48 // do not pay performance penalty to guarantee sort order correctness for
49 // non-FCD (http://unicode.org/notes/tn5/#FCD) file names. This should be a
50 // reasonable tradeoff because such file names should be rare and the sort
51 // order doesn't change much anyway.
52 }
53
54 // Note: A similar function is available in l10n_util.
55 // We cannot use it because base should not depend on l10n_util.
56 // TODO(yuzo): Move some of l10n_util to base.
57 int Compare(const string16& a, const string16& b) {
58 // We are not sure if Collator::compare is thread-safe.
59 // Use an AutoLock just in case.
60 AutoLock auto_lock(lock_);
61
62 UErrorCode error_code = U_ZERO_ERROR;
63 UCollationResult result = collator_->compare(
64 static_cast<const UChar*>(a.c_str()),
65 static_cast<int>(a.length()),
66 static_cast<const UChar*>(b.c_str()),
67 static_cast<int>(b.length()),
68 error_code);
69 DCHECK(U_SUCCESS(error_code));
70 return result;
71 }
72
73 private:
[email protected]b5b2385a2009-08-18 05:12:2974 scoped_ptr<icu::Collator> collator_;
[email protected]807bc042009-07-15 01:32:0275 Lock lock_;
76 friend struct DefaultSingletonTraits<LocaleAwareComparator>;
77
78 DISALLOW_COPY_AND_ASSIGN(LocaleAwareComparator);
79};
80
[email protected]172c5502009-06-24 03:29:2681} // namespace
82
[email protected]b2e97292008-09-02 18:20:3483namespace file_util {
84
[email protected]22a087f2009-03-17 19:17:4385#if defined(GOOGLE_CHROME_BUILD)
[email protected]392264c2008-11-11 00:01:3886static const char* kTempFileName = "com.google.chrome.XXXXXX";
[email protected]22a087f2009-03-17 19:17:4387#else
88static const char* kTempFileName = "org.chromium.XXXXXX";
89#endif
[email protected]778e8c52008-09-11 17:36:2390
[email protected]b2e97292008-09-02 18:20:3491std::wstring GetDirectoryFromPath(const std::wstring& path) {
92 if (EndsWithSeparator(path)) {
[email protected]a97f2b42009-04-21 23:15:4593 std::wstring dir = path;
94 TrimTrailingSeparator(&dir);
95 return dir;
[email protected]b2e97292008-09-02 18:20:3496 } else {
97 char full_path[PATH_MAX];
98 base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path));
99 return UTF8ToWide(dirname(full_path));
100 }
101}
[email protected]9e51af92009-02-04 00:58:39102
[email protected]640517f2008-10-30 23:54:04103bool AbsolutePath(FilePath* path) {
[email protected]b2e97292008-09-02 18:20:34104 char full_path[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04105 if (realpath(path->value().c_str(), full_path) == NULL)
[email protected]b2e97292008-09-02 18:20:34106 return false;
[email protected]640517f2008-10-30 23:54:04107 *path = FilePath(full_path);
[email protected]b2e97292008-09-02 18:20:34108 return true;
109}
110
[email protected]4b7743de2009-04-21 01:50:39111int CountFilesCreatedAfter(const FilePath& path,
112 const base::Time& comparison_time) {
113 int file_count = 0;
114
115 DIR* dir = opendir(path.value().c_str());
116 if (dir) {
[email protected]49930c3a2009-08-06 21:23:07117#if !defined(OS_LINUX) && !defined(OS_MACOSX)
118 #error Depending on the definition of struct dirent, additional space for \
119 pathname may be needed
120#endif
[email protected]2126577b2009-04-23 15:05:19121 struct dirent ent_buf;
[email protected]4b7743de2009-04-21 01:50:39122 struct dirent* ent;
[email protected]2126577b2009-04-23 15:05:19123 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
[email protected]4b7743de2009-04-21 01:50:39124 if ((strcmp(ent->d_name, ".") == 0) ||
125 (strcmp(ent->d_name, "..") == 0))
126 continue;
127
128 struct stat64 st;
129 int test = stat64(path.Append(ent->d_name).value().c_str(), &st);
130 if (test != 0) {
131 LOG(ERROR) << "stat64 failed: " << strerror(errno);
132 continue;
133 }
[email protected]2126577b2009-04-23 15:05:19134 // Here, we use Time::TimeT(), which discards microseconds. This
135 // means that files which are newer than |comparison_time| may
136 // be considered older. If we don't discard microseconds, it
137 // introduces another issue. Suppose the following case:
138 //
139 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
140 // 2. Create a file and the current time is 10.3 (secs).
141 //
142 // As POSIX doesn't have microsecond precision for |st_ctime|,
143 // the creation time of the file created in the step 2 is 10 and
144 // the file is considered older than |comparison_time|. After
145 // all, we may have to accept either of the two issues: 1. files
146 // which are older than |comparison_time| are considered newer
147 // (current implementation) 2. files newer than
148 // |comparison_time| are considered older.
[email protected]4b7743de2009-04-21 01:50:39149 if (st.st_ctime >= comparison_time.ToTimeT())
150 ++file_count;
151 }
152 closedir(dir);
153 }
154 return file_count;
155}
156
[email protected]b2e97292008-09-02 18:20:34157// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
158// which works both with and without the recursive flag. I'm not sure we need
159// that functionality. If not, remove from file_util_win.cc, otherwise add it
160// here.
[email protected]640517f2008-10-30 23:54:04161bool Delete(const FilePath& path, bool recursive) {
162 const char* path_str = path.value().c_str();
[email protected]b2e97292008-09-02 18:20:34163 struct stat64 file_info;
[email protected]640517f2008-10-30 23:54:04164 int test = stat64(path_str, &file_info);
[email protected]b2e97292008-09-02 18:20:34165 if (test != 0) {
166 // The Windows version defines this condition as success.
[email protected]9e51af92009-02-04 00:58:39167 bool ret = (errno == ENOENT || errno == ENOTDIR);
[email protected]b2e97292008-09-02 18:20:34168 return ret;
169 }
170 if (!S_ISDIR(file_info.st_mode))
[email protected]640517f2008-10-30 23:54:04171 return (unlink(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34172 if (!recursive)
[email protected]640517f2008-10-30 23:54:04173 return (rmdir(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34174
175 bool success = true;
[email protected]49930c3a2009-08-06 21:23:07176 std::stack<std::string> directories;
177 directories.push(path.value());
178 FileEnumerator traversal(path, true, static_cast<FileEnumerator::FILE_TYPE>(
179 FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
180 FileEnumerator::SHOW_SYM_LINKS));
181 for (FilePath current = traversal.Next(); success && !current.empty();
182 current = traversal.Next()) {
183 FileEnumerator::FindInfo info;
184 traversal.GetFindInfo(&info);
185
186 if (S_ISDIR(info.stat.st_mode))
187 directories.push(current.value());
188 else
189 success = (unlink(current.value().c_str()) == 0);
[email protected]21dec3872008-09-18 19:15:54190 }
[email protected]49930c3a2009-08-06 21:23:07191
192 while (success && !directories.empty()) {
193 FilePath dir = FilePath(directories.top());
194 directories.pop();
195 success = (rmdir(dir.value().c_str()) == 0);
[email protected]b2e97292008-09-02 18:20:34196 }
[email protected]49930c3a2009-08-06 21:23:07197
[email protected]b2e97292008-09-02 18:20:34198 return success;
199}
200
[email protected]640517f2008-10-30 23:54:04201bool Move(const FilePath& from_path, const FilePath& to_path) {
[email protected]cc7948a2009-03-13 20:01:43202 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
203 return true;
204
205 if (!CopyDirectory(from_path, to_path, true))
206 return false;
207
208 Delete(from_path, true);
209 return true;
[email protected]b2e97292008-09-02 18:20:34210}
211
[email protected]c5866dca2009-05-19 17:21:07212bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
213 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
214}
215
[email protected]640517f2008-10-30 23:54:04216bool CopyDirectory(const FilePath& from_path,
217 const FilePath& to_path,
[email protected]21dec3872008-09-18 19:15:54218 bool recursive) {
[email protected]21dec3872008-09-18 19:15:54219 // Some old callers of CopyDirectory want it to support wildcards.
220 // After some discussion, we decided to fix those callers.
221 // Break loudly here if anyone tries to do this.
222 // TODO(evanm): remove this once we're sure it's ok.
[email protected]640517f2008-10-30 23:54:04223 DCHECK(to_path.value().find('*') == std::string::npos);
224 DCHECK(from_path.value().find('*') == std::string::npos);
[email protected]21dec3872008-09-18 19:15:54225
226 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04227 if (base::strlcpy(top_dir, from_path.value().c_str(),
[email protected]21dec3872008-09-18 19:15:54228 arraysize(top_dir)) >= arraysize(top_dir)) {
229 return false;
230 }
231
[email protected]49930c3a2009-08-06 21:23:07232 // This function does not properly handle destinations within the source
233 FilePath real_to_path = to_path;
234 if (PathExists(real_to_path)) {
235 if (!AbsolutePath(&real_to_path))
236 return false;
237 } else {
238 real_to_path = real_to_path.DirName();
239 if (!AbsolutePath(&real_to_path))
240 return false;
241 }
242 FilePath real_from_path = from_path;
243 if (!AbsolutePath(&real_from_path))
[email protected]21dec3872008-09-18 19:15:54244 return false;
[email protected]49930c3a2009-08-06 21:23:07245 if (real_to_path.value().size() >= real_from_path.value().size() &&
246 real_to_path.value().compare(0, real_from_path.value().size(),
247 real_from_path.value()) == 0)
248 return false;
249
250 bool success = true;
251 FileEnumerator::FILE_TYPE traverse_type =
252 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES |
253 FileEnumerator::SHOW_SYM_LINKS);
254 if (recursive)
255 traverse_type = static_cast<FileEnumerator::FILE_TYPE>(
256 traverse_type | FileEnumerator::DIRECTORIES);
257 FileEnumerator traversal(from_path, recursive, traverse_type);
258
259 // to_path may not exist yet, start the loop with to_path
260 FileEnumerator::FindInfo info;
261 FilePath current = from_path;
262 if (stat(from_path.value().c_str(), &info.stat) < 0) {
263 LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " <<
264 from_path.value() << " errno = " << errno;
265 success = false;
[email protected]21dec3872008-09-18 19:15:54266 }
267
[email protected]49930c3a2009-08-06 21:23:07268 while (success && !current.empty()) {
269 // current is the source path, including from_path, so paste
[email protected]21dec3872008-09-18 19:15:54270 // the suffix after from_path onto to_path to create the target_path.
[email protected]49930c3a2009-08-06 21:23:07271 std::string suffix(&current.value().c_str()[from_path.value().size()]);
[email protected]ca0209612009-01-13 18:57:46272 // Strip the leading '/' (if any).
273 if (!suffix.empty()) {
[email protected]6ae340f2009-03-06 09:56:28274 DCHECK_EQ('/', suffix[0]);
[email protected]ca0209612009-01-13 18:57:46275 suffix.erase(0, 1);
276 }
277 const FilePath target_path = to_path.Append(suffix);
[email protected]21dec3872008-09-18 19:15:54278
[email protected]49930c3a2009-08-06 21:23:07279 if (S_ISDIR(info.stat.st_mode)) {
280 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
281 errno != EEXIST) {
282 LOG(ERROR) << "CopyDirectory() couldn't create directory: " <<
283 target_path.value() << " errno = " << errno;
284 success = false;
285 }
286 } else if (S_ISREG(info.stat.st_mode)) {
287 if (!CopyFile(current, target_path)) {
288 LOG(ERROR) << "CopyDirectory() couldn't create file: " <<
289 target_path.value();
290 success = false;
291 }
292 } else {
293 LOG(WARNING) << "CopyDirectory() skipping non-regular file: " <<
294 current.value();
[email protected]21dec3872008-09-18 19:15:54295 }
[email protected]21dec3872008-09-18 19:15:54296
[email protected]49930c3a2009-08-06 21:23:07297 current = traversal.Next();
298 traversal.GetFindInfo(&info);
[email protected]21dec3872008-09-18 19:15:54299 }
300
[email protected]49930c3a2009-08-06 21:23:07301 return success;
[email protected]b2e97292008-09-02 18:20:34302}
303
[email protected]640517f2008-10-30 23:54:04304bool PathExists(const FilePath& path) {
[email protected]b2e97292008-09-02 18:20:34305 struct stat64 file_info;
[email protected]640517f2008-10-30 23:54:04306 return (stat64(path.value().c_str(), &file_info) == 0);
[email protected]b2e97292008-09-02 18:20:34307}
308
[email protected]7e1fde6a2008-12-23 20:20:10309bool PathIsWritable(const FilePath& path) {
310 FilePath test_path(path);
311 struct stat64 file_info;
312 if (stat64(test_path.value().c_str(), &file_info) != 0) {
313 // If the path doesn't exist, test the parent dir.
314 test_path = test_path.DirName();
315 // If the parent dir doesn't exist, then return false (the path is not
316 // directly writable).
317 if (stat64(test_path.value().c_str(), &file_info) != 0)
318 return false;
319 }
320 if (S_IWOTH & file_info.st_mode)
321 return true;
322 if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
323 return true;
324 if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
325 return true;
326 return false;
327}
328
[email protected]640517f2008-10-30 23:54:04329bool DirectoryExists(const FilePath& path) {
[email protected]806b9c62008-09-11 16:09:11330 struct stat64 file_info;
[email protected]640517f2008-10-30 23:54:04331 if (stat64(path.value().c_str(), &file_info) == 0)
[email protected]806b9c62008-09-11 16:09:11332 return S_ISDIR(file_info.st_mode);
333 return false;
334}
335
[email protected]b2e97292008-09-02 18:20:34336// TODO(erikkay): implement
337#if 0
338bool GetFileCreationLocalTimeFromHandle(int fd,
339 LPSYSTEMTIME creation_time) {
340 if (!file_handle)
341 return false;
[email protected]9e51af92009-02-04 00:58:39342
[email protected]b2e97292008-09-02 18:20:34343 FILETIME utc_filetime;
344 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
345 return false;
[email protected]9e51af92009-02-04 00:58:39346
[email protected]b2e97292008-09-02 18:20:34347 FILETIME local_filetime;
348 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
349 return false;
[email protected]9e51af92009-02-04 00:58:39350
[email protected]b2e97292008-09-02 18:20:34351 return !!FileTimeToSystemTime(&local_filetime, creation_time);
352}
353
354bool GetFileCreationLocalTime(const std::string& filename,
355 LPSYSTEMTIME creation_time) {
356 ScopedHandle file_handle(
[email protected]9e51af92009-02-04 00:58:39357 CreateFile(filename.c_str(), GENERIC_READ,
[email protected]b2e97292008-09-02 18:20:34358 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
359 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
360 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
361}
362#endif
363
[email protected]45301492009-04-23 12:38:08364bool ReadFromFD(int fd, char* buffer, size_t bytes) {
365 size_t total_read = 0;
366 while (total_read < bytes) {
[email protected]157c61b2009-05-01 21:37:31367 ssize_t bytes_read =
368 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
369 if (bytes_read <= 0)
[email protected]45301492009-04-23 12:38:08370 break;
[email protected]157c61b2009-05-01 21:37:31371 total_read += bytes_read;
[email protected]45301492009-04-23 12:38:08372 }
373 return total_read == bytes;
374}
375
[email protected]9e51af92009-02-04 00:58:39376// Creates and opens a temporary file in |directory|, returning the
377// file descriptor. |path| is set to the temporary file path.
378// Note TODO(erikkay) comment in header for BlahFileName() calls; the
379// intent is to rename these files BlahFile() (since they create
380// files, not filenames). This function does NOT unlink() the file.
381int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
382 *path = directory.Append(kTempFileName);
383 const std::string& tmpdir_string = path->value();
[email protected]778e8c52008-09-11 17:36:23384 // this should be OK since mkstemp just replaces characters in place
385 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]392264c2008-11-11 00:01:38386
[email protected]9e51af92009-02-04 00:58:39387 return mkstemp(buffer);
388}
389
[email protected]33edeab2009-08-18 16:07:55390bool CreateTemporaryFile(FilePath* path) {
[email protected]9e51af92009-02-04 00:58:39391 FilePath directory;
392 if (!GetTempDir(&directory))
393 return false;
394 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
[email protected]b2e97292008-09-02 18:20:34395 if (fd < 0)
396 return false;
[email protected]392264c2008-11-11 00:01:38397 close(fd);
[email protected]b2e97292008-09-02 18:20:34398 return true;
399}
400
[email protected]9e51af92009-02-04 00:58:39401FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
402 FilePath directory;
403 if (!GetShmemTempDir(&directory))
404 return false;
405
[email protected]6faa0e0d2009-04-28 06:50:36406 return CreateAndOpenTemporaryFileInDir(directory, path);
407}
408
409FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
410 int fd = CreateAndOpenFdForTemporaryFile(dir, path);
[email protected]9e51af92009-02-04 00:58:39411 if (fd < 0)
412 return NULL;
413
[email protected]6faa0e0d2009-04-28 06:50:36414 return fdopen(fd, "a+");
[email protected]9e51af92009-02-04 00:58:39415}
[email protected]33edeab2009-08-18 16:07:55416// TODO(port): implement me.
417bool CreateTemporaryFileInDir(const FilePath& dir,
418 FilePath* temp_file) {
[email protected]9ccbb372008-10-10 18:50:32419 NOTREACHED();
420 return false;
421}
422
[email protected]7e1fde6a2008-12-23 20:20:10423bool CreateNewTempDirectory(const FilePath::StringType& prefix,
424 FilePath* new_temp_path) {
[email protected]392264c2008-11-11 00:01:38425 FilePath tmpdir;
[email protected]b2e97292008-09-02 18:20:34426 if (!GetTempDir(&tmpdir))
427 return false;
[email protected]392264c2008-11-11 00:01:38428 tmpdir = tmpdir.Append(kTempFileName);
429 std::string tmpdir_string = tmpdir.value();
[email protected]b2e97292008-09-02 18:20:34430 // this should be OK since mkdtemp just replaces characters in place
[email protected]778e8c52008-09-11 17:36:23431 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]b2e97292008-09-02 18:20:34432 char* dtemp = mkdtemp(buffer);
433 if (!dtemp)
434 return false;
[email protected]7e1fde6a2008-12-23 20:20:10435 *new_temp_path = FilePath(dtemp);
[email protected]b2e97292008-09-02 18:20:34436 return true;
437}
438
[email protected]640517f2008-10-30 23:54:04439bool CreateDirectory(const FilePath& full_path) {
440 std::vector<FilePath> subpaths;
441
442 // Collect a list of all parent directories.
443 FilePath last_path = full_path;
444 subpaths.push_back(full_path);
445 for (FilePath path = full_path.DirName();
446 path.value() != last_path.value(); path = path.DirName()) {
447 subpaths.push_back(path);
448 last_path = path;
449 }
450
451 // Iterate through the parents and create the missing ones.
452 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
453 i != subpaths.rend(); ++i) {
454 if (!DirectoryExists(*i)) {
[email protected]39a1a062009-05-12 20:12:24455 if (mkdir(i->value().c_str(), 0700) != 0)
[email protected]b2e97292008-09-02 18:20:34456 return false;
457 }
458 }
459 return true;
460}
461
[email protected]eac0709a2008-11-04 21:00:46462bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
[email protected]b2e97292008-09-02 18:20:34463 struct stat64 file_info;
[email protected]eac0709a2008-11-04 21:00:46464 if (stat64(file_path.value().c_str(), &file_info) != 0)
[email protected]b2e97292008-09-02 18:20:34465 return false;
[email protected]f5e3da4d2008-09-26 01:04:08466 results->is_directory = S_ISDIR(file_info.st_mode);
467 results->size = file_info.st_size;
[email protected]5ef323892009-07-24 16:13:53468 results->last_modified = base::Time::FromTimeT(file_info.st_mtime);
[email protected]b2e97292008-09-02 18:20:34469 return true;
470}
471
[email protected]825003f2009-05-14 17:49:23472bool GetInode(const FilePath& path, ino_t* inode) {
473 struct stat buffer;
474 int result = stat(path.value().c_str(), &buffer);
475 if (result < 0)
476 return false;
477
478 *inode = buffer.st_ino;
479 return true;
480}
481
[email protected]836f1342008-10-01 17:40:13482FILE* OpenFile(const std::string& filename, const char* mode) {
[email protected]a9cd2a652008-11-17 21:01:19483 return OpenFile(FilePath(filename), mode);
[email protected]836f1342008-10-01 17:40:13484}
485
[email protected]a9cd2a652008-11-17 21:01:19486FILE* OpenFile(const FilePath& filename, const char* mode) {
487 return fopen(filename.value().c_str(), mode);
[email protected]836f1342008-10-01 17:40:13488}
489
[email protected]c870c762009-01-28 05:47:15490int ReadFile(const FilePath& filename, char* data, int size) {
491 int fd = open(filename.value().c_str(), O_RDONLY);
[email protected]b2e97292008-09-02 18:20:34492 if (fd < 0)
493 return -1;
[email protected]a9cd2a652008-11-17 21:01:19494
[email protected]157c61b2009-05-01 21:37:31495 int ret_value = HANDLE_EINTR(read(fd, data, size));
496 HANDLE_EINTR(close(fd));
[email protected]b2e97292008-09-02 18:20:34497 return ret_value;
498}
499
[email protected]c870c762009-01-28 05:47:15500int WriteFile(const FilePath& filename, const char* data, int size) {
501 int fd = creat(filename.value().c_str(), 0666);
[email protected]b2e97292008-09-02 18:20:34502 if (fd < 0)
503 return -1;
[email protected]778e8c52008-09-11 17:36:23504
505 // Allow for partial writes
506 ssize_t bytes_written_total = 0;
507 do {
[email protected]157c61b2009-05-01 21:37:31508 ssize_t bytes_written_partial =
509 HANDLE_EINTR(write(fd, data + bytes_written_total,
510 size - bytes_written_total));
[email protected]778e8c52008-09-11 17:36:23511 if (bytes_written_partial < 0) {
[email protected]157c61b2009-05-01 21:37:31512 HANDLE_EINTR(close(fd));
[email protected]a9cd2a652008-11-17 21:01:19513 return -1;
[email protected]778e8c52008-09-11 17:36:23514 }
515 bytes_written_total += bytes_written_partial;
516 } while (bytes_written_total < size);
517
[email protected]157c61b2009-05-01 21:37:31518 HANDLE_EINTR(close(fd));
[email protected]778e8c52008-09-11 17:36:23519 return bytes_written_total;
[email protected]b2e97292008-09-02 18:20:34520}
521
522// Gets the current working directory for the process.
[email protected]640517f2008-10-30 23:54:04523bool GetCurrentDirectory(FilePath* dir) {
[email protected]b2e97292008-09-02 18:20:34524 char system_buffer[PATH_MAX] = "";
[email protected]640517f2008-10-30 23:54:04525 if (!getcwd(system_buffer, sizeof(system_buffer))) {
526 NOTREACHED();
527 return false;
528 }
529 *dir = FilePath(system_buffer);
[email protected]b2e97292008-09-02 18:20:34530 return true;
531}
532
533// Sets the current working directory for the process.
[email protected]a9cd2a652008-11-17 21:01:19534bool SetCurrentDirectory(const FilePath& path) {
535 int ret = chdir(path.value().c_str());
536 return !ret;
[email protected]b2e97292008-09-02 18:20:34537}
[email protected]a9cd2a652008-11-17 21:01:19538
[email protected]7856bb82008-12-12 23:43:03539///////////////////////////////////////////////
540// FileEnumerator
541
[email protected]0b733222008-12-11 14:55:12542FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34543 bool recursive,
544 FileEnumerator::FILE_TYPE file_type)
[email protected]49930c3a2009-08-06 21:23:07545 : root_path_(root_path),
546 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34547 file_type_(file_type),
548 is_in_find_op_(false),
[email protected]49930c3a2009-08-06 21:23:07549 current_directory_entry_(0) {
[email protected]8199b3a2009-06-09 05:57:38550 // INCLUDE_DOT_DOT must not be specified if recursive.
551 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]b2e97292008-09-02 18:20:34552 pending_paths_.push(root_path);
553}
554
[email protected]0b733222008-12-11 14:55:12555FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34556 bool recursive,
557 FileEnumerator::FILE_TYPE file_type,
[email protected]0b733222008-12-11 14:55:12558 const FilePath::StringType& pattern)
[email protected]49930c3a2009-08-06 21:23:07559 : root_path_(root_path),
560 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34561 file_type_(file_type),
[email protected]49930c3a2009-08-06 21:23:07562 pattern_(root_path.Append(pattern)),
[email protected]b2e97292008-09-02 18:20:34563 is_in_find_op_(false),
[email protected]49930c3a2009-08-06 21:23:07564 current_directory_entry_(0) {
[email protected]8199b3a2009-06-09 05:57:38565 // INCLUDE_DOT_DOT must not be specified if recursive.
566 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]49930c3a2009-08-06 21:23:07567 // The Windows version of this code appends the pattern to the root_path,
568 // potentially only matching against items in the top-most directory.
569 // Do the same here.
570 if (pattern.size() == 0)
571 pattern_ = FilePath();
[email protected]b2e97292008-09-02 18:20:34572 pending_paths_.push(root_path);
573}
[email protected]a9cd2a652008-11-17 21:01:19574
[email protected]b2e97292008-09-02 18:20:34575FileEnumerator::~FileEnumerator() {
[email protected]b2e97292008-09-02 18:20:34576}
577
[email protected]13ef7c02008-11-20 22:30:13578void FileEnumerator::GetFindInfo(FindInfo* info) {
579 DCHECK(info);
580
[email protected]49930c3a2009-08-06 21:23:07581 if (current_directory_entry_ >= directory_entries_.size())
[email protected]13ef7c02008-11-20 22:30:13582 return;
583
[email protected]49930c3a2009-08-06 21:23:07584 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_];
585 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat));
586 info->filename.assign(cur_entry->filename.value());
[email protected]13ef7c02008-11-20 22:30:13587}
588
[email protected]0b733222008-12-11 14:55:12589FilePath FileEnumerator::Next() {
[email protected]49930c3a2009-08-06 21:23:07590 ++current_directory_entry_;
591
592 // While we've exhausted the entries in the current directory, do the next
593 while (current_directory_entry_ >= directory_entries_.size()) {
[email protected]b2e97292008-09-02 18:20:34594 if (pending_paths_.empty())
[email protected]0b733222008-12-11 14:55:12595 return FilePath();
[email protected]13ef7c02008-11-20 22:30:13596
[email protected]b2e97292008-09-02 18:20:34597 root_path_ = pending_paths_.top();
[email protected]0b733222008-12-11 14:55:12598 root_path_ = root_path_.StripTrailingSeparators();
[email protected]b2e97292008-09-02 18:20:34599 pending_paths_.pop();
[email protected]13ef7c02008-11-20 22:30:13600
[email protected]49930c3a2009-08-06 21:23:07601 std::vector<DirectoryEntryInfo> entries;
602 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
603 continue;
[email protected]13ef7c02008-11-20 22:30:13604
[email protected]49930c3a2009-08-06 21:23:07605 // The API says that order is not guaranteed, but order affects UX
606 std::sort(entries.begin(), entries.end(), CompareFiles);
[email protected]13ef7c02008-11-20 22:30:13607
[email protected]49930c3a2009-08-06 21:23:07608 directory_entries_.clear();
609 current_directory_entry_ = 0;
610 for (std::vector<DirectoryEntryInfo>::const_iterator
611 i = entries.begin(); i != entries.end(); ++i) {
612 FilePath full_path = root_path_.Append(i->filename);
613 if (ShouldSkip(full_path))
614 continue;
[email protected]13ef7c02008-11-20 22:30:13615
[email protected]49930c3a2009-08-06 21:23:07616 if (pattern_.value().size() &&
617 fnmatch(pattern_.value().c_str(), full_path.value().c_str(),
618 FNM_NOESCAPE))
619 continue;
620
621 if (recursive_ && S_ISDIR(i->stat.st_mode))
622 pending_paths_.push(full_path);
623
624 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) ||
625 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES)))
626 directory_entries_.push_back(*i);
[email protected]b2e97292008-09-02 18:20:34627 }
628 }
[email protected]13ef7c02008-11-20 22:30:13629
[email protected]49930c3a2009-08-06 21:23:07630 return root_path_.Append(directory_entries_[current_directory_entry_
631 ].filename);
632}
[email protected]8199b3a2009-06-09 05:57:38633
[email protected]49930c3a2009-08-06 21:23:07634bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
635 const FilePath& source, bool show_links) {
636 DIR* dir = opendir(source.value().c_str());
637 if (!dir)
638 return false;
639
640#if !defined(OS_LINUX) && !defined(OS_MACOSX)
641 #error Depending on the definition of struct dirent, additional space for \
642 pathname may be needed
643#endif
644 struct dirent dent_buf;
645 struct dirent* dent;
646 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
647 DirectoryEntryInfo info;
648 FilePath full_name;
649 int stat_value;
650
651 info.filename = FilePath(dent->d_name);
652 full_name = source.Append(dent->d_name);
653 if (show_links)
654 stat_value = lstat(full_name.value().c_str(), &info.stat);
655 else
656 stat_value = stat(full_name.value().c_str(), &info.stat);
657 if (stat_value < 0) {
658 LOG(ERROR) << "Couldn't stat file: " <<
659 source.Append(dent->d_name).value().c_str() << " errno = " << errno;
660 memset(&info.stat, 0, sizeof(info.stat));
[email protected]8199b3a2009-06-09 05:57:38661 }
[email protected]49930c3a2009-08-06 21:23:07662 entries->push_back(info);
[email protected]b2e97292008-09-02 18:20:34663 }
[email protected]49930c3a2009-08-06 21:23:07664
665 closedir(dir);
666 return true;
667}
668
669bool FileEnumerator::CompareFiles(const DirectoryEntryInfo& a,
670 const DirectoryEntryInfo& b) {
671 // Order lexicographically with directories before other files.
672 if (S_ISDIR(a.stat.st_mode) != S_ISDIR(b.stat.st_mode))
673 return S_ISDIR(a.stat.st_mode);
674
675 // On linux, the file system encoding is not defined. We assume
676 // SysNativeMBToWide takes care of it.
677 //
678 // ICU's collator can take strings in OS native encoding. But we convert the
679 // strings to UTF-16 ourselves to ensure conversion consistency.
680 // TODO(yuzo): Perhaps we should define SysNativeMBToUTF16?
681 return Singleton<LocaleAwareComparator>()->Compare(
682 WideToUTF16(base::SysNativeMBToWide(a.filename.value().c_str())),
683 WideToUTF16(base::SysNativeMBToWide(b.filename.value().c_str()))) < 0;
[email protected]b2e97292008-09-02 18:20:34684}
[email protected]13ef7c02008-11-20 22:30:13685
[email protected]7856bb82008-12-12 23:43:03686///////////////////////////////////////////////
687// MemoryMappedFile
688
689MemoryMappedFile::MemoryMappedFile()
690 : file_(-1),
691 data_(NULL),
692 length_(0) {
693}
694
695bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
[email protected]cc8f1462009-06-12 17:36:55696 file_ = open(file_name.value().c_str(), O_RDONLY);
697
[email protected]4883a4e2009-06-06 19:59:36698 if (file_ == -1) {
699 LOG(ERROR) << "Couldn't open " << file_name.value();
[email protected]7856bb82008-12-12 23:43:03700 return false;
[email protected]4883a4e2009-06-06 19:59:36701 }
[email protected]7856bb82008-12-12 23:43:03702
703 struct stat file_stat;
[email protected]4883a4e2009-06-06 19:59:36704 if (fstat(file_, &file_stat) == -1) {
705 LOG(ERROR) << "Couldn't fstat " << file_name.value() << ", errno " << errno;
[email protected]7856bb82008-12-12 23:43:03706 return false;
[email protected]4883a4e2009-06-06 19:59:36707 }
[email protected]7856bb82008-12-12 23:43:03708 length_ = file_stat.st_size;
709
710 data_ = static_cast<uint8*>(
711 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
712 if (data_ == MAP_FAILED)
[email protected]4883a4e2009-06-06 19:59:36713 LOG(ERROR) << "Couldn't mmap " << file_name.value() << ", errno " << errno;
714
715 return data_ != MAP_FAILED;
[email protected]7856bb82008-12-12 23:43:03716}
717
718void MemoryMappedFile::CloseHandles() {
719 if (data_ != NULL)
720 munmap(data_, length_);
721 if (file_ != -1)
722 close(file_);
723
724 data_ = NULL;
725 length_ = 0;
726 file_ = -1;
727}
[email protected]13ef7c02008-11-20 22:30:13728
[email protected]b2e97292008-09-02 18:20:34729} // namespace file_util