#include "extensions/common/extension_resource.h"
#include "base/check.h"
#include "base/files/file_util.h"
namespace extensions {
ExtensionResource::ExtensionResource() : follow_symlinks_anywhere_(false) {}
ExtensionResource::ExtensionResource(const ExtensionId& extension_id,
const base::FilePath& extension_root,
const base::FilePath& relative_path)
: extension_id_(extension_id),
extension_root_(extension_root),
relative_path_(relative_path),
follow_symlinks_anywhere_(false) {}
ExtensionResource::ExtensionResource(const ExtensionResource& other) = default;
ExtensionResource::ExtensionResource(ExtensionResource&& other) = default;
ExtensionResource& ExtensionResource::operator=(ExtensionResource&& other) =
default;
ExtensionResource::~ExtensionResource() = default;
void ExtensionResource::set_follow_symlinks_anywhere() {
follow_symlinks_anywhere_ = true;
}
const base::FilePath& ExtensionResource::GetFilePath() const {
if (extension_root_.empty() || relative_path_.empty()) {
DCHECK(full_resource_path_.empty());
return full_resource_path_;
}
if (!full_resource_path_.empty())
return full_resource_path_;
full_resource_path_ = GetFilePath(
extension_root_, relative_path_,
follow_symlinks_anywhere_ ?
FOLLOW_SYMLINKS_ANYWHERE : SYMLINKS_MUST_RESOLVE_WITHIN_ROOT);
return full_resource_path_;
}
base::FilePath ExtensionResource::GetFilePath(
const base::FilePath& extension_root,
const base::FilePath& relative_path,
SymlinkPolicy symlink_policy) {
bool extension_root_normalization_skipped = false;
base::FilePath normalized_extension_root;
if (!base::NormalizeFilePath(extension_root, &normalized_extension_root)) {
#if BUILDFLAG(IS_WIN)
normalized_extension_root = base::MakeAbsoluteFilePath(extension_root);
if (normalized_extension_root.empty() ||
!base::PathExists(normalized_extension_root)) {
return base::FilePath();
}
extension_root_normalization_skipped = true;
#else
return base::FilePath();
#endif
}
base::FilePath full_path = normalized_extension_root.Append(relative_path);
if (symlink_policy == FOLLOW_SYMLINKS_ANYWHERE) {
int depth = 0;
for (const auto& component : relative_path.GetComponents()) {
if (component == base::FilePath::kParentDirectory) {
depth--;
} else if (component != base::FilePath::kCurrentDirectory) {
depth++;
}
if (depth < 0) {
return base::FilePath();
}
}
}
if (base::FilePath full_path_normalized;
!extension_root_normalization_skipped &&
base::NormalizeFilePath(full_path, &full_path_normalized)) {
full_path = std::move(full_path_normalized);
} else {
#if BUILDFLAG(IS_WIN)
full_path = base::MakeAbsoluteFilePath(full_path);
if (full_path.empty() || !base::PathExists(full_path)) {
return base::FilePath();
}
#else
return base::FilePath();
#endif
}
if (symlink_policy != FOLLOW_SYMLINKS_ANYWHERE &&
!normalized_extension_root.IsParent(full_path)) {
return base::FilePath();
}
#if BUILDFLAG(IS_MAC)
if (relative_path.EndsWithSeparator() && !base::DirectoryExists(full_path)) {
return base::FilePath();
}
#endif
#if BUILDFLAG(IS_WIN)
if (!relative_path.empty()) {
const char last_char = relative_path.value().back();
if (last_char == '.' || last_char == ' ') {
return base::FilePath();
}
}
#endif
return full_path;
}
}