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