blob: ad3fc5811d0998d5de2fe329f82ef90196d21cc4 [file] [log] [blame]
mmentovai@google.comaa13be62008-09-03 03:20:34 +09001// 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
evanm@google.com5c1d39b2008-09-19 04:15:54 +09007#include <errno.h>
mmentovai@google.comaa13be62008-09-03 03:20:34 +09008#include <fcntl.h>
9#include <fnmatch.h>
10#include <fts.h>
11#include <libgen.h>
mark@chromium.orgd1bafc62008-10-02 02:40:13 +090012#include <stdio.h>
evanm@google.com5c1d39b2008-09-19 04:15:54 +090013#include <string.h>
mmentovai@google.comaa13be62008-09-03 03:20:34 +090014#include <sys/errno.h>
estade@chromium.org2c233532008-12-13 08:43:03 +090015#include <sys/mman.h>
mmentovai@google.comaa13be62008-09-03 03:20:34 +090016#include <sys/stat.h>
17#include <time.h>
18
19#include <fstream>
20
21#include "base/basictypes.h"
evanm@google.com874d1672008-10-31 08:54:04 +090022#include "base/file_path.h"
mmentovai@google.comaa13be62008-09-03 03:20:34 +090023#include "base/logging.h"
24#include "base/string_util.h"
25
26namespace file_util {
27
estade@chromium.orgf3b73202009-03-18 04:17:43 +090028#if defined(GOOGLE_CHROME_BUILD)
estade@chromium.orgf474a1b2008-11-11 09:01:38 +090029static const char* kTempFileName = "com.google.chrome.XXXXXX";
estade@chromium.orgf3b73202009-03-18 04:17:43 +090030#else
31static const char* kTempFileName = "org.chromium.XXXXXX";
32#endif
mark@chromium.org8ca0d272008-09-12 02:36:23 +090033
mmentovai@google.comaa13be62008-09-03 03:20:34 +090034std::wstring GetDirectoryFromPath(const std::wstring& path) {
35 if (EndsWithSeparator(path)) {
36 std::wstring dir = path;
37 TrimTrailingSeparator(&dir);
38 return dir;
39 } else {
40 char full_path[PATH_MAX];
41 base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path));
42 return UTF8ToWide(dirname(full_path));
43 }
44}
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +090045
evanm@google.com874d1672008-10-31 08:54:04 +090046bool AbsolutePath(FilePath* path) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +090047 char full_path[PATH_MAX];
evanm@google.com874d1672008-10-31 08:54:04 +090048 if (realpath(path->value().c_str(), full_path) == NULL)
mmentovai@google.comaa13be62008-09-03 03:20:34 +090049 return false;
evanm@google.com874d1672008-10-31 08:54:04 +090050 *path = FilePath(full_path);
mmentovai@google.comaa13be62008-09-03 03:20:34 +090051 return true;
52}
53
54// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
55// which works both with and without the recursive flag. I'm not sure we need
56// that functionality. If not, remove from file_util_win.cc, otherwise add it
57// here.
evanm@google.com874d1672008-10-31 08:54:04 +090058bool Delete(const FilePath& path, bool recursive) {
59 const char* path_str = path.value().c_str();
mmentovai@google.comaa13be62008-09-03 03:20:34 +090060 struct stat64 file_info;
evanm@google.com874d1672008-10-31 08:54:04 +090061 int test = stat64(path_str, &file_info);
mmentovai@google.comaa13be62008-09-03 03:20:34 +090062 if (test != 0) {
63 // The Windows version defines this condition as success.
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +090064 bool ret = (errno == ENOENT || errno == ENOTDIR);
mmentovai@google.comaa13be62008-09-03 03:20:34 +090065 return ret;
66 }
67 if (!S_ISDIR(file_info.st_mode))
evanm@google.com874d1672008-10-31 08:54:04 +090068 return (unlink(path_str) == 0);
mmentovai@google.comaa13be62008-09-03 03:20:34 +090069 if (!recursive)
evanm@google.com874d1672008-10-31 08:54:04 +090070 return (rmdir(path_str) == 0);
mmentovai@google.comaa13be62008-09-03 03:20:34 +090071
72 bool success = true;
73 int ftsflags = FTS_PHYSICAL | FTS_NOSTAT;
74 char top_dir[PATH_MAX];
evanm@google.com874d1672008-10-31 08:54:04 +090075 if (base::strlcpy(top_dir, path_str,
evanm@google.com5c1d39b2008-09-19 04:15:54 +090076 arraysize(top_dir)) >= arraysize(top_dir)) {
77 return false;
78 }
mmentovai@google.comaa13be62008-09-03 03:20:34 +090079 char* dir_list[2] = { top_dir, NULL };
80 FTS* fts = fts_open(dir_list, ftsflags, NULL);
81 if (fts) {
82 FTSENT* fts_ent = fts_read(fts);
83 while (success && fts_ent != NULL) {
84 switch (fts_ent->fts_info) {
85 case FTS_DNR:
86 case FTS_ERR:
87 // log error
88 success = false;
89 continue;
90 break;
91 case FTS_DP:
erikkay@google.comcce83822008-12-24 05:20:10 +090092 success = (rmdir(fts_ent->fts_accpath) == 0);
mmentovai@google.comaa13be62008-09-03 03:20:34 +090093 break;
94 case FTS_D:
95 break;
96 case FTS_NSOK:
97 case FTS_F:
98 case FTS_SL:
99 case FTS_SLNONE:
erikkay@google.comcce83822008-12-24 05:20:10 +0900100 success = (unlink(fts_ent->fts_accpath) == 0);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900101 break;
102 default:
103 DCHECK(false);
104 break;
105 }
106 fts_ent = fts_read(fts);
107 }
108 fts_close(fts);
109 }
110 return success;
111}
112
evanm@google.com874d1672008-10-31 08:54:04 +0900113bool Move(const FilePath& from_path, const FilePath& to_path) {
estade@chromium.orga2fd8d82009-03-14 05:01:43 +0900114 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
115 return true;
116
117 if (!CopyDirectory(from_path, to_path, true))
118 return false;
119
120 Delete(from_path, true);
121 return true;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900122}
123
evanm@google.com874d1672008-10-31 08:54:04 +0900124bool CopyDirectory(const FilePath& from_path,
125 const FilePath& to_path,
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900126 bool recursive) {
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900127 // Some old callers of CopyDirectory want it to support wildcards.
128 // After some discussion, we decided to fix those callers.
129 // Break loudly here if anyone tries to do this.
130 // TODO(evanm): remove this once we're sure it's ok.
evanm@google.com874d1672008-10-31 08:54:04 +0900131 DCHECK(to_path.value().find('*') == std::string::npos);
132 DCHECK(from_path.value().find('*') == std::string::npos);
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900133
134 char top_dir[PATH_MAX];
evanm@google.com874d1672008-10-31 08:54:04 +0900135 if (base::strlcpy(top_dir, from_path.value().c_str(),
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900136 arraysize(top_dir)) >= arraysize(top_dir)) {
137 return false;
138 }
139
140 char* dir_list[] = { top_dir, NULL };
141 FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL);
142 if (!fts) {
143 LOG(ERROR) << "fts_open failed: " << strerror(errno);
144 return false;
145 }
146
147 int error = 0;
148 FTSENT* ent;
149 while (!error && (ent = fts_read(fts)) != NULL) {
150 // ent->fts_path is the source path, including from_path, so paste
151 // the suffix after from_path onto to_path to create the target_path.
phajdan.jr@chromium.orgecf50752009-01-14 03:57:46 +0900152 std::string suffix(&ent->fts_path[from_path.value().size()]);
153 // Strip the leading '/' (if any).
154 if (!suffix.empty()) {
phajdan.jr@chromium.org8c9b4512009-03-06 18:56:28 +0900155 DCHECK_EQ('/', suffix[0]);
phajdan.jr@chromium.orgecf50752009-01-14 03:57:46 +0900156 suffix.erase(0, 1);
157 }
158 const FilePath target_path = to_path.Append(suffix);
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900159 switch (ent->fts_info) {
160 case FTS_D: // Preorder directory.
161 // If we encounter a subdirectory in a non-recursive copy, prune it
162 // from the traversal.
163 if (!recursive && ent->fts_level > 0) {
164 if (fts_set(fts, ent, FTS_SKIP) != 0)
165 error = errno;
166 continue;
167 }
168
169 // Try creating the target dir, continuing on it if it exists already.
phajdan.jr@chromium.org8c9b4512009-03-06 18:56:28 +0900170 // Rely on the user's umask to produce correct permissions.
phajdan.jr@chromium.orgecf50752009-01-14 03:57:46 +0900171 if (mkdir(target_path.value().c_str(), 0777) != 0) {
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900172 if (errno != EEXIST)
173 error = errno;
174 }
175 break;
176 case FTS_F: // Regular file.
177 case FTS_NSOK: // File, no stat info requested.
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900178 errno = 0;
phajdan.jr@chromium.orgecf50752009-01-14 03:57:46 +0900179 if (!CopyFile(FilePath(ent->fts_path), target_path))
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900180 error = errno ? errno : EINVAL;
181 break;
182 case FTS_DP: // Postorder directory.
183 case FTS_DOT: // "." or ".."
184 // Skip it.
185 continue;
186 case FTS_DC: // Directory causing a cycle.
187 // Skip this branch.
188 if (fts_set(fts, ent, FTS_SKIP) != 0)
189 error = errno;
190 break;
191 case FTS_DNR: // Directory cannot be read.
192 case FTS_ERR: // Error.
193 case FTS_NS: // Stat failed.
194 // Abort with the error.
195 error = ent->fts_errno;
196 break;
197 case FTS_SL: // Symlink.
198 case FTS_SLNONE: // Symlink with broken target.
phajdan.jr@chromium.org8c9b4512009-03-06 18:56:28 +0900199 LOG(WARNING) << "CopyDirectory() skipping symbolic link: " <<
200 ent->fts_path;
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900201 continue;
202 case FTS_DEFAULT: // Some other sort of file.
phajdan.jr@chromium.org8c9b4512009-03-06 18:56:28 +0900203 LOG(WARNING) << "CopyDirectory() skipping file of unknown type: " <<
204 ent->fts_path;
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900205 continue;
206 default:
207 NOTREACHED();
208 continue; // Hope for the best!
209 }
210 }
211 // fts_read may have returned NULL and set errno to indicate an error.
212 if (!error && errno != 0)
213 error = errno;
214
215 if (!fts_close(fts)) {
216 // If we already have an error, let's use that error instead of the error
217 // fts_close set.
218 if (!error)
219 error = errno;
220 }
221
222 if (error) {
223 LOG(ERROR) << "CopyDirectory(): " << strerror(error);
224 return false;
225 }
226 return true;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900227}
228
evanm@google.com874d1672008-10-31 08:54:04 +0900229bool PathExists(const FilePath& path) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900230 struct stat64 file_info;
evanm@google.com874d1672008-10-31 08:54:04 +0900231 return (stat64(path.value().c_str(), &file_info) == 0);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900232}
233
erikkay@google.comcce83822008-12-24 05:20:10 +0900234bool PathIsWritable(const FilePath& path) {
235 FilePath test_path(path);
236 struct stat64 file_info;
237 if (stat64(test_path.value().c_str(), &file_info) != 0) {
238 // If the path doesn't exist, test the parent dir.
239 test_path = test_path.DirName();
240 // If the parent dir doesn't exist, then return false (the path is not
241 // directly writable).
242 if (stat64(test_path.value().c_str(), &file_info) != 0)
243 return false;
244 }
245 if (S_IWOTH & file_info.st_mode)
246 return true;
247 if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
248 return true;
249 if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
250 return true;
251 return false;
252}
253
evanm@google.com874d1672008-10-31 08:54:04 +0900254bool DirectoryExists(const FilePath& path) {
mmoss@google.com733df6b2008-09-12 01:09:11 +0900255 struct stat64 file_info;
evanm@google.com874d1672008-10-31 08:54:04 +0900256 if (stat64(path.value().c_str(), &file_info) == 0)
mmoss@google.com733df6b2008-09-12 01:09:11 +0900257 return S_ISDIR(file_info.st_mode);
258 return false;
259}
260
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900261// TODO(erikkay): implement
262#if 0
263bool GetFileCreationLocalTimeFromHandle(int fd,
264 LPSYSTEMTIME creation_time) {
265 if (!file_handle)
266 return false;
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900267
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900268 FILETIME utc_filetime;
269 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
270 return false;
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900271
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900272 FILETIME local_filetime;
273 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
274 return false;
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900275
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900276 return !!FileTimeToSystemTime(&local_filetime, creation_time);
277}
278
279bool GetFileCreationLocalTime(const std::string& filename,
280 LPSYSTEMTIME creation_time) {
281 ScopedHandle file_handle(
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900282 CreateFile(filename.c_str(), GENERIC_READ,
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900283 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
284 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
285 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
286}
287#endif
288
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900289// Creates and opens a temporary file in |directory|, returning the
290// file descriptor. |path| is set to the temporary file path.
291// Note TODO(erikkay) comment in header for BlahFileName() calls; the
292// intent is to rename these files BlahFile() (since they create
293// files, not filenames). This function does NOT unlink() the file.
294int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
295 *path = directory.Append(kTempFileName);
296 const std::string& tmpdir_string = path->value();
mark@chromium.org8ca0d272008-09-12 02:36:23 +0900297 // this should be OK since mkstemp just replaces characters in place
298 char* buffer = const_cast<char*>(tmpdir_string.c_str());
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900299
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900300 return mkstemp(buffer);
301}
302
303bool CreateTemporaryFileName(FilePath* path) {
304 FilePath directory;
305 if (!GetTempDir(&directory))
306 return false;
307 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900308 if (fd < 0)
309 return false;
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900310 close(fd);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900311 return true;
312}
313
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900314FILE* CreateAndOpenTemporaryFile(FilePath* path) {
315 FilePath directory;
316 if (!GetTempDir(&directory))
317 return false;
318
319 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
320 if (fd < 0)
321 return NULL;
322
323 FILE *fp = fdopen(fd, "a+");
324 return fp;
325}
326
327FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
328 FilePath directory;
329 if (!GetShmemTempDir(&directory))
330 return false;
331
332 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
333 if (fd < 0)
334 return NULL;
335
336 FILE *fp = fdopen(fd, "a+");
337 return fp;
338}
339
jcampan@chromium.orgbf29e602008-10-11 03:50:32 +0900340bool CreateTemporaryFileNameInDir(const std::wstring& dir,
341 std::wstring* temp_file) {
342 // Not implemented yet.
343 NOTREACHED();
344 return false;
345}
346
erikkay@google.comcce83822008-12-24 05:20:10 +0900347bool CreateNewTempDirectory(const FilePath::StringType& prefix,
348 FilePath* new_temp_path) {
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900349 FilePath tmpdir;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900350 if (!GetTempDir(&tmpdir))
351 return false;
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900352 tmpdir = tmpdir.Append(kTempFileName);
353 std::string tmpdir_string = tmpdir.value();
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900354 // this should be OK since mkdtemp just replaces characters in place
mark@chromium.org8ca0d272008-09-12 02:36:23 +0900355 char* buffer = const_cast<char*>(tmpdir_string.c_str());
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900356 char* dtemp = mkdtemp(buffer);
357 if (!dtemp)
358 return false;
erikkay@google.comcce83822008-12-24 05:20:10 +0900359 *new_temp_path = FilePath(dtemp);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900360 return true;
361}
362
evanm@google.com874d1672008-10-31 08:54:04 +0900363bool CreateDirectory(const FilePath& full_path) {
364 std::vector<FilePath> subpaths;
365
366 // Collect a list of all parent directories.
367 FilePath last_path = full_path;
368 subpaths.push_back(full_path);
369 for (FilePath path = full_path.DirName();
370 path.value() != last_path.value(); path = path.DirName()) {
371 subpaths.push_back(path);
372 last_path = path;
373 }
374
375 // Iterate through the parents and create the missing ones.
376 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
377 i != subpaths.rend(); ++i) {
378 if (!DirectoryExists(*i)) {
379 if (mkdir(i->value().c_str(), 0777) != 0)
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900380 return false;
381 }
382 }
383 return true;
384}
385
dkegel@google.com44982682008-11-05 06:00:46 +0900386bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900387 struct stat64 file_info;
dkegel@google.com44982682008-11-05 06:00:46 +0900388 if (stat64(file_path.value().c_str(), &file_info) != 0)
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900389 return false;
darin@google.com7f479f22008-09-26 10:04:08 +0900390 results->is_directory = S_ISDIR(file_info.st_mode);
391 results->size = file_info.st_size;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900392 return true;
393}
394
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900395FILE* OpenFile(const std::string& filename, const char* mode) {
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900396 return OpenFile(FilePath(filename), mode);
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900397}
398
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900399FILE* OpenFile(const FilePath& filename, const char* mode) {
400 return fopen(filename.value().c_str(), mode);
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900401}
402
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900403int ReadFile(const FilePath& filename, char* data, int size) {
404 int fd = open(filename.value().c_str(), O_RDONLY);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900405 if (fd < 0)
406 return -1;
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900407
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900408 int ret_value = read(fd, data, size);
409 close(fd);
410 return ret_value;
411}
412
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900413int WriteFile(const FilePath& filename, const char* data, int size) {
414 int fd = creat(filename.value().c_str(), 0666);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900415 if (fd < 0)
416 return -1;
mark@chromium.org8ca0d272008-09-12 02:36:23 +0900417
418 // Allow for partial writes
419 ssize_t bytes_written_total = 0;
420 do {
421 ssize_t bytes_written_partial = write(fd,
422 data + bytes_written_total,
423 size - bytes_written_total);
424 if (bytes_written_partial < 0) {
425 close(fd);
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900426 return -1;
mark@chromium.org8ca0d272008-09-12 02:36:23 +0900427 }
428 bytes_written_total += bytes_written_partial;
429 } while (bytes_written_total < size);
430
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900431 close(fd);
mark@chromium.org8ca0d272008-09-12 02:36:23 +0900432 return bytes_written_total;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900433}
434
435// Gets the current working directory for the process.
evanm@google.com874d1672008-10-31 08:54:04 +0900436bool GetCurrentDirectory(FilePath* dir) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900437 char system_buffer[PATH_MAX] = "";
evanm@google.com874d1672008-10-31 08:54:04 +0900438 if (!getcwd(system_buffer, sizeof(system_buffer))) {
439 NOTREACHED();
440 return false;
441 }
442 *dir = FilePath(system_buffer);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900443 return true;
444}
445
446// Sets the current working directory for the process.
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900447bool SetCurrentDirectory(const FilePath& path) {
448 int ret = chdir(path.value().c_str());
449 return !ret;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900450}
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900451
estade@chromium.org2c233532008-12-13 08:43:03 +0900452///////////////////////////////////////////////
453// FileEnumerator
454
avi@google.com5cb79352008-12-11 23:55:12 +0900455FileEnumerator::FileEnumerator(const FilePath& root_path,
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900456 bool recursive,
457 FileEnumerator::FILE_TYPE file_type)
458 : recursive_(recursive),
459 file_type_(file_type),
460 is_in_find_op_(false),
461 fts_(NULL) {
462 pending_paths_.push(root_path);
463}
464
avi@google.com5cb79352008-12-11 23:55:12 +0900465FileEnumerator::FileEnumerator(const FilePath& root_path,
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900466 bool recursive,
467 FileEnumerator::FILE_TYPE file_type,
avi@google.com5cb79352008-12-11 23:55:12 +0900468 const FilePath::StringType& pattern)
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900469 : recursive_(recursive),
470 file_type_(file_type),
avi@google.com5cb79352008-12-11 23:55:12 +0900471 pattern_(root_path.value()),
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900472 is_in_find_op_(false),
473 fts_(NULL) {
474 // The Windows version of this code only matches against items in the top-most
475 // directory, and we're comparing fnmatch against full paths, so this is the
476 // easiest way to get the right pattern.
avi@google.com5cb79352008-12-11 23:55:12 +0900477 pattern_ = pattern_.Append(pattern);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900478 pending_paths_.push(root_path);
479}
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900480
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900481FileEnumerator::~FileEnumerator() {
482 if (fts_)
483 fts_close(fts_);
484}
485
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900486void FileEnumerator::GetFindInfo(FindInfo* info) {
487 DCHECK(info);
488
489 if (!is_in_find_op_)
490 return;
491
492 memcpy(&(info->stat), fts_ent_->fts_statp, sizeof(info->stat));
493 info->filename.assign(fts_ent_->fts_name);
494}
495
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900496// As it stands, this method calls itself recursively when the next item of
497// the fts enumeration doesn't match (type, pattern, etc.). In the case of
498// large directories with many files this can be quite deep.
499// TODO(erikkay) - get rid of this recursive pattern
avi@google.com5cb79352008-12-11 23:55:12 +0900500FilePath FileEnumerator::Next() {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900501 if (!is_in_find_op_) {
502 if (pending_paths_.empty())
avi@google.com5cb79352008-12-11 23:55:12 +0900503 return FilePath();
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900504
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900505 // The last find FindFirstFile operation is done, prepare a new one.
506 root_path_ = pending_paths_.top();
avi@google.com5cb79352008-12-11 23:55:12 +0900507 root_path_ = root_path_.StripTrailingSeparators();
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900508 pending_paths_.pop();
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900509
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900510 // Start a new find operation.
511 int ftsflags = FTS_LOGICAL;
512 char top_dir[PATH_MAX];
deanm@chromium.org93358282009-01-02 21:57:53 +0900513 base::strlcpy(top_dir, root_path_.value().c_str(), arraysize(top_dir));
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900514 char* dir_list[2] = { top_dir, NULL };
515 fts_ = fts_open(dir_list, ftsflags, NULL);
516 if (!fts_)
517 return Next();
518 is_in_find_op_ = true;
519 }
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900520
521 fts_ent_ = fts_read(fts_);
522 if (fts_ent_ == NULL) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900523 fts_close(fts_);
524 fts_ = NULL;
525 is_in_find_op_ = false;
526 return Next();
527 }
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900528
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900529 // Level 0 is the top, which is always skipped.
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900530 if (fts_ent_->fts_level == 0)
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900531 return Next();
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900532
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900533 // Patterns are only matched on the items in the top-most directory.
534 // (see Windows implementation)
avi@google.com5cb79352008-12-11 23:55:12 +0900535 if (fts_ent_->fts_level == 1 && pattern_.value().length() > 0) {
536 if (fnmatch(pattern_.value().c_str(), fts_ent_->fts_path, 0) != 0) {
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900537 if (fts_ent_->fts_info == FTS_D)
538 fts_set(fts_, fts_ent_, FTS_SKIP);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900539 return Next();
540 }
541 }
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900542
avi@google.com5cb79352008-12-11 23:55:12 +0900543 FilePath cur_file(fts_ent_->fts_path);
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900544 if (fts_ent_->fts_info == FTS_D) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900545 // If not recursive, then prune children.
546 if (!recursive_)
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900547 fts_set(fts_, fts_ent_, FTS_SKIP);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900548 return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900549 } else if (fts_ent_->fts_info == FTS_F) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900550 return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
551 }
552 // TODO(erikkay) - verify that the other fts_info types aren't interesting
553 return Next();
554}
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900555
estade@chromium.org2c233532008-12-13 08:43:03 +0900556///////////////////////////////////////////////
557// MemoryMappedFile
558
559MemoryMappedFile::MemoryMappedFile()
560 : file_(-1),
561 data_(NULL),
562 length_(0) {
563}
564
565bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
566 file_ = open(file_name.value().c_str(), O_RDONLY);
567 if (file_ == -1)
568 return false;
569
570 struct stat file_stat;
571 if (fstat(file_, &file_stat) == -1)
572 return false;
573 length_ = file_stat.st_size;
574
575 data_ = static_cast<uint8*>(
576 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
577 if (data_ == MAP_FAILED)
578 data_ = NULL;
579 return data_ != NULL;
580}
581
582void MemoryMappedFile::CloseHandles() {
583 if (data_ != NULL)
584 munmap(data_, length_);
585 if (file_ != -1)
586 close(file_);
587
588 data_ = NULL;
589 length_ = 0;
590 file_ = -1;
591}
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900592
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900593} // namespace file_util