#include "base/files/file_util.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <bit>
#include <iomanip>
#include <memory>
#include <optional>
#include <string_view>
#include "base/base_export.h"
#include "base/base_switches.h"
#include "base/bits.h"
#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/containers/heap_array.h"
#include "base/containers/stack.h"
#include "base/environment.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/cstring_view.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/time/time.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_APPLE)
#include <AvailabilityMacros.h>
#include "base/apple/foundation_util.h"
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
#include <sys/sendfile.h>
#endif
#if BUILDFLAG(IS_ANDROID)
#include "base/android/content_uri_utils.h"
#include "base/os_compat_android.h"
#endif
#if !BUILDFLAG(IS_IOS)
#include <grp.h>
#endif
#if BUILDFLAG(IS_AIX)
extern "C" char* mkdtemp(char* path);
#endif
namespace base {
namespace {
#if BUILDFLAG(IS_MAC)
bool VerifySpecificPathControlledByUser(const FilePath& path,
uid_t owner_uid,
const std::set<gid_t>& group_gids) {
stat_wrapper_t stat_info;
if (File::Lstat(path, &stat_info) != 0) {
DPLOG(ERROR) << "Failed to get information on path " << path.value();
return false;
}
if (S_ISLNK(stat_info.st_mode)) {
DLOG(ERROR) << "Path " << path.value() << " is a symbolic link.";
return false;
}
if (stat_info.st_uid != owner_uid) {
DLOG(ERROR) << "Path " << path.value() << " is owned by the wrong user.";
return false;
}
if ((stat_info.st_mode & S_IWGRP) &&
!Contains(group_gids, stat_info.st_gid)) {
DLOG(ERROR) << "Path " << path.value()
<< " is writable by an unprivileged group.";
return false;
}
if (stat_info.st_mode & S_IWOTH) {
DLOG(ERROR) << "Path " << path.value() << " is writable by any user.";
return false;
}
return true;
}
#endif
base::FilePath GetTempTemplate() { … }
bool AdvanceEnumeratorWithStat(FileEnumerator* traversal,
FilePath* out_next_path,
stat_wrapper_t* out_next_stat) { … }
bool DoCopyDirectory(const FilePath& from_path,
const FilePath& to_path,
bool recursive,
bool open_exclusive) { … }
struct CloseDir { … };
bool DoDeleteFile(const PlatformFile at_fd,
const char* const path,
const bool recursive) { … }
bool DoDeleteFile(const FilePath& path, bool recursive) { … }
#if !BUILDFLAG(IS_APPLE)
std::string AppendModeCharacter(std::string_view mode, char mode_char) { … }
#endif
#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_APPLE) && \
!(BUILDFLAG(IS_ANDROID) && __ANDROID_API__ >= 21)
bool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes) {
DCHECK_GE(max_bytes, 0);
File file(file_path, File::FLAG_OPEN | File::FLAG_READ);
if (!file.IsValid()) {
return false;
}
constexpr size_t kBufferSize = 1024 * 1024;
auto buffer = base::HeapArray<uint8_t>::Uninit(kBufferSize);
while (max_bytes > 0) {
const size_t read_size = base::checked_cast<size_t>(
std::min<uint64_t>(static_cast<uint64_t>(max_bytes), buffer.size()));
std::optional<size_t> read_bytes =
file.ReadAtCurrentPos(buffer.first(read_size));
if (!read_bytes.has_value()) {
return false;
}
if (read_bytes.value() == 0) {
break;
}
max_bytes -= read_bytes.value();
}
return true;
}
#endif
}
FilePath MakeAbsoluteFilePath(const FilePath& input) { … }
std::optional<FilePath> MakeAbsoluteFilePathNoResolveSymbolicLinks(
const FilePath& input) { … }
bool DeleteFile(const FilePath& path) { … }
bool DeletePathRecursively(const FilePath& path) { … }
bool ReplaceFile(const FilePath& from_path,
const FilePath& to_path,
File::Error* error) { … }
bool CopyDirectory(const FilePath& from_path,
const FilePath& to_path,
bool recursive) { … }
bool CopyDirectoryExcl(const FilePath& from_path,
const FilePath& to_path,
bool recursive) { … }
bool CreatePipe(ScopedFD* read_fd, ScopedFD* write_fd, bool non_blocking) { … }
bool CreateLocalNonBlockingPipe(span<int, 2u> fds) { … }
bool SetNonBlocking(int fd) { … }
bool SetCloseOnExec(int fd) { … }
bool RemoveCloseOnExec(int fd) { … }
bool PathExists(const FilePath& path) { … }
bool PathIsReadable(const FilePath& path) { … }
bool PathIsWritable(const FilePath& path) { … }
bool DirectoryExists(const FilePath& path) { … }
bool ReadFromFD(int fd, span<char> buffer) { … }
ScopedFD CreateAndOpenFdForTemporaryFileInDir(const FilePath& directory,
FilePath* path) { … }
#if !BUILDFLAG(IS_FUCHSIA)
bool CreateSymbolicLink(const FilePath& target_path,
const FilePath& symlink_path) { … }
bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) { … }
std::optional<FilePath> ReadSymbolicLinkAbsolute(const FilePath& symlink_path) { … }
bool GetPosixFilePermissions(const FilePath& path, int* mode) { … }
bool SetPosixFilePermissions(const FilePath& path, int mode) { … }
bool ExecutableExistsInPath(Environment* env,
const FilePath::StringType& executable) { … }
#endif
#if !BUILDFLAG(IS_APPLE)
bool GetTempDir(FilePath* path) { … }
#endif
#if !BUILDFLAG(IS_APPLE)
FilePath GetHomeDir() { … }
#endif
File CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { … }
bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { … }
FilePath FormatTemporaryFileName(FilePath::StringPieceType identifier) { … }
ScopedFILE CreateAndOpenTemporaryStreamInDir(const FilePath& dir,
FilePath* path) { … }
static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
const FilePath& name_tmpl,
FilePath* new_dir) { … }
bool CreateTemporaryDirInDir(const FilePath& base_dir,
FilePath::StringPieceType prefix,
FilePath* new_dir) { … }
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
FilePath* new_temp_path) { … }
bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) { … }
bool ReadFileToStringNonBlocking(const base::FilePath& file, std::string* ret) { … }
bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { … }
bool IsLink(const FilePath& file_path) { … }
bool GetFileInfo(const FilePath& file_path, File::Info* results) { … }
FILE* OpenFile(const FilePath& filename, const char* mode) { … }
#if !BUILDFLAG(IS_NACL)
FILE* FileToFILE(File file, const char* mode) { … }
File FILEToFile(FILE* file_stream) { … }
#endif
std::optional<uint64_t> ReadFile(const FilePath& filename, span<char> buffer) { … }
bool WriteFile(const FilePath& filename, span<const uint8_t> data) { … }
bool WriteFileDescriptor(int fd, span<const uint8_t> data) { … }
bool WriteFileDescriptor(int fd, std::string_view data) { … }
bool AllocateFileRegion(File* file, int64_t offset, size_t size) { … }
bool AppendToFile(const FilePath& filename, span<const uint8_t> data) { … }
bool AppendToFile(const FilePath& filename, std::string_view data) { … }
bool GetCurrentDirectory(FilePath* dir) { … }
bool SetCurrentDirectory(const FilePath& path) { … }
#if BUILDFLAG(IS_MAC)
bool VerifyPathControlledByUser(const FilePath& base,
const FilePath& path,
uid_t owner_uid,
const std::set<gid_t>& group_gids) {
if (base != path && !base.IsParent(path)) {
DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \""
<< base.value() << "\", path = \"" << path.value() << "\"";
return false;
}
std::vector<FilePath::StringType> base_components = base.GetComponents();
std::vector<FilePath::StringType> path_components = path.GetComponents();
std::vector<FilePath::StringType>::const_iterator ib, ip;
for (ib = base_components.begin(), ip = path_components.begin();
ib != base_components.end(); ++ib, ++ip) {
CHECK(ip != path_components.end(), base::NotFatalUntil::M125);
DCHECK(*ip == *ib);
}
FilePath current_path = base;
if (!VerifySpecificPathControlledByUser(current_path, owner_uid,
group_gids)) {
return false;
}
for (; ip != path_components.end(); ++ip) {
current_path = current_path.Append(*ip);
if (!VerifySpecificPathControlledByUser(current_path, owner_uid,
group_gids)) {
return false;
}
}
return true;
}
bool VerifyPathControlledByAdmin(const FilePath& path) {
constexpr unsigned kRootUid = 0;
const FilePath kFileSystemRoot("/");
const char* const kAdminGroupNames[] = {"admin", "wheel"};
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
std::set<gid_t> allowed_group_ids;
for (const char* name : kAdminGroupNames) {
struct group* group_record = getgrnam(name);
if (!group_record) {
DPLOG(ERROR) << "Could not get the group ID of group \"" << name << "\".";
continue;
}
allowed_group_ids.insert(group_record->gr_gid);
}
return VerifyPathControlledByUser(kFileSystemRoot, path, kRootUid,
allowed_group_ids);
}
#endif
int GetMaximumPathComponentLength(const FilePath& path) { … }
#if !BUILDFLAG(IS_ANDROID)
bool GetShmemTempDir(bool executable, FilePath* path) { … }
#endif
#if !BUILDFLAG(IS_APPLE)
bool CopyFile(const FilePath& from_path, const FilePath& to_path) { … }
#endif
bool PreReadFile(const FilePath& file_path,
bool is_executable,
bool sequential,
int64_t max_bytes) { … }
namespace internal {
bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { … }
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
bool CopyFileContentsWithSendfile(File& infile,
File& outfile,
bool& retry_slow) { … }
#endif
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)
BASE_EXPORT bool IsPathExecutable(const FilePath& path) { … }
#endif
}