1 // Copyright (c) 2010 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"
14 #include <sys/errno.h>
18 #include <sys/types.h>
22 #if defined(OS_MACOSX)
23 #include <AvailabilityMacros.h>
28 #include "base/basictypes.h"
29 #include "base/eintr_wrapper.h"
30 #include "base/file_path.h"
31 #include "base/lock.h"
32 #include "base/logging.h"
33 #include "base/scoped_ptr.h"
34 #include "base/singleton.h"
35 #include "base/string_util.h"
36 #include "base/sys_string_conversions.h"
37 #include "base/time.h"
38 #include "base/utf_string_conversions.h"
42 #if defined(OS_OPENBSD) || defined(OS_FREEBSD) || \
43 (defined(OS_MACOSX) && \
44 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
45 typedef struct stat stat_wrapper_t
;
46 static int CallStat(const char *path
, stat_wrapper_t
*sb
) {
47 return stat(path
, sb
);
50 typedef struct stat64 stat_wrapper_t
;
51 static int CallStat(const char *path
, stat_wrapper_t
*sb
) {
52 return stat64(path
, sb
);
57 #if defined(GOOGLE_CHROME_BUILD)
58 static const char* kTempFileName
= "com.google.chrome.XXXXXX";
60 static const char* kTempFileName
= "org.chromium.XXXXXX";
63 bool AbsolutePath(FilePath
* path
) {
64 char full_path
[PATH_MAX
];
65 if (realpath(path
->value().c_str(), full_path
) == NULL
)
67 *path
= FilePath(full_path
);
71 int CountFilesCreatedAfter(const FilePath
& path
,
72 const base::Time
& comparison_time
) {
75 DIR* dir
= opendir(path
.value().c_str());
77 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
78 !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
79 #error Port warning: depending on the definition of struct dirent, \
80 additional space for pathname may be needed
82 struct dirent ent_buf
;
84 while (readdir_r(dir
, &ent_buf
, &ent
) == 0 && ent
) {
85 if ((strcmp(ent
->d_name
, ".") == 0) ||
86 (strcmp(ent
->d_name
, "..") == 0))
90 int test
= CallStat(path
.Append(ent
->d_name
).value().c_str(), &st
);
92 PLOG(ERROR
) << "stat64 failed";
95 // Here, we use Time::TimeT(), which discards microseconds. This
96 // means that files which are newer than |comparison_time| may
97 // be considered older. If we don't discard microseconds, it
98 // introduces another issue. Suppose the following case:
100 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
101 // 2. Create a file and the current time is 10.3 (secs).
103 // As POSIX doesn't have microsecond precision for |st_ctime|,
104 // the creation time of the file created in the step 2 is 10 and
105 // the file is considered older than |comparison_time|. After
106 // all, we may have to accept either of the two issues: 1. files
107 // which are older than |comparison_time| are considered newer
108 // (current implementation) 2. files newer than
109 // |comparison_time| are considered older.
110 if (st
.st_ctime
>= comparison_time
.ToTimeT())
118 // TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
119 // which works both with and without the recursive flag. I'm not sure we need
120 // that functionality. If not, remove from file_util_win.cc, otherwise add it
122 bool Delete(const FilePath
& path
, bool recursive
) {
123 const char* path_str
= path
.value().c_str();
124 stat_wrapper_t file_info
;
125 int test
= CallStat(path_str
, &file_info
);
127 // The Windows version defines this condition as success.
128 bool ret
= (errno
== ENOENT
|| errno
== ENOTDIR
);
131 if (!S_ISDIR(file_info
.st_mode
))
132 return (unlink(path_str
) == 0);
134 return (rmdir(path_str
) == 0);
137 std::stack
<std::string
> directories
;
138 directories
.push(path
.value());
139 FileEnumerator
traversal(path
, true, static_cast<FileEnumerator::FILE_TYPE
>(
140 FileEnumerator::FILES
| FileEnumerator::DIRECTORIES
|
141 FileEnumerator::SHOW_SYM_LINKS
));
142 for (FilePath current
= traversal
.Next(); success
&& !current
.empty();
143 current
= traversal
.Next()) {
144 FileEnumerator::FindInfo info
;
145 traversal
.GetFindInfo(&info
);
147 if (S_ISDIR(info
.stat
.st_mode
))
148 directories
.push(current
.value());
150 success
= (unlink(current
.value().c_str()) == 0);
153 while (success
&& !directories
.empty()) {
154 FilePath dir
= FilePath(directories
.top());
156 success
= (rmdir(dir
.value().c_str()) == 0);
162 bool Move(const FilePath
& from_path
, const FilePath
& to_path
) {
163 // Windows compatibility: if to_path exists, from_path and to_path
164 // must be the same type, either both files, or both directories.
165 stat_wrapper_t to_file_info
;
166 if (CallStat(to_path
.value().c_str(), &to_file_info
) == 0) {
167 stat_wrapper_t from_file_info
;
168 if (CallStat(from_path
.value().c_str(), &from_file_info
) == 0) {
169 if (S_ISDIR(to_file_info
.st_mode
) != S_ISDIR(from_file_info
.st_mode
))
176 if (rename(from_path
.value().c_str(), to_path
.value().c_str()) == 0)
179 if (!CopyDirectory(from_path
, to_path
, true))
182 Delete(from_path
, true);
186 bool ReplaceFile(const FilePath
& from_path
, const FilePath
& to_path
) {
187 return (rename(from_path
.value().c_str(), to_path
.value().c_str()) == 0);
190 bool CopyDirectory(const FilePath
& from_path
,
191 const FilePath
& to_path
,
193 // Some old callers of CopyDirectory want it to support wildcards.
194 // After some discussion, we decided to fix those callers.
195 // Break loudly here if anyone tries to do this.
196 // TODO(evanm): remove this once we're sure it's ok.
197 DCHECK(to_path
.value().find('*') == std::string::npos
);
198 DCHECK(from_path
.value().find('*') == std::string::npos
);
200 char top_dir
[PATH_MAX
];
201 if (base::strlcpy(top_dir
, from_path
.value().c_str(),
202 arraysize(top_dir
)) >= arraysize(top_dir
)) {
206 // This function does not properly handle destinations within the source
207 FilePath real_to_path
= to_path
;
208 if (PathExists(real_to_path
)) {
209 if (!AbsolutePath(&real_to_path
))
212 real_to_path
= real_to_path
.DirName();
213 if (!AbsolutePath(&real_to_path
))
216 FilePath real_from_path
= from_path
;
217 if (!AbsolutePath(&real_from_path
))
219 if (real_to_path
.value().size() >= real_from_path
.value().size() &&
220 real_to_path
.value().compare(0, real_from_path
.value().size(),
221 real_from_path
.value()) == 0)
225 FileEnumerator::FILE_TYPE traverse_type
=
226 static_cast<FileEnumerator::FILE_TYPE
>(FileEnumerator::FILES
|
227 FileEnumerator::SHOW_SYM_LINKS
);
229 traverse_type
= static_cast<FileEnumerator::FILE_TYPE
>(
230 traverse_type
| FileEnumerator::DIRECTORIES
);
231 FileEnumerator
traversal(from_path
, recursive
, traverse_type
);
233 // We have to mimic windows behavior here. |to_path| may not exist yet,
234 // start the loop with |to_path|.
235 FileEnumerator::FindInfo info
;
236 FilePath current
= from_path
;
237 if (stat(from_path
.value().c_str(), &info
.stat
) < 0) {
238 LOG(ERROR
) << "CopyDirectory() couldn't stat source directory: " <<
239 from_path
.value() << " errno = " << errno
;
242 struct stat to_path_stat
;
243 FilePath from_path_base
= from_path
;
244 if (recursive
&& stat(to_path
.value().c_str(), &to_path_stat
) == 0 &&
245 S_ISDIR(to_path_stat
.st_mode
)) {
246 // If the destination already exists and is a directory, then the
247 // top level of source needs to be copied.
248 from_path_base
= from_path
.DirName();
251 // The Windows version of this function assumes that non-recursive calls
252 // will always have a directory for from_path.
253 DCHECK(recursive
|| S_ISDIR(info
.stat
.st_mode
));
255 while (success
&& !current
.empty()) {
256 // current is the source path, including from_path, so paste
257 // the suffix after from_path onto to_path to create the target_path.
258 std::string
suffix(¤t
.value().c_str()[from_path_base
.value().size()]);
259 // Strip the leading '/' (if any).
260 if (!suffix
.empty()) {
261 DCHECK_EQ('/', suffix
[0]);
264 const FilePath target_path
= to_path
.Append(suffix
);
266 if (S_ISDIR(info
.stat
.st_mode
)) {
267 if (mkdir(target_path
.value().c_str(), info
.stat
.st_mode
& 01777) != 0 &&
269 LOG(ERROR
) << "CopyDirectory() couldn't create directory: " <<
270 target_path
.value() << " errno = " << errno
;
273 } else if (S_ISREG(info
.stat
.st_mode
)) {
274 if (!CopyFile(current
, target_path
)) {
275 LOG(ERROR
) << "CopyDirectory() couldn't create file: " <<
280 LOG(WARNING
) << "CopyDirectory() skipping non-regular file: " <<
284 current
= traversal
.Next();
285 traversal
.GetFindInfo(&info
);
291 bool PathExists(const FilePath
& path
) {
292 stat_wrapper_t file_info
;
293 return CallStat(path
.value().c_str(), &file_info
) == 0;
296 bool PathIsWritable(const FilePath
& path
) {
297 FilePath
test_path(path
);
298 stat_wrapper_t file_info
;
299 if (CallStat(test_path
.value().c_str(), &file_info
) != 0)
301 if (S_IWOTH
& file_info
.st_mode
)
303 if (getegid() == file_info
.st_gid
&& (S_IWGRP
& file_info
.st_mode
))
305 if (geteuid() == file_info
.st_uid
&& (S_IWUSR
& file_info
.st_mode
))
310 bool DirectoryExists(const FilePath
& path
) {
311 stat_wrapper_t file_info
;
312 if (CallStat(path
.value().c_str(), &file_info
) == 0)
313 return S_ISDIR(file_info
.st_mode
);
317 // TODO(erikkay): implement
319 bool GetFileCreationLocalTimeFromHandle(int fd
,
320 LPSYSTEMTIME creation_time
) {
324 FILETIME utc_filetime
;
325 if (!GetFileTime(file_handle
, &utc_filetime
, NULL
, NULL
))
328 FILETIME local_filetime
;
329 if (!FileTimeToLocalFileTime(&utc_filetime
, &local_filetime
))
332 return !!FileTimeToSystemTime(&local_filetime
, creation_time
);
335 bool GetFileCreationLocalTime(const std::string
& filename
,
336 LPSYSTEMTIME creation_time
) {
337 ScopedHandle
file_handle(
338 CreateFile(filename
.c_str(), GENERIC_READ
,
339 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
340 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
));
341 return GetFileCreationLocalTimeFromHandle(file_handle
.Get(), creation_time
);
345 bool ReadFromFD(int fd
, char* buffer
, size_t bytes
) {
346 size_t total_read
= 0;
347 while (total_read
< bytes
) {
349 HANDLE_EINTR(read(fd
, buffer
+ total_read
, bytes
- total_read
));
352 total_read
+= bytes_read
;
354 return total_read
== bytes
;
357 // Creates and opens a temporary file in |directory|, returning the
358 // file descriptor. |path| is set to the temporary file path.
359 // This function does NOT unlink() the file.
360 int CreateAndOpenFdForTemporaryFile(FilePath directory
, FilePath
* path
) {
361 *path
= directory
.Append(kTempFileName
);
362 const std::string
& tmpdir_string
= path
->value();
363 // this should be OK since mkstemp just replaces characters in place
364 char* buffer
= const_cast<char*>(tmpdir_string
.c_str());
366 return mkstemp(buffer
);
369 bool CreateTemporaryFile(FilePath
* path
) {
371 if (!GetTempDir(&directory
))
373 int fd
= CreateAndOpenFdForTemporaryFile(directory
, path
);
380 FILE* CreateAndOpenTemporaryShmemFile(FilePath
* path
) {
382 if (!GetShmemTempDir(&directory
))
385 return CreateAndOpenTemporaryFileInDir(directory
, path
);
388 FILE* CreateAndOpenTemporaryFileInDir(const FilePath
& dir
, FilePath
* path
) {
389 int fd
= CreateAndOpenFdForTemporaryFile(dir
, path
);
393 return fdopen(fd
, "a+");
396 bool CreateTemporaryFileInDir(const FilePath
& dir
, FilePath
* temp_file
) {
397 int fd
= CreateAndOpenFdForTemporaryFile(dir
, temp_file
);
398 return ((fd
>= 0) && !close(fd
));
401 bool CreateNewTempDirectory(const FilePath::StringType
& prefix
,
402 FilePath
* new_temp_path
) {
404 if (!GetTempDir(&tmpdir
))
406 tmpdir
= tmpdir
.Append(kTempFileName
);
407 std::string tmpdir_string
= tmpdir
.value();
408 // this should be OK since mkdtemp just replaces characters in place
409 char* buffer
= const_cast<char*>(tmpdir_string
.c_str());
410 char* dtemp
= mkdtemp(buffer
);
413 *new_temp_path
= FilePath(dtemp
);
417 bool CreateDirectory(const FilePath
& full_path
) {
418 std::vector
<FilePath
> subpaths
;
420 // Collect a list of all parent directories.
421 FilePath last_path
= full_path
;
422 subpaths
.push_back(full_path
);
423 for (FilePath path
= full_path
.DirName();
424 path
.value() != last_path
.value(); path
= path
.DirName()) {
425 subpaths
.push_back(path
);
429 // Iterate through the parents and create the missing ones.
430 for (std::vector
<FilePath
>::reverse_iterator i
= subpaths
.rbegin();
431 i
!= subpaths
.rend(); ++i
) {
432 if (!DirectoryExists(*i
)) {
433 if (mkdir(i
->value().c_str(), 0700) != 0)
440 bool GetFileInfo(const FilePath
& file_path
, FileInfo
* results
) {
441 stat_wrapper_t file_info
;
442 if (CallStat(file_path
.value().c_str(), &file_info
) != 0)
444 results
->is_directory
= S_ISDIR(file_info
.st_mode
);
445 results
->size
= file_info
.st_size
;
446 results
->last_modified
= base::Time::FromTimeT(file_info
.st_mtime
);
450 bool SetLastModifiedTime(const FilePath
& file_path
, base::Time last_modified
) {
451 struct timeval times
[2];
452 times
[0] = last_modified
.ToTimeVal();
453 times
[1] = last_modified
.ToTimeVal();
454 return (utimes(file_path
.value().c_str(), times
) == 0);
457 bool GetInode(const FilePath
& path
, ino_t
* inode
) {
459 int result
= stat(path
.value().c_str(), &buffer
);
463 *inode
= buffer
.st_ino
;
467 FILE* OpenFile(const std::string
& filename
, const char* mode
) {
468 return OpenFile(FilePath(filename
), mode
);
471 FILE* OpenFile(const FilePath
& filename
, const char* mode
) {
472 return fopen(filename
.value().c_str(), mode
);
475 int ReadFile(const FilePath
& filename
, char* data
, int size
) {
476 int fd
= open(filename
.value().c_str(), O_RDONLY
);
480 ssize_t bytes_read
= HANDLE_EINTR(read(fd
, data
, size
));
481 if (int ret
= HANDLE_EINTR(close(fd
)) < 0)
486 int WriteFile(const FilePath
& filename
, const char* data
, int size
) {
487 int fd
= creat(filename
.value().c_str(), 0666);
491 int bytes_written
= WriteFileDescriptor(fd
, data
, size
);
492 if (int ret
= HANDLE_EINTR(close(fd
)) < 0)
494 return bytes_written
;
497 int WriteFileDescriptor(const int fd
, const char* data
, int size
) {
498 // Allow for partial writes.
499 ssize_t bytes_written_total
= 0;
500 for (ssize_t bytes_written_partial
= 0; bytes_written_total
< size
;
501 bytes_written_total
+= bytes_written_partial
) {
502 bytes_written_partial
=
503 HANDLE_EINTR(write(fd
, data
+ bytes_written_total
,
504 size
- bytes_written_total
));
505 if (bytes_written_partial
< 0)
509 return bytes_written_total
;
512 // Gets the current working directory for the process.
513 bool GetCurrentDirectory(FilePath
* dir
) {
514 char system_buffer
[PATH_MAX
] = "";
515 if (!getcwd(system_buffer
, sizeof(system_buffer
))) {
519 *dir
= FilePath(system_buffer
);
523 // Sets the current working directory for the process.
524 bool SetCurrentDirectory(const FilePath
& path
) {
525 int ret
= chdir(path
.value().c_str());
529 ///////////////////////////////////////////////
532 FileEnumerator::FileEnumerator(const FilePath
& root_path
,
534 FileEnumerator::FILE_TYPE file_type
)
535 : current_directory_entry_(0),
536 root_path_(root_path
),
537 recursive_(recursive
),
538 file_type_(file_type
),
539 is_in_find_op_(false) {
540 // INCLUDE_DOT_DOT must not be specified if recursive.
541 DCHECK(!(recursive
&& (INCLUDE_DOT_DOT
& file_type_
)));
542 pending_paths_
.push(root_path
);
545 FileEnumerator::FileEnumerator(const FilePath
& root_path
,
547 FileEnumerator::FILE_TYPE file_type
,
548 const FilePath::StringType
& pattern
)
549 : current_directory_entry_(0),
550 root_path_(root_path
),
551 recursive_(recursive
),
552 file_type_(file_type
),
553 pattern_(root_path
.Append(pattern
).value()),
554 is_in_find_op_(false) {
555 // INCLUDE_DOT_DOT must not be specified if recursive.
556 DCHECK(!(recursive
&& (INCLUDE_DOT_DOT
& file_type_
)));
557 // The Windows version of this code appends the pattern to the root_path,
558 // potentially only matching against items in the top-most directory.
560 if (pattern
.size() == 0)
561 pattern_
= FilePath::StringType();
562 pending_paths_
.push(root_path
);
565 FileEnumerator::~FileEnumerator() {
568 void FileEnumerator::GetFindInfo(FindInfo
* info
) {
571 if (current_directory_entry_
>= directory_entries_
.size())
574 DirectoryEntryInfo
* cur_entry
= &directory_entries_
[current_directory_entry_
];
575 memcpy(&(info
->stat
), &(cur_entry
->stat
), sizeof(info
->stat
));
576 info
->filename
.assign(cur_entry
->filename
.value());
579 bool FileEnumerator::IsDirectory(const FindInfo
& info
) {
580 return S_ISDIR(info
.stat
.st_mode
);
584 FilePath
FileEnumerator::GetFilename(const FindInfo
& find_info
) {
585 return FilePath(find_info
.filename
);
588 FilePath
FileEnumerator::Next() {
589 ++current_directory_entry_
;
591 // While we've exhausted the entries in the current directory, do the next
592 while (current_directory_entry_
>= directory_entries_
.size()) {
593 if (pending_paths_
.empty())
596 root_path_
= pending_paths_
.top();
597 root_path_
= root_path_
.StripTrailingSeparators();
598 pending_paths_
.pop();
600 std::vector
<DirectoryEntryInfo
> entries
;
601 if (!ReadDirectory(&entries
, root_path_
, file_type_
& SHOW_SYM_LINKS
))
604 directory_entries_
.clear();
605 current_directory_entry_
= 0;
606 for (std::vector
<DirectoryEntryInfo
>::const_iterator
607 i
= entries
.begin(); i
!= entries
.end(); ++i
) {
608 FilePath full_path
= root_path_
.Append(i
->filename
);
609 if (ShouldSkip(full_path
))
612 if (pattern_
.size() &&
613 fnmatch(pattern_
.c_str(), full_path
.value().c_str(), FNM_NOESCAPE
))
616 if (recursive_
&& S_ISDIR(i
->stat
.st_mode
))
617 pending_paths_
.push(full_path
);
619 if ((S_ISDIR(i
->stat
.st_mode
) && (file_type_
& DIRECTORIES
)) ||
620 (!S_ISDIR(i
->stat
.st_mode
) && (file_type_
& FILES
)))
621 directory_entries_
.push_back(*i
);
625 return root_path_
.Append(directory_entries_
[current_directory_entry_
629 bool FileEnumerator::ReadDirectory(std::vector
<DirectoryEntryInfo
>* entries
,
630 const FilePath
& source
, bool show_links
) {
631 DIR* dir
= opendir(source
.value().c_str());
635 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
636 !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
637 #error Port warning: depending on the definition of struct dirent, \
638 additional space for pathname may be needed
641 struct dirent dent_buf
;
643 while (readdir_r(dir
, &dent_buf
, &dent
) == 0 && dent
) {
644 DirectoryEntryInfo info
;
645 info
.filename
= FilePath(dent
->d_name
);
647 FilePath full_name
= source
.Append(dent
->d_name
);
650 ret
= lstat(full_name
.value().c_str(), &info
.stat
);
652 ret
= stat(full_name
.value().c_str(), &info
.stat
);
654 // Print the stat() error message unless it was ENOENT and we're
655 // following symlinks.
656 if (!(ret
== ENOENT
&& !show_links
)) {
657 PLOG(ERROR
) << "Couldn't stat "
658 << source
.Append(dent
->d_name
).value();
660 memset(&info
.stat
, 0, sizeof(info
.stat
));
662 entries
->push_back(info
);
669 ///////////////////////////////////////////////
672 MemoryMappedFile::MemoryMappedFile()
673 : file_(base::kInvalidPlatformFileValue
),
678 bool MemoryMappedFile::MapFileToMemoryInternal() {
679 struct stat file_stat
;
680 if (fstat(file_
, &file_stat
) == base::kInvalidPlatformFileValue
) {
681 LOG(ERROR
) << "Couldn't fstat " << file_
<< ", errno " << errno
;
684 length_
= file_stat
.st_size
;
686 data_
= static_cast<uint8
*>(
687 mmap(NULL
, length_
, PROT_READ
, MAP_SHARED
, file_
, 0));
688 if (data_
== MAP_FAILED
)
689 LOG(ERROR
) << "Couldn't mmap " << file_
<< ", errno " << errno
;
691 return data_
!= MAP_FAILED
;
694 void MemoryMappedFile::CloseHandles() {
696 munmap(data_
, length_
);
697 if (file_
!= base::kInvalidPlatformFileValue
)
702 file_
= base::kInvalidPlatformFileValue
;
705 bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo
& find_info
,
706 const base::Time
& cutoff_time
) {
707 return find_info
.stat
.st_mtime
>= cutoff_time
.ToTimeT();
710 } // namespace file_util