blob: 0258961bb887b8f4620b29dcaaa7be82f2ea9b3a [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/util/posix_util.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <optional>
#include <string>
#include <vector>
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/threading/scoped_blocking_call.h"
#include "chrome/updater/updater_branding.h"
#include "chrome/updater/util/util.h"
#if BUILDFLAG(IS_LINUX)
#include "chrome/updater/util/linux_util.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "chrome/updater/util/mac_util.h"
#endif
namespace updater {
namespace {
bool AdvanceEnumeratorWithStat(base::FileEnumerator* traversal,
base::FilePath* out_next_path,
base::stat_wrapper_t* out_next_stat) {
*out_next_path = traversal->Next();
if (out_next_path->empty()) {
return false;
}
*out_next_stat = traversal->GetInfo().stat();
return true;
}
} // namespace
// Recursively delete a folder and its contents, returning `true` on success.
bool DeleteFolder(std::optional<base::FilePath> installed_path) {
if (!installed_path) {
return false;
}
if (!base::DeletePathRecursively(*installed_path)) {
PLOG(ERROR) << "Deleting " << *installed_path << " failed";
return false;
}
return true;
}
bool DeleteCandidateInstallFolder(UpdaterScope scope) {
return DeleteFolder(GetVersionedInstallDirectory(scope));
}
bool CopyDir(const base::FilePath& from_path,
const base::FilePath& to_path,
bool world_readable) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
base::FileEnumerator traversal(from_path, true,
base::FileEnumerator::FILES |
base::FileEnumerator::SHOW_SYM_LINKS |
base::FileEnumerator::DIRECTORIES);
base::FilePath current = from_path;
base::FilePath from_path_base = from_path.DirName();
base::stat_wrapper_t from_stat = {};
if (base::File::Stat(from_path, &from_stat) < 0) {
DPLOG(ERROR) << "Can't stat source directory: " << from_path;
return false;
}
const mode_t mode_executable =
world_readable ? S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
: S_IRWXU;
const mode_t mode_normal = world_readable
? S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
: S_IRUSR | S_IWUSR;
do {
// current is the source path, including from_path, so append
// the suffix after from_path to to_path to create the target_path.
base::FilePath target_path(to_path);
if (from_path_base != current &&
!from_path_base.AppendRelativePath(current, &target_path)) {
return false;
}
if (S_ISDIR(from_stat.st_mode)) {
if (mkdir(target_path.value().c_str(), mode_executable)) {
VPLOG(0) << "Can't create directory: " << target_path;
return false;
}
if (chmod(target_path.value().c_str(), mode_executable)) {
VLOG(0) << "Can't chmod directory: " << target_path;
return false;
}
continue;
}
if (S_ISREG(from_stat.st_mode)) {
base::File infile(open(current.value().c_str(), O_RDONLY));
if (!infile.IsValid()) {
VPLOG(0) << "Can't open file: " << current;
return false;
}
const int mode = (from_stat.st_mode & S_IXUSR) == S_IXUSR
? mode_executable
: mode_normal;
base::File outfile(open(target_path.value().c_str(),
O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode));
if (!outfile.IsValid()) {
VPLOG(0) << "Can't create file: " << target_path;
return false;
}
if (fchmod(outfile.GetPlatformFile(), mode)) {
VLOG(0) << "Can't fchmod file: " << current;
return false;
}
if (!base::CopyFileContents(infile, outfile)) {
VLOG(0) << "Can't copy file: " << current;
return false;
}
continue;
}
if (S_ISLNK(from_stat.st_mode)) {
char buffer[1000] = {};
const ssize_t size =
readlink(current.value().c_str(), buffer, std::size(buffer));
if (size < 0 || size >= std::ssize(buffer)) {
VLOG(0) << "Can't read symlink: " << current;
return false;
}
if (symlink(buffer, target_path.value().c_str())) {
VLOG(0) << "Can't create symlink: " << current;
return false;
}
continue;
}
VLOG(0) << current << " is not a directory, regular file, or symlink.";
return false;
} while (AdvanceEnumeratorWithStat(&traversal, &current, &from_stat));
return true;
}
bool WrongUser(UpdaterScope scope) {
return (scope == UpdaterScope::kSystem) != (geteuid() == 0);
}
bool EulaAccepted(const std::vector<std::string>& app_ids) {
// On POSIX, there does not exist a way for apps to mark EULA acceptance.
return false;
}
} // namespace updater