1 : // 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 :
7 : #include <dirent.h>
8 : #include <errno.h>
9 : #include <fcntl.h>
10 : #include <fnmatch.h>
11 : #ifndef ANDROID
12 : #include <fts.h>
13 : #endif
14 : #include <libgen.h>
15 : #include <stdio.h>
16 : #include <string.h>
17 : #include <sys/errno.h>
18 : #include <sys/mman.h>
19 : #include <sys/stat.h>
20 : #include <sys/types.h>
21 : #include <time.h>
22 : #include <unistd.h>
23 :
24 : #include <fstream>
25 :
26 : #include "base/basictypes.h"
27 : #include "base/eintr_wrapper.h"
28 : #include "base/file_path.h"
29 : #include "base/logging.h"
30 : #include "base/string_util.h"
31 : #include "base/time.h"
32 :
33 : // FreeBSD/OpenBSD lacks stat64, but its stat handles files >2GB just fine
34 : #if defined(OS_FREEBSD) || defined(OS_OPENBSD)
35 : #define stat64 stat
36 : #endif
37 :
38 : namespace file_util {
39 :
40 : #if defined(GOOGLE_CHROME_BUILD)
41 : static const char* kTempFileName = "com.google.chrome.XXXXXX";
42 : #else
43 : static const char* kTempFileName = "org.chromium.XXXXXX";
44 : #endif
45 :
46 0 : std::wstring GetDirectoryFromPath(const std::wstring& path) {
47 0 : if (EndsWithSeparator(path)) {
48 0 : std::wstring dir = path;
49 0 : TrimTrailingSeparator(&dir);
50 0 : return dir;
51 : } else {
52 : char full_path[PATH_MAX];
53 0 : base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path));
54 0 : return UTF8ToWide(dirname(full_path));
55 : }
56 : }
57 :
58 0 : bool AbsolutePath(FilePath* path) {
59 : char full_path[PATH_MAX];
60 0 : if (realpath(path->value().c_str(), full_path) == NULL)
61 0 : return false;
62 0 : *path = FilePath(full_path);
63 0 : return true;
64 : }
65 :
66 0 : int CountFilesCreatedAfter(const FilePath& path,
67 : const base::Time& comparison_time) {
68 0 : int file_count = 0;
69 :
70 0 : DIR* dir = opendir(path.value().c_str());
71 0 : if (dir) {
72 : struct dirent ent_buf;
73 : struct dirent* ent;
74 0 : while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
75 0 : if ((strcmp(ent->d_name, ".") == 0) ||
76 0 : (strcmp(ent->d_name, "..") == 0))
77 0 : continue;
78 :
79 : struct stat64 st;
80 0 : int test = stat64(path.Append(ent->d_name).value().c_str(), &st);
81 0 : if (test != 0) {
82 0 : LOG(ERROR) << "stat64 failed: " << strerror(errno);
83 0 : continue;
84 : }
85 : // Here, we use Time::TimeT(), which discards microseconds. This
86 : // means that files which are newer than |comparison_time| may
87 : // be considered older. If we don't discard microseconds, it
88 : // introduces another issue. Suppose the following case:
89 : //
90 : // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
91 : // 2. Create a file and the current time is 10.3 (secs).
92 : //
93 : // As POSIX doesn't have microsecond precision for |st_ctime|,
94 : // the creation time of the file created in the step 2 is 10 and
95 : // the file is considered older than |comparison_time|. After
96 : // all, we may have to accept either of the two issues: 1. files
97 : // which are older than |comparison_time| are considered newer
98 : // (current implementation) 2. files newer than
99 : // |comparison_time| are considered older.
100 0 : if (st.st_ctime >= comparison_time.ToTimeT())
101 0 : ++file_count;
102 : }
103 0 : closedir(dir);
104 : }
105 0 : return file_count;
106 : }
107 :
108 : // TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
109 : // which works both with and without the recursive flag. I'm not sure we need
110 : // that functionality. If not, remove from file_util_win.cc, otherwise add it
111 : // here.
112 0 : bool Delete(const FilePath& path, bool recursive) {
113 0 : const char* path_str = path.value().c_str();
114 : struct stat64 file_info;
115 0 : int test = stat64(path_str, &file_info);
116 0 : if (test != 0) {
117 : // The Windows version defines this condition as success.
118 0 : bool ret = (errno == ENOENT || errno == ENOTDIR);
119 0 : return ret;
120 : }
121 0 : if (!S_ISDIR(file_info.st_mode))
122 0 : return (unlink(path_str) == 0);
123 0 : if (!recursive)
124 0 : return (rmdir(path_str) == 0);
125 :
126 : #ifdef ANDROID
127 : // XXX Need ftsless impl for bionic
128 : return false;
129 : #else
130 0 : bool success = true;
131 0 : int ftsflags = FTS_PHYSICAL | FTS_NOSTAT;
132 : char top_dir[PATH_MAX];
133 0 : if (base::strlcpy(top_dir, path_str,
134 0 : arraysize(top_dir)) >= arraysize(top_dir)) {
135 0 : return false;
136 : }
137 0 : char* dir_list[2] = { top_dir, NULL };
138 0 : FTS* fts = fts_open(dir_list, ftsflags, NULL);
139 0 : if (fts) {
140 0 : FTSENT* fts_ent = fts_read(fts);
141 0 : while (success && fts_ent != NULL) {
142 0 : switch (fts_ent->fts_info) {
143 : case FTS_DNR:
144 : case FTS_ERR:
145 : // log error
146 0 : success = false;
147 0 : continue;
148 : break;
149 : case FTS_DP:
150 0 : success = (rmdir(fts_ent->fts_accpath) == 0);
151 0 : break;
152 : case FTS_D:
153 0 : break;
154 : case FTS_NSOK:
155 : case FTS_F:
156 : case FTS_SL:
157 : case FTS_SLNONE:
158 0 : success = (unlink(fts_ent->fts_accpath) == 0);
159 0 : break;
160 : default:
161 0 : DCHECK(false);
162 0 : break;
163 : }
164 0 : fts_ent = fts_read(fts);
165 : }
166 0 : fts_close(fts);
167 : }
168 0 : return success;
169 : #endif
170 : }
171 :
172 0 : bool Move(const FilePath& from_path, const FilePath& to_path) {
173 0 : if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
174 0 : return true;
175 :
176 0 : if (!CopyDirectory(from_path, to_path, true))
177 0 : return false;
178 :
179 0 : Delete(from_path, true);
180 0 : return true;
181 : }
182 :
183 0 : bool CopyDirectory(const FilePath& from_path,
184 : const FilePath& to_path,
185 : bool recursive) {
186 : // Some old callers of CopyDirectory want it to support wildcards.
187 : // After some discussion, we decided to fix those callers.
188 : // Break loudly here if anyone tries to do this.
189 : // TODO(evanm): remove this once we're sure it's ok.
190 0 : DCHECK(to_path.value().find('*') == std::string::npos);
191 0 : DCHECK(from_path.value().find('*') == std::string::npos);
192 :
193 : char top_dir[PATH_MAX];
194 0 : if (base::strlcpy(top_dir, from_path.value().c_str(),
195 0 : arraysize(top_dir)) >= arraysize(top_dir)) {
196 0 : return false;
197 : }
198 :
199 : #ifdef ANDROID
200 : // XXX Need ftsless impl for bionic
201 : return false;
202 : #else
203 0 : char* dir_list[] = { top_dir, NULL };
204 0 : FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL);
205 0 : if (!fts) {
206 0 : LOG(ERROR) << "fts_open failed: " << strerror(errno);
207 0 : return false;
208 : }
209 :
210 0 : int error = 0;
211 : FTSENT* ent;
212 0 : while (!error && (ent = fts_read(fts)) != NULL) {
213 : // ent->fts_path is the source path, including from_path, so paste
214 : // the suffix after from_path onto to_path to create the target_path.
215 0 : std::string suffix(&ent->fts_path[from_path.value().size()]);
216 : // Strip the leading '/' (if any).
217 0 : if (!suffix.empty()) {
218 0 : DCHECK_EQ('/', suffix[0]);
219 0 : suffix.erase(0, 1);
220 : }
221 0 : const FilePath target_path = to_path.Append(suffix);
222 0 : switch (ent->fts_info) {
223 : case FTS_D: // Preorder directory.
224 : // If we encounter a subdirectory in a non-recursive copy, prune it
225 : // from the traversal.
226 0 : if (!recursive && ent->fts_level > 0) {
227 0 : if (fts_set(fts, ent, FTS_SKIP) != 0)
228 0 : error = errno;
229 0 : continue;
230 : }
231 :
232 : // Try creating the target dir, continuing on it if it exists already.
233 : // Rely on the user's umask to produce correct permissions.
234 0 : if (mkdir(target_path.value().c_str(), 0777) != 0) {
235 0 : if (errno != EEXIST)
236 0 : error = errno;
237 : }
238 0 : break;
239 : case FTS_F: // Regular file.
240 : case FTS_NSOK: // File, no stat info requested.
241 0 : errno = 0;
242 0 : if (!CopyFile(FilePath(ent->fts_path), target_path))
243 0 : error = errno ? errno : EINVAL;
244 0 : break;
245 : case FTS_DP: // Postorder directory.
246 : case FTS_DOT: // "." or ".."
247 : // Skip it.
248 0 : continue;
249 : case FTS_DC: // Directory causing a cycle.
250 : // Skip this branch.
251 0 : if (fts_set(fts, ent, FTS_SKIP) != 0)
252 0 : error = errno;
253 0 : break;
254 : case FTS_DNR: // Directory cannot be read.
255 : case FTS_ERR: // Error.
256 : case FTS_NS: // Stat failed.
257 : // Abort with the error.
258 0 : error = ent->fts_errno;
259 0 : break;
260 : case FTS_SL: // Symlink.
261 : case FTS_SLNONE: // Symlink with broken target.
262 0 : LOG(WARNING) << "CopyDirectory() skipping symbolic link: " <<
263 0 : ent->fts_path;
264 0 : continue;
265 : case FTS_DEFAULT: // Some other sort of file.
266 0 : LOG(WARNING) << "CopyDirectory() skipping file of unknown type: " <<
267 0 : ent->fts_path;
268 0 : continue;
269 : default:
270 0 : NOTREACHED();
271 0 : continue; // Hope for the best!
272 : }
273 : }
274 : // fts_read may have returned NULL and set errno to indicate an error.
275 0 : if (!error && errno != 0)
276 0 : error = errno;
277 :
278 0 : if (!fts_close(fts)) {
279 : // If we already have an error, let's use that error instead of the error
280 : // fts_close set.
281 0 : if (!error)
282 0 : error = errno;
283 : }
284 :
285 0 : if (error) {
286 0 : LOG(ERROR) << "CopyDirectory(): " << strerror(error);
287 0 : return false;
288 : }
289 0 : return true;
290 : #endif
291 : }
292 :
293 0 : bool PathExists(const FilePath& path) {
294 : struct stat64 file_info;
295 0 : return (stat64(path.value().c_str(), &file_info) == 0);
296 : }
297 :
298 0 : bool PathIsWritable(const FilePath& path) {
299 0 : FilePath test_path(path);
300 : struct stat64 file_info;
301 0 : if (stat64(test_path.value().c_str(), &file_info) != 0) {
302 : // If the path doesn't exist, test the parent dir.
303 0 : test_path = test_path.DirName();
304 : // If the parent dir doesn't exist, then return false (the path is not
305 : // directly writable).
306 0 : if (stat64(test_path.value().c_str(), &file_info) != 0)
307 0 : return false;
308 : }
309 0 : if (S_IWOTH & file_info.st_mode)
310 0 : return true;
311 0 : if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
312 0 : return true;
313 0 : if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
314 0 : return true;
315 0 : return false;
316 : }
317 :
318 0 : bool DirectoryExists(const FilePath& path) {
319 : struct stat64 file_info;
320 0 : if (stat64(path.value().c_str(), &file_info) == 0)
321 0 : return S_ISDIR(file_info.st_mode);
322 0 : return false;
323 : }
324 :
325 : // TODO(erikkay): implement
326 : #if 0
327 : bool GetFileCreationLocalTimeFromHandle(int fd,
328 : LPSYSTEMTIME creation_time) {
329 : if (!file_handle)
330 : return false;
331 :
332 : FILETIME utc_filetime;
333 : if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
334 : return false;
335 :
336 : FILETIME local_filetime;
337 : if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
338 : return false;
339 :
340 : return !!FileTimeToSystemTime(&local_filetime, creation_time);
341 : }
342 :
343 : bool GetFileCreationLocalTime(const std::string& filename,
344 : LPSYSTEMTIME creation_time) {
345 : ScopedHandle file_handle(
346 : CreateFile(filename.c_str(), GENERIC_READ,
347 : FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
348 : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
349 : return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
350 : }
351 : #endif
352 :
353 0 : bool ReadFromFD(int fd, char* buffer, size_t bytes) {
354 0 : size_t total_read = 0;
355 0 : while (total_read < bytes) {
356 : ssize_t bytes_read =
357 0 : HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
358 0 : if (bytes_read <= 0)
359 0 : break;
360 0 : total_read += bytes_read;
361 : }
362 0 : return total_read == bytes;
363 : }
364 :
365 : // Creates and opens a temporary file in |directory|, returning the
366 : // file descriptor. |path| is set to the temporary file path.
367 : // Note TODO(erikkay) comment in header for BlahFileName() calls; the
368 : // intent is to rename these files BlahFile() (since they create
369 : // files, not filenames). This function does NOT unlink() the file.
370 0 : int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
371 0 : *path = directory.Append(kTempFileName);
372 0 : const std::string& tmpdir_string = path->value();
373 : // this should be OK since mkstemp just replaces characters in place
374 0 : char* buffer = const_cast<char*>(tmpdir_string.c_str());
375 :
376 0 : return mkstemp(buffer);
377 : }
378 :
379 0 : bool CreateTemporaryFileName(FilePath* path) {
380 0 : FilePath directory;
381 0 : if (!GetTempDir(&directory))
382 0 : return false;
383 0 : int fd = CreateAndOpenFdForTemporaryFile(directory, path);
384 0 : if (fd < 0)
385 0 : return false;
386 0 : close(fd);
387 0 : return true;
388 : }
389 :
390 0 : FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
391 0 : FilePath directory;
392 0 : if (!GetShmemTempDir(&directory))
393 0 : return false;
394 :
395 0 : return CreateAndOpenTemporaryFileInDir(directory, path);
396 : }
397 :
398 0 : FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
399 0 : int fd = CreateAndOpenFdForTemporaryFile(dir, path);
400 0 : if (fd < 0)
401 0 : return NULL;
402 :
403 0 : return fdopen(fd, "a+");
404 : }
405 :
406 0 : bool CreateTemporaryFileNameInDir(const std::wstring& dir,
407 : std::wstring* temp_file) {
408 : // Not implemented yet.
409 0 : NOTREACHED();
410 0 : return false;
411 : }
412 :
413 0 : bool CreateNewTempDirectory(const FilePath::StringType& prefix,
414 : FilePath* new_temp_path) {
415 0 : FilePath tmpdir;
416 0 : if (!GetTempDir(&tmpdir))
417 0 : return false;
418 0 : tmpdir = tmpdir.Append(kTempFileName);
419 0 : std::string tmpdir_string = tmpdir.value();
420 : // this should be OK since mkdtemp just replaces characters in place
421 0 : char* buffer = const_cast<char*>(tmpdir_string.c_str());
422 : #ifdef ANDROID
423 : char* dtemp = NULL;
424 : #else
425 0 : char* dtemp = mkdtemp(buffer);
426 : #endif
427 0 : if (!dtemp)
428 0 : return false;
429 0 : *new_temp_path = FilePath(dtemp);
430 0 : return true;
431 : }
432 :
433 0 : bool CreateDirectory(const FilePath& full_path) {
434 0 : std::vector<FilePath> subpaths;
435 :
436 : // Collect a list of all parent directories.
437 0 : FilePath last_path = full_path;
438 0 : subpaths.push_back(full_path);
439 0 : for (FilePath path = full_path.DirName();
440 0 : path.value() != last_path.value(); path = path.DirName()) {
441 0 : subpaths.push_back(path);
442 0 : last_path = path;
443 : }
444 :
445 : // Iterate through the parents and create the missing ones.
446 0 : for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
447 0 : i != subpaths.rend(); ++i) {
448 0 : if (!DirectoryExists(*i)) {
449 0 : if (mkdir(i->value().c_str(), 0777) != 0)
450 0 : return false;
451 : }
452 : }
453 0 : return true;
454 : }
455 :
456 0 : bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
457 : struct stat64 file_info;
458 0 : if (stat64(file_path.value().c_str(), &file_info) != 0)
459 0 : return false;
460 0 : results->is_directory = S_ISDIR(file_info.st_mode);
461 0 : results->size = file_info.st_size;
462 0 : return true;
463 : }
464 :
465 0 : FILE* OpenFile(const std::string& filename, const char* mode) {
466 0 : return OpenFile(FilePath(filename), mode);
467 : }
468 :
469 0 : FILE* OpenFile(const FilePath& filename, const char* mode) {
470 0 : return fopen(filename.value().c_str(), mode);
471 : }
472 :
473 0 : int ReadFile(const FilePath& filename, char* data, int size) {
474 0 : int fd = open(filename.value().c_str(), O_RDONLY);
475 0 : if (fd < 0)
476 0 : return -1;
477 :
478 0 : int ret_value = HANDLE_EINTR(read(fd, data, size));
479 0 : HANDLE_EINTR(close(fd));
480 0 : return ret_value;
481 : }
482 :
483 0 : int WriteFile(const FilePath& filename, const char* data, int size) {
484 0 : int fd = creat(filename.value().c_str(), 0666);
485 0 : if (fd < 0)
486 0 : return -1;
487 :
488 : // Allow for partial writes
489 0 : ssize_t bytes_written_total = 0;
490 0 : do {
491 : ssize_t bytes_written_partial =
492 0 : HANDLE_EINTR(write(fd, data + bytes_written_total,
493 : size - bytes_written_total));
494 0 : if (bytes_written_partial < 0) {
495 0 : HANDLE_EINTR(close(fd));
496 0 : return -1;
497 : }
498 0 : bytes_written_total += bytes_written_partial;
499 : } while (bytes_written_total < size);
500 :
501 0 : HANDLE_EINTR(close(fd));
502 0 : return bytes_written_total;
503 : }
504 :
505 : // Gets the current working directory for the process.
506 0 : bool GetCurrentDirectory(FilePath* dir) {
507 0 : char system_buffer[PATH_MAX] = "";
508 0 : if (!getcwd(system_buffer, sizeof(system_buffer))) {
509 0 : NOTREACHED();
510 0 : return false;
511 : }
512 0 : *dir = FilePath(system_buffer);
513 0 : return true;
514 : }
515 :
516 : // Sets the current working directory for the process.
517 0 : bool SetCurrentDirectory(const FilePath& path) {
518 0 : int ret = chdir(path.value().c_str());
519 0 : return !ret;
520 : }
521 :
522 : ///////////////////////////////////////////////
523 : // FileEnumerator
524 :
525 0 : FileEnumerator::FileEnumerator(const FilePath& root_path,
526 : bool recursive,
527 : FileEnumerator::FILE_TYPE file_type)
528 : : recursive_(recursive),
529 : file_type_(file_type),
530 : is_in_find_op_(false),
531 0 : fts_(NULL) {
532 0 : pending_paths_.push(root_path);
533 0 : }
534 :
535 0 : FileEnumerator::FileEnumerator(const FilePath& root_path,
536 : bool recursive,
537 : FileEnumerator::FILE_TYPE file_type,
538 : const FilePath::StringType& pattern)
539 : : recursive_(recursive),
540 : file_type_(file_type),
541 0 : pattern_(root_path.value()),
542 : is_in_find_op_(false),
543 0 : fts_(NULL) {
544 : // The Windows version of this code only matches against items in the top-most
545 : // directory, and we're comparing fnmatch against full paths, so this is the
546 : // easiest way to get the right pattern.
547 0 : pattern_ = pattern_.Append(pattern);
548 0 : pending_paths_.push(root_path);
549 0 : }
550 :
551 0 : FileEnumerator::~FileEnumerator() {
552 : #ifndef ANDROID
553 0 : if (fts_)
554 0 : fts_close(fts_);
555 : #endif
556 0 : }
557 :
558 0 : void FileEnumerator::GetFindInfo(FindInfo* info) {
559 0 : DCHECK(info);
560 :
561 0 : if (!is_in_find_op_)
562 0 : return;
563 :
564 : #ifndef ANDROID
565 0 : memcpy(&(info->stat), fts_ent_->fts_statp, sizeof(info->stat));
566 0 : info->filename.assign(fts_ent_->fts_name);
567 : #endif
568 : }
569 :
570 : // As it stands, this method calls itself recursively when the next item of
571 : // the fts enumeration doesn't match (type, pattern, etc.). In the case of
572 : // large directories with many files this can be quite deep.
573 : // TODO(erikkay) - get rid of this recursive pattern
574 0 : FilePath FileEnumerator::Next() {
575 : #ifdef ANDROID
576 : return FilePath();
577 : #else
578 0 : if (!is_in_find_op_) {
579 0 : if (pending_paths_.empty())
580 0 : return FilePath();
581 :
582 : // The last find FindFirstFile operation is done, prepare a new one.
583 0 : root_path_ = pending_paths_.top();
584 0 : root_path_ = root_path_.StripTrailingSeparators();
585 0 : pending_paths_.pop();
586 :
587 : // Start a new find operation.
588 0 : int ftsflags = FTS_LOGICAL;
589 : char top_dir[PATH_MAX];
590 0 : base::strlcpy(top_dir, root_path_.value().c_str(), arraysize(top_dir));
591 0 : char* dir_list[2] = { top_dir, NULL };
592 0 : fts_ = fts_open(dir_list, ftsflags, NULL);
593 0 : if (!fts_)
594 0 : return Next();
595 0 : is_in_find_op_ = true;
596 : }
597 :
598 0 : fts_ent_ = fts_read(fts_);
599 0 : if (fts_ent_ == NULL) {
600 0 : fts_close(fts_);
601 0 : fts_ = NULL;
602 0 : is_in_find_op_ = false;
603 0 : return Next();
604 : }
605 :
606 : // Level 0 is the top, which is always skipped.
607 0 : if (fts_ent_->fts_level == 0)
608 0 : return Next();
609 :
610 : // Patterns are only matched on the items in the top-most directory.
611 : // (see Windows implementation)
612 0 : if (fts_ent_->fts_level == 1 && pattern_.value().length() > 0) {
613 0 : if (fnmatch(pattern_.value().c_str(), fts_ent_->fts_path, 0) != 0) {
614 0 : if (fts_ent_->fts_info == FTS_D)
615 0 : fts_set(fts_, fts_ent_, FTS_SKIP);
616 0 : return Next();
617 : }
618 : }
619 :
620 0 : FilePath cur_file(fts_ent_->fts_path);
621 0 : if (fts_ent_->fts_info == FTS_D) {
622 : // If not recursive, then prune children.
623 0 : if (!recursive_)
624 0 : fts_set(fts_, fts_ent_, FTS_SKIP);
625 0 : return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
626 0 : } else if (fts_ent_->fts_info == FTS_F) {
627 0 : return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
628 : }
629 : // TODO(erikkay) - verify that the other fts_info types aren't interesting
630 0 : return Next();
631 : #endif
632 : }
633 :
634 : ///////////////////////////////////////////////
635 : // MemoryMappedFile
636 :
637 0 : MemoryMappedFile::MemoryMappedFile()
638 : : file_(-1),
639 : data_(NULL),
640 0 : length_(0) {
641 0 : }
642 :
643 0 : bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
644 0 : file_ = open(file_name.value().c_str(), O_RDONLY);
645 0 : if (file_ == -1)
646 0 : return false;
647 :
648 : struct stat file_stat;
649 0 : if (fstat(file_, &file_stat) == -1)
650 0 : return false;
651 0 : length_ = file_stat.st_size;
652 :
653 : data_ = static_cast<uint8*>(
654 0 : mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
655 0 : if (data_ == MAP_FAILED)
656 0 : data_ = NULL;
657 0 : return data_ != NULL;
658 : }
659 :
660 0 : void MemoryMappedFile::CloseHandles() {
661 0 : if (data_ != NULL)
662 0 : munmap(data_, length_);
663 0 : if (file_ != -1)
664 0 : close(file_);
665 :
666 0 : data_ = NULL;
667 0 : length_ = 0;
668 0 : file_ = -1;
669 0 : }
670 :
671 : } // namespace file_util
|