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