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, ¤t, &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, ¶ms) != -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