Merge "libsnapshot: Don't force-unmap in DeleteSnapshot()."
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 9a0f4fe..bc197cd 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -46,7 +46,7 @@
namespace fs_mgr {
namespace {
-const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
+constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
struct FlagList {
const char *name;
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index b504161..5b377ae 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -36,6 +36,7 @@
"builder.cpp",
"images.cpp",
"partition_opener.cpp",
+ "property_fetcher.cpp",
"reader.cpp",
"utility.cpp",
"writer.cpp",
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index a434c93..247e3a9 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -19,23 +19,17 @@
#include <string.h>
#include <algorithm>
-#include <string_view>
-#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include "liblp/liblp.h"
+#include "liblp/property_fetcher.h"
#include "reader.h"
#include "utility.h"
namespace android {
namespace fs_mgr {
-std::optional<bool> MetadataBuilder::sABOverride;
-std::optional<bool> MetadataBuilder::sRetrofitDap;
-
-static constexpr std::string_view kDefaultGroup = "default";
-
bool LinearExtent::AddTo(LpMetadata* out) const {
if (device_index_ >= out->block_devices.size()) {
LERROR << "Extent references unknown block device.";
@@ -211,14 +205,6 @@
return New(*metadata.get(), &opener);
}
-void MetadataBuilder::OverrideABForTesting(bool ab_device) {
- sABOverride = ab_device;
-}
-
-void MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(bool retrofit) {
- sRetrofitDap = retrofit;
-}
-
MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
memset(&geometry_, 0, sizeof(geometry_));
geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
@@ -1051,17 +1037,12 @@
}
bool MetadataBuilder::IsABDevice() {
- if (sABOverride.has_value()) {
- return *sABOverride;
- }
- return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
+ return !IPropertyFetcher::GetInstance()->GetProperty("ro.boot.slot_suffix", "").empty();
}
bool MetadataBuilder::IsRetrofitDynamicPartitionsDevice() {
- if (sRetrofitDap.has_value()) {
- return *sRetrofitDap;
- }
- return android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false);
+ return IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions_retrofit",
+ false);
}
bool MetadataBuilder::IsRetrofitMetadata() const {
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 377ec68..6d27873 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -19,36 +19,40 @@
#include <gtest/gtest.h>
#include <liblp/builder.h>
+#include "mock_property_fetcher.h"
#include "utility.h"
using namespace std;
using namespace android::fs_mgr;
+using ::android::fs_mgr::MockPropertyFetcher;
+using ::testing::_;
+using ::testing::AnyNumber;
using ::testing::ElementsAre;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+static void ResetPropertyFetcher() {
+ IPropertyFetcher::OverrideForTesting(std::make_unique<NiceMock<MockPropertyFetcher>>());
+}
+
+MockPropertyFetcher* GetMockedInstance() {
+ return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
+}
class Environment : public ::testing::Environment {
public:
- void SetUp() override {
- MetadataBuilder::OverrideABForTesting(false);
- MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(false);
- }
+ void SetUp() override { ResetPropertyFetcher(); }
};
int main(int argc, char** argv) {
- ::testing::AddGlobalTestEnvironment(new Environment);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
class BuilderTest : public ::testing::Test {
public:
- void SetUp() override {
- MetadataBuilder::OverrideABForTesting(false);
- MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(false);
- }
- void TearDown() override {
- MetadataBuilder::OverrideABForTesting(false);
- MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(false);
- }
+ void SetUp() override { ResetPropertyFetcher(); }
+ void TearDown() override { ResetPropertyFetcher(); }
};
TEST_F(BuilderTest, BuildBasic) {
@@ -785,7 +789,9 @@
// A and B slots should be allocated from separate halves of the partition,
// to mitigate allocating too many extents. (b/120433288)
- MetadataBuilder::OverrideABForTesting(true);
+ ON_CALL(*GetMockedInstance(), GetProperty("ro.boot.slot_suffix", _))
+ .WillByDefault(Return("_a"));
+
auto builder = MetadataBuilder::New(device_info, 65536, 2);
ASSERT_NE(builder, nullptr);
Partition* system_a = builder->AddPartition("system_a", 0);
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index a2221ef..a2294ef 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -24,6 +24,7 @@
#include <memory>
#include <optional>
#include <set>
+#include <string_view>
#include "liblp.h"
#include "partition_opener.h"
@@ -37,6 +38,9 @@
static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
static const uint32_t kDefaultBlockSize = 4096;
+// Name of the default group in a metadata.
+static constexpr std::string_view kDefaultGroup = "default";
+
// Abstraction around dm-targets that can be encoded into logical partition tables.
class Extent {
public:
@@ -196,12 +200,6 @@
return New(device_info, metadata_max_size, metadata_slot_count);
}
- // Used by the test harness to override whether the device is "A/B".
- static void OverrideABForTesting(bool ab_device);
-
- // Used by the test harness to override whether the device is "retrofitting dynamic partitions".
- static void OverrideRetrofitDynamicParititonsForTesting(bool retrofit);
-
// Define a new partition group. By default there is one group called
// "default", with an unrestricted size. A non-zero size will restrict the
// total space used by all partitions in the group.
@@ -347,9 +345,6 @@
const std::vector<Interval>& free_list,
uint64_t sectors_needed) const;
- static std::optional<bool> sABOverride;
- static std::optional<bool> sRetrofitDap;
-
LpMetadataGeometry geometry_;
LpMetadataHeader header_;
std::vector<std::unique_ptr<Partition>> partitions_;
diff --git a/fs_mgr/liblp/include/liblp/property_fetcher.h b/fs_mgr/liblp/include/liblp/property_fetcher.h
new file mode 100644
index 0000000..e73a1f5
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/property_fetcher.h
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <memory>
+
+namespace android {
+namespace fs_mgr {
+
+class IPropertyFetcher {
+ public:
+ virtual ~IPropertyFetcher() = default;
+ virtual std::string GetProperty(const std::string& key, const std::string& defaultValue) = 0;
+ virtual bool GetBoolProperty(const std::string& key, bool defaultValue) = 0;
+
+ static IPropertyFetcher* GetInstance();
+ static void OverrideForTesting(std::unique_ptr<IPropertyFetcher>&&);
+};
+
+class PropertyFetcher : public IPropertyFetcher {
+ public:
+ ~PropertyFetcher() = default;
+ std::string GetProperty(const std::string& key, const std::string& defaultValue) override;
+ bool GetBoolProperty(const std::string& key, bool defaultValue) override;
+};
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 70dd85f..2990863 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -23,10 +23,12 @@
#include <android-base/unique_fd.h>
#include <fs_mgr.h>
#include <fstab/fstab.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
#include "images.h"
+#include "mock_property_fetcher.h"
#include "reader.h"
#include "test_partition_opener.h"
#include "utility.h"
@@ -34,6 +36,8 @@
using namespace std;
using namespace android::fs_mgr;
+using ::testing::_;
+using ::testing::Return;
using unique_fd = android::base::unique_fd;
// Our tests assume a 128KiB disk with two 512 byte metadata slots.
@@ -664,7 +668,8 @@
}
TEST(liblp, UpdateRetrofit) {
- MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(true);
+ ON_CALL(*GetMockedInstance(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+ .WillByDefault(Return(true));
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
ASSERT_NE(builder, nullptr);
@@ -695,7 +700,8 @@
}
TEST(liblp, UpdateNonRetrofit) {
- MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(false);
+ ON_CALL(*GetMockedInstance(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+ .WillByDefault(Return(false));
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
diff --git a/fs_mgr/liblp/mock_property_fetcher.h b/fs_mgr/liblp/mock_property_fetcher.h
new file mode 100644
index 0000000..eb91de2
--- /dev/null
+++ b/fs_mgr/liblp/mock_property_fetcher.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include <liblp/property_fetcher.h>
+
+namespace android {
+namespace fs_mgr {
+
+class MockPropertyFetcher : public IPropertyFetcher {
+ public:
+ MOCK_METHOD2(GetProperty, std::string(const std::string&, const std::string&));
+ MOCK_METHOD2(GetBoolProperty, bool(const std::string&, bool));
+
+ // By default, return default_value for all functions.
+ MockPropertyFetcher() {
+ using ::testing::_;
+ using ::testing::Invoke;
+ ON_CALL(*this, GetProperty(_, _)).WillByDefault(Invoke([](const auto&, const auto& def) {
+ return def;
+ }));
+ ON_CALL(*this, GetBoolProperty(_, _)).WillByDefault(Invoke([](const auto&, auto def) {
+ return def;
+ }));
+ }
+};
+
+} // namespace fs_mgr
+} // namespace android
+
+android::fs_mgr::MockPropertyFetcher* GetMockedInstance();
diff --git a/fs_mgr/liblp/property_fetcher.cpp b/fs_mgr/liblp/property_fetcher.cpp
new file mode 100644
index 0000000..038ef4d
--- /dev/null
+++ b/fs_mgr/liblp/property_fetcher.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "liblp/property_fetcher.h"
+
+#include <memory>
+
+#include <android-base/properties.h>
+
+namespace android {
+namespace fs_mgr {
+
+std::string PropertyFetcher::GetProperty(const std::string& key, const std::string& default_value) {
+ return android::base::GetProperty(key, default_value);
+}
+
+bool PropertyFetcher::GetBoolProperty(const std::string& key, bool default_value) {
+ return android::base::GetBoolProperty(key, default_value);
+}
+
+static std::unique_ptr<IPropertyFetcher>* GetInstanceAllocation() {
+ static std::unique_ptr<IPropertyFetcher> instance = std::make_unique<PropertyFetcher>();
+ return &instance;
+}
+
+IPropertyFetcher* IPropertyFetcher::GetInstance() {
+ return GetInstanceAllocation()->get();
+}
+
+void IPropertyFetcher::OverrideForTesting(std::unique_ptr<IPropertyFetcher>&& fetcher) {
+ GetInstanceAllocation()->swap(fetcher);
+ fetcher.reset();
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
index a2824d1..78da46c 100644
--- a/libmodprobe/Android.bp
+++ b/libmodprobe/Android.bp
@@ -3,6 +3,7 @@
cflags: [
"-Werror",
],
+ vendor_available: true,
recovery_available: true,
srcs: [
"libmodprobe.cpp",
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 0ec766a..dcb4ffb 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -16,6 +16,7 @@
#pragma once
+#include <set>
#include <string>
#include <unordered_map>
#include <vector>
@@ -25,12 +26,21 @@
Modprobe(const std::vector<std::string>&);
bool LoadListedModules();
- bool LoadWithAliases(const std::string& module_name, bool strict);
+ bool LoadWithAliases(const std::string& module_name, bool strict,
+ const std::string& parameters = "");
+ bool Remove(const std::string& module_name);
+ std::vector<std::string> ListModules(const std::string& pattern);
+ bool GetAllDependencies(const std::string& module, std::vector<std::string>* pre_dependencies,
+ std::vector<std::string>* dependencies,
+ std::vector<std::string>* post_dependencies);
+ void EnableBlacklist(bool enable);
+ void EnableVerbose(bool enable);
private:
std::string MakeCanonical(const std::string& module_path);
- bool InsmodWithDeps(const std::string& module_name);
- bool Insmod(const std::string& path_name);
+ bool InsmodWithDeps(const std::string& module_name, const std::string& parameters);
+ bool Insmod(const std::string& path_name, const std::string& parameters);
+ bool Rmmod(const std::string& module_name);
std::vector<std::string> GetDependencies(const std::string& module);
bool ModuleExists(const std::string& module_name);
@@ -39,6 +49,7 @@
bool ParseSoftdepCallback(const std::vector<std::string>& args);
bool ParseLoadCallback(const std::vector<std::string>& args);
bool ParseOptionsCallback(const std::vector<std::string>& args);
+ bool ParseBlacklistCallback(const std::vector<std::string>& args);
void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
std::vector<std::pair<std::string, std::string>> module_aliases_;
@@ -47,4 +58,6 @@
std::vector<std::pair<std::string, std::string>> module_post_softdep_;
std::vector<std::string> module_load_;
std::unordered_map<std::string, std::string> module_options_;
+ std::set<std::string> module_blacklist_;
+ bool blacklist_enabled = false;
};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 01cf2e3..73ae15b 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -194,6 +194,31 @@
return true;
}
+bool Modprobe::ParseBlacklistCallback(const std::vector<std::string>& args) {
+ auto it = args.begin();
+ const std::string& type = *it++;
+
+ if (type != "blacklist") {
+ LOG(ERROR) << "non-blacklist line encountered in modules.blacklist";
+ return false;
+ }
+
+ if (args.size() != 2) {
+ LOG(ERROR) << "lines in modules.blacklist must have exactly 2 entries, not " << args.size();
+ return false;
+ }
+
+ const std::string& module = *it++;
+
+ const std::string& canonical_name = MakeCanonical(module);
+ if (canonical_name.empty()) {
+ return false;
+ }
+ this->module_blacklist_.emplace(canonical_name);
+
+ return true;
+}
+
void Modprobe::ParseCfg(const std::string& cfg,
std::function<bool(const std::vector<std::string>&)> f) {
std::string cfg_contents;
@@ -231,6 +256,23 @@
auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1);
ParseCfg(base_path + "/modules.options", options_callback);
+
+ auto blacklist_callback = std::bind(&Modprobe::ParseBlacklistCallback, this, _1);
+ ParseCfg(base_path + "/modules.blacklist", blacklist_callback);
+ }
+
+ android::base::SetMinimumLogSeverity(android::base::INFO);
+}
+
+void Modprobe::EnableBlacklist(bool enable) {
+ blacklist_enabled = enable;
+}
+
+void Modprobe::EnableVerbose(bool enable) {
+ if (enable) {
+ android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ } else {
+ android::base::SetMinimumLogSeverity(android::base::INFO);
}
}
@@ -242,7 +284,7 @@
return it->second;
}
-bool Modprobe::InsmodWithDeps(const std::string& module_name) {
+bool Modprobe::InsmodWithDeps(const std::string& module_name, const std::string& parameters) {
if (module_name.empty()) {
LOG(ERROR) << "Need valid module name, given: " << module_name;
return false;
@@ -256,11 +298,8 @@
// load module dependencies in reverse order
for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
- const std::string& canonical_name = MakeCanonical(*dep);
- if (canonical_name.empty()) {
- return false;
- }
- if (!LoadWithAliases(canonical_name, true)) {
+ LOG(VERBOSE) << "Loading hard dep for '" << module_name << "': " << *dep;
+ if (!LoadWithAliases(*dep, true)) {
return false;
}
}
@@ -268,18 +307,20 @@
// try to load soft pre-dependencies
for (const auto& [module, softdep] : module_pre_softdep_) {
if (module_name == module) {
+ LOG(VERBOSE) << "Loading soft pre-dep for '" << module << "': " << softdep;
LoadWithAliases(softdep, false);
}
}
// load target module itself with args
- if (!Insmod(dependencies[0])) {
+ if (!Insmod(dependencies[0], parameters)) {
return false;
}
// try to load soft post-dependencies
for (const auto& [module, softdep] : module_post_softdep_) {
if (module_name == module) {
+ LOG(VERBOSE) << "Loading soft post-dep for '" << module << "': " << softdep;
LoadWithAliases(softdep, false);
}
}
@@ -287,25 +328,27 @@
return true;
}
-bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict) {
- std::set<std::string> modules_to_load = {module_name};
+bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict,
+ const std::string& parameters) {
+ std::set<std::string> modules_to_load = {MakeCanonical(module_name)};
bool module_loaded = false;
// use aliases to expand list of modules to load (multiple modules
// may alias themselves to the requested name)
for (const auto& [alias, aliased_module] : module_aliases_) {
if (fnmatch(alias.c_str(), module_name.c_str(), 0) != 0) continue;
+ LOG(VERBOSE) << "Found alias for '" << module_name << "': '" << aliased_module;
modules_to_load.emplace(aliased_module);
}
// attempt to load all modules aliased to this name
for (const auto& module : modules_to_load) {
if (!ModuleExists(module)) continue;
- if (InsmodWithDeps(module)) module_loaded = true;
+ if (InsmodWithDeps(module, parameters)) module_loaded = true;
}
if (strict && !module_loaded) {
- LOG(ERROR) << "LoadWithAliases did not find a module for " << module_name;
+ LOG(ERROR) << "LoadWithAliases was unable to load " << module_name;
return false;
}
return true;
@@ -319,3 +362,64 @@
}
return true;
}
+
+bool Modprobe::Remove(const std::string& module_name) {
+ auto dependencies = GetDependencies(MakeCanonical(module_name));
+ if (dependencies.empty()) {
+ LOG(ERROR) << "Empty dependencies for module " << module_name;
+ return false;
+ }
+ if (!Rmmod(dependencies[0])) {
+ return false;
+ }
+ for (auto dep = dependencies.begin() + 1; dep != dependencies.end(); ++dep) {
+ Rmmod(*dep);
+ }
+ return true;
+}
+
+std::vector<std::string> Modprobe::ListModules(const std::string& pattern) {
+ std::vector<std::string> rv;
+ for (const auto& [module, deps] : module_deps_) {
+ // Attempt to match both the canonical module name and the module filename.
+ if (!fnmatch(pattern.c_str(), module.c_str(), 0)) {
+ rv.emplace_back(module);
+ } else if (!fnmatch(pattern.c_str(), basename(deps[0].c_str()), 0)) {
+ rv.emplace_back(deps[0]);
+ }
+ }
+ return rv;
+}
+
+bool Modprobe::GetAllDependencies(const std::string& module,
+ std::vector<std::string>* pre_dependencies,
+ std::vector<std::string>* dependencies,
+ std::vector<std::string>* post_dependencies) {
+ std::string canonical_name = MakeCanonical(module);
+ if (pre_dependencies) {
+ pre_dependencies->clear();
+ for (const auto& [it_module, it_softdep] : module_pre_softdep_) {
+ if (canonical_name == it_module) {
+ pre_dependencies->emplace_back(it_softdep);
+ }
+ }
+ }
+ if (dependencies) {
+ dependencies->clear();
+ auto hard_deps = GetDependencies(canonical_name);
+ if (hard_deps.empty()) {
+ return false;
+ }
+ for (auto dep = hard_deps.rbegin(); dep != hard_deps.rend(); dep++) {
+ dependencies->emplace_back(*dep);
+ }
+ }
+ if (post_dependencies) {
+ for (const auto& [it_module, it_softdep] : module_post_softdep_) {
+ if (canonical_name == it_module) {
+ post_dependencies->emplace_back(it_softdep);
+ }
+ }
+ }
+ return true;
+}
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 5f3a04d..2efcac2 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -22,7 +22,7 @@
#include <modprobe/modprobe.h>
-bool Modprobe::Insmod(const std::string& path_name) {
+bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd == -1) {
@@ -35,6 +35,9 @@
if (options_iter != module_options_.end()) {
options = options_iter->second;
}
+ if (!parameters.empty()) {
+ options = options + " " + parameters;
+ }
LOG(INFO) << "Loading module " << path_name << " with args \"" << options << "\"";
int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0);
@@ -51,17 +54,32 @@
return true;
}
+bool Modprobe::Rmmod(const std::string& module_name) {
+ int ret = syscall(__NR_delete_module, MakeCanonical(module_name).c_str(), O_NONBLOCK);
+ if (ret != 0) {
+ PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
+ return false;
+ }
+ return true;
+}
+
bool Modprobe::ModuleExists(const std::string& module_name) {
struct stat fileStat;
+ if (blacklist_enabled && module_blacklist_.count(module_name)) {
+ LOG(INFO) << "module " << module_name << " is blacklisted";
+ return false;
+ }
auto deps = GetDependencies(module_name);
if (deps.empty()) {
// missing deps can happen in the case of an alias
return false;
}
if (stat(deps.front().c_str(), &fileStat)) {
+ LOG(INFO) << "module " << module_name << " does not exist";
return false;
}
if (!S_ISREG(fileStat.st_mode)) {
+ LOG(INFO) << "module " << module_name << " is not a regular file";
return false;
}
return true;
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
index 0f073cb..7d817b1 100644
--- a/libmodprobe/libmodprobe_ext_test.cpp
+++ b/libmodprobe/libmodprobe_ext_test.cpp
@@ -29,7 +29,7 @@
#include "libmodprobe_test.h"
-bool Modprobe::Insmod(const std::string& path_name) {
+bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
auto deps = GetDependencies(MakeCanonical(path_name));
if (deps.empty()) {
return false;
@@ -47,12 +47,29 @@
if (options_iter != module_options_.end()) {
options = " " + options_iter->second;
}
+ if (!parameters.empty()) {
+ options = options + " " + parameters;
+ }
+
modules_loaded.emplace_back(path_name + options);
return true;
}
+bool Modprobe::Rmmod(const std::string& module_name) {
+ for (auto it = modules_loaded.begin(); it != modules_loaded.end(); it++) {
+ if (*it == module_name) {
+ modules_loaded.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
bool Modprobe::ModuleExists(const std::string& module_name) {
auto deps = GetDependencies(module_name);
+ if (blacklist_enabled && module_blacklist_.count(module_name)) {
+ return false;
+ }
if (deps.empty()) {
// missing deps can happen in the case of an alias
return false;
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index 481658d..a711631 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -56,6 +56,14 @@
"/test13.ko",
};
+ std::vector<std::string> expected_after_remove = {
+ "/test14.ko", "/test15.ko", "/test1.ko",
+ "/test6.ko", "/test2.ko", "/test5.ko",
+ "/test8.ko", "/test7.ko param1=4", "/test9.ko param_x=1 param_y=2 param_z=3",
+ "/test10.ko", "/test12.ko", "/test11.ko",
+ "/test13.ko",
+ };
+
const std::string modules_dep =
"test1.ko:\n"
"test2.ko:\n"
@@ -91,6 +99,10 @@
"options test9.ko param_x=1 param_y=2 param_z=3\n"
"options test100.ko param_1=1\n";
+ const std::string modules_blacklist =
+ "blacklist test9.ko\n"
+ "blacklist test3.ko\n";
+
const std::string modules_load =
"test4.ko\n"
"test1.ko\n"
@@ -101,17 +113,20 @@
"test11.ko\n";
TemporaryDir dir;
- ASSERT_TRUE(android::base::WriteStringToFile(
- modules_alias, std::string(dir.path) + "/modules.alias", 0600, getuid(), getgid()));
+ auto dir_path = std::string(dir.path);
+ ASSERT_TRUE(android::base::WriteStringToFile(modules_alias, dir_path + "/modules.alias", 0600,
+ getuid(), getgid()));
- ASSERT_TRUE(android::base::WriteStringToFile(
- modules_dep, std::string(dir.path) + "/modules.dep", 0600, getuid(), getgid()));
- ASSERT_TRUE(android::base::WriteStringToFile(
- modules_softdep, std::string(dir.path) + "/modules.softdep", 0600, getuid(), getgid()));
- ASSERT_TRUE(android::base::WriteStringToFile(
- modules_options, std::string(dir.path) + "/modules.options", 0600, getuid(), getgid()));
- ASSERT_TRUE(android::base::WriteStringToFile(
- modules_load, std::string(dir.path) + "/modules.load", 0600, getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(modules_dep, dir_path + "/modules.dep", 0600,
+ getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(modules_softdep, dir_path + "/modules.softdep",
+ 0600, getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(modules_options, dir_path + "/modules.options",
+ 0600, getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(modules_load, dir_path + "/modules.load", 0600,
+ getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(modules_blacklist, dir_path + "/modules.blacklist",
+ 0600, getuid(), getgid()));
for (auto i = test_modules.begin(); i != test_modules.end(); ++i) {
*i = dir.path + *i;
@@ -131,4 +146,21 @@
}
EXPECT_TRUE(modules_loaded == expected_modules_loaded);
+
+ EXPECT_TRUE(m.Remove("test4"));
+
+ GTEST_LOG_(INFO) << "Expected modules loaded after removing test4 (in order):";
+ for (auto i = expected_after_remove.begin(); i != expected_after_remove.end(); ++i) {
+ *i = dir.path + *i;
+ GTEST_LOG_(INFO) << "\"" << *i << "\"";
+ }
+ GTEST_LOG_(INFO) << "Actual modules loaded after removing test4 (in order):";
+ for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {
+ GTEST_LOG_(INFO) << "\"" << *i << "\"";
+ }
+
+ EXPECT_TRUE(modules_loaded == expected_after_remove);
+
+ m.EnableBlacklist(true);
+ EXPECT_FALSE(m.LoadWithAliases("test4", true));
}
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 0cc603a..4ca5f5a 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -24,6 +24,7 @@
"toolbox.c",
"getevent.c",
"getprop.cpp",
+ "modprobe.cpp",
"setprop.cpp",
"start.cpp",
],
@@ -33,11 +34,15 @@
shared_libs: [
"libbase",
],
- static_libs: ["libpropertyinfoparser"],
+ static_libs: [
+ "libmodprobe",
+ "libpropertyinfoparser",
+ ],
symlinks: [
"getevent",
"getprop",
+ "modprobe",
"setprop",
"start",
"stop",
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
new file mode 100644
index 0000000..1b5f54e
--- /dev/null
+++ b/toolbox/modprobe.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <android-base/strings.h>
+#include <modprobe/modprobe.h>
+
+enum modprobe_mode {
+ AddModulesMode,
+ RemoveModulesMode,
+ ListModulesMode,
+ ShowDependenciesMode,
+};
+
+static void print_usage(void) {
+ std::cerr << "Usage:" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << " modprobe [-alrqvsDb] [-d DIR] [MODULE]+" << std::endl;
+ std::cerr << " modprobe [-alrqvsDb] [-d DIR] MODULE [symbol=value][...]" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << "Options:" << std::endl;
+ std::cerr << " -b: Apply blacklist to module names too" << std::endl;
+ std::cerr << " -d: Load modules from DIR, option may be used multiple times" << std::endl;
+ std::cerr << " -D: Print dependencies for modules only, do not load";
+ std::cerr << " -h: Print this help" << std::endl;
+ std::cerr << " -l: List modules matching pattern" << std::endl;
+ std::cerr << " -r: Remove MODULE (multiple modules may be specified)" << std::endl;
+ std::cerr << " -q: Quiet" << std::endl;
+ std::cerr << " -v: Verbose" << std::endl;
+ std::cerr << std::endl;
+}
+
+#define check_mode() \
+ if (mode != AddModulesMode) { \
+ std::cerr << "Error, multiple mode flags specified" << std::endl; \
+ print_usage(); \
+ return EXIT_FAILURE; \
+ }
+
+extern "C" int modprobe_main(int argc, char** argv) {
+ std::vector<std::string> modules;
+ std::string module_parameters;
+ std::vector<std::string> mod_dirs;
+ modprobe_mode mode = AddModulesMode;
+ bool blacklist = false;
+ bool verbose = false;
+ int rv = EXIT_SUCCESS;
+
+ int opt;
+ while ((opt = getopt(argc, argv, "abd:Dhlqrv")) != -1) {
+ switch (opt) {
+ case 'a':
+ // toybox modprobe supported -a to load multiple modules, this
+ // is supported here by default, ignore flag
+ check_mode();
+ break;
+ case 'b':
+ blacklist = true;
+ break;
+ case 'd':
+ mod_dirs.emplace_back(optarg);
+ break;
+ case 'D':
+ check_mode();
+ mode = ShowDependenciesMode;
+ break;
+ case 'h':
+ print_usage();
+ return EXIT_SUCCESS;
+ case 'l':
+ check_mode();
+ mode = ListModulesMode;
+ break;
+ case 'q':
+ verbose = false;
+ break;
+ case 'r':
+ check_mode();
+ mode = RemoveModulesMode;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ std::cerr << "Unrecognized option: " << opt << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ int parameter_count = 0;
+ for (opt = optind; opt < argc; opt++) {
+ if (!strchr(argv[opt], '=')) {
+ modules.emplace_back(argv[opt]);
+ } else {
+ parameter_count++;
+ if (module_parameters.empty()) {
+ module_parameters = argv[opt];
+ } else {
+ module_parameters = module_parameters + " " + argv[opt];
+ }
+ }
+ }
+
+ if (verbose) {
+ std::cout << "mode is " << mode << std::endl;
+ std::cout << "verbose is " << verbose << std::endl;
+ std::cout << "mod_dirs is: " << android::base::Join(mod_dirs, "") << std::endl;
+ std::cout << "modules is: " << android::base::Join(modules, "") << std::endl;
+ std::cout << "module parameters is: " << android::base::Join(module_parameters, "")
+ << std::endl;
+ }
+
+ if (modules.empty()) {
+ if (mode == ListModulesMode) {
+ // emulate toybox modprobe list with no pattern (list all)
+ modules.emplace_back("*");
+ } else {
+ std::cerr << "No modules given." << std::endl;
+ print_usage();
+ return EXIT_FAILURE;
+ }
+ }
+ if (mod_dirs.empty()) {
+ std::cerr << "No module configuration directories given." << std::endl;
+ print_usage();
+ return EXIT_FAILURE;
+ }
+ if (parameter_count && modules.size() > 1) {
+ std::cerr << "Only one module may be loaded when specifying module parameters."
+ << std::endl;
+ print_usage();
+ return EXIT_FAILURE;
+ }
+
+ Modprobe m(mod_dirs);
+ m.EnableVerbose(verbose);
+ if (blacklist) {
+ m.EnableBlacklist(true);
+ }
+
+ for (const auto& module : modules) {
+ switch (mode) {
+ case AddModulesMode:
+ if (!m.LoadWithAliases(module, true, module_parameters)) {
+ std::cerr << "Failed to load module " << module;
+ rv = EXIT_FAILURE;
+ }
+ break;
+ case RemoveModulesMode:
+ if (!m.Remove(module)) {
+ std::cerr << "Failed to remove module " << module;
+ rv = EXIT_FAILURE;
+ }
+ break;
+ case ListModulesMode: {
+ std::vector<std::string> list = m.ListModules(module);
+ std::cout << android::base::Join(list, "\n") << std::endl;
+ break;
+ }
+ case ShowDependenciesMode: {
+ std::vector<std::string> pre_deps;
+ std::vector<std::string> deps;
+ std::vector<std::string> post_deps;
+ if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
+ rv = EXIT_FAILURE;
+ break;
+ }
+ std::cout << "Dependencies for " << module << ":" << std::endl;
+ std::cout << "Soft pre-dependencies:" << std::endl;
+ std::cout << android::base::Join(pre_deps, "\n") << std::endl;
+ std::cout << "Hard dependencies:" << std::endl;
+ std::cout << android::base::Join(deps, "\n") << std::endl;
+ std::cout << "Soft post-dependencies:" << std::endl;
+ std::cout << android::base::Join(post_deps, "\n") << std::endl;
+ break;
+ }
+ default:
+ std::cerr << "Bad mode";
+ rv = EXIT_FAILURE;
+ }
+ }
+
+ return rv;
+}
diff --git a/toolbox/tools.h b/toolbox/tools.h
index 9a7ebd2..bb57e67 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,5 +1,6 @@
TOOL(getevent)
TOOL(getprop)
+TOOL(modprobe)
TOOL(setprop)
TOOL(start)
TOOL(stop)