Home | History | Annotate | Download | only in files
      1 // Copyright (c) 2012 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/files/file_util.h"
      6 
      7 #include <dirent.h>
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <libgen.h>
     11 #include <limits.h>
     12 #include <stddef.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 #include "base/files/file_enumerator.h"
     26 #include "base/files/file_path.h"
     27 #include "base/files/scoped_file.h"
     28 #include "base/logging.h"
     29 #include "base/macros.h"
     30 #include "base/memory/scoped_ptr.h"
     31 #include "base/memory/singleton.h"
     32 #include "base/posix/eintr_wrapper.h"
     33 #include "base/stl_util.h"
     34 #include "base/strings/string_util.h"
     35 #include "base/strings/stringprintf.h"
     36 #include "base/strings/sys_string_conversions.h"
     37 #include "base/strings/utf_string_conversions.h"
     38 #include "base/sys_info.h"
     39 #include "base/threading/thread_restrictions.h"
     40 #include "base/time/time.h"
     41 #include "build/build_config.h"
     42 
     43 #if defined(OS_MACOSX)
     44 #include <AvailabilityMacros.h>
     45 #include "base/mac/foundation_util.h"
     46 #endif
     47 
     48 #if defined(OS_ANDROID)
     49 #include "base/android/content_uri_utils.h"
     50 #include "base/os_compat_android.h"
     51 #include "base/path_service.h"
     52 #endif
     53 
     54 #if !defined(OS_IOS)
     55 #include <grp.h>
     56 #endif
     57 
     58 namespace base {
     59 
     60 namespace {
     61 
     62 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
     63 static int CallStat(const char *path, stat_wrapper_t *sb) {
     64   ThreadRestrictions::AssertIOAllowed();
     65   return stat(path, sb);
     66 }
     67 static int CallLstat(const char *path, stat_wrapper_t *sb) {
     68   ThreadRestrictions::AssertIOAllowed();
     69   return lstat(path, sb);
     70 }
     71 #else  //  defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
     72 static int CallStat(const char *path, stat_wrapper_t *sb) {
     73   ThreadRestrictions::AssertIOAllowed();
     74   return stat64(path, sb);
     75 }
     76 static int CallLstat(const char *path, stat_wrapper_t *sb) {
     77   ThreadRestrictions::AssertIOAllowed();
     78   return lstat64(path, sb);
     79 }
     80 #endif  // !(defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL))
     81 
     82 #if !defined(OS_NACL_NONSFI)
     83 // Helper for NormalizeFilePath(), defined below.
     84 bool RealPath(const FilePath& path, FilePath* real_path) {
     85   ThreadRestrictions::AssertIOAllowed();  // For realpath().
     86   FilePath::CharType buf[PATH_MAX];
     87   if (!realpath(path.value().c_str(), buf))
     88     return false;
     89 
     90   *real_path = FilePath(buf);
     91   return true;
     92 }
     93 
     94 // Helper for VerifyPathControlledByUser.
     95 bool VerifySpecificPathControlledByUser(const FilePath& path,
     96                                         uid_t owner_uid,
     97                                         const std::set<gid_t>& group_gids) {
     98   stat_wrapper_t stat_info;
     99   if (CallLstat(path.value().c_str(), &stat_info) != 0) {
    100     DPLOG(ERROR) << "Failed to get information on path "
    101                  << path.value();
    102     return false;
    103   }
    104 
    105   if (S_ISLNK(stat_info.st_mode)) {
    106     DLOG(ERROR) << "Path " << path.value()
    107                << " is a symbolic link.";
    108     return false;
    109   }
    110 
    111   if (stat_info.st_uid != owner_uid) {
    112     DLOG(ERROR) << "Path " << path.value()
    113                 << " is owned by the wrong user.";
    114     return false;
    115   }
    116 
    117   if ((stat_info.st_mode & S_IWGRP) &&
    118       !ContainsKey(group_gids, stat_info.st_gid)) {
    119     DLOG(ERROR) << "Path " << path.value()
    120                 << " is writable by an unprivileged group.";
    121     return false;
    122   }
    123 
    124   if (stat_info.st_mode & S_IWOTH) {
    125     DLOG(ERROR) << "Path " << path.value()
    126                 << " is writable by any user.";
    127     return false;
    128   }
    129 
    130   return true;
    131 }
    132 
    133 std::string TempFileName() {
    134 #if defined(OS_MACOSX)
    135   return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID());
    136 #endif
    137 
    138 #if defined(GOOGLE_CHROME_BUILD)
    139   return std::string(".com.google.Chrome.XXXXXX");
    140 #else
    141   return std::string(".org.chromium.Chromium.XXXXXX");
    142 #endif
    143 }
    144 
    145 // Creates and opens a temporary file in |directory|, returning the
    146 // file descriptor. |path| is set to the temporary file path.
    147 // This function does NOT unlink() the file.
    148 int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
    149   ThreadRestrictions::AssertIOAllowed();  // For call to mkstemp().
    150   *path = directory.Append(base::TempFileName());
    151   const std::string& tmpdir_string = path->value();
    152   // this should be OK since mkstemp just replaces characters in place
    153   char* buffer = const_cast<char*>(tmpdir_string.c_str());
    154 
    155   return HANDLE_EINTR(mkstemp(buffer));
    156 }
    157 
    158 #if defined(OS_LINUX)
    159 // Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC.
    160 // This depends on the mount options used for /dev/shm, which vary among
    161 // different Linux distributions and possibly local configuration.  It also
    162 // depends on details of kernel--ChromeOS uses the noexec option for /dev/shm
    163 // but its kernel allows mprotect with PROT_EXEC anyway.
    164 bool DetermineDevShmExecutable() {
    165   bool result = false;
    166   FilePath path;
    167 
    168   ScopedFD fd(CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path));
    169   if (fd.is_valid()) {
    170     DeleteFile(path, false);
    171     long sysconf_result = sysconf(_SC_PAGESIZE);
    172     CHECK_GE(sysconf_result, 0);
    173     size_t pagesize = static_cast<size_t>(sysconf_result);
    174     CHECK_GE(sizeof(pagesize), sizeof(sysconf_result));
    175     void* mapping = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0);
    176     if (mapping != MAP_FAILED) {
    177       if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0)
    178         result = true;
    179       munmap(mapping, pagesize);
    180     }
    181   }
    182   return result;
    183 }
    184 #endif  // defined(OS_LINUX)
    185 #endif  // !defined(OS_NACL_NONSFI)
    186 
    187 }  // namespace
    188 
    189 #if !defined(OS_NACL_NONSFI)
    190 FilePath MakeAbsoluteFilePath(const FilePath& input) {
    191   ThreadRestrictions::AssertIOAllowed();
    192   char full_path[PATH_MAX];
    193   if (realpath(input.value().c_str(), full_path) == NULL)
    194     return FilePath();
    195   return FilePath(full_path);
    196 }
    197 
    198 // TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
    199 // which works both with and without the recursive flag.  I'm not sure we need
    200 // that functionality. If not, remove from file_util_win.cc, otherwise add it
    201 // here.
    202 bool DeleteFile(const FilePath& path, bool recursive) {
    203   ThreadRestrictions::AssertIOAllowed();
    204   const char* path_str = path.value().c_str();
    205   stat_wrapper_t file_info;
    206   int test = CallLstat(path_str, &file_info);
    207   if (test != 0) {
    208     // The Windows version defines this condition as success.
    209     bool ret = (errno == ENOENT || errno == ENOTDIR);
    210     return ret;
    211   }
    212   if (!S_ISDIR(file_info.st_mode))
    213     return (unlink(path_str) == 0);
    214   if (!recursive)
    215     return (rmdir(path_str) == 0);
    216 
    217   bool success = true;
    218   std::stack<std::string> directories;
    219   directories.push(path.value());
    220   FileEnumerator traversal(path, true,
    221       FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
    222       FileEnumerator::SHOW_SYM_LINKS);
    223   for (FilePath current = traversal.Next(); success && !current.empty();
    224        current = traversal.Next()) {
    225     if (traversal.GetInfo().IsDirectory())
    226       directories.push(current.value());
    227     else
    228       success = (unlink(current.value().c_str()) == 0);
    229   }
    230 
    231   while (success && !directories.empty()) {
    232     FilePath dir = FilePath(directories.top());
    233     directories.pop();
    234     success = (rmdir(dir.value().c_str()) == 0);
    235   }
    236   return success;
    237 }
    238 
    239 bool ReplaceFile(const FilePath& from_path,
    240                  const FilePath& to_path,
    241                  File::Error* error) {
    242   ThreadRestrictions::AssertIOAllowed();
    243   if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
    244     return true;
    245   if (error)
    246     *error = File::OSErrorToFileError(errno);
    247   return false;
    248 }
    249 
    250 bool CopyDirectory(const FilePath& from_path,
    251                    const FilePath& to_path,
    252                    bool recursive) {
    253   ThreadRestrictions::AssertIOAllowed();
    254   // Some old callers of CopyDirectory want it to support wildcards.
    255   // After some discussion, we decided to fix those callers.
    256   // Break loudly here if anyone tries to do this.
    257   DCHECK(to_path.value().find('*') == std::string::npos);
    258   DCHECK(from_path.value().find('*') == std::string::npos);
    259 
    260   if (from_path.value().size() >= PATH_MAX) {
    261     return false;
    262   }
    263 
    264   // This function does not properly handle destinations within the source
    265   FilePath real_to_path = to_path;
    266   if (PathExists(real_to_path)) {
    267     real_to_path = MakeAbsoluteFilePath(real_to_path);
    268     if (real_to_path.empty())
    269       return false;
    270   } else {
    271     real_to_path = MakeAbsoluteFilePath(real_to_path.DirName());
    272     if (real_to_path.empty())
    273       return false;
    274   }
    275   FilePath real_from_path = MakeAbsoluteFilePath(from_path);
    276   if (real_from_path.empty())
    277     return false;
    278   if (real_to_path.value().size() >= real_from_path.value().size() &&
    279       real_to_path.value().compare(0, real_from_path.value().size(),
    280                                    real_from_path.value()) == 0) {
    281     return false;
    282   }
    283 
    284   int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS;
    285   if (recursive)
    286     traverse_type |= FileEnumerator::DIRECTORIES;
    287   FileEnumerator traversal(from_path, recursive, traverse_type);
    288 
    289   // We have to mimic windows behavior here. |to_path| may not exist yet,
    290   // start the loop with |to_path|.
    291   struct stat from_stat;
    292   FilePath current = from_path;
    293   if (stat(from_path.value().c_str(), &from_stat) < 0) {
    294     DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
    295                 << from_path.value() << " errno = " << errno;
    296     return false;
    297   }
    298   struct stat to_path_stat;
    299   FilePath from_path_base = from_path;
    300   if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 &&
    301       S_ISDIR(to_path_stat.st_mode)) {
    302     // If the destination already exists and is a directory, then the
    303     // top level of source needs to be copied.
    304     from_path_base = from_path.DirName();
    305   }
    306 
    307   // The Windows version of this function assumes that non-recursive calls
    308   // will always have a directory for from_path.
    309   // TODO(maruel): This is not necessary anymore.
    310   DCHECK(recursive || S_ISDIR(from_stat.st_mode));
    311 
    312   bool success = true;
    313   while (success && !current.empty()) {
    314     // current is the source path, including from_path, so append
    315     // the suffix after from_path to to_path to create the target_path.
    316     FilePath target_path(to_path);
    317     if (from_path_base != current) {
    318       if (!from_path_base.AppendRelativePath(current, &target_path)) {
    319         success = false;
    320         break;
    321       }
    322     }
    323 
    324     if (S_ISDIR(from_stat.st_mode)) {
    325       if (mkdir(target_path.value().c_str(),
    326                 (from_stat.st_mode & 01777) | S_IRUSR | S_IXUSR | S_IWUSR) !=
    327               0 &&
    328           errno != EEXIST) {
    329         DLOG(ERROR) << "CopyDirectory() couldn't create directory: "
    330                     << target_path.value() << " errno = " << errno;
    331         success = false;
    332       }
    333     } else if (S_ISREG(from_stat.st_mode)) {
    334       if (!CopyFile(current, target_path)) {
    335         DLOG(ERROR) << "CopyDirectory() couldn't create file: "
    336                     << target_path.value();
    337         success = false;
    338       }
    339     } else {
    340       DLOG(WARNING) << "CopyDirectory() skipping non-regular file: "
    341                     << current.value();
    342     }
    343 
    344     current = traversal.Next();
    345     if (!current.empty())
    346       from_stat = traversal.GetInfo().stat();
    347   }
    348 
    349   return success;
    350 }
    351 #endif  // !defined(OS_NACL_NONSFI)
    352 
    353 bool SetNonBlocking(int fd) {
    354   int flags = fcntl(fd, F_GETFL, 0);
    355   if (flags == -1)
    356     return false;
    357   if (flags & O_NONBLOCK)
    358     return true;
    359   if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
    360     return false;
    361   return true;
    362 }
    363 
    364 bool PathExists(const FilePath& path) {
    365   ThreadRestrictions::AssertIOAllowed();
    366 #if defined(OS_ANDROID)
    367   if (path.IsContentUri()) {
    368     return ContentUriExists(path);
    369   }
    370 #endif
    371   return access(path.value().c_str(), F_OK) == 0;
    372 }
    373 
    374 #if !defined(OS_NACL_NONSFI)
    375 bool PathIsWritable(const FilePath& path) {
    376   ThreadRestrictions::AssertIOAllowed();
    377   return access(path.value().c_str(), W_OK) == 0;
    378 }
    379 #endif  // !defined(OS_NACL_NONSFI)
    380 
    381 bool DirectoryExists(const FilePath& path) {
    382   ThreadRestrictions::AssertIOAllowed();
    383   stat_wrapper_t file_info;
    384   if (CallStat(path.value().c_str(), &file_info) == 0)
    385     return S_ISDIR(file_info.st_mode);
    386   return false;
    387 }
    388 
    389 bool ReadFromFD(int fd, char* buffer, size_t bytes) {
    390   size_t total_read = 0;
    391   while (total_read < bytes) {
    392     ssize_t bytes_read =
    393         HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
    394     if (bytes_read <= 0)
    395       break;
    396     total_read += bytes_read;
    397   }
    398   return total_read == bytes;
    399 }
    400 
    401 #if !defined(OS_NACL_NONSFI)
    402 bool CreateSymbolicLink(const FilePath& target_path,
    403                         const FilePath& symlink_path) {
    404   DCHECK(!symlink_path.empty());
    405   DCHECK(!target_path.empty());
    406   return ::symlink(target_path.value().c_str(),
    407                    symlink_path.value().c_str()) != -1;
    408 }
    409 
    410 bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) {
    411   DCHECK(!symlink_path.empty());
    412   DCHECK(target_path);
    413   char buf[PATH_MAX];
    414   ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf));
    415 
    416   if (count <= 0) {
    417     target_path->clear();
    418     return false;
    419   }
    420 
    421   *target_path = FilePath(FilePath::StringType(buf, count));
    422   return true;
    423 }
    424 
    425 bool GetPosixFilePermissions(const FilePath& path, int* mode) {
    426   ThreadRestrictions::AssertIOAllowed();
    427   DCHECK(mode);
    428 
    429   stat_wrapper_t file_info;
    430   // Uses stat(), because on symbolic link, lstat() does not return valid
    431   // permission bits in st_mode
    432   if (CallStat(path.value().c_str(), &file_info) != 0)
    433     return false;
    434 
    435   *mode = file_info.st_mode & FILE_PERMISSION_MASK;
    436   return true;
    437 }
    438 
    439 bool SetPosixFilePermissions(const FilePath& path,
    440                              int mode) {
    441   ThreadRestrictions::AssertIOAllowed();
    442   DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0);
    443 
    444   // Calls stat() so that we can preserve the higher bits like S_ISGID.
    445   stat_wrapper_t stat_buf;
    446   if (CallStat(path.value().c_str(), &stat_buf) != 0)
    447     return false;
    448 
    449   // Clears the existing permission bits, and adds the new ones.
    450   mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK;
    451   updated_mode_bits |= mode & FILE_PERMISSION_MASK;
    452 
    453   if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0)
    454     return false;
    455 
    456   return true;
    457 }
    458 
    459 #if !defined(OS_MACOSX)
    460 // This is implemented in file_util_mac.mm for Mac.
    461 bool GetTempDir(FilePath* path) {
    462   const char* tmp = getenv("TMPDIR");
    463   if (tmp) {
    464     *path = FilePath(tmp);
    465   } else {
    466 #if defined(OS_ANDROID)
    467     return PathService::Get(base::DIR_CACHE, path);
    468 #elif defined(__ANDROID__)
    469     *path = FilePath("/data/local/tmp");
    470 #else
    471     *path = FilePath("/tmp");
    472 #endif
    473   }
    474   return true;
    475 }
    476 #endif  // !defined(OS_MACOSX)
    477 
    478 #if !defined(OS_MACOSX)  // Mac implementation is in file_util_mac.mm.
    479 FilePath GetHomeDir() {
    480 #if defined(OS_CHROMEOS)
    481   if (SysInfo::IsRunningOnChromeOS()) {
    482     // On Chrome OS chrome::DIR_USER_DATA is overridden with a primary user
    483     // homedir once it becomes available. Return / as the safe option.
    484     return FilePath("/");
    485   }
    486 #endif
    487 
    488   const char* home_dir = getenv("HOME");
    489   if (home_dir && home_dir[0])
    490     return FilePath(home_dir);
    491 
    492 #if defined(OS_ANDROID)
    493   DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented.";
    494 #endif
    495 
    496   FilePath rv;
    497   if (GetTempDir(&rv))
    498     return rv;
    499 
    500   // Last resort.
    501   return FilePath("/tmp");
    502 }
    503 #endif  // !defined(OS_MACOSX)
    504 
    505 bool CreateTemporaryFile(FilePath* path) {
    506   ThreadRestrictions::AssertIOAllowed();  // For call to close().
    507   FilePath directory;
    508   if (!GetTempDir(&directory))
    509     return false;
    510   int fd = CreateAndOpenFdForTemporaryFile(directory, path);
    511   if (fd < 0)
    512     return false;
    513   close(fd);
    514   return true;
    515 }
    516 
    517 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
    518   int fd = CreateAndOpenFdForTemporaryFile(dir, path);
    519   if (fd < 0)
    520     return NULL;
    521 
    522   FILE* file = fdopen(fd, "a+");
    523   if (!file)
    524     close(fd);
    525   return file;
    526 }
    527 
    528 bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
    529   ThreadRestrictions::AssertIOAllowed();  // For call to close().
    530   int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
    531   return ((fd >= 0) && !IGNORE_EINTR(close(fd)));
    532 }
    533 
    534 static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
    535                                         const FilePath::StringType& name_tmpl,
    536                                         FilePath* new_dir) {
    537   ThreadRestrictions::AssertIOAllowed();  // For call to mkdtemp().
    538   DCHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos)
    539       << "Directory name template must contain \"XXXXXX\".";
    540 
    541   FilePath sub_dir = base_dir.Append(name_tmpl);
    542   std::string sub_dir_string = sub_dir.value();
    543 
    544   // this should be OK since mkdtemp just replaces characters in place
    545   char* buffer = const_cast<char*>(sub_dir_string.c_str());
    546   char* dtemp = mkdtemp(buffer);
    547   if (!dtemp) {
    548     DPLOG(ERROR) << "mkdtemp";
    549     return false;
    550   }
    551   *new_dir = FilePath(dtemp);
    552   return true;
    553 }
    554 
    555 bool CreateTemporaryDirInDir(const FilePath& base_dir,
    556                              const FilePath::StringType& prefix,
    557                              FilePath* new_dir) {
    558   FilePath::StringType mkdtemp_template = prefix;
    559   mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX"));
    560   return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir);
    561 }
    562 
    563 bool CreateNewTempDirectory(const FilePath::StringType& /* prefix */,
    564                             FilePath* new_temp_path) {
    565   FilePath tmpdir;
    566   if (!GetTempDir(&tmpdir))
    567     return false;
    568 
    569   return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path);
    570 }
    571 
    572 bool CreateDirectoryAndGetError(const FilePath& full_path,
    573                                 File::Error* error) {
    574   ThreadRestrictions::AssertIOAllowed();  // For call to mkdir().
    575   std::vector<FilePath> subpaths;
    576 
    577   // Collect a list of all parent directories.
    578   FilePath last_path = full_path;
    579   subpaths.push_back(full_path);
    580   for (FilePath path = full_path.DirName();
    581        path.value() != last_path.value(); path = path.DirName()) {
    582     subpaths.push_back(path);
    583     last_path = path;
    584   }
    585 
    586   // Iterate through the parents and create the missing ones.
    587   for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
    588        i != subpaths.rend(); ++i) {
    589     if (DirectoryExists(*i))
    590       continue;
    591     if (mkdir(i->value().c_str(), 0700) == 0)
    592       continue;
    593     // Mkdir failed, but it might have failed with EEXIST, or some other error
    594     // due to the the directory appearing out of thin air. This can occur if
    595     // two processes are trying to create the same file system tree at the same
    596     // time. Check to see if it exists and make sure it is a directory.
    597     int saved_errno = errno;
    598     if (!DirectoryExists(*i)) {
    599       if (error)
    600         *error = File::OSErrorToFileError(saved_errno);
    601       return false;
    602     }
    603   }
    604   return true;
    605 }
    606 
    607 bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
    608   FilePath real_path_result;
    609   if (!RealPath(path, &real_path_result))
    610     return false;
    611 
    612   // To be consistant with windows, fail if |real_path_result| is a
    613   // directory.
    614   stat_wrapper_t file_info;
    615   if (CallStat(real_path_result.value().c_str(), &file_info) != 0 ||
    616       S_ISDIR(file_info.st_mode))
    617     return false;
    618 
    619   *normalized_path = real_path_result;
    620   return true;
    621 }
    622 
    623 // TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks
    624 // correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948
    625 bool IsLink(const FilePath& file_path) {
    626   stat_wrapper_t st;
    627   // If we can't lstat the file, it's safe to assume that the file won't at
    628   // least be a 'followable' link.
    629   if (CallLstat(file_path.value().c_str(), &st) != 0)
    630     return false;
    631 
    632   if (S_ISLNK(st.st_mode))
    633     return true;
    634   else
    635     return false;
    636 }
    637 
    638 bool GetFileInfo(const FilePath& file_path, File::Info* results) {
    639   stat_wrapper_t file_info;
    640 #if defined(OS_ANDROID)
    641   if (file_path.IsContentUri()) {
    642     File file = OpenContentUriForRead(file_path);
    643     if (!file.IsValid())
    644       return false;
    645     return file.GetInfo(results);
    646   } else {
    647 #endif  // defined(OS_ANDROID)
    648     if (CallStat(file_path.value().c_str(), &file_info) != 0)
    649       return false;
    650 #if defined(OS_ANDROID)
    651   }
    652 #endif  // defined(OS_ANDROID)
    653 
    654   results->FromStat(file_info);
    655   return true;
    656 }
    657 #endif  // !defined(OS_NACL_NONSFI)
    658 
    659 FILE* OpenFile(const FilePath& filename, const char* mode) {
    660   ThreadRestrictions::AssertIOAllowed();
    661   FILE* result = NULL;
    662   do {
    663     result = fopen(filename.value().c_str(), mode);
    664   } while (!result && errno == EINTR);
    665   return result;
    666 }
    667 
    668 // NaCl doesn't implement system calls to open files directly.
    669 #if !defined(OS_NACL)
    670 FILE* FileToFILE(File file, const char* mode) {
    671   FILE* stream = fdopen(file.GetPlatformFile(), mode);
    672   if (stream)
    673     file.TakePlatformFile();
    674   return stream;
    675 }
    676 #endif  // !defined(OS_NACL)
    677 
    678 int ReadFile(const FilePath& filename, char* data, int max_size) {
    679   ThreadRestrictions::AssertIOAllowed();
    680   int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY));
    681   if (fd < 0)
    682     return -1;
    683 
    684   ssize_t bytes_read = HANDLE_EINTR(read(fd, data, max_size));
    685   if (IGNORE_EINTR(close(fd)) < 0)
    686     return -1;
    687   return bytes_read;
    688 }
    689 
    690 int WriteFile(const FilePath& filename, const char* data, int size) {
    691   ThreadRestrictions::AssertIOAllowed();
    692   int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));
    693   if (fd < 0)
    694     return -1;
    695 
    696   int bytes_written = WriteFileDescriptor(fd, data, size) ? size : -1;
    697   if (IGNORE_EINTR(close(fd)) < 0)
    698     return -1;
    699   return bytes_written;
    700 }
    701 
    702 bool WriteFileDescriptor(const int fd, const char* data, int size) {
    703   // Allow for partial writes.
    704   ssize_t bytes_written_total = 0;
    705   for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
    706        bytes_written_total += bytes_written_partial) {
    707     bytes_written_partial =
    708         HANDLE_EINTR(write(fd, data + bytes_written_total,
    709                            size - bytes_written_total));
    710     if (bytes_written_partial < 0)
    711       return false;
    712   }
    713 
    714   return true;
    715 }
    716 
    717 #if !defined(OS_NACL_NONSFI)
    718 
    719 bool AppendToFile(const FilePath& filename, const char* data, int size) {
    720   ThreadRestrictions::AssertIOAllowed();
    721   bool ret = true;
    722   int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND));
    723   if (fd < 0) {
    724     VPLOG(1) << "Unable to create file " << filename.value();
    725     return false;
    726   }
    727 
    728   // This call will either write all of the data or return false.
    729   if (!WriteFileDescriptor(fd, data, size)) {
    730     VPLOG(1) << "Error while writing to file " << filename.value();
    731     ret = false;
    732   }
    733 
    734   if (IGNORE_EINTR(close(fd)) < 0) {
    735     VPLOG(1) << "Error while closing file " << filename.value();
    736     return false;
    737   }
    738 
    739   return ret;
    740 }
    741 
    742 // Gets the current working directory for the process.
    743 bool GetCurrentDirectory(FilePath* dir) {
    744   // getcwd can return ENOENT, which implies it checks against the disk.
    745   ThreadRestrictions::AssertIOAllowed();
    746 
    747   char system_buffer[PATH_MAX] = "";
    748   if (!getcwd(system_buffer, sizeof(system_buffer))) {
    749     NOTREACHED();
    750     return false;
    751   }
    752   *dir = FilePath(system_buffer);
    753   return true;
    754 }
    755 
    756 // Sets the current working directory for the process.
    757 bool SetCurrentDirectory(const FilePath& path) {
    758   ThreadRestrictions::AssertIOAllowed();
    759   int ret = chdir(path.value().c_str());
    760   return !ret;
    761 }
    762 
    763 bool VerifyPathControlledByUser(const FilePath& base,
    764                                 const FilePath& path,
    765                                 uid_t owner_uid,
    766                                 const std::set<gid_t>& group_gids) {
    767   if (base != path && !base.IsParent(path)) {
    768      DLOG(ERROR) << "|base| must be a subdirectory of |path|.  base = \""
    769                  << base.value() << "\", path = \"" << path.value() << "\"";
    770      return false;
    771   }
    772 
    773   std::vector<FilePath::StringType> base_components;
    774   std::vector<FilePath::StringType> path_components;
    775 
    776   base.GetComponents(&base_components);
    777   path.GetComponents(&path_components);
    778 
    779   std::vector<FilePath::StringType>::const_iterator ib, ip;
    780   for (ib = base_components.begin(), ip = path_components.begin();
    781        ib != base_components.end(); ++ib, ++ip) {
    782     // |base| must be a subpath of |path|, so all components should match.
    783     // If these CHECKs fail, look at the test that base is a parent of
    784     // path at the top of this function.
    785     DCHECK(ip != path_components.end());
    786     DCHECK(*ip == *ib);
    787   }
    788 
    789   FilePath current_path = base;
    790   if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids))
    791     return false;
    792 
    793   for (; ip != path_components.end(); ++ip) {
    794     current_path = current_path.Append(*ip);
    795     if (!VerifySpecificPathControlledByUser(
    796             current_path, owner_uid, group_gids))
    797       return false;
    798   }
    799   return true;
    800 }
    801 
    802 #if defined(OS_MACOSX) && !defined(OS_IOS)
    803 bool VerifyPathControlledByAdmin(const FilePath& path) {
    804   const unsigned kRootUid = 0;
    805   const FilePath kFileSystemRoot("/");
    806 
    807   // The name of the administrator group on mac os.
    808   const char* const kAdminGroupNames[] = {
    809     "admin",
    810     "wheel"
    811   };
    812 
    813   // Reading the groups database may touch the file system.
    814   ThreadRestrictions::AssertIOAllowed();
    815 
    816   std::set<gid_t> allowed_group_ids;
    817   for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) {
    818     struct group *group_record = getgrnam(kAdminGroupNames[i]);
    819     if (!group_record) {
    820       DPLOG(ERROR) << "Could not get the group ID of group \""
    821                    << kAdminGroupNames[i] << "\".";
    822       continue;
    823     }
    824 
    825     allowed_group_ids.insert(group_record->gr_gid);
    826   }
    827 
    828   return VerifyPathControlledByUser(
    829       kFileSystemRoot, path, kRootUid, allowed_group_ids);
    830 }
    831 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
    832 
    833 int GetMaximumPathComponentLength(const FilePath& path) {
    834   ThreadRestrictions::AssertIOAllowed();
    835   return pathconf(path.value().c_str(), _PC_NAME_MAX);
    836 }
    837 
    838 #if !defined(OS_ANDROID)
    839 // This is implemented in file_util_android.cc for that platform.
    840 bool GetShmemTempDir(bool executable, FilePath* path) {
    841 #if defined(OS_LINUX)
    842   bool use_dev_shm = true;
    843   if (executable) {
    844     static const bool s_dev_shm_executable = DetermineDevShmExecutable();
    845     use_dev_shm = s_dev_shm_executable;
    846   }
    847   if (use_dev_shm) {
    848     *path = FilePath("/dev/shm");
    849     return true;
    850   }
    851 #else
    852   (void)executable;  // avoid unused warning when !defined(OS_LINUX)
    853 #endif
    854   return GetTempDir(path);
    855 }
    856 #endif  // !defined(OS_ANDROID)
    857 
    858 #if !defined(OS_MACOSX)
    859 // Mac has its own implementation, this is for all other Posix systems.
    860 bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
    861   ThreadRestrictions::AssertIOAllowed();
    862   File infile;
    863 #if defined(OS_ANDROID)
    864   if (from_path.IsContentUri()) {
    865     infile = OpenContentUriForRead(from_path);
    866   } else {
    867     infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ);
    868   }
    869 #else
    870   infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ);
    871 #endif
    872   if (!infile.IsValid())
    873     return false;
    874 
    875   File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS);
    876   if (!outfile.IsValid())
    877     return false;
    878 
    879   const size_t kBufferSize = 32768;
    880   std::vector<char> buffer(kBufferSize);
    881   bool result = true;
    882 
    883   while (result) {
    884     ssize_t bytes_read = infile.ReadAtCurrentPos(&buffer[0], buffer.size());
    885     if (bytes_read < 0) {
    886       result = false;
    887       break;
    888     }
    889     if (bytes_read == 0)
    890       break;
    891     // Allow for partial writes
    892     ssize_t bytes_written_per_read = 0;
    893     do {
    894       ssize_t bytes_written_partial = outfile.WriteAtCurrentPos(
    895           &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read);
    896       if (bytes_written_partial < 0) {
    897         result = false;
    898         break;
    899       }
    900       bytes_written_per_read += bytes_written_partial;
    901     } while (bytes_written_per_read < bytes_read);
    902   }
    903 
    904   return result;
    905 }
    906 #endif  // !defined(OS_MACOSX)
    907 
    908 // -----------------------------------------------------------------------------
    909 
    910 namespace internal {
    911 
    912 bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
    913   ThreadRestrictions::AssertIOAllowed();
    914   // Windows compatibility: if to_path exists, from_path and to_path
    915   // must be the same type, either both files, or both directories.
    916   stat_wrapper_t to_file_info;
    917   if (CallStat(to_path.value().c_str(), &to_file_info) == 0) {
    918     stat_wrapper_t from_file_info;
    919     if (CallStat(from_path.value().c_str(), &from_file_info) == 0) {
    920       if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))
    921         return false;
    922     } else {
    923       return false;
    924     }
    925   }
    926 
    927   if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
    928     return true;
    929 
    930   if (!CopyDirectory(from_path, to_path, true))
    931     return false;
    932 
    933   DeleteFile(from_path, true);
    934   return true;
    935 }
    936 
    937 }  // namespace internal
    938 
    939 #endif  // !defined(OS_NACL_NONSFI)
    940 }  // namespace base
    941