| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/common/extension_resource.h" |
| |
| #include "base/file_util.h" |
| #include "base/logging.h" |
| #include "base/threading/thread_restrictions.h" |
| |
| namespace extensions { |
| |
| ExtensionResource::ExtensionResource() : follow_symlinks_anywhere_(false) { |
| } |
| |
| ExtensionResource::ExtensionResource(const std::string& 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() {} |
| |
| 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_; |
| } |
| |
| // We've already checked, just return last value. |
| 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_; |
| } |
| |
| // static |
| base::FilePath ExtensionResource::GetFilePath( |
| const base::FilePath& extension_root, |
| const base::FilePath& relative_path, |
| SymlinkPolicy symlink_policy) { |
| // We need to resolve the parent references in the extension_root |
| // path on its own because IsParent doesn't like parent references. |
| base::FilePath clean_extension_root( |
| base::MakeAbsoluteFilePath(extension_root)); |
| if (clean_extension_root.empty()) |
| return base::FilePath(); |
| |
| base::FilePath full_path = clean_extension_root.Append(relative_path); |
| |
| // If we are allowing the file to be a symlink outside of the root, then the |
| // path before resolving the symlink must still be within it. |
| if (symlink_policy == FOLLOW_SYMLINKS_ANYWHERE) { |
| std::vector<base::FilePath::StringType> components; |
| relative_path.GetComponents(&components); |
| int depth = 0; |
| |
| for (std::vector<base::FilePath::StringType>::const_iterator |
| i = components.begin(); i != components.end(); i++) { |
| if (*i == base::FilePath::kParentDirectory) { |
| depth--; |
| } else if (*i != base::FilePath::kCurrentDirectory) { |
| depth++; |
| } |
| if (depth < 0) { |
| return base::FilePath(); |
| } |
| } |
| } |
| |
| // We must resolve the absolute path of the combined path when |
| // the relative path contains references to a parent folder (i.e., '..'). |
| // We also check if the path exists because the posix version of |
| // MakeAbsoluteFilePath will fail if the path doesn't exist, and we want the |
| // same behavior on Windows... So until the posix and Windows version of |
| // MakeAbsoluteFilePath are unified, we need an extra call to PathExists, |
| // unfortunately. |
| // TODO(mad): Fix this once MakeAbsoluteFilePath is unified. |
| full_path = base::MakeAbsoluteFilePath(full_path); |
| if (base::PathExists(full_path) && |
| (symlink_policy == FOLLOW_SYMLINKS_ANYWHERE || |
| clean_extension_root.IsParent(full_path))) { |
| return full_path; |
| } |
| |
| return base::FilePath(); |
| } |
| |
| // Unit-testing helpers. |
| base::FilePath::StringType ExtensionResource::NormalizeSeperators( |
| const base::FilePath::StringType& path) const { |
| #if defined(FILE_PATH_USES_WIN_SEPARATORS) |
| base::FilePath::StringType win_path = path; |
| for (size_t i = 0; i < win_path.length(); i++) { |
| if (base::FilePath::IsSeparator(win_path[i])) |
| win_path[i] = base::FilePath::kSeparators[0]; |
| } |
| return win_path; |
| #else |
| return path; |
| #endif // FILE_PATH_USES_WIN_SEPARATORS |
| } |
| |
| bool ExtensionResource::ComparePathWithDefault( |
| const base::FilePath& path) const { |
| // Make sure we have a cached value to test against... |
| if (full_resource_path_.empty()) |
| GetFilePath(); |
| if (NormalizeSeperators(path.value()) == |
| NormalizeSeperators(full_resource_path_.value())) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| } // namespace extensions |