blob: d98601ada6e8963b49afcde9ed88c359e3ac7004 [file] [log] [blame]
Sergei Datsenkoc9904bb2020-12-11 12:46:02 +11001// Copyright 2020 The Chromium OS 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 "cros-disks/rar_mounter.h"
6
7#include <algorithm>
8#include <memory>
9#include <utility>
10
Qijiang Fan713061e2021-03-08 15:45:12 +090011#include <base/check.h>
12#include <base/check_op.h>
Sergei Datsenkoc9904bb2020-12-11 12:46:02 +110013#include <base/files/file_path.h>
14#include <base/files/file_util.h>
15#include <base/logging.h>
16#include <base/strings/string_util.h>
17#include <base/strings/stringprintf.h>
18
19#include "cros-disks/error_logger.h"
20#include "cros-disks/fuse_mounter.h"
21#include "cros-disks/metrics.h"
22#include "cros-disks/platform.h"
23#include "cros-disks/quote.h"
24
25namespace cros_disks {
26namespace {
27const char kExtension[] = ".rar";
28} // namespace
29
30RarMounter::RarMounter(const Platform* platform,
31 brillo::ProcessReaper* process_reaper,
32 Metrics* metrics,
33 std::unique_ptr<SandboxedProcessFactory> sandbox_factory)
34 : ArchiveMounter(platform,
35 process_reaper,
36 "rar",
37 metrics,
38 "Rar2fs",
39 {12, // ERAR_BAD_DATA
40 22, // ERAR_MISSING_PASSWORD
41 24}, // ERAR_BAD_PASSWORD
42 std::move(sandbox_factory)) {}
43
44RarMounter::~RarMounter() = default;
45
46MountErrorType RarMounter::FormatInvocationCommand(
47 const base::FilePath& archive,
48 std::vector<std::string> params,
49 SandboxedProcess* sandbox) const {
50 // Bind-mount parts of a multipart archive if any.
51 for (const auto& path : GetBindPaths(archive.value())) {
52 if (!sandbox->BindMount(path, path, /* writeable= */ false,
53 /* recursive= */ false)) {
54 PLOG(ERROR) << "Could not bind " << quote(path);
55 return MOUNT_ERROR_INTERNAL;
56 }
57 }
58
59 std::vector<std::string> opts = {
60 "ro", "umask=0222", "locale=en_US.UTF8",
61 base::StringPrintf("uid=%d", kChronosUID),
62 base::StringPrintf("gid=%d", kChronosAccessGID)};
63
Sergei Datsenko1bb0bdc2021-02-17 11:26:43 +110064 std::string options;
65 if (!JoinParamsIntoOptions(opts, &options)) {
66 return MOUNT_ERROR_INVALID_MOUNT_OPTIONS;
67 }
Sergei Datsenkoc9904bb2020-12-11 12:46:02 +110068 sandbox->AddArgument("-o");
Sergei Datsenko1bb0bdc2021-02-17 11:26:43 +110069 sandbox->AddArgument(options);
70
Sergei Datsenkoc9904bb2020-12-11 12:46:02 +110071 sandbox->AddArgument(archive.value());
72
73 return MOUNT_ERROR_NONE;
74}
75
76bool RarMounter::Increment(const std::string::iterator begin,
77 std::string::iterator end) {
78 while (true) {
79 if (begin == end) {
80 // Overflow.
81 return false;
82 }
83
84 char& c = *--end;
85
86 if (c == '9') {
87 // Roll 9 to 0.
88 c = '0';
89 } else if (c == 'z') {
90 // Roll z to a.
91 c = 'a';
92 } else if (c == 'Z') {
93 // Roll Z to A.
94 c = 'A';
95 } else {
96 // Increment any other character and done.
97 ++c;
98 return true;
99 }
100 }
101}
102
103RarMounter::IndexRange RarMounter::ParseDigits(base::StringPiece path) {
104 const base::StringPiece extension = kExtension;
105
106 if (!base::EndsWith(path, extension, base::CompareCase::INSENSITIVE_ASCII))
107 return {};
108
109 path.remove_suffix(extension.size());
110 const size_t end = path.size();
111
112 while (!path.empty() && base::IsAsciiDigit(path.back()))
113 path.remove_suffix(1);
114
115 return {path.size(), end};
116}
117
118void RarMounter::AddPathsWithOldNamingScheme(
119 std::vector<std::string>* const bind_paths,
120 const base::StringPiece original_path) const {
121 DCHECK(bind_paths);
122
123 // Is the extension right?
124 if (!base::EndsWith(original_path, kExtension,
125 base::CompareCase::INSENSITIVE_ASCII))
126 return;
127
128 // Prepare candidate path.
129 std::string candidate_path(original_path);
130 const std::string::iterator end = candidate_path.end();
131
132 // Set the last 2 characters to '0', so that extension '.rar' becomes '.r00'
133 // and extension '.RAR' becomes '.R00'.
134 std::fill(end - 2, end, '0');
135
136 // Is there at least the first supplementary file of the multipart archive?
137 if (!platform()->PathExists(candidate_path))
138 return;
139
140 bind_paths->push_back(candidate_path);
141
142 // Iterate by incrementing the last 3 characters of the extension:
143 // '.r00' -> '.r01' -> ... -> '.r99' -> '.s00' -> ... -> '.z99'
144 // or
145 // '.R00' -> '.R01' -> ... -> '.R99' -> '.S00' -> ... -> '.Z99'
146 while (Increment(end - 3, end) && platform()->PathExists(candidate_path))
147 bind_paths->push_back(candidate_path);
148}
149
150void RarMounter::AddPathsWithNewNamingScheme(
151 std::vector<std::string>* const bind_paths,
152 const base::StringPiece original_path,
153 const IndexRange& digits) const {
154 DCHECK(bind_paths);
155 DCHECK_LT(digits.begin, digits.end);
156 DCHECK_LE(digits.end, original_path.size());
157
158 // Prepare candidate path.
159 std::string candidate_path(original_path);
160
161 // [begin, end) is the digit range to increment.
162 const std::string::iterator begin = candidate_path.begin() + digits.begin;
163 const std::string::iterator end = candidate_path.begin() + digits.end;
164
165 // Fill the digit range with zeros.
166 std::fill(begin, end, '0');
167
168 // Find all the files making the multipart archive.
169 while (Increment(begin, end) && platform()->PathExists(candidate_path)) {
170 if (candidate_path != original_path)
171 bind_paths->push_back(candidate_path);
172 }
173}
174
175std::vector<std::string> RarMounter::GetBindPaths(
176 const base::StringPiece original_path) const {
177 std::vector<std::string> bind_paths = {std::string(original_path)};
178
179 // Delimit the digit range assuming original_path uses the new naming scheme.
180 const IndexRange digits = ParseDigits(original_path);
181 if (digits.empty()) {
182 // Use the old naming scheme.
183 AddPathsWithOldNamingScheme(&bind_paths, original_path);
184 } else {
185 // Use the new naming scheme.
186 AddPathsWithNewNamingScheme(&bind_paths, original_path, digits);
187 }
188
189 return bind_paths;
190}
191
192} // namespace cros_disks