1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
// 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, ¤t, &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
|