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