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