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/mman.h>
17 #include <sys/param.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #include <time.h>
21 #include <unistd.h>
22 
23 #include "base/base_switches.h"
24 #include "base/bits.h"
25 #include "base/command_line.h"
26 #include "base/containers/stack.h"
27 #include "base/environment.h"
28 #include "base/files/file_enumerator.h"
29 #include "base/files/file_path.h"
30 #include "base/files/scoped_file.h"
31 #include "base/logging.h"
32 #include "base/memory/singleton.h"
33 #include "base/numerics/safe_conversions.h"
34 #include "base/path_service.h"
35 #include "base/posix/eintr_wrapper.h"
36 #include "base/stl_util.h"
37 #include "base/strings/strcat.h"
38 #include "base/strings/string_piece.h"
39 #include "base/strings/string_split.h"
40 #include "base/strings/string_util.h"
41 #include "base/strings/stringprintf.h"
42 #include "base/strings/sys_string_conversions.h"
43 #include "base/strings/utf_string_conversions.h"
44 #include "base/system/sys_info.h"
45 #include "base/threading/scoped_blocking_call.h"
46 #include "base/time/time.h"
47 #include "build/branding_buildflags.h"
48 #include "build/build_config.h"
49 #include "build/chromeos_buildflags.h"
50 
51 #if defined(OS_APPLE)
52 #include <AvailabilityMacros.h>
53 #include "base/mac/foundation_util.h"
54 #endif
55 
56 #if defined(OS_ANDROID)
57 #include "base/android/content_uri_utils.h"
58 #include "base/os_compat_android.h"
59 #endif
60 
61 #if !defined(OS_IOS)
62 #include <grp.h>
63 #endif
64 
65 // We need to do this on AIX due to some inconsistencies in how AIX
66 // handles XOPEN_SOURCE and ALL_SOURCE.
67 #if defined(OS_AIX)
68 extern "C" char* mkdtemp(char* path);
69 #endif
70 
71 namespace base {
72 
73 namespace {
74 
75 #if !defined(OS_NACL_NONSFI)
76 // Helper for VerifyPathControlledByUser.
VerifySpecificPathControlledByUser(const FilePath & path,uid_t owner_uid,const std::set<gid_t> & group_gids)77 bool VerifySpecificPathControlledByUser(const FilePath& path,
78                                         uid_t owner_uid,
79                                         const std::set<gid_t>& group_gids) {
80   stat_wrapper_t stat_info;
81   if (File::Lstat(path.value().c_str(), &stat_info) != 0) {
82     DPLOG(ERROR) << "Failed to get information on path "
83                  << path.value();
84     return false;
85   }
86 
87   if (S_ISLNK(stat_info.st_mode)) {
88     DLOG(ERROR) << "Path " << path.value() << " is a symbolic link.";
89     return false;
90   }
91 
92   if (stat_info.st_uid != owner_uid) {
93     DLOG(ERROR) << "Path " << path.value() << " is owned by the wrong user.";
94     return false;
95   }
96 
97   if ((stat_info.st_mode & S_IWGRP) &&
98       !Contains(group_gids, stat_info.st_gid)) {
99     DLOG(ERROR) << "Path " << path.value()
100                 << " is writable by an unprivileged group.";
101     return false;
102   }
103 
104   if (stat_info.st_mode & S_IWOTH) {
105     DLOG(ERROR) << "Path " << path.value() << " is writable by any user.";
106     return false;
107   }
108 
109   return true;
110 }
111 
GetTempTemplate()112 base::FilePath GetTempTemplate() {
113   return FormatTemporaryFileName("XXXXXX");
114 }
115 
AdvanceEnumeratorWithStat(FileEnumerator * traversal,FilePath * out_next_path,stat_wrapper_t * out_next_stat)116 bool AdvanceEnumeratorWithStat(FileEnumerator* traversal,
117                                FilePath* out_next_path,
118                                stat_wrapper_t* out_next_stat) {
119   DCHECK(out_next_path);
120   DCHECK(out_next_stat);
121   *out_next_path = traversal->Next();
122   if (out_next_path->empty())
123     return false;
124 
125   *out_next_stat = traversal->GetInfo().stat();
126   return true;
127 }
128 
CopyFileContents(File * infile,File * outfile)129 bool CopyFileContents(File* infile, File* outfile) {
130   static constexpr size_t kBufferSize = 32768;
131   std::vector<char> buffer(kBufferSize);
132 
133   for (;;) {
134     ssize_t bytes_read = infile->ReadAtCurrentPos(buffer.data(), buffer.size());
135     if (bytes_read < 0)
136       return false;
137     if (bytes_read == 0)
138       return true;
139     // Allow for partial writes
140     ssize_t bytes_written_per_read = 0;
141     do {
142       ssize_t bytes_written_partial = outfile->WriteAtCurrentPos(
143           &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read);
144       if (bytes_written_partial < 0)
145         return false;
146 
147       bytes_written_per_read += bytes_written_partial;
148     } while (bytes_written_per_read < bytes_read);
149   }
150 
151   NOTREACHED();
152   return false;
153 }
154 
DoCopyDirectory(const FilePath & from_path,const FilePath & to_path,bool recursive,bool open_exclusive)155 bool DoCopyDirectory(const FilePath& from_path,
156                      const FilePath& to_path,
157                      bool recursive,
158                      bool open_exclusive) {
159   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
160   // Some old callers of CopyDirectory want it to support wildcards.
161   // After some discussion, we decided to fix those callers.
162   // Break loudly here if anyone tries to do this.
163   DCHECK(to_path.value().find('*') == std::string::npos);
164   DCHECK(from_path.value().find('*') == std::string::npos);
165 
166   if (from_path.value().size() >= PATH_MAX) {
167     return false;
168   }
169 
170   // This function does not properly handle destinations within the source
171   FilePath real_to_path = to_path;
172   if (PathExists(real_to_path))
173     real_to_path = MakeAbsoluteFilePath(real_to_path);
174   else
175     real_to_path = MakeAbsoluteFilePath(real_to_path.DirName());
176   if (real_to_path.empty())
177     return false;
178 
179   FilePath real_from_path = MakeAbsoluteFilePath(from_path);
180   if (real_from_path.empty())
181     return false;
182   if (real_to_path == real_from_path || real_from_path.IsParent(real_to_path))
183     return false;
184 
185   int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS;
186   if (recursive)
187     traverse_type |= FileEnumerator::DIRECTORIES;
188   FileEnumerator traversal(from_path, recursive, traverse_type);
189 
190   // We have to mimic windows behavior here. |to_path| may not exist yet,
191   // start the loop with |to_path|.
192   stat_wrapper_t from_stat;
193   FilePath current = from_path;
194   if (File::Stat(from_path.value().c_str(), &from_stat) < 0) {
195     DPLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
196                  << from_path.value();
197     return false;
198   }
199   FilePath from_path_base = from_path;
200   if (recursive && DirectoryExists(to_path)) {
201     // If the destination already exists and is a directory, then the
202     // top level of source needs to be copied.
203     from_path_base = from_path.DirName();
204   }
205 
206   // The Windows version of this function assumes that non-recursive calls
207   // will always have a directory for from_path.
208   // TODO(maruel): This is not necessary anymore.
209   DCHECK(recursive || S_ISDIR(from_stat.st_mode));
210 
211   do {
212     // current is the source path, including from_path, so append
213     // the suffix after from_path to to_path to create the target_path.
214     FilePath target_path(to_path);
215     if (from_path_base != current &&
216         !from_path_base.AppendRelativePath(current, &target_path)) {
217       return false;
218     }
219 
220     if (S_ISDIR(from_stat.st_mode)) {
221       mode_t mode = (from_stat.st_mode & 01777) | S_IRUSR | S_IXUSR | S_IWUSR;
222       if (mkdir(target_path.value().c_str(), mode) == 0)
223         continue;
224       if (errno == EEXIST && !open_exclusive)
225         continue;
226 
227       DPLOG(ERROR) << "CopyDirectory() couldn't create directory: "
228                    << target_path.value();
229       return false;
230     }
231 
232     if (!S_ISREG(from_stat.st_mode)) {
233       DLOG(WARNING) << "CopyDirectory() skipping non-regular file: "
234                     << current.value();
235       continue;
236     }
237 
238     // Add O_NONBLOCK so we can't block opening a pipe.
239     File infile(open(current.value().c_str(), O_RDONLY | O_NONBLOCK));
240     if (!infile.IsValid()) {
241       DPLOG(ERROR) << "CopyDirectory() couldn't open file: " << current.value();
242       return false;
243     }
244 
245     stat_wrapper_t stat_at_use;
246     if (File::Fstat(infile.GetPlatformFile(), &stat_at_use) < 0) {
247       DPLOG(ERROR) << "CopyDirectory() couldn't stat file: " << current.value();
248       return false;
249     }
250 
251     if (!S_ISREG(stat_at_use.st_mode)) {
252       DLOG(WARNING) << "CopyDirectory() skipping non-regular file: "
253                     << current.value();
254       continue;
255     }
256 
257     int open_flags = O_WRONLY | O_CREAT;
258     // If |open_exclusive| is set then we should always create the destination
259     // file, so O_NONBLOCK is not necessary to ensure we don't block on the
260     // open call for the target file below, and since the destination will
261     // always be a regular file it wouldn't affect the behavior of the
262     // subsequent write calls anyway.
263     if (open_exclusive)
264       open_flags |= O_EXCL;
265     else
266       open_flags |= O_TRUNC | O_NONBLOCK;
267     // Each platform has different default file opening modes for CopyFile which
268     // we want to replicate here. On OS X, we use copyfile(3) which takes the
269     // source file's permissions into account. On the other platforms, we just
270     // use the base::File constructor. On Chrome OS, base::File uses a different
271     // set of permissions than it does on other POSIX platforms.
272 #if defined(OS_APPLE)
273     int mode = 0600 | (stat_at_use.st_mode & 0177);
274 #elif defined(OS_CHROMEOS) || BUILDFLAG(IS_LACROS)
275     int mode = 0644;
276 #else
277     int mode = 0600;
278 #endif
279     File outfile(open(target_path.value().c_str(), open_flags, mode));
280     if (!outfile.IsValid()) {
281       DPLOG(ERROR) << "CopyDirectory() couldn't create file: "
282                    << target_path.value();
283       return false;
284     }
285 
286     if (!CopyFileContents(&infile, &outfile)) {
287       DLOG(ERROR) << "CopyDirectory() couldn't copy file: " << current.value();
288       return false;
289     }
290   } while (AdvanceEnumeratorWithStat(&traversal, &current, &from_stat));
291 
292   return true;
293 }
294 
295 // TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
296 // which works both with and without the recursive flag.  I'm not sure we need
297 // that functionality. If not, remove from file_util_win.cc, otherwise add it
298 // here.
DoDeleteFile(const FilePath & path,bool recursive)299 bool DoDeleteFile(const FilePath& path, bool recursive) {
300   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
301 
302 #if defined(OS_ANDROID)
303   if (path.IsContentUri())
304     return DeleteContentUri(path);
305 #endif  // defined(OS_ANDROID)
306 
307   const char* path_str = path.value().c_str();
308   stat_wrapper_t file_info;
309   if (File::Lstat(path_str, &file_info) != 0) {
310     // The Windows version defines this condition as success.
311     return (errno == ENOENT || errno == ENOTDIR);
312   }
313   if (!S_ISDIR(file_info.st_mode))
314     return (unlink(path_str) == 0);
315   if (!recursive)
316     return (rmdir(path_str) == 0);
317 
318   bool success = true;
319   stack<std::string> directories;
320   directories.push(path.value());
321   FileEnumerator traversal(path, true,
322       FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
323       FileEnumerator::SHOW_SYM_LINKS);
324   for (FilePath current = traversal.Next(); !current.empty();
325        current = traversal.Next()) {
326     if (traversal.GetInfo().IsDirectory())
327       directories.push(current.value());
328     else
329       success &= (unlink(current.value().c_str()) == 0);
330   }
331 
332   while (!directories.empty()) {
333     FilePath dir = FilePath(directories.top());
334     directories.pop();
335     success &= (rmdir(dir.value().c_str()) == 0);
336   }
337   return success;
338 }
339 #endif  // !defined(OS_NACL_NONSFI)
340 
341 #if !defined(OS_APPLE)
342 // Appends |mode_char| to |mode| before the optional character set encoding; see
343 // https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for
344 // details.
AppendModeCharacter(StringPiece mode,char mode_char)345 std::string AppendModeCharacter(StringPiece mode, char mode_char) {
346   std::string result(mode.as_string());
347   size_t comma_pos = result.find(',');
348   result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1,
349                 mode_char);
350   return result;
351 }
352 #endif
353 
354 }  // namespace
355 
356 #if !defined(OS_NACL_NONSFI)
MakeAbsoluteFilePath(const FilePath & input)357 FilePath MakeAbsoluteFilePath(const FilePath& input) {
358   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
359   char full_path[PATH_MAX];
360   if (realpath(input.value().c_str(), full_path) == nullptr)
361     return FilePath();
362   return FilePath(full_path);
363 }
364 
DeleteFile(const FilePath & path)365 bool DeleteFile(const FilePath& path) {
366   return DoDeleteFile(path, /*recursive=*/false);
367 }
368 
DeletePathRecursively(const FilePath & path)369 bool DeletePathRecursively(const FilePath& path) {
370   return DoDeleteFile(path, /*recursive=*/true);
371 }
372 
ReplaceFile(const FilePath & from_path,const FilePath & to_path,File::Error * error)373 bool ReplaceFile(const FilePath& from_path,
374                  const FilePath& to_path,
375                  File::Error* error) {
376   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
377   if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
378     return true;
379   if (error)
380     *error = File::GetLastFileError();
381   return false;
382 }
383 
CopyDirectory(const FilePath & from_path,const FilePath & to_path,bool recursive)384 bool CopyDirectory(const FilePath& from_path,
385                    const FilePath& to_path,
386                    bool recursive) {
387   return DoCopyDirectory(from_path, to_path, recursive, false);
388 }
389 
CopyDirectoryExcl(const FilePath & from_path,const FilePath & to_path,bool recursive)390 bool CopyDirectoryExcl(const FilePath& from_path,
391                        const FilePath& to_path,
392                        bool recursive) {
393   return DoCopyDirectory(from_path, to_path, recursive, true);
394 }
395 #endif  // !defined(OS_NACL_NONSFI)
396 
CreatePipe(ScopedFD * read_fd,ScopedFD * write_fd,bool non_blocking)397 bool CreatePipe(ScopedFD* read_fd, ScopedFD* write_fd, bool non_blocking) {
398   int fds[2];
399   bool created =
400       non_blocking ? CreateLocalNonBlockingPipe(fds) : (0 == pipe(fds));
401   if (!created)
402     return false;
403   read_fd->reset(fds[0]);
404   write_fd->reset(fds[1]);
405   return true;
406 }
407 
CreateLocalNonBlockingPipe(int fds[2])408 bool CreateLocalNonBlockingPipe(int fds[2]) {
409 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
410   return pipe2(fds, O_CLOEXEC | O_NONBLOCK) == 0;
411 #else
412   int raw_fds[2];
413   if (pipe(raw_fds) != 0)
414     return false;
415   ScopedFD fd_out(raw_fds[0]);
416   ScopedFD fd_in(raw_fds[1]);
417   if (!SetCloseOnExec(fd_out.get()))
418     return false;
419   if (!SetCloseOnExec(fd_in.get()))
420     return false;
421   if (!SetNonBlocking(fd_out.get()))
422     return false;
423   if (!SetNonBlocking(fd_in.get()))
424     return false;
425   fds[0] = fd_out.release();
426   fds[1] = fd_in.release();
427   return true;
428 #endif
429 }
430 
SetNonBlocking(int fd)431 bool SetNonBlocking(int fd) {
432   const int flags = fcntl(fd, F_GETFL);
433   if (flags == -1)
434     return false;
435   if (flags & O_NONBLOCK)
436     return true;
437   if (HANDLE_EINTR(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1)
438     return false;
439   return true;
440 }
441 
SetCloseOnExec(int fd)442 bool SetCloseOnExec(int fd) {
443 #if defined(OS_NACL_NONSFI)
444   const int flags = 0;
445 #else
446   const int flags = fcntl(fd, F_GETFD);
447   if (flags == -1)
448     return false;
449   if (flags & FD_CLOEXEC)
450     return true;
451 #endif  // defined(OS_NACL_NONSFI)
452   if (HANDLE_EINTR(fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1)
453     return false;
454   return true;
455 }
456 
PathExists(const FilePath & path)457 bool PathExists(const FilePath& path) {
458   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
459 #if defined(OS_ANDROID)
460   if (path.IsContentUri()) {
461     return ContentUriExists(path);
462   }
463 #endif
464   return access(path.value().c_str(), F_OK) == 0;
465 }
466 
467 #if !defined(OS_NACL_NONSFI)
PathIsReadable(const FilePath & path)468 bool PathIsReadable(const FilePath& path) {
469   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
470   return access(path.value().c_str(), R_OK) == 0;
471 }
472 #endif  // !defined(OS_NACL_NONSFI)
473 
474 #if !defined(OS_NACL_NONSFI)
PathIsWritable(const FilePath & path)475 bool PathIsWritable(const FilePath& path) {
476   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
477   return access(path.value().c_str(), W_OK) == 0;
478 }
479 #endif  // !defined(OS_NACL_NONSFI)
480 
DirectoryExists(const FilePath & path)481 bool DirectoryExists(const FilePath& path) {
482   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
483   stat_wrapper_t file_info;
484   if (File::Stat(path.value().c_str(), &file_info) != 0)
485     return false;
486   return S_ISDIR(file_info.st_mode);
487 }
488 
ReadFromFD(int fd,char * buffer,size_t bytes)489 bool ReadFromFD(int fd, char* buffer, size_t bytes) {
490   size_t total_read = 0;
491   while (total_read < bytes) {
492     ssize_t bytes_read =
493         HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
494     if (bytes_read <= 0)
495       break;
496     total_read += bytes_read;
497   }
498   return total_read == bytes;
499 }
500 
501 #if !defined(OS_NACL_NONSFI)
502 
CreateAndOpenFdForTemporaryFileInDir(const FilePath & directory,FilePath * path)503 ScopedFD CreateAndOpenFdForTemporaryFileInDir(const FilePath& directory,
504                                               FilePath* path) {
505   ScopedBlockingCall scoped_blocking_call(
506       FROM_HERE,
507       BlockingType::MAY_BLOCK);  // For call to mkstemp().
508   *path = directory.Append(GetTempTemplate());
509   const std::string& tmpdir_string = path->value();
510   // this should be OK since mkstemp just replaces characters in place
511   char* buffer = const_cast<char*>(tmpdir_string.c_str());
512 
513   return ScopedFD(HANDLE_EINTR(mkstemp(buffer)));
514 }
515 
516 #if !defined(OS_FUCHSIA)
CreateSymbolicLink(const FilePath & target_path,const FilePath & symlink_path)517 bool CreateSymbolicLink(const FilePath& target_path,
518                         const FilePath& symlink_path) {
519   DCHECK(!symlink_path.empty());
520   DCHECK(!target_path.empty());
521   return ::symlink(target_path.value().c_str(),
522                    symlink_path.value().c_str()) != -1;
523 }
524 
ReadSymbolicLink(const FilePath & symlink_path,FilePath * target_path)525 bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) {
526   DCHECK(!symlink_path.empty());
527   DCHECK(target_path);
528   char buf[PATH_MAX];
529   ssize_t count =
530       ::readlink(symlink_path.value().c_str(), buf, base::size(buf));
531 
532 #if defined(OS_ANDROID) && defined(__LP64__)
533   // A few 64-bit Android L/M devices return INT_MAX instead of -1 here for
534   // errors; this is related to bionic's (incorrect) definition of ssize_t as
535   // being long int instead of int. Cast it so the compiler generates the
536   // comparison we want here. https://crbug.com/1101940
537   bool error = static_cast<int32_t>(count) <= 0;
538 #else
539   bool error = count <= 0;
540 #endif
541 
542   if (error) {
543     target_path->clear();
544     return false;
545   }
546 
547   *target_path = FilePath(FilePath::StringType(buf, count));
548   return true;
549 }
550 
GetPosixFilePermissions(const FilePath & path,int * mode)551 bool GetPosixFilePermissions(const FilePath& path, int* mode) {
552   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
553   DCHECK(mode);
554 
555   stat_wrapper_t file_info;
556   // Uses stat(), because on symbolic link, lstat() does not return valid
557   // permission bits in st_mode
558   if (File::Stat(path.value().c_str(), &file_info) != 0)
559     return false;
560 
561   *mode = file_info.st_mode & FILE_PERMISSION_MASK;
562   return true;
563 }
564 
SetPosixFilePermissions(const FilePath & path,int mode)565 bool SetPosixFilePermissions(const FilePath& path,
566                              int mode) {
567   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
568   DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0);
569 
570   // Calls stat() so that we can preserve the higher bits like S_ISGID.
571   stat_wrapper_t stat_buf;
572   if (File::Stat(path.value().c_str(), &stat_buf) != 0)
573     return false;
574 
575   // Clears the existing permission bits, and adds the new ones.
576   mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK;
577   updated_mode_bits |= mode & FILE_PERMISSION_MASK;
578 
579   if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0)
580     return false;
581 
582   return true;
583 }
584 
ExecutableExistsInPath(Environment * env,const FilePath::StringType & executable)585 bool ExecutableExistsInPath(Environment* env,
586                             const FilePath::StringType& executable) {
587   std::string path;
588   if (!env->GetVar("PATH", &path)) {
589     LOG(ERROR) << "No $PATH variable. Assuming no " << executable << ".";
590     return false;
591   }
592 
593   for (const StringPiece& cur_path :
594        SplitStringPiece(path, ":", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
595     FilePath file(cur_path);
596     int permissions;
597     if (GetPosixFilePermissions(file.Append(executable), &permissions) &&
598         (permissions & FILE_PERMISSION_EXECUTE_BY_USER))
599       return true;
600   }
601   return false;
602 }
603 
604 #endif  // !OS_FUCHSIA
605 
606 #if !defined(OS_APPLE)
607 // This is implemented in file_util_mac.mm for Mac.
GetTempDir(FilePath * path)608 bool GetTempDir(FilePath* path) {
609   const char* tmp = getenv("TMPDIR");
610   if (tmp) {
611     *path = FilePath(tmp);
612     return true;
613   }
614 
615 #if defined(OS_ANDROID)
616   return PathService::Get(DIR_CACHE, path);
617 #else
618   *path = FilePath("/tmp");
619   return true;
620 #endif
621 }
622 #endif  // !defined(OS_APPLE)
623 
624 #if !defined(OS_APPLE)  // Mac implementation is in file_util_mac.mm.
GetHomeDir()625 FilePath GetHomeDir() {
626 #if defined(OS_CHROMEOS) || BUILDFLAG(IS_LACROS)
627   if (SysInfo::IsRunningOnChromeOS()) {
628     // On Chrome OS chrome::DIR_USER_DATA is overridden with a primary user
629     // homedir once it becomes available. Return / as the safe option.
630     return FilePath("/");
631   }
632 #endif
633 
634   const char* home_dir = getenv("HOME");
635   if (home_dir && home_dir[0])
636     return FilePath(home_dir);
637 
638 #if defined(OS_ANDROID)
639   DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented.";
640 #endif
641 
642   FilePath rv;
643   if (GetTempDir(&rv))
644     return rv;
645 
646   // Last resort.
647   return FilePath("/tmp");
648 }
649 #endif  // !defined(OS_APPLE)
650 
CreateAndOpenTemporaryFileInDir(const FilePath & dir,FilePath * temp_file)651 File CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
652   // For call to close() inside ScopedFD.
653   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
654   return File(CreateAndOpenFdForTemporaryFileInDir(dir, temp_file));
655 }
656 
CreateTemporaryFileInDir(const FilePath & dir,FilePath * temp_file)657 bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
658   // For call to close() inside ScopedFD.
659   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
660   ScopedFD fd = CreateAndOpenFdForTemporaryFileInDir(dir, temp_file);
661   return fd.is_valid();
662 }
663 
FormatTemporaryFileName(FilePath::StringPieceType identifier)664 FilePath FormatTemporaryFileName(FilePath::StringPieceType identifier) {
665 #if defined(OS_APPLE)
666   StringPiece prefix = base::mac::BaseBundleID();
667 #elif BUILDFLAG(GOOGLE_CHROME_BRANDING)
668   StringPiece prefix = "com.google.Chrome";
669 #else
670   StringPiece prefix = "org.chromium.Chromium";
671 #endif
672   return FilePath(StrCat({".", prefix, ".", identifier}));
673 }
674 
CreateAndOpenTemporaryStreamInDir(const FilePath & dir,FilePath * path)675 ScopedFILE CreateAndOpenTemporaryStreamInDir(const FilePath& dir,
676                                              FilePath* path) {
677   ScopedFD scoped_fd = CreateAndOpenFdForTemporaryFileInDir(dir, path);
678   if (!scoped_fd.is_valid())
679     return nullptr;
680 
681   int fd = scoped_fd.release();
682   FILE* file = fdopen(fd, "a+");
683   if (!file)
684     close(fd);
685   return ScopedFILE(file);
686 }
687 
CreateTemporaryDirInDirImpl(const FilePath & base_dir,const FilePath & name_tmpl,FilePath * new_dir)688 static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
689                                         const FilePath& name_tmpl,
690                                         FilePath* new_dir) {
691   ScopedBlockingCall scoped_blocking_call(
692       FROM_HERE, BlockingType::MAY_BLOCK);  // For call to mkdtemp().
693   DCHECK(EndsWith(name_tmpl.value(), "XXXXXX"))
694       << "Directory name template must end with \"XXXXXX\".";
695 
696   FilePath sub_dir = base_dir.Append(name_tmpl);
697   std::string sub_dir_string = sub_dir.value();
698 
699   // this should be OK since mkdtemp just replaces characters in place
700   char* buffer = const_cast<char*>(sub_dir_string.c_str());
701   char* dtemp = mkdtemp(buffer);
702   if (!dtemp) {
703     DPLOG(ERROR) << "mkdtemp";
704     return false;
705   }
706   *new_dir = FilePath(dtemp);
707   return true;
708 }
709 
CreateTemporaryDirInDir(const FilePath & base_dir,const FilePath::StringType & prefix,FilePath * new_dir)710 bool CreateTemporaryDirInDir(const FilePath& base_dir,
711                              const FilePath::StringType& prefix,
712                              FilePath* new_dir) {
713   FilePath::StringType mkdtemp_template = prefix;
714   mkdtemp_template.append("XXXXXX");
715   return CreateTemporaryDirInDirImpl(base_dir, FilePath(mkdtemp_template),
716                                      new_dir);
717 }
718 
CreateNewTempDirectory(const FilePath::StringType & prefix,FilePath * new_temp_path)719 bool CreateNewTempDirectory(const FilePath::StringType& prefix,
720                             FilePath* new_temp_path) {
721   FilePath tmpdir;
722   if (!GetTempDir(&tmpdir))
723     return false;
724 
725   return CreateTemporaryDirInDirImpl(tmpdir, GetTempTemplate(), new_temp_path);
726 }
727 
CreateDirectoryAndGetError(const FilePath & full_path,File::Error * error)728 bool CreateDirectoryAndGetError(const FilePath& full_path,
729                                 File::Error* error) {
730   ScopedBlockingCall scoped_blocking_call(
731       FROM_HERE, BlockingType::MAY_BLOCK);  // For call to mkdir().
732   std::vector<FilePath> subpaths;
733 
734   // Collect a list of all parent directories.
735   FilePath last_path = full_path;
736   subpaths.push_back(full_path);
737   for (FilePath path = full_path.DirName();
738        path.value() != last_path.value(); path = path.DirName()) {
739     subpaths.push_back(path);
740     last_path = path;
741   }
742 
743   // Iterate through the parents and create the missing ones.
744   for (auto i = subpaths.rbegin(); i != subpaths.rend(); ++i) {
745     if (DirectoryExists(*i))
746       continue;
747     if (mkdir(i->value().c_str(), 0700) == 0)
748       continue;
749     // Mkdir failed, but it might have failed with EEXIST, or some other error
750     // due to the directory appearing out of thin air. This can occur if
751     // two processes are trying to create the same file system tree at the same
752     // time. Check to see if it exists and make sure it is a directory.
753     int saved_errno = errno;
754     if (!DirectoryExists(*i)) {
755       if (error)
756         *error = File::OSErrorToFileError(saved_errno);
757       return false;
758     }
759   }
760   return true;
761 }
762 
763 // ReadFileToStringNonBlockingNonBlocking will read a file to a string. This
764 // method should only be used on files which are known to be non-blocking such
765 // as procfs or sysfs nodes. Additionally, the file is opened as O_NONBLOCK so
766 // it WILL NOT block even if opened on a blocking file. It will return true if
767 // the file read until EOF and it will return false otherwise, errno will remain
768 // set on error conditions. |ret| will be populated with the contents of the
769 // file.
ReadFileToStringNonBlocking(const base::FilePath & file,std::string * ret)770 bool ReadFileToStringNonBlocking(const base::FilePath& file, std::string* ret) {
771   DCHECK(ret);
772   ret->clear();
773 
774   base::ScopedFD fd(HANDLE_EINTR(
775       open(file.MaybeAsASCII().c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY)));
776   if (!fd.is_valid()) {
777     return false;
778   }
779 
780   ssize_t bytes_read = 0;
781   do {
782     char buf[4096];
783     bytes_read = HANDLE_EINTR(read(fd.get(), buf, sizeof(buf)));
784     if (bytes_read < 0) {
785       return false;
786     } else if (bytes_read > 0) {
787       ret->append(buf, bytes_read);
788     }
789   } while (bytes_read > 0);
790 
791   return true;
792 }
793 
NormalizeFilePath(const FilePath & path,FilePath * normalized_path)794 bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
795   FilePath real_path_result = MakeAbsoluteFilePath(path);
796   if (real_path_result.empty())
797     return false;
798 
799   // To be consistant with windows, fail if |real_path_result| is a
800   // directory.
801   if (DirectoryExists(real_path_result))
802     return false;
803 
804   *normalized_path = real_path_result;
805   return true;
806 }
807 
808 // TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks
809 // correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948
IsLink(const FilePath & file_path)810 bool IsLink(const FilePath& file_path) {
811   stat_wrapper_t st;
812   // If we can't lstat the file, it's safe to assume that the file won't at
813   // least be a 'followable' link.
814   if (File::Lstat(file_path.value().c_str(), &st) != 0)
815     return false;
816   return S_ISLNK(st.st_mode);
817 }
818 
GetFileInfo(const FilePath & file_path,File::Info * results)819 bool GetFileInfo(const FilePath& file_path, File::Info* results) {
820   stat_wrapper_t file_info;
821 #if defined(OS_ANDROID)
822   if (file_path.IsContentUri()) {
823     File file = OpenContentUriForRead(file_path);
824     if (!file.IsValid())
825       return false;
826     return file.GetInfo(results);
827   } else {
828 #endif  // defined(OS_ANDROID)
829     if (File::Stat(file_path.value().c_str(), &file_info) != 0)
830       return false;
831 #if defined(OS_ANDROID)
832   }
833 #endif  // defined(OS_ANDROID)
834 
835   results->FromStat(file_info);
836   return true;
837 }
838 #endif  // !defined(OS_NACL_NONSFI)
839 
OpenFile(const FilePath & filename,const char * mode)840 FILE* OpenFile(const FilePath& filename, const char* mode) {
841   // 'e' is unconditionally added below, so be sure there is not one already
842   // present before a comma in |mode|.
843   DCHECK(
844       strchr(mode, 'e') == nullptr ||
845       (strchr(mode, ',') != nullptr && strchr(mode, 'e') > strchr(mode, ',')));
846   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
847   FILE* result = nullptr;
848 #if defined(OS_APPLE)
849   // macOS does not provide a mode character to set O_CLOEXEC; see
850   // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/fopen.3.html.
851   const char* the_mode = mode;
852 #else
853   std::string mode_with_e(AppendModeCharacter(mode, 'e'));
854   const char* the_mode = mode_with_e.c_str();
855 #endif
856   do {
857     result = fopen(filename.value().c_str(), the_mode);
858   } while (!result && errno == EINTR);
859 #if defined(OS_APPLE)
860   // Mark the descriptor as close-on-exec.
861   if (result)
862     SetCloseOnExec(fileno(result));
863 #endif
864   return result;
865 }
866 
867 // NaCl doesn't implement system calls to open files directly.
868 #if !defined(OS_NACL)
FileToFILE(File file,const char * mode)869 FILE* FileToFILE(File file, const char* mode) {
870   FILE* stream = fdopen(file.GetPlatformFile(), mode);
871   if (stream)
872     file.TakePlatformFile();
873   return stream;
874 }
875 
FILEToFile(FILE * file_stream)876 File FILEToFile(FILE* file_stream) {
877   if (!file_stream)
878     return File();
879 
880   PlatformFile fd = fileno(file_stream);
881   DCHECK_NE(fd, -1);
882   ScopedPlatformFile other_fd(HANDLE_EINTR(dup(fd)));
883   if (!other_fd.is_valid())
884     return File(File::GetLastFileError());
885   return File(std::move(other_fd));
886 }
887 #endif  // !defined(OS_NACL)
888 
ReadFile(const FilePath & filename,char * data,int max_size)889 int ReadFile(const FilePath& filename, char* data, int max_size) {
890   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
891   int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY));
892   if (fd < 0)
893     return -1;
894 
895   ssize_t bytes_read = HANDLE_EINTR(read(fd, data, max_size));
896   if (IGNORE_EINTR(close(fd)) < 0)
897     return -1;
898   return bytes_read;
899 }
900 
WriteFile(const FilePath & filename,const char * data,int size)901 int WriteFile(const FilePath& filename, const char* data, int size) {
902   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
903   int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));
904   if (fd < 0)
905     return -1;
906 
907   int bytes_written = WriteFileDescriptor(fd, data, size) ? size : -1;
908   if (IGNORE_EINTR(close(fd)) < 0)
909     return -1;
910   return bytes_written;
911 }
912 
WriteFileDescriptor(const int fd,const char * data,int size)913 bool WriteFileDescriptor(const int fd, const char* data, int size) {
914   // Allow for partial writes.
915   ssize_t bytes_written_total = 0;
916   for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
917        bytes_written_total += bytes_written_partial) {
918     bytes_written_partial =
919         HANDLE_EINTR(write(fd, data + bytes_written_total,
920                            size - bytes_written_total));
921     if (bytes_written_partial < 0)
922       return false;
923   }
924 
925   return true;
926 }
927 
AllocateFileRegion(File * file,int64_t offset,size_t size)928 bool AllocateFileRegion(File* file, int64_t offset, size_t size) {
929   DCHECK(file);
930 
931   // Explicitly extend |file| to the maximum size. Zeros will fill the new
932   // space. It is assumed that the existing file is fully realized as
933   // otherwise the entire file would have to be read and possibly written.
934   const int64_t original_file_len = file->GetLength();
935   if (original_file_len < 0) {
936     DPLOG(ERROR) << "fstat " << file->GetPlatformFile();
937     return false;
938   }
939 
940   // Increase the actual length of the file, if necessary. This can fail if
941   // the disk is full and the OS doesn't support sparse files.
942   const int64_t new_file_len = offset + size;
943   if (!file->SetLength(std::max(original_file_len, new_file_len))) {
944     DPLOG(ERROR) << "ftruncate " << file->GetPlatformFile();
945     return false;
946   }
947 
948   // Realize the extent of the file so that it can't fail (and crash) later
949   // when trying to write to a memory page that can't be created. This can
950   // fail if the disk is full and the file is sparse.
951 
952   // First try the more effective platform-specific way of allocating the disk
953   // space. It can fail because the filesystem doesn't support it. In that case,
954   // use the manual method below.
955 
956 //
957 // We don't have posix_fallocate() or even fallocate()
958 //
959 #ifndef __DragonFly__
960 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
961 #if defined(OS_BSD)
962   if (HANDLE_EINTR(posix_fallocate(file->GetPlatformFile(), offset, size)) != -1)
963 #else
964   if (HANDLE_EINTR(fallocate(file->GetPlatformFile(), 0, offset, size)) != -1)
965 #endif
966     return true;
967   DPLOG(ERROR) << "fallocate";
968 #elif defined(OS_APPLE)
969   // MacOS doesn't support fallocate even though their new APFS filesystem
970   // does support sparse files. It does, however, have the functionality
971   // available via fcntl.
972   // See also: https://openradar.appspot.com/32720223
973   fstore_t params = {F_ALLOCATEALL, F_PEOFPOSMODE, offset, size, 0};
974   if (fcntl(file->GetPlatformFile(), F_PREALLOCATE, &params) != -1)
975     return true;
976   DPLOG(ERROR) << "F_PREALLOCATE";
977 #endif
978 
979 #endif	/* __DragonFly__ */
980 
981   // Manually realize the extended file by writing bytes to it at intervals.
982   int64_t block_size = 512;  // Start with something safe.
983   stat_wrapper_t statbuf;
984   if (File::Fstat(file->GetPlatformFile(), &statbuf) == 0 &&
985       statbuf.st_blksize > 0 && base::bits::IsPowerOfTwo(statbuf.st_blksize)) {
986     block_size = statbuf.st_blksize;
987   }
988 
989   // Write starting at the next block boundary after the old file length.
990   const int64_t extension_start =
991       base::bits::Align(original_file_len, block_size);
992   for (int64_t i = extension_start; i < new_file_len; i += block_size) {
993     char existing_byte;
994     if (HANDLE_EINTR(pread(file->GetPlatformFile(), &existing_byte, 1, i)) !=
995         1) {
996       return false;  // Can't read? Not viable.
997     }
998     if (existing_byte != 0) {
999       continue;  // Block has data so must already exist.
1000     }
1001     if (HANDLE_EINTR(pwrite(file->GetPlatformFile(), &existing_byte, 1, i)) !=
1002         1) {
1003       return false;  // Can't write? Not viable.
1004     }
1005   }
1006 
1007   return true;
1008 }
1009 
1010 #if !defined(OS_NACL_NONSFI)
1011 
AppendToFile(const FilePath & filename,const char * data,int size)1012 bool AppendToFile(const FilePath& filename, const char* data, int size) {
1013   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
1014   bool ret = true;
1015   int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND));
1016   if (fd < 0) {
1017     VPLOG(1) << "Unable to create file " << filename.value();
1018     return false;
1019   }
1020 
1021   // This call will either write all of the data or return false.
1022   if (!WriteFileDescriptor(fd, data, size)) {
1023     VPLOG(1) << "Error while writing to file " << filename.value();
1024     ret = false;
1025   }
1026 
1027   if (IGNORE_EINTR(close(fd)) < 0) {
1028     VPLOG(1) << "Error while closing file " << filename.value();
1029     return false;
1030   }
1031 
1032   return ret;
1033 }
1034 
GetCurrentDirectory(FilePath * dir)1035 bool GetCurrentDirectory(FilePath* dir) {
1036   // getcwd can return ENOENT, which implies it checks against the disk.
1037   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
1038 
1039   char system_buffer[PATH_MAX] = "";
1040   if (!getcwd(system_buffer, sizeof(system_buffer))) {
1041     NOTREACHED();
1042     return false;
1043   }
1044   *dir = FilePath(system_buffer);
1045   return true;
1046 }
1047 
SetCurrentDirectory(const FilePath & path)1048 bool SetCurrentDirectory(const FilePath& path) {
1049   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
1050   return chdir(path.value().c_str()) == 0;
1051 }
1052 
VerifyPathControlledByUser(const FilePath & base,const FilePath & path,uid_t owner_uid,const std::set<gid_t> & group_gids)1053 bool VerifyPathControlledByUser(const FilePath& base,
1054                                 const FilePath& path,
1055                                 uid_t owner_uid,
1056                                 const std::set<gid_t>& group_gids) {
1057   if (base != path && !base.IsParent(path)) {
1058      DLOG(ERROR) << "|base| must be a subdirectory of |path|.  base = \""
1059                  << base.value() << "\", path = \"" << path.value() << "\"";
1060      return false;
1061   }
1062 
1063   std::vector<FilePath::StringType> base_components;
1064   std::vector<FilePath::StringType> path_components;
1065 
1066   base.GetComponents(&base_components);
1067   path.GetComponents(&path_components);
1068 
1069   std::vector<FilePath::StringType>::const_iterator ib, ip;
1070   for (ib = base_components.begin(), ip = path_components.begin();
1071        ib != base_components.end(); ++ib, ++ip) {
1072     // |base| must be a subpath of |path|, so all components should match.
1073     // If these CHECKs fail, look at the test that base is a parent of
1074     // path at the top of this function.
1075     DCHECK(ip != path_components.end());
1076     DCHECK(*ip == *ib);
1077   }
1078 
1079   FilePath current_path = base;
1080   if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids))
1081     return false;
1082 
1083   for (; ip != path_components.end(); ++ip) {
1084     current_path = current_path.Append(*ip);
1085     if (!VerifySpecificPathControlledByUser(
1086             current_path, owner_uid, group_gids))
1087       return false;
1088   }
1089   return true;
1090 }
1091 
1092 #if defined(OS_MAC)
VerifyPathControlledByAdmin(const FilePath & path)1093 bool VerifyPathControlledByAdmin(const FilePath& path) {
1094   const unsigned kRootUid = 0;
1095   const FilePath kFileSystemRoot("/");
1096 
1097   // The name of the administrator group on mac os.
1098   const char* const kAdminGroupNames[] = {
1099     "admin",
1100     "wheel"
1101   };
1102 
1103   // Reading the groups database may touch the file system.
1104   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
1105 
1106   std::set<gid_t> allowed_group_ids;
1107   for (int i = 0, ie = base::size(kAdminGroupNames); i < ie; ++i) {
1108     struct group *group_record = getgrnam(kAdminGroupNames[i]);
1109     if (!group_record) {
1110       DPLOG(ERROR) << "Could not get the group ID of group \""
1111                    << kAdminGroupNames[i] << "\".";
1112       continue;
1113     }
1114 
1115     allowed_group_ids.insert(group_record->gr_gid);
1116   }
1117 
1118   return VerifyPathControlledByUser(
1119       kFileSystemRoot, path, kRootUid, allowed_group_ids);
1120 }
1121 #endif  // defined(OS_MAC)
1122 
GetMaximumPathComponentLength(const FilePath & path)1123 int GetMaximumPathComponentLength(const FilePath& path) {
1124 #if defined(OS_FUCHSIA)
1125   // Return a value we do not expect anyone ever to reach, but which is small
1126   // enough to guard against e.g. bugs causing multi-megabyte paths.
1127   return 1024;
1128 #else
1129   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
1130   return pathconf(path.value().c_str(), _PC_NAME_MAX);
1131 #endif
1132 }
1133 
1134 #if !defined(OS_ANDROID)
1135 // This is implemented in file_util_android.cc for that platform.
GetShmemTempDir(bool executable,FilePath * path)1136 bool GetShmemTempDir(bool executable, FilePath* path) {
1137 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_AIX)
1138   bool disable_dev_shm = false;
1139 #if !defined(OS_CHROMEOS) && !BUILDFLAG(IS_LACROS)
1140   disable_dev_shm = CommandLine::ForCurrentProcess()->HasSwitch(
1141       switches::kDisableDevShmUsage);
1142 #endif
1143   bool use_dev_shm = true;
1144   if (executable) {
1145     static const bool s_dev_shm_executable =
1146         IsPathExecutable(FilePath("/dev/shm"));
1147     use_dev_shm = s_dev_shm_executable;
1148   }
1149   if (use_dev_shm && !disable_dev_shm) {
1150     *path = FilePath("/dev/shm");
1151     return true;
1152   }
1153 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_AIX)
1154   return GetTempDir(path);
1155 }
1156 #endif  // !defined(OS_ANDROID)
1157 
1158 #if !defined(OS_APPLE)
1159 // Mac has its own implementation, this is for all other Posix systems.
CopyFile(const FilePath & from_path,const FilePath & to_path)1160 bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
1161   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
1162   File infile;
1163 #if defined(OS_ANDROID)
1164   if (from_path.IsContentUri()) {
1165     infile = OpenContentUriForRead(from_path);
1166   } else {
1167     infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ);
1168   }
1169 #else
1170   infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ);
1171 #endif
1172   if (!infile.IsValid())
1173     return false;
1174 
1175   File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS);
1176   if (!outfile.IsValid())
1177     return false;
1178 
1179   return CopyFileContents(&infile, &outfile);
1180 }
1181 #endif  // !defined(OS_APPLE)
1182 
PreReadFile(const FilePath & file_path,bool is_executable,int64_t max_bytes)1183 PrefetchResult PreReadFile(const FilePath& file_path,
1184                            bool is_executable,
1185                            int64_t max_bytes) {
1186   DCHECK_GE(max_bytes, 0);
1187 
1188   // posix_fadvise() is only available in the Android NDK in API 21+. Older
1189   // versions may have the required kernel support, but don't have enough usage
1190   // to justify backporting.
1191 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD) || \
1192     (defined(OS_ANDROID) && __ANDROID_API__ >= 21)
1193   File file(file_path, File::FLAG_OPEN | File::FLAG_READ);
1194   if (!file.IsValid())
1195     return PrefetchResult{PrefetchResultCode::kInvalidFile};
1196 
1197   if (max_bytes == 0) {
1198     // fadvise() pre-fetches the entire file when given a zero length.
1199     return PrefetchResult{PrefetchResultCode::kSuccess};
1200   }
1201 
1202   const PlatformFile fd = file.GetPlatformFile();
1203   const ::off_t len = base::saturated_cast<::off_t>(max_bytes);
1204   return posix_fadvise(fd, /*offset=*/0, len, POSIX_FADV_WILLNEED) == 0
1205              ? PrefetchResult{PrefetchResultCode::kSuccess}
1206              : PrefetchResult{PrefetchResultCode::kFastFailed};
1207 #elif defined(OS_APPLE)
1208   File file(file_path, File::FLAG_OPEN | File::FLAG_READ);
1209   if (!file.IsValid())
1210     return PrefetchResult{PrefetchResultCode::kInvalidFile};
1211 
1212   if (max_bytes == 0) {
1213     // fcntl(F_RDADVISE) fails when given a zero length.
1214     return PrefetchResult{PrefetchResultCode::kSuccess};
1215   }
1216 
1217   const PlatformFile fd = file.GetPlatformFile();
1218   ::radvisory read_advise_data = {
1219       .ra_offset = 0, .ra_count = base::saturated_cast<int>(max_bytes)};
1220   return fcntl(fd, F_RDADVISE, &read_advise_data) != -1
1221              ? PrefetchResult{PrefetchResultCode::kSuccess}
1222              : PrefetchResult{PrefetchResultCode::kFastFailed};
1223 #else
1224   return internal::PreReadFileSlow(file_path, max_bytes)
1225              ? PrefetchResult{PrefetchResultCode::kSlowSuccess}
1226              : PrefetchResult{PrefetchResultCode::kSlowFailed};
1227 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD) || (defined(OS_ANDROID) &&
1228         // __ANDROID_API__ >= 21)
1229 }
1230 
1231 // -----------------------------------------------------------------------------
1232 
1233 namespace internal {
1234 
MoveUnsafe(const FilePath & from_path,const FilePath & to_path)1235 bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
1236   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
1237   // Windows compatibility: if |to_path| exists, |from_path| and |to_path|
1238   // must be the same type, either both files, or both directories.
1239   stat_wrapper_t to_file_info;
1240   if (File::Stat(to_path.value().c_str(), &to_file_info) == 0) {
1241     stat_wrapper_t from_file_info;
1242     if (File::Stat(from_path.value().c_str(), &from_file_info) != 0)
1243       return false;
1244     if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))
1245       return false;
1246   }
1247 
1248   if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
1249     return true;
1250 
1251   if (!CopyDirectory(from_path, to_path, true))
1252     return false;
1253 
1254   DeletePathRecursively(from_path);
1255   return true;
1256 }
1257 
1258 }  // namespace internal
1259 
1260 #endif  // !defined(OS_NACL_NONSFI)
1261 
1262 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_AIX) || defined(OS_BSD)
IsPathExecutable(const FilePath & path)1263 BASE_EXPORT bool IsPathExecutable(const FilePath& path) {
1264   bool result = false;
1265   FilePath tmp_file_path;
1266 
1267   ScopedFD fd = CreateAndOpenFdForTemporaryFileInDir(path, &tmp_file_path);
1268   if (fd.is_valid()) {
1269     DeleteFile(tmp_file_path);
1270     long sysconf_result = sysconf(_SC_PAGESIZE);
1271     CHECK_GE(sysconf_result, 0);
1272     size_t pagesize = static_cast<size_t>(sysconf_result);
1273     CHECK_GE(sizeof(pagesize), sizeof(sysconf_result));
1274     void* mapping = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0);
1275     if (mapping != MAP_FAILED) {
1276       if (HANDLE_EINTR(mprotect(mapping, pagesize, PROT_READ | PROT_EXEC)) == 0)
1277         result = true;
1278       munmap(mapping, pagesize);
1279     }
1280   }
1281   return result;
1282 }
1283 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_AIX) || defined(OS_BSD)
1284 
1285 }  // namespace base
1286