file_util_posix.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
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. 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#include <libgen.h> 12#include <limits.h> 13#include <stdio.h> 14#include <stdlib.h> 15#include <string.h> 16#include <sys/errno.h> 17#include <sys/mman.h> 18#include <sys/param.h> 19#include <sys/stat.h> 20#include <sys/time.h> 21#include <sys/types.h> 22#include <time.h> 23#include <unistd.h> 24 25#if defined(OS_MACOSX) 26#include <AvailabilityMacros.h> 27#elif !defined(ANDROID) 28#include <glib.h> 29#endif 30 31#include <fstream> 32 33#include "base/basictypes.h" 34#include "base/eintr_wrapper.h" 35#include "base/file_path.h" 36#include "base/logging.h" 37#include "base/scoped_ptr.h" 38#include "base/singleton.h" 39#include "base/string_util.h" 40#include "base/sys_string_conversions.h" 41#include "base/thread_restrictions.h" 42#include "base/time.h" 43#include "base/utf_string_conversions.h" 44 45namespace file_util { 46 47namespace { 48 49// Helper for NormalizeFilePath(), defined below. 50bool RealPath(const FilePath& path, FilePath* real_path) { 51 base::ThreadRestrictions::AssertIOAllowed(); // For realpath(). 52 FilePath::CharType buf[PATH_MAX]; 53 if (!realpath(path.value().c_str(), buf)) 54 return false; 55 56 *real_path = FilePath(buf); 57 return true; 58} 59 60} // namespace 61 62#if defined(OS_OPENBSD) || defined(OS_FREEBSD) || \ 63 (defined(OS_MACOSX) && \ 64 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) 65typedef struct stat stat_wrapper_t; 66static int CallStat(const char *path, stat_wrapper_t *sb) { 67 base::ThreadRestrictions::AssertIOAllowed(); 68 return stat(path, sb); 69} 70#else 71typedef struct stat64 stat_wrapper_t; 72static int CallStat(const char *path, stat_wrapper_t *sb) { 73 base::ThreadRestrictions::AssertIOAllowed(); 74 return stat64(path, sb); 75} 76#endif 77 78 79#if defined(GOOGLE_CHROME_BUILD) 80static const char* kTempFileName = ".com.google.chrome.XXXXXX"; 81#else 82static const char* kTempFileName = ".org.chromium.XXXXXX"; 83#endif 84 85bool AbsolutePath(FilePath* path) { 86 base::ThreadRestrictions::AssertIOAllowed(); // For realpath(). 87 char full_path[PATH_MAX]; 88 if (realpath(path->value().c_str(), full_path) == NULL) 89 return false; 90 *path = FilePath(full_path); 91 return true; 92} 93 94int CountFilesCreatedAfter(const FilePath& path, 95 const base::Time& comparison_time) { 96 base::ThreadRestrictions::AssertIOAllowed(); 97 int file_count = 0; 98 99 DIR* dir = opendir(path.value().c_str()); 100 if (dir) { 101#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \ 102 !defined(OS_OPENBSD) && !defined(OS_SOLARIS) 103 #error Port warning: depending on the definition of struct dirent, \ 104 additional space for pathname may be needed 105#endif 106 struct dirent ent_buf; 107 struct dirent* ent; 108 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) { 109 if ((strcmp(ent->d_name, ".") == 0) || 110 (strcmp(ent->d_name, "..") == 0)) 111 continue; 112 113 stat_wrapper_t st; 114 int test = CallStat(path.Append(ent->d_name).value().c_str(), &st); 115 if (test != 0) { 116 PLOG(ERROR) << "stat64 failed"; 117 continue; 118 } 119 // Here, we use Time::TimeT(), which discards microseconds. This 120 // means that files which are newer than |comparison_time| may 121 // be considered older. If we don't discard microseconds, it 122 // introduces another issue. Suppose the following case: 123 // 124 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs). 125 // 2. Create a file and the current time is 10.3 (secs). 126 // 127 // As POSIX doesn't have microsecond precision for |st_ctime|, 128 // the creation time of the file created in the step 2 is 10 and 129 // the file is considered older than |comparison_time|. After 130 // all, we may have to accept either of the two issues: 1. files 131 // which are older than |comparison_time| are considered newer 132 // (current implementation) 2. files newer than 133 // |comparison_time| are considered older. 134 if (static_cast<time_t>(st.st_ctime) >= comparison_time.ToTimeT()) 135 ++file_count; 136 } 137 closedir(dir); 138 } 139 return file_count; 140} 141 142// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" 143// which works both with and without the recursive flag. I'm not sure we need 144// that functionality. If not, remove from file_util_win.cc, otherwise add it 145// here. 146bool Delete(const FilePath& path, bool recursive) { 147 base::ThreadRestrictions::AssertIOAllowed(); 148 const char* path_str = path.value().c_str(); 149 stat_wrapper_t file_info; 150 int test = CallStat(path_str, &file_info); 151 if (test != 0) { 152 // The Windows version defines this condition as success. 153 bool ret = (errno == ENOENT || errno == ENOTDIR); 154 return ret; 155 } 156 if (!S_ISDIR(file_info.st_mode)) 157 return (unlink(path_str) == 0); 158 if (!recursive) 159 return (rmdir(path_str) == 0); 160 161 bool success = true; 162 std::stack<std::string> directories; 163 directories.push(path.value()); 164 FileEnumerator traversal(path, true, static_cast<FileEnumerator::FILE_TYPE>( 165 FileEnumerator::FILES | FileEnumerator::DIRECTORIES | 166 FileEnumerator::SHOW_SYM_LINKS)); 167 for (FilePath current = traversal.Next(); success && !current.empty(); 168 current = traversal.Next()) { 169 FileEnumerator::FindInfo info; 170 traversal.GetFindInfo(&info); 171 172 if (S_ISDIR(info.stat.st_mode)) 173 directories.push(current.value()); 174 else 175 success = (unlink(current.value().c_str()) == 0); 176 } 177 178 while (success && !directories.empty()) { 179 FilePath dir = FilePath(directories.top()); 180 directories.pop(); 181 success = (rmdir(dir.value().c_str()) == 0); 182 } 183 return success; 184} 185 186bool Move(const FilePath& from_path, const FilePath& to_path) { 187 base::ThreadRestrictions::AssertIOAllowed(); 188 // Windows compatibility: if to_path exists, from_path and to_path 189 // must be the same type, either both files, or both directories. 190 stat_wrapper_t to_file_info; 191 if (CallStat(to_path.value().c_str(), &to_file_info) == 0) { 192 stat_wrapper_t from_file_info; 193 if (CallStat(from_path.value().c_str(), &from_file_info) == 0) { 194 if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode)) 195 return false; 196 } else { 197 return false; 198 } 199 } 200 201 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) 202 return true; 203 204 if (!CopyDirectory(from_path, to_path, true)) 205 return false; 206 207 Delete(from_path, true); 208 return true; 209} 210 211bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { 212 base::ThreadRestrictions::AssertIOAllowed(); 213 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0); 214} 215 216bool CopyDirectory(const FilePath& from_path, 217 const FilePath& to_path, 218 bool recursive) { 219 base::ThreadRestrictions::AssertIOAllowed(); 220 // Some old callers of CopyDirectory want it to support wildcards. 221 // After some discussion, we decided to fix those callers. 222 // Break loudly here if anyone tries to do this. 223 // TODO(evanm): remove this once we're sure it's ok. 224 DCHECK(to_path.value().find('*') == std::string::npos); 225 DCHECK(from_path.value().find('*') == std::string::npos); 226 227 char top_dir[PATH_MAX]; 228 if (base::strlcpy(top_dir, from_path.value().c_str(), 229 arraysize(top_dir)) >= arraysize(top_dir)) { 230 return false; 231 } 232 233 // This function does not properly handle destinations within the source 234 FilePath real_to_path = to_path; 235 if (PathExists(real_to_path)) { 236 if (!AbsolutePath(&real_to_path)) 237 return false; 238 } else { 239 real_to_path = real_to_path.DirName(); 240 if (!AbsolutePath(&real_to_path)) 241 return false; 242 } 243 FilePath real_from_path = from_path; 244 if (!AbsolutePath(&real_from_path)) 245 return false; 246 if (real_to_path.value().size() >= real_from_path.value().size() && 247 real_to_path.value().compare(0, real_from_path.value().size(), 248 real_from_path.value()) == 0) 249 return false; 250 251 bool success = true; 252 FileEnumerator::FILE_TYPE traverse_type = 253 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES | 254 FileEnumerator::SHOW_SYM_LINKS); 255 if (recursive) 256 traverse_type = static_cast<FileEnumerator::FILE_TYPE>( 257 traverse_type | FileEnumerator::DIRECTORIES); 258 FileEnumerator traversal(from_path, recursive, traverse_type); 259 260 // We have to mimic windows behavior here. |to_path| may not exist yet, 261 // start the loop with |to_path|. 262 FileEnumerator::FindInfo info; 263 FilePath current = from_path; 264 if (stat(from_path.value().c_str(), &info.stat) < 0) { 265 LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " << 266 from_path.value() << " errno = " << errno; 267 success = false; 268 } 269 struct stat to_path_stat; 270 FilePath from_path_base = from_path; 271 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 && 272 S_ISDIR(to_path_stat.st_mode)) { 273 // If the destination already exists and is a directory, then the 274 // top level of source needs to be copied. 275 from_path_base = from_path.DirName(); 276 } 277 278 // The Windows version of this function assumes that non-recursive calls 279 // will always have a directory for from_path. 280 DCHECK(recursive || S_ISDIR(info.stat.st_mode)); 281 282 while (success && !current.empty()) { 283 // current is the source path, including from_path, so paste 284 // the suffix after from_path onto to_path to create the target_path. 285 std::string suffix(¤t.value().c_str()[from_path_base.value().size()]); 286 // Strip the leading '/' (if any). 287 if (!suffix.empty()) { 288 DCHECK_EQ('/', suffix[0]); 289 suffix.erase(0, 1); 290 } 291 const FilePath target_path = to_path.Append(suffix); 292 293 if (S_ISDIR(info.stat.st_mode)) { 294 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 && 295 errno != EEXIST) { 296 LOG(ERROR) << "CopyDirectory() couldn't create directory: " << 297 target_path.value() << " errno = " << errno; 298 success = false; 299 } 300 } else if (S_ISREG(info.stat.st_mode)) { 301 if (!CopyFile(current, target_path)) { 302 LOG(ERROR) << "CopyDirectory() couldn't create file: " << 303 target_path.value(); 304 success = false; 305 } 306 } else { 307 LOG(WARNING) << "CopyDirectory() skipping non-regular file: " << 308 current.value(); 309 } 310 311 current = traversal.Next(); 312 traversal.GetFindInfo(&info); 313 } 314 315 return success; 316} 317 318bool PathExists(const FilePath& path) { 319 base::ThreadRestrictions::AssertIOAllowed(); 320 return access(path.value().c_str(), F_OK) == 0; 321} 322 323bool PathIsWritable(const FilePath& path) { 324 base::ThreadRestrictions::AssertIOAllowed(); 325 return access(path.value().c_str(), W_OK) == 0; 326} 327 328bool DirectoryExists(const FilePath& path) { 329 base::ThreadRestrictions::AssertIOAllowed(); 330 stat_wrapper_t file_info; 331 if (CallStat(path.value().c_str(), &file_info) == 0) 332 return S_ISDIR(file_info.st_mode); 333 return false; 334} 335 336// TODO(erikkay): implement 337#if 0 338bool GetFileCreationLocalTimeFromHandle(int fd, 339 LPSYSTEMTIME creation_time) { 340 if (!file_handle) 341 return false; 342 343 FILETIME utc_filetime; 344 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL)) 345 return false; 346 347 FILETIME local_filetime; 348 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime)) 349 return false; 350 351 return !!FileTimeToSystemTime(&local_filetime, creation_time); 352} 353 354bool GetFileCreationLocalTime(const std::string& filename, 355 LPSYSTEMTIME creation_time) { 356 ScopedHandle file_handle( 357 CreateFile(filename.c_str(), GENERIC_READ, 358 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, 359 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); 360 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time); 361} 362#endif 363 364bool ReadFromFD(int fd, char* buffer, size_t bytes) { 365 size_t total_read = 0; 366 while (total_read < bytes) { 367 ssize_t bytes_read = 368 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); 369 if (bytes_read <= 0) 370 break; 371 total_read += bytes_read; 372 } 373 return total_read == bytes; 374} 375 376bool CreateSymbolicLink(const FilePath& target_path, 377 const FilePath& symlink_path) { 378 DCHECK(!symlink_path.empty()); 379 DCHECK(!target_path.empty()); 380 return ::symlink(target_path.value().c_str(), 381 symlink_path.value().c_str()) != -1; 382} 383 384bool ReadSymbolicLink(const FilePath& symlink_path, 385 FilePath* target_path) { 386 DCHECK(!symlink_path.empty()); 387 DCHECK(target_path); 388 char buf[PATH_MAX]; 389 ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf)); 390 391 if (count <= 0) { 392 target_path->clear(); 393 return false; 394 } 395 396 *target_path = FilePath(FilePath::StringType(buf, count)); 397 return true; 398} 399 400// Creates and opens a temporary file in |directory|, returning the 401// file descriptor. |path| is set to the temporary file path. 402// This function does NOT unlink() the file. 403int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { 404 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp(). 405 *path = directory.Append(kTempFileName); 406 const std::string& tmpdir_string = path->value(); 407 // this should be OK since mkstemp just replaces characters in place 408 char* buffer = const_cast<char*>(tmpdir_string.c_str()); 409 410 return mkstemp(buffer); 411} 412 413bool CreateTemporaryFile(FilePath* path) { 414 base::ThreadRestrictions::AssertIOAllowed(); // For call to close(). 415 FilePath directory; 416 if (!GetTempDir(&directory)) 417 return false; 418 int fd = CreateAndOpenFdForTemporaryFile(directory, path); 419 if (fd < 0) 420 return false; 421 close(fd); 422 return true; 423} 424 425FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) { 426 FilePath directory; 427 if (!GetShmemTempDir(&directory)) 428 return NULL; 429 430 return CreateAndOpenTemporaryFileInDir(directory, path); 431} 432 433FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { 434 int fd = CreateAndOpenFdForTemporaryFile(dir, path); 435 if (fd < 0) 436 return NULL; 437 438 return fdopen(fd, "a+"); 439} 440 441bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { 442 base::ThreadRestrictions::AssertIOAllowed(); // For call to close(). 443 int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file); 444 return ((fd >= 0) && !close(fd)); 445} 446 447static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir, 448 const FilePath::StringType& name_tmpl, 449 FilePath* new_dir) { 450 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdtemp(). 451 CHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos) 452 << "Directory name template must contain \"XXXXXX\"."; 453 454 FilePath sub_dir = base_dir.Append(name_tmpl); 455 std::string sub_dir_string = sub_dir.value(); 456 457 // this should be OK since mkdtemp just replaces characters in place 458 char* buffer = const_cast<char*>(sub_dir_string.c_str()); 459 char* dtemp = mkdtemp(buffer); 460 if (!dtemp) { 461 DPLOG(ERROR) << "mkdtemp"; 462 return false; 463 } 464 *new_dir = FilePath(dtemp); 465 return true; 466} 467 468bool CreateTemporaryDirInDir(const FilePath& base_dir, 469 const FilePath::StringType& prefix, 470 FilePath* new_dir) { 471 FilePath::StringType mkdtemp_template = prefix; 472 mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX")); 473 return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir); 474} 475 476bool CreateNewTempDirectory(const FilePath::StringType& prefix, 477 FilePath* new_temp_path) { 478 FilePath tmpdir; 479 if (!GetTempDir(&tmpdir)) 480 return false; 481 482 return CreateTemporaryDirInDirImpl(tmpdir, kTempFileName, new_temp_path); 483} 484 485bool CreateDirectory(const FilePath& full_path) { 486 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdir(). 487 std::vector<FilePath> subpaths; 488 489 // Collect a list of all parent directories. 490 FilePath last_path = full_path; 491 subpaths.push_back(full_path); 492 for (FilePath path = full_path.DirName(); 493 path.value() != last_path.value(); path = path.DirName()) { 494 subpaths.push_back(path); 495 last_path = path; 496 } 497 498 // Iterate through the parents and create the missing ones. 499 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin(); 500 i != subpaths.rend(); ++i) { 501 if (DirectoryExists(*i)) 502 continue; 503 if (mkdir(i->value().c_str(), 0700) == 0) 504 continue; 505 // Mkdir failed, but it might have failed with EEXIST, or some other error 506 // due to the the directory appearing out of thin air. This can occur if 507 // two processes are trying to create the same file system tree at the same 508 // time. Check to see if it exists and make sure it is a directory. 509 if (!DirectoryExists(*i)) 510 return false; 511 } 512 return true; 513} 514 515bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) { 516 stat_wrapper_t file_info; 517 if (CallStat(file_path.value().c_str(), &file_info) != 0) 518 return false; 519 results->is_directory = S_ISDIR(file_info.st_mode); 520 results->size = file_info.st_size; 521 results->last_modified = base::Time::FromTimeT(file_info.st_mtime); 522 results->last_accessed = base::Time::FromTimeT(file_info.st_atime); 523 results->creation_time = base::Time::FromTimeT(file_info.st_ctime); 524 return true; 525} 526 527bool GetInode(const FilePath& path, ino_t* inode) { 528 base::ThreadRestrictions::AssertIOAllowed(); // For call to stat(). 529 struct stat buffer; 530 int result = stat(path.value().c_str(), &buffer); 531 if (result < 0) 532 return false; 533 534 *inode = buffer.st_ino; 535 return true; 536} 537 538FILE* OpenFile(const std::string& filename, const char* mode) { 539 return OpenFile(FilePath(filename), mode); 540} 541 542FILE* OpenFile(const FilePath& filename, const char* mode) { 543 base::ThreadRestrictions::AssertIOAllowed(); 544 return fopen(filename.value().c_str(), mode); 545} 546 547int ReadFile(const FilePath& filename, char* data, int size) { 548 base::ThreadRestrictions::AssertIOAllowed(); 549 int fd = open(filename.value().c_str(), O_RDONLY); 550 if (fd < 0) 551 return -1; 552 553 ssize_t bytes_read = HANDLE_EINTR(read(fd, data, size)); 554 if (int ret = HANDLE_EINTR(close(fd)) < 0) 555 return ret; 556 return bytes_read; 557} 558 559int WriteFile(const FilePath& filename, const char* data, int size) { 560 base::ThreadRestrictions::AssertIOAllowed(); 561 int fd = creat(filename.value().c_str(), 0666); 562 if (fd < 0) 563 return -1; 564 565 int bytes_written = WriteFileDescriptor(fd, data, size); 566 if (int ret = HANDLE_EINTR(close(fd)) < 0) 567 return ret; 568 return bytes_written; 569} 570 571int WriteFileDescriptor(const int fd, const char* data, int size) { 572 // Allow for partial writes. 573 ssize_t bytes_written_total = 0; 574 for (ssize_t bytes_written_partial = 0; bytes_written_total < size; 575 bytes_written_total += bytes_written_partial) { 576 bytes_written_partial = 577 HANDLE_EINTR(write(fd, data + bytes_written_total, 578 size - bytes_written_total)); 579 if (bytes_written_partial < 0) 580 return -1; 581 } 582 583 return bytes_written_total; 584} 585 586// Gets the current working directory for the process. 587bool GetCurrentDirectory(FilePath* dir) { 588 // getcwd can return ENOENT, which implies it checks against the disk. 589 base::ThreadRestrictions::AssertIOAllowed(); 590 591 char system_buffer[PATH_MAX] = ""; 592 if (!getcwd(system_buffer, sizeof(system_buffer))) { 593 NOTREACHED(); 594 return false; 595 } 596 *dir = FilePath(system_buffer); 597 return true; 598} 599 600// Sets the current working directory for the process. 601bool SetCurrentDirectory(const FilePath& path) { 602 base::ThreadRestrictions::AssertIOAllowed(); 603 int ret = chdir(path.value().c_str()); 604 return !ret; 605} 606 607/////////////////////////////////////////////// 608// FileEnumerator 609 610FileEnumerator::FileEnumerator(const FilePath& root_path, 611 bool recursive, 612 FileEnumerator::FILE_TYPE file_type) 613 : current_directory_entry_(0), 614 root_path_(root_path), 615 recursive_(recursive), 616 file_type_(file_type) { 617 // INCLUDE_DOT_DOT must not be specified if recursive. 618 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); 619 pending_paths_.push(root_path); 620} 621 622FileEnumerator::FileEnumerator(const FilePath& root_path, 623 bool recursive, 624 FileEnumerator::FILE_TYPE file_type, 625 const FilePath::StringType& pattern) 626 : current_directory_entry_(0), 627 root_path_(root_path), 628 recursive_(recursive), 629 file_type_(file_type), 630 pattern_(root_path.Append(pattern).value()) { 631 // INCLUDE_DOT_DOT must not be specified if recursive. 632 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); 633 // The Windows version of this code appends the pattern to the root_path, 634 // potentially only matching against items in the top-most directory. 635 // Do the same here. 636 if (pattern.size() == 0) 637 pattern_ = FilePath::StringType(); 638 pending_paths_.push(root_path); 639} 640 641FileEnumerator::~FileEnumerator() { 642} 643 644void FileEnumerator::GetFindInfo(FindInfo* info) { 645 DCHECK(info); 646 647 if (current_directory_entry_ >= directory_entries_.size()) 648 return; 649 650 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_]; 651 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat)); 652 info->filename.assign(cur_entry->filename.value()); 653} 654 655bool FileEnumerator::IsDirectory(const FindInfo& info) { 656 return S_ISDIR(info.stat.st_mode); 657} 658 659// static 660FilePath FileEnumerator::GetFilename(const FindInfo& find_info) { 661 return FilePath(find_info.filename); 662} 663 664FilePath FileEnumerator::Next() { 665 ++current_directory_entry_; 666 667 // While we've exhausted the entries in the current directory, do the next 668 while (current_directory_entry_ >= directory_entries_.size()) { 669 if (pending_paths_.empty()) 670 return FilePath(); 671 672 root_path_ = pending_paths_.top(); 673 root_path_ = root_path_.StripTrailingSeparators(); 674 pending_paths_.pop(); 675 676 std::vector<DirectoryEntryInfo> entries; 677 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) 678 continue; 679 680 directory_entries_.clear(); 681 current_directory_entry_ = 0; 682 for (std::vector<DirectoryEntryInfo>::const_iterator 683 i = entries.begin(); i != entries.end(); ++i) { 684 FilePath full_path = root_path_.Append(i->filename); 685 if (ShouldSkip(full_path)) 686 continue; 687 688 if (pattern_.size() && 689 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) 690 continue; 691 692 if (recursive_ && S_ISDIR(i->stat.st_mode)) 693 pending_paths_.push(full_path); 694 695 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) || 696 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES))) 697 directory_entries_.push_back(*i); 698 } 699 } 700 701 return root_path_.Append(directory_entries_[current_directory_entry_ 702 ].filename); 703} 704 705bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries, 706 const FilePath& source, bool show_links) { 707 base::ThreadRestrictions::AssertIOAllowed(); 708 DIR* dir = opendir(source.value().c_str()); 709 if (!dir) 710 return false; 711 712#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \ 713 !defined(OS_OPENBSD) && !defined(OS_SOLARIS) 714 #error Port warning: depending on the definition of struct dirent, \ 715 additional space for pathname may be needed 716#endif 717 718 struct dirent dent_buf; 719 struct dirent* dent; 720 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { 721 DirectoryEntryInfo info; 722 info.filename = FilePath(dent->d_name); 723 724 FilePath full_name = source.Append(dent->d_name); 725 int ret; 726 if (show_links) 727 ret = lstat(full_name.value().c_str(), &info.stat); 728 else 729 ret = stat(full_name.value().c_str(), &info.stat); 730 if (ret < 0) { 731 // Print the stat() error message unless it was ENOENT and we're 732 // following symlinks. 733 if (!(errno == ENOENT && !show_links)) { 734 PLOG(ERROR) << "Couldn't stat " 735 << source.Append(dent->d_name).value(); 736 } 737 memset(&info.stat, 0, sizeof(info.stat)); 738 } 739 entries->push_back(info); 740 } 741 742 closedir(dir); 743 return true; 744} 745 746/////////////////////////////////////////////// 747// MemoryMappedFile 748 749MemoryMappedFile::MemoryMappedFile() 750 : file_(base::kInvalidPlatformFileValue), 751 data_(NULL), 752 length_(0) { 753} 754 755bool MemoryMappedFile::MapFileToMemoryInternal() { 756 base::ThreadRestrictions::AssertIOAllowed(); 757 758 struct stat file_stat; 759 if (fstat(file_, &file_stat) == base::kInvalidPlatformFileValue) { 760 LOG(ERROR) << "Couldn't fstat " << file_ << ", errno " << errno; 761 return false; 762 } 763 length_ = file_stat.st_size; 764 765 data_ = static_cast<uint8*>( 766 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0)); 767 if (data_ == MAP_FAILED) 768 LOG(ERROR) << "Couldn't mmap " << file_ << ", errno " << errno; 769 770 return data_ != MAP_FAILED; 771} 772 773void MemoryMappedFile::CloseHandles() { 774 base::ThreadRestrictions::AssertIOAllowed(); 775 776 if (data_ != NULL) 777 munmap(data_, length_); 778 if (file_ != base::kInvalidPlatformFileValue) 779 close(file_); 780 781 data_ = NULL; 782 length_ = 0; 783 file_ = base::kInvalidPlatformFileValue; 784} 785 786bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info, 787 const base::Time& cutoff_time) { 788 return static_cast<time_t>(find_info.stat.st_mtime) >= cutoff_time.ToTimeT(); 789} 790 791bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { 792 FilePath real_path_result; 793 if (!RealPath(path, &real_path_result)) 794 return false; 795 796 // To be consistant with windows, fail if |real_path_result| is a 797 // directory. 798 stat_wrapper_t file_info; 799 if (CallStat(real_path_result.value().c_str(), &file_info) != 0 || 800 S_ISDIR(file_info.st_mode)) 801 return false; 802 803 *normalized_path = real_path_result; 804 return true; 805} 806 807#if !defined(OS_MACOSX) 808bool GetTempDir(FilePath* path) { 809 const char* tmp = getenv("TMPDIR"); 810 if (tmp) 811 *path = FilePath(tmp); 812 else 813 *path = FilePath("/tmp"); 814 return true; 815} 816 817bool GetShmemTempDir(FilePath* path) { 818 *path = FilePath("/dev/shm"); 819 return true; 820} 821 822FilePath GetHomeDir() { 823#ifndef ANDROID 824 const char* home_dir = getenv("HOME"); 825 if (home_dir && home_dir[0]) 826 return FilePath(home_dir); 827 828 // g_get_home_dir calls getpwent, which can fall through to LDAP calls. 829 base::ThreadRestrictions::AssertIOAllowed(); 830 831 home_dir = g_get_home_dir(); 832 if (home_dir && home_dir[0]) 833 return FilePath(home_dir); 834 835 FilePath rv; 836 if (file_util::GetTempDir(&rv)) 837 return rv; 838#endif 839 // Last resort. 840 return FilePath("/tmp"); 841} 842 843bool CopyFile(const FilePath& from_path, const FilePath& to_path) { 844 base::ThreadRestrictions::AssertIOAllowed(); 845 int infile = open(from_path.value().c_str(), O_RDONLY); 846 if (infile < 0) 847 return false; 848 849 int outfile = creat(to_path.value().c_str(), 0666); 850 if (outfile < 0) { 851 close(infile); 852 return false; 853 } 854 855 const size_t kBufferSize = 32768; 856 std::vector<char> buffer(kBufferSize); 857 bool result = true; 858 859 while (result) { 860 ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size())); 861 if (bytes_read < 0) { 862 result = false; 863 break; 864 } 865 if (bytes_read == 0) 866 break; 867 // Allow for partial writes 868 ssize_t bytes_written_per_read = 0; 869 do { 870 ssize_t bytes_written_partial = HANDLE_EINTR(write( 871 outfile, 872 &buffer[bytes_written_per_read], 873 bytes_read - bytes_written_per_read)); 874 if (bytes_written_partial < 0) { 875 result = false; 876 break; 877 } 878 bytes_written_per_read += bytes_written_partial; 879 } while (bytes_written_per_read < bytes_read); 880 } 881 882 if (HANDLE_EINTR(close(infile)) < 0) 883 result = false; 884 if (HANDLE_EINTR(close(outfile)) < 0) 885 result = false; 886 887 return result; 888} 889#endif // defined(OS_MACOSX) 890 891} // namespace file_util 892