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