1 // Copyright (c) 2011 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.
5 #include "base/file_util.h"
16 #include <sys/errno.h>
18 #include <sys/param.h>
21 #include <sys/types.h>
25 #if defined(OS_MACOSX)
26 #include <AvailabilityMacros.h>
27 #include "base/mac/foundation_util.h"
28 #elif !defined(OS_ANDROID)
34 #include "base/basictypes.h"
35 #include "base/eintr_wrapper.h"
36 #include "base/file_path.h"
37 #include "base/logging.h"
38 #include "base/memory/scoped_ptr.h"
39 #include "base/memory/singleton.h"
40 #include "base/string_util.h"
41 #include "base/stringprintf.h"
42 #include "base/sys_string_conversions.h"
43 #include "base/threading/thread_restrictions.h"
44 #include "base/time.h"
45 #include "base/utf_string_conversions.h"
47 #if defined(OS_ANDROID)
48 #include "base/os_compat_android.h"
55 // Helper for NormalizeFilePath(), defined below.
56 bool RealPath(const FilePath
& path
, FilePath
* real_path
) {
57 base::ThreadRestrictions::AssertIOAllowed(); // For realpath().
58 FilePath::CharType buf
[PATH_MAX
];
59 if (!realpath(path
.value().c_str(), buf
))
62 *real_path
= FilePath(buf
);
68 #if defined(OS_OPENBSD) || defined(OS_FREEBSD) || \
69 (defined(OS_MACOSX) && \
70 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
71 typedef struct stat stat_wrapper_t
;
72 static int CallStat(const char *path
, stat_wrapper_t
*sb
) {
73 base::ThreadRestrictions::AssertIOAllowed();
74 return stat(path
, sb
);
77 typedef struct stat64 stat_wrapper_t
;
78 static int CallStat(const char *path
, stat_wrapper_t
*sb
) {
79 base::ThreadRestrictions::AssertIOAllowed();
80 return stat64(path
, sb
);
84 static std::string
TempFileName() {
85 #if defined(OS_MACOSX)
86 return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID());
89 #if defined(GOOGLE_CHROME_BUILD)
90 return std::string(".com.google.Chrome.XXXXXX");
92 return std::string(".org.chromium.Chromium.XXXXXX");
96 bool AbsolutePath(FilePath
* path
) {
97 base::ThreadRestrictions::AssertIOAllowed(); // For realpath().
98 char full_path
[PATH_MAX
];
99 if (realpath(path
->value().c_str(), full_path
) == NULL
)
101 *path
= FilePath(full_path
);
105 int CountFilesCreatedAfter(const FilePath
& path
,
106 const base::Time
& comparison_time
) {
107 base::ThreadRestrictions::AssertIOAllowed();
110 DIR* dir
= opendir(path
.value().c_str());
112 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
113 !defined(OS_OPENBSD) && !defined(OS_SOLARIS) && !defined(OS_ANDROID)
114 #error Port warning: depending on the definition of struct dirent, \
115 additional space for pathname may be needed
117 struct dirent ent_buf
;
119 while (readdir_r(dir
, &ent_buf
, &ent
) == 0 && ent
) {
120 if ((strcmp(ent
->d_name
, ".") == 0) ||
121 (strcmp(ent
->d_name
, "..") == 0))
125 int test
= CallStat(path
.Append(ent
->d_name
).value().c_str(), &st
);
127 PLOG(ERROR
) << "stat64 failed";
130 // Here, we use Time::TimeT(), which discards microseconds. This
131 // means that files which are newer than |comparison_time| may
132 // be considered older. If we don't discard microseconds, it
133 // introduces another issue. Suppose the following case:
135 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
136 // 2. Create a file and the current time is 10.3 (secs).
138 // As POSIX doesn't have microsecond precision for |st_ctime|,
139 // the creation time of the file created in the step 2 is 10 and
140 // the file is considered older than |comparison_time|. After
141 // all, we may have to accept either of the two issues: 1. files
142 // which are older than |comparison_time| are considered newer
143 // (current implementation) 2. files newer than
144 // |comparison_time| are considered older.
145 if (static_cast<time_t>(st
.st_ctime
) >= comparison_time
.ToTimeT())
153 // TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
154 // which works both with and without the recursive flag. I'm not sure we need
155 // that functionality. If not, remove from file_util_win.cc, otherwise add it
157 bool Delete(const FilePath
& path
, bool recursive
) {
158 base::ThreadRestrictions::AssertIOAllowed();
159 const char* path_str
= path
.value().c_str();
160 stat_wrapper_t file_info
;
161 int test
= CallStat(path_str
, &file_info
);
163 // The Windows version defines this condition as success.
164 bool ret
= (errno
== ENOENT
|| errno
== ENOTDIR
);
167 if (!S_ISDIR(file_info
.st_mode
))
168 return (unlink(path_str
) == 0);
170 return (rmdir(path_str
) == 0);
173 std::stack
<std::string
> directories
;
174 directories
.push(path
.value());
175 FileEnumerator
traversal(path
, true, static_cast<FileEnumerator::FileType
>(
176 FileEnumerator::FILES
| FileEnumerator::DIRECTORIES
|
177 FileEnumerator::SHOW_SYM_LINKS
));
178 for (FilePath current
= traversal
.Next(); success
&& !current
.empty();
179 current
= traversal
.Next()) {
180 FileEnumerator::FindInfo info
;
181 traversal
.GetFindInfo(&info
);
183 if (S_ISDIR(info
.stat
.st_mode
))
184 directories
.push(current
.value());
186 success
= (unlink(current
.value().c_str()) == 0);
189 while (success
&& !directories
.empty()) {
190 FilePath dir
= FilePath(directories
.top());
192 success
= (rmdir(dir
.value().c_str()) == 0);
197 bool Move(const FilePath
& from_path
, const FilePath
& to_path
) {
198 base::ThreadRestrictions::AssertIOAllowed();
199 // Windows compatibility: if to_path exists, from_path and to_path
200 // must be the same type, either both files, or both directories.
201 stat_wrapper_t to_file_info
;
202 if (CallStat(to_path
.value().c_str(), &to_file_info
) == 0) {
203 stat_wrapper_t from_file_info
;
204 if (CallStat(from_path
.value().c_str(), &from_file_info
) == 0) {
205 if (S_ISDIR(to_file_info
.st_mode
) != S_ISDIR(from_file_info
.st_mode
))
212 if (rename(from_path
.value().c_str(), to_path
.value().c_str()) == 0)
215 if (!CopyDirectory(from_path
, to_path
, true))
218 Delete(from_path
, true);
222 bool ReplaceFile(const FilePath
& from_path
, const FilePath
& to_path
) {
223 base::ThreadRestrictions::AssertIOAllowed();
224 return (rename(from_path
.value().c_str(), to_path
.value().c_str()) == 0);
227 bool CopyDirectory(const FilePath
& from_path
,
228 const FilePath
& to_path
,
230 base::ThreadRestrictions::AssertIOAllowed();
231 // Some old callers of CopyDirectory want it to support wildcards.
232 // After some discussion, we decided to fix those callers.
233 // Break loudly here if anyone tries to do this.
234 // TODO(evanm): remove this once we're sure it's ok.
235 DCHECK(to_path
.value().find('*') == std::string::npos
);
236 DCHECK(from_path
.value().find('*') == std::string::npos
);
238 char top_dir
[PATH_MAX
];
239 if (base::strlcpy(top_dir
, from_path
.value().c_str(),
240 arraysize(top_dir
)) >= arraysize(top_dir
)) {
244 // This function does not properly handle destinations within the source
245 FilePath real_to_path
= to_path
;
246 if (PathExists(real_to_path
)) {
247 if (!AbsolutePath(&real_to_path
))
250 real_to_path
= real_to_path
.DirName();
251 if (!AbsolutePath(&real_to_path
))
254 FilePath real_from_path
= from_path
;
255 if (!AbsolutePath(&real_from_path
))
257 if (real_to_path
.value().size() >= real_from_path
.value().size() &&
258 real_to_path
.value().compare(0, real_from_path
.value().size(),
259 real_from_path
.value()) == 0)
263 FileEnumerator::FileType traverse_type
=
264 static_cast<FileEnumerator::FileType
>(FileEnumerator::FILES
|
265 FileEnumerator::SHOW_SYM_LINKS
);
267 traverse_type
= static_cast<FileEnumerator::FileType
>(
268 traverse_type
| FileEnumerator::DIRECTORIES
);
269 FileEnumerator
traversal(from_path
, recursive
, traverse_type
);
271 // We have to mimic windows behavior here. |to_path| may not exist yet,
272 // start the loop with |to_path|.
273 FileEnumerator::FindInfo info
;
274 FilePath current
= from_path
;
275 if (stat(from_path
.value().c_str(), &info
.stat
) < 0) {
276 LOG(ERROR
) << "CopyDirectory() couldn't stat source directory: " <<
277 from_path
.value() << " errno = " << errno
;
280 struct stat to_path_stat
;
281 FilePath from_path_base
= from_path
;
282 if (recursive
&& stat(to_path
.value().c_str(), &to_path_stat
) == 0 &&
283 S_ISDIR(to_path_stat
.st_mode
)) {
284 // If the destination already exists and is a directory, then the
285 // top level of source needs to be copied.
286 from_path_base
= from_path
.DirName();
289 // The Windows version of this function assumes that non-recursive calls
290 // will always have a directory for from_path.
291 DCHECK(recursive
|| S_ISDIR(info
.stat
.st_mode
));
293 while (success
&& !current
.empty()) {
294 // current is the source path, including from_path, so paste
295 // the suffix after from_path onto to_path to create the target_path.
296 std::string
suffix(¤t
.value().c_str()[from_path_base
.value().size()]);
297 // Strip the leading '/' (if any).
298 if (!suffix
.empty()) {
299 DCHECK_EQ('/', suffix
[0]);
302 const FilePath target_path
= to_path
.Append(suffix
);
304 if (S_ISDIR(info
.stat
.st_mode
)) {
305 if (mkdir(target_path
.value().c_str(), info
.stat
.st_mode
& 01777) != 0 &&
307 LOG(ERROR
) << "CopyDirectory() couldn't create directory: " <<
308 target_path
.value() << " errno = " << errno
;
311 } else if (S_ISREG(info
.stat
.st_mode
)) {
312 if (!CopyFile(current
, target_path
)) {
313 LOG(ERROR
) << "CopyDirectory() couldn't create file: " <<
318 LOG(WARNING
) << "CopyDirectory() skipping non-regular file: " <<
322 current
= traversal
.Next();
323 traversal
.GetFindInfo(&info
);
329 bool PathExists(const FilePath
& path
) {
330 base::ThreadRestrictions::AssertIOAllowed();
331 return access(path
.value().c_str(), F_OK
) == 0;
334 bool PathIsWritable(const FilePath
& path
) {
335 base::ThreadRestrictions::AssertIOAllowed();
336 return access(path
.value().c_str(), W_OK
) == 0;
339 bool DirectoryExists(const FilePath
& path
) {
340 base::ThreadRestrictions::AssertIOAllowed();
341 stat_wrapper_t file_info
;
342 if (CallStat(path
.value().c_str(), &file_info
) == 0)
343 return S_ISDIR(file_info
.st_mode
);
347 // TODO(erikkay): implement
349 bool GetFileCreationLocalTimeFromHandle(int fd
,
350 LPSYSTEMTIME creation_time
) {
354 FILETIME utc_filetime
;
355 if (!GetFileTime(file_handle
, &utc_filetime
, NULL
, NULL
))
358 FILETIME local_filetime
;
359 if (!FileTimeToLocalFileTime(&utc_filetime
, &local_filetime
))
362 return !!FileTimeToSystemTime(&local_filetime
, creation_time
);
365 bool GetFileCreationLocalTime(const std::string
& filename
,
366 LPSYSTEMTIME creation_time
) {
367 ScopedHandle
file_handle(
368 CreateFile(filename
.c_str(), GENERIC_READ
,
369 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
370 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
));
371 return GetFileCreationLocalTimeFromHandle(file_handle
.Get(), creation_time
);
375 bool ReadFromFD(int fd
, char* buffer
, size_t bytes
) {
376 size_t total_read
= 0;
377 while (total_read
< bytes
) {
379 HANDLE_EINTR(read(fd
, buffer
+ total_read
, bytes
- total_read
));
382 total_read
+= bytes_read
;
384 return total_read
== bytes
;
387 bool CreateSymbolicLink(const FilePath
& target_path
,
388 const FilePath
& symlink_path
) {
389 DCHECK(!symlink_path
.empty());
390 DCHECK(!target_path
.empty());
391 return ::symlink(target_path
.value().c_str(),
392 symlink_path
.value().c_str()) != -1;
395 bool ReadSymbolicLink(const FilePath
& symlink_path
,
396 FilePath
* target_path
) {
397 DCHECK(!symlink_path
.empty());
400 ssize_t count
= ::readlink(symlink_path
.value().c_str(), buf
, arraysize(buf
));
403 target_path
->clear();
407 *target_path
= FilePath(FilePath::StringType(buf
, count
));
411 // Creates and opens a temporary file in |directory|, returning the
412 // file descriptor. |path| is set to the temporary file path.
413 // This function does NOT unlink() the file.
414 int CreateAndOpenFdForTemporaryFile(FilePath directory
, FilePath
* path
) {
415 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp().
416 *path
= directory
.Append(TempFileName());
417 const std::string
& tmpdir_string
= path
->value();
418 // this should be OK since mkstemp just replaces characters in place
419 char* buffer
= const_cast<char*>(tmpdir_string
.c_str());
421 return HANDLE_EINTR(mkstemp(buffer
));
424 bool CreateTemporaryFile(FilePath
* path
) {
425 base::ThreadRestrictions::AssertIOAllowed(); // For call to close().
427 if (!GetTempDir(&directory
))
429 int fd
= CreateAndOpenFdForTemporaryFile(directory
, path
);
432 ignore_result(HANDLE_EINTR(close(fd
)));
436 FILE* CreateAndOpenTemporaryShmemFile(FilePath
* path
) {
438 if (!GetShmemTempDir(&directory
))
441 return CreateAndOpenTemporaryFileInDir(directory
, path
);
444 FILE* CreateAndOpenTemporaryFileInDir(const FilePath
& dir
, FilePath
* path
) {
445 int fd
= CreateAndOpenFdForTemporaryFile(dir
, path
);
449 FILE* file
= fdopen(fd
, "a+");
451 ignore_result(HANDLE_EINTR(close(fd
)));
455 bool CreateTemporaryFileInDir(const FilePath
& dir
, FilePath
* temp_file
) {
456 base::ThreadRestrictions::AssertIOAllowed(); // For call to close().
457 int fd
= CreateAndOpenFdForTemporaryFile(dir
, temp_file
);
458 return ((fd
>= 0) && !HANDLE_EINTR(close(fd
)));
461 static bool CreateTemporaryDirInDirImpl(const FilePath
& base_dir
,
462 const FilePath::StringType
& name_tmpl
,
464 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdtemp().
465 CHECK(name_tmpl
.find("XXXXXX") != FilePath::StringType::npos
)
466 << "Directory name template must contain \"XXXXXX\".";
468 FilePath sub_dir
= base_dir
.Append(name_tmpl
);
469 std::string sub_dir_string
= sub_dir
.value();
471 // this should be OK since mkdtemp just replaces characters in place
472 char* buffer
= const_cast<char*>(sub_dir_string
.c_str());
473 char* dtemp
= mkdtemp(buffer
);
475 DPLOG(ERROR
) << "mkdtemp";
478 *new_dir
= FilePath(dtemp
);
482 bool CreateTemporaryDirInDir(const FilePath
& base_dir
,
483 const FilePath::StringType
& prefix
,
485 FilePath::StringType mkdtemp_template
= prefix
;
486 mkdtemp_template
.append(FILE_PATH_LITERAL("XXXXXX"));
487 return CreateTemporaryDirInDirImpl(base_dir
, mkdtemp_template
, new_dir
);
490 bool CreateNewTempDirectory(const FilePath::StringType
& prefix
,
491 FilePath
* new_temp_path
) {
493 if (!GetTempDir(&tmpdir
))
496 return CreateTemporaryDirInDirImpl(tmpdir
, TempFileName(), new_temp_path
);
499 bool CreateDirectory(const FilePath
& full_path
) {
500 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdir().
501 std::vector
<FilePath
> subpaths
;
503 // Collect a list of all parent directories.
504 FilePath last_path
= full_path
;
505 subpaths
.push_back(full_path
);
506 for (FilePath path
= full_path
.DirName();
507 path
.value() != last_path
.value(); path
= path
.DirName()) {
508 subpaths
.push_back(path
);
512 // Iterate through the parents and create the missing ones.
513 for (std::vector
<FilePath
>::reverse_iterator i
= subpaths
.rbegin();
514 i
!= subpaths
.rend(); ++i
) {
515 if (DirectoryExists(*i
))
517 if (mkdir(i
->value().c_str(), 0700) == 0)
519 // Mkdir failed, but it might have failed with EEXIST, or some other error
520 // due to the the directory appearing out of thin air. This can occur if
521 // two processes are trying to create the same file system tree at the same
522 // time. Check to see if it exists and make sure it is a directory.
523 if (!DirectoryExists(*i
))
529 // TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks
530 // correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948
531 bool IsLink(const FilePath
& file_path
) {
533 // If we can't lstat the file, it's safe to assume that the file won't at
534 // least be a 'followable' link.
535 if (lstat(file_path
.value().c_str(), &st
) != 0)
538 if (S_ISLNK(st
.st_mode
))
544 bool GetFileInfo(const FilePath
& file_path
, base::PlatformFileInfo
* results
) {
545 stat_wrapper_t file_info
;
546 if (CallStat(file_path
.value().c_str(), &file_info
) != 0)
548 results
->is_directory
= S_ISDIR(file_info
.st_mode
);
549 results
->size
= file_info
.st_size
;
550 results
->last_modified
= base::Time::FromTimeT(file_info
.st_mtime
);
551 results
->last_accessed
= base::Time::FromTimeT(file_info
.st_atime
);
552 results
->creation_time
= base::Time::FromTimeT(file_info
.st_ctime
);
556 bool GetInode(const FilePath
& path
, ino_t
* inode
) {
557 base::ThreadRestrictions::AssertIOAllowed(); // For call to stat().
559 int result
= stat(path
.value().c_str(), &buffer
);
563 *inode
= buffer
.st_ino
;
567 FILE* OpenFile(const std::string
& filename
, const char* mode
) {
568 return OpenFile(FilePath(filename
), mode
);
571 FILE* OpenFile(const FilePath
& filename
, const char* mode
) {
572 base::ThreadRestrictions::AssertIOAllowed();
575 result
= fopen(filename
.value().c_str(), mode
);
576 } while (!result
&& errno
== EINTR
);
580 int ReadFile(const FilePath
& filename
, char* data
, int size
) {
581 base::ThreadRestrictions::AssertIOAllowed();
582 int fd
= HANDLE_EINTR(open(filename
.value().c_str(), O_RDONLY
));
586 ssize_t bytes_read
= HANDLE_EINTR(read(fd
, data
, size
));
587 if (int ret
= HANDLE_EINTR(close(fd
)) < 0)
592 int WriteFile(const FilePath
& filename
, const char* data
, int size
) {
593 base::ThreadRestrictions::AssertIOAllowed();
594 int fd
= HANDLE_EINTR(creat(filename
.value().c_str(), 0666));
598 int bytes_written
= WriteFileDescriptor(fd
, data
, size
);
599 if (int ret
= HANDLE_EINTR(close(fd
)) < 0)
601 return bytes_written
;
604 int WriteFileDescriptor(const int fd
, const char* data
, int size
) {
605 // Allow for partial writes.
606 ssize_t bytes_written_total
= 0;
607 for (ssize_t bytes_written_partial
= 0; bytes_written_total
< size
;
608 bytes_written_total
+= bytes_written_partial
) {
609 bytes_written_partial
=
610 HANDLE_EINTR(write(fd
, data
+ bytes_written_total
,
611 size
- bytes_written_total
));
612 if (bytes_written_partial
< 0)
616 return bytes_written_total
;
619 // Gets the current working directory for the process.
620 bool GetCurrentDirectory(FilePath
* dir
) {
621 // getcwd can return ENOENT, which implies it checks against the disk.
622 base::ThreadRestrictions::AssertIOAllowed();
624 char system_buffer
[PATH_MAX
] = "";
625 if (!getcwd(system_buffer
, sizeof(system_buffer
))) {
629 *dir
= FilePath(system_buffer
);
633 // Sets the current working directory for the process.
634 bool SetCurrentDirectory(const FilePath
& path
) {
635 base::ThreadRestrictions::AssertIOAllowed();
636 int ret
= chdir(path
.value().c_str());
640 ///////////////////////////////////////////////
643 FileEnumerator::FileEnumerator(const FilePath
& root_path
,
646 : current_directory_entry_(0),
647 root_path_(root_path
),
648 recursive_(recursive
),
649 file_type_(file_type
) {
650 // INCLUDE_DOT_DOT must not be specified if recursive.
651 DCHECK(!(recursive
&& (INCLUDE_DOT_DOT
& file_type_
)));
652 pending_paths_
.push(root_path
);
655 FileEnumerator::FileEnumerator(const FilePath
& root_path
,
658 const FilePath::StringType
& pattern
)
659 : current_directory_entry_(0),
660 root_path_(root_path
),
661 recursive_(recursive
),
662 file_type_(file_type
),
663 pattern_(root_path
.Append(pattern
).value()) {
664 // INCLUDE_DOT_DOT must not be specified if recursive.
665 DCHECK(!(recursive
&& (INCLUDE_DOT_DOT
& file_type_
)));
666 // The Windows version of this code appends the pattern to the root_path,
667 // potentially only matching against items in the top-most directory.
670 pattern_
= FilePath::StringType();
671 pending_paths_
.push(root_path
);
674 FileEnumerator::~FileEnumerator() {
677 FilePath
FileEnumerator::Next() {
678 ++current_directory_entry_
;
680 // While we've exhausted the entries in the current directory, do the next
681 while (current_directory_entry_
>= directory_entries_
.size()) {
682 if (pending_paths_
.empty())
685 root_path_
= pending_paths_
.top();
686 root_path_
= root_path_
.StripTrailingSeparators();
687 pending_paths_
.pop();
689 std::vector
<DirectoryEntryInfo
> entries
;
690 if (!ReadDirectory(&entries
, root_path_
, file_type_
& SHOW_SYM_LINKS
))
693 directory_entries_
.clear();
694 current_directory_entry_
= 0;
695 for (std::vector
<DirectoryEntryInfo
>::const_iterator
696 i
= entries
.begin(); i
!= entries
.end(); ++i
) {
697 FilePath full_path
= root_path_
.Append(i
->filename
);
698 if (ShouldSkip(full_path
))
701 if (pattern_
.size() &&
702 fnmatch(pattern_
.c_str(), full_path
.value().c_str(), FNM_NOESCAPE
))
705 if (recursive_
&& S_ISDIR(i
->stat
.st_mode
))
706 pending_paths_
.push(full_path
);
708 if ((S_ISDIR(i
->stat
.st_mode
) && (file_type_
& DIRECTORIES
)) ||
709 (!S_ISDIR(i
->stat
.st_mode
) && (file_type_
& FILES
)))
710 directory_entries_
.push_back(*i
);
714 return root_path_
.Append(directory_entries_
[current_directory_entry_
718 void FileEnumerator::GetFindInfo(FindInfo
* info
) {
721 if (current_directory_entry_
>= directory_entries_
.size())
724 DirectoryEntryInfo
* cur_entry
= &directory_entries_
[current_directory_entry_
];
725 memcpy(&(info
->stat
), &(cur_entry
->stat
), sizeof(info
->stat
));
726 info
->filename
.assign(cur_entry
->filename
.value());
729 bool FileEnumerator::IsDirectory(const FindInfo
& info
) {
730 return S_ISDIR(info
.stat
.st_mode
);
734 FilePath
FileEnumerator::GetFilename(const FindInfo
& find_info
) {
735 return FilePath(find_info
.filename
);
739 int64
FileEnumerator::GetFilesize(const FindInfo
& find_info
) {
740 return find_info
.stat
.st_size
;
744 base::Time
FileEnumerator::GetLastModifiedTime(const FindInfo
& find_info
) {
745 return base::Time::FromTimeT(find_info
.stat
.st_mtime
);
748 bool FileEnumerator::ReadDirectory(std::vector
<DirectoryEntryInfo
>* entries
,
749 const FilePath
& source
, bool show_links
) {
750 base::ThreadRestrictions::AssertIOAllowed();
751 DIR* dir
= opendir(source
.value().c_str());
755 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
756 !defined(OS_OPENBSD) && !defined(OS_SOLARIS) && !defined(OS_ANDROID)
757 #error Port warning: depending on the definition of struct dirent, \
758 additional space for pathname may be needed
761 struct dirent dent_buf
;
763 while (readdir_r(dir
, &dent_buf
, &dent
) == 0 && dent
) {
764 DirectoryEntryInfo info
;
765 info
.filename
= FilePath(dent
->d_name
);
767 FilePath full_name
= source
.Append(dent
->d_name
);
770 ret
= lstat(full_name
.value().c_str(), &info
.stat
);
772 ret
= stat(full_name
.value().c_str(), &info
.stat
);
774 // Print the stat() error message unless it was ENOENT and we're
775 // following symlinks.
776 if (!(errno
== ENOENT
&& !show_links
)) {
777 PLOG(ERROR
) << "Couldn't stat "
778 << source
.Append(dent
->d_name
).value();
780 memset(&info
.stat
, 0, sizeof(info
.stat
));
782 entries
->push_back(info
);
789 ///////////////////////////////////////////////
792 MemoryMappedFile::MemoryMappedFile()
793 : file_(base::kInvalidPlatformFileValue
),
798 bool MemoryMappedFile::MapFileToMemoryInternal() {
799 base::ThreadRestrictions::AssertIOAllowed();
801 struct stat file_stat
;
802 if (fstat(file_
, &file_stat
) == base::kInvalidPlatformFileValue
) {
803 LOG(ERROR
) << "Couldn't fstat " << file_
<< ", errno " << errno
;
806 length_
= file_stat
.st_size
;
808 data_
= static_cast<uint8
*>(
809 mmap(NULL
, length_
, PROT_READ
, MAP_SHARED
, file_
, 0));
810 if (data_
== MAP_FAILED
)
811 LOG(ERROR
) << "Couldn't mmap " << file_
<< ", errno " << errno
;
813 return data_
!= MAP_FAILED
;
816 void MemoryMappedFile::CloseHandles() {
817 base::ThreadRestrictions::AssertIOAllowed();
820 munmap(data_
, length_
);
821 if (file_
!= base::kInvalidPlatformFileValue
)
822 ignore_result(HANDLE_EINTR(close(file_
)));
826 file_
= base::kInvalidPlatformFileValue
;
829 bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo
& find_info
,
830 const base::Time
& cutoff_time
) {
831 return static_cast<time_t>(find_info
.stat
.st_mtime
) >= cutoff_time
.ToTimeT();
834 bool NormalizeFilePath(const FilePath
& path
, FilePath
* normalized_path
) {
835 FilePath real_path_result
;
836 if (!RealPath(path
, &real_path_result
))
839 // To be consistant with windows, fail if |real_path_result| is a
841 stat_wrapper_t file_info
;
842 if (CallStat(real_path_result
.value().c_str(), &file_info
) != 0 ||
843 S_ISDIR(file_info
.st_mode
))
846 *normalized_path
= real_path_result
;
850 #if !defined(OS_MACOSX)
851 bool GetTempDir(FilePath
* path
) {
852 const char* tmp
= getenv("TMPDIR");
854 *path
= FilePath(tmp
);
856 #if defined(OS_ANDROID)
857 *path
= FilePath("/data/local/tmp");
859 *path
= FilePath("/tmp");
864 #if !defined(OS_ANDROID)
865 bool GetShmemTempDir(FilePath
* path
) {
866 *path
= FilePath("/dev/shm");
871 FilePath
GetHomeDir() {
872 const char* home_dir
= getenv("HOME");
873 if (home_dir
&& home_dir
[0])
874 return FilePath(home_dir
);
876 #if defined(OS_ANDROID)
877 LOG(WARNING
) << "OS_ANDROID: Home directory lookup not yet implemented.";
879 // g_get_home_dir calls getpwent, which can fall through to LDAP calls.
880 base::ThreadRestrictions::AssertIOAllowed();
882 home_dir
= g_get_home_dir();
883 if (home_dir
&& home_dir
[0])
884 return FilePath(home_dir
);
888 if (file_util::GetTempDir(&rv
))
892 return FilePath("/tmp");
895 bool CopyFile(const FilePath
& from_path
, const FilePath
& to_path
) {
896 base::ThreadRestrictions::AssertIOAllowed();
897 int infile
= HANDLE_EINTR(open(from_path
.value().c_str(), O_RDONLY
));
901 int outfile
= HANDLE_EINTR(creat(to_path
.value().c_str(), 0666));
903 ignore_result(HANDLE_EINTR(close(infile
)));
907 const size_t kBufferSize
= 32768;
908 std::vector
<char> buffer(kBufferSize
);
912 ssize_t bytes_read
= HANDLE_EINTR(read(infile
, &buffer
[0], buffer
.size()));
913 if (bytes_read
< 0) {
919 // Allow for partial writes
920 ssize_t bytes_written_per_read
= 0;
922 ssize_t bytes_written_partial
= HANDLE_EINTR(write(
924 &buffer
[bytes_written_per_read
],
925 bytes_read
- bytes_written_per_read
));
926 if (bytes_written_partial
< 0) {
930 bytes_written_per_read
+= bytes_written_partial
;
931 } while (bytes_written_per_read
< bytes_read
);
934 if (HANDLE_EINTR(close(infile
)) < 0)
936 if (HANDLE_EINTR(close(outfile
)) < 0)
941 #endif // defined(OS_MACOSX)
943 } // namespace file_util