1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/offline_pages/core/archive_manager.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/files/file_enumerator.h"
10 #include "base/files/file_util.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/system/sys_info.h"
18 #include "base/threading/thread_task_runner_handle.h"
19 #include "build/build_config.h"
20
21 namespace offline_pages {
22
23 namespace {
24
25 using StorageStatsCallback =
26 base::OnceCallback<void(const ArchiveManager::StorageStats& storage_stats)>;
27
EnsureArchivesDirCreatedImpl(const base::FilePath & archives_dir,bool is_temp)28 void EnsureArchivesDirCreatedImpl(const base::FilePath& archives_dir,
29 bool is_temp) {
30 base::File::Error error = base::File::FILE_OK;
31 if (!base::DirectoryExists(archives_dir)) {
32 if (!base::CreateDirectoryAndGetError(archives_dir, &error)) {
33 LOG(ERROR) << "Failed to create offline pages archive directory: "
34 << base::File::ErrorToString(error);
35 }
36 if (is_temp) {
37 UMA_HISTOGRAM_ENUMERATION(
38 "OfflinePages.ArchiveManager.ArchiveDirsCreationResult2.Temporary",
39 -error, -base::File::FILE_ERROR_MAX);
40 } else {
41 UMA_HISTOGRAM_ENUMERATION(
42 "OfflinePages.ArchiveManager.ArchiveDirsCreationResult2.Persistent",
43 -error, -base::File::FILE_ERROR_MAX);
44 }
45 }
46 }
47
GetStorageStatsImpl(const base::FilePath & temporary_archives_dir,const base::FilePath & private_archives_dir,const base::FilePath & public_archives_dir,scoped_refptr<base::SequencedTaskRunner> task_runner,StorageStatsCallback callback)48 void GetStorageStatsImpl(const base::FilePath& temporary_archives_dir,
49 const base::FilePath& private_archives_dir,
50 const base::FilePath& public_archives_dir,
51 scoped_refptr<base::SequencedTaskRunner> task_runner,
52 StorageStatsCallback callback) {
53 ArchiveManager::StorageStats storage_stats = {0, 0, 0, 0, 0};
54
55 // Getting the free disk space of the volume that contains the temporary
56 // archives directory. This value will be -1 if the directory is invalid.
57 // Currently both temporary and private archive directories are in the
58 // internal storage.
59 storage_stats.internal_free_disk_space =
60 base::SysInfo::AmountOfFreeDiskSpace(temporary_archives_dir);
61 storage_stats.external_free_disk_space =
62 base::SysInfo::AmountOfFreeDiskSpace(public_archives_dir);
63 if (!temporary_archives_dir.empty()) {
64 storage_stats.temporary_archives_size =
65 base::ComputeDirectorySize(temporary_archives_dir);
66 }
67 if (!private_archives_dir.empty()) {
68 storage_stats.private_archives_size =
69 base::ComputeDirectorySize(private_archives_dir);
70 }
71 if (!public_archives_dir.empty()) {
72 base::FileEnumerator file_enumerator(public_archives_dir, false,
73 base::FileEnumerator::FILES);
74 while (!file_enumerator.Next().empty()) {
75 #if defined(OS_WIN)
76 std::string extension = base::WideToUTF8(
77 file_enumerator.GetInfo().GetName().FinalExtension());
78 #else
79 std::string extension =
80 file_enumerator.GetInfo().GetName().FinalExtension();
81 #endif
82 if (extension == "mhtml" || extension == "mht")
83 storage_stats.public_archives_size +=
84 file_enumerator.GetInfo().GetSize();
85 }
86 }
87 task_runner->PostTask(FROM_HERE,
88 base::BindOnce(std::move(callback), storage_stats));
89 }
90
91 } // namespace
92
93 // protected and used for testing.
ArchiveManager()94 ArchiveManager::ArchiveManager() {}
95
ArchiveManager(const base::FilePath & temporary_archives_dir,const base::FilePath & private_archives_dir,const base::FilePath & public_archives_dir,const scoped_refptr<base::SequencedTaskRunner> & task_runner)96 ArchiveManager::ArchiveManager(
97 const base::FilePath& temporary_archives_dir,
98 const base::FilePath& private_archives_dir,
99 const base::FilePath& public_archives_dir,
100 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
101 : temporary_archives_dir_(temporary_archives_dir),
102 private_archives_dir_(private_archives_dir),
103 public_archives_dir_(public_archives_dir),
104 task_runner_(task_runner) {}
105
~ArchiveManager()106 ArchiveManager::~ArchiveManager() {}
107
EnsureArchivesDirCreated(base::OnceCallback<void ()> callback)108 void ArchiveManager::EnsureArchivesDirCreated(
109 base::OnceCallback<void()> callback) {
110 // The callback will only be invoked once both directories are created.
111 if (!temporary_archives_dir_.empty()) {
112 task_runner_->PostTask(
113 FROM_HERE, base::BindOnce(EnsureArchivesDirCreatedImpl,
114 temporary_archives_dir_, true /* is_temp */));
115 }
116 task_runner_->PostTaskAndReply(
117 FROM_HERE,
118 base::BindOnce(EnsureArchivesDirCreatedImpl, private_archives_dir_,
119 false /* is_temp */),
120 std::move(callback));
121 }
122
GetStorageStats(StorageStatsCallback callback) const123 void ArchiveManager::GetStorageStats(StorageStatsCallback callback) const {
124 task_runner_->PostTask(
125 FROM_HERE,
126 base::BindOnce(GetStorageStatsImpl, temporary_archives_dir_,
127 private_archives_dir_, public_archives_dir_,
128 base::ThreadTaskRunnerHandle::Get(), std::move(callback)));
129 }
130
GetTemporaryArchivesDir() const131 const base::FilePath& ArchiveManager::GetTemporaryArchivesDir() const {
132 return temporary_archives_dir_;
133 }
134
GetPrivateArchivesDir() const135 const base::FilePath& ArchiveManager::GetPrivateArchivesDir() const {
136 return private_archives_dir_;
137 }
138
GetPublicArchivesDir()139 const base::FilePath& ArchiveManager::GetPublicArchivesDir() {
140 return public_archives_dir_;
141 }
142
143 } // namespace offline_pages
144