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