Merge "init: set SetKptrRestrict before early-init"
diff --git a/base/include/android-base/format.h b/base/include/android-base/format.h
index 6799c1f..330040d 100644
--- a/base/include/android-base/format.h
+++ b/base/include/android-base/format.h
@@ -18,8 +18,11 @@
// We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
// It is accessed through its normal fmt:: namespace.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wshadow"
+#include <fmt/chrono.h>
+#pragma clang diagnostic pop
#include <fmt/core.h>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <fmt/printf.h>
-#include <fmt/time.h>
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 2e226da..0602e0a 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -194,6 +194,7 @@
cc_test {
name: "debuggerd_test",
defaults: ["debuggerd_defaults"],
+ require_root: true,
cflags: ["-Wno-missing-field-initializers"],
srcs: [
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 1b85b47..fa756a0 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -212,6 +212,17 @@
return true;
}
+std::string CreateLogicalPartitionParams::GetDeviceName() const {
+ if (!device_name.empty()) return device_name;
+ return GetPartitionName();
+}
+
+std::string CreateLogicalPartitionParams::GetPartitionName() const {
+ if (!partition_name.empty()) return partition_name;
+ if (partition) return android::fs_mgr::GetPartitionName(*partition);
+ return "<unknown partition>";
+}
+
bool UnmapDevice(const std::string& name) {
DeviceMapper& dm = DeviceMapper::Instance();
if (!dm.DeleteDevice(name)) {
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 8e2fdbb..2054fa1 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -77,6 +77,10 @@
// If non-null, this will use the specified IPartitionOpener rather than
// the default one.
const IPartitionOpener* partition_opener = nullptr;
+
+ // Helpers for determining the effective partition and device name.
+ std::string GetPartitionName() const;
+ std::string GetDeviceName() const;
};
bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path);
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 7c9804c..65f6e12 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -244,7 +244,8 @@
}
std::string DmTargetDefaultKey::GetParameterString() const {
- return cipher_ + " " + key_ + " " + blockdev_ + " " + std::to_string(start_sector_);
+ return cipher_ + " " + key_ + " " + blockdev_ + " " + std::to_string(start_sector_) +
+ (set_dun_ ? " 1 set_dun" : "");
}
} // namespace dm
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index a66ab7a..a78bc71 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -280,12 +280,14 @@
class DmTargetDefaultKey final : public DmTarget {
public:
DmTargetDefaultKey(uint64_t start, uint64_t length, const std::string& cipher,
- const std::string& key, const std::string& blockdev, uint64_t start_sector)
+ const std::string& key, const std::string& blockdev, uint64_t start_sector,
+ bool set_dun = false)
: DmTarget(start, length),
cipher_(cipher),
key_(key),
blockdev_(blockdev),
- start_sector_(start_sector) {}
+ start_sector_(start_sector),
+ set_dun_(set_dun) {}
std::string name() const override { return "default-key"; }
bool Valid() const override { return true; }
@@ -296,6 +298,7 @@
std::string key_;
std::string blockdev_;
uint64_t start_sector_;
+ bool set_dun_;
};
} // namespace dm
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 2b11da2..f0142bb 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -55,13 +55,14 @@
export_include_dirs: ["include"],
}
-cc_test {
- name: "liblp_test_static",
+cc_defaults {
+ name: "liblp_test_defaults",
defaults: ["fs_mgr_defaults"],
cppflags: [
"-Wno-unused-parameter",
],
static_libs: [
+ "libcutils",
"libgmock",
"libfs_mgr",
"liblp",
@@ -74,11 +75,20 @@
"test_partition_opener.cpp",
"utility_test.cpp",
],
- target: {
- android: {
- static_libs: [
- "libcutils",
- ],
- },
- },
}
+
+cc_test {
+ name: "liblp_test",
+ defaults: ["liblp_test_defaults"],
+ test_config: "liblp_test.xml",
+ test_suites: [
+ "device-tests",
+ "vts-core",
+ ],
+}
+
+cc_test {
+ name: "vts_kernel_liblp_test",
+ defaults: ["liblp_test_defaults"],
+}
+
diff --git a/fs_mgr/liblp/AndroidTest.xml b/fs_mgr/liblp/AndroidTest.xml
index fe1002c..2eb0ad1 100644
--- a/fs_mgr/liblp/AndroidTest.xml
+++ b/fs_mgr/liblp/AndroidTest.xml
@@ -21,8 +21,8 @@
</target_preparer>
<test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
<option name="test-module-name" value="VtsKernelLiblpTest"/>
- <option name="binary-test-source" value="_32bit::DATA/nativetest/liblp_test_static/liblp_test_static" />
- <option name="binary-test-source" value="_64bit::DATA/nativetest64/liblp_test_static/liblp_test_static" />
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_kernel_liblp_test/vts_kernel_liblp_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_kernel_liblp_test/vts_kernel_liblp_test" />
<option name="binary-test-type" value="gtest"/>
<option name="test-timeout" value="1m"/>
<option name="precondition-first-api-level" value="29" />
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
new file mode 100644
index 0000000..04bcbda
--- /dev/null
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "liblp_test"
+ }
+ ]
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 77c9c28..8a74745 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -232,7 +232,7 @@
memset(&header_, 0, sizeof(header_));
header_.magic = LP_METADATA_HEADER_MAGIC;
header_.major_version = LP_METADATA_MAJOR_VERSION;
- header_.minor_version = LP_METADATA_MINOR_VERSION;
+ header_.minor_version = LP_METADATA_MINOR_VERSION_MIN;
header_.header_size = sizeof(header_);
header_.partitions.entry_size = sizeof(LpMetadataPartition);
header_.extents.entry_size = sizeof(LpMetadataExtent);
@@ -796,6 +796,11 @@
return nullptr;
}
+ if (partition->attributes() & LP_PARTITION_ATTR_UPDATED) {
+ static const uint16_t kMinVersion = LP_METADATA_VERSION_FOR_UPDATED_ATTR;
+ metadata->header.minor_version = std::max(metadata->header.minor_version, kMinVersion);
+ }
+
strncpy(part.name, partition->name().c_str(), sizeof(part.name));
part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
part.num_extents = static_cast<uint32_t>(partition->extents().size());
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index ecad7d4..c5b4047 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -19,29 +19,20 @@
#include <liblp/builder.h>
#include "liblp_test.h"
-#include "mock_property_fetcher.h"
#include "utility.h"
using namespace std;
using namespace android::fs_mgr;
-using ::android::fs_mgr::MockPropertyFetcher;
+using namespace android::fs_mgr::testing;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::ElementsAre;
using ::testing::NiceMock;
using ::testing::Return;
-void ResetPropertyFetcher() {
- IPropertyFetcher::OverrideForTesting(std::make_unique<NiceMock<MockPropertyFetcher>>());
-}
-
-MockPropertyFetcher* GetMockedPropertyFetcher() {
- return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
-}
-
class Environment : public ::testing::Environment {
public:
- void SetUp() override { ResetPropertyFetcher(); }
+ void SetUp() override { ResetMockPropertyFetcher(); }
};
int main(int argc, char** argv) {
@@ -358,7 +349,7 @@
const LpMetadataHeader& header = exported->header;
EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
- EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
+ EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION_MIN);
ASSERT_EQ(exported->partitions.size(), 2);
ASSERT_EQ(exported->extents.size(), 3);
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index b759fd1..382d53d 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -24,9 +24,9 @@
#include <liblp/property_fetcher.h>
#include "liblp_test.h"
-#include "mock_property_fetcher.h"
using namespace android::fs_mgr;
+using namespace android::fs_mgr::testing;
using ::testing::Return;
// Compliance test on the actual device with dynamic partitions.
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 8934aaf..6e928b4 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -39,7 +39,11 @@
/* Current metadata version. */
#define LP_METADATA_MAJOR_VERSION 10
-#define LP_METADATA_MINOR_VERSION 0
+#define LP_METADATA_MINOR_VERSION_MIN 0
+#define LP_METADATA_MINOR_VERSION_MAX 1
+
+/* Metadata version needed to use the UPDATED partition attribute. */
+#define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1
/* Attributes for the LpMetadataPartition::attributes field.
*
@@ -58,8 +62,20 @@
*/
#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
-/* Mask that defines all valid attributes. */
-#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
+/* This flag is applied automatically when using MetadataBuilder::NewForUpdate.
+ * It signals that the partition was created (or modified) for a snapshot-based
+ * update. If this flag is not present, the partition was likely flashed via
+ * fastboot.
+ */
+#define LP_PARTITION_ATTR_UPDATED (1 << 2)
+
+/* Mask that defines all valid attributes. When changing this, make sure to
+ * update ParseMetadata().
+ */
+#define LP_PARTITION_ATTRIBUTE_MASK_V0 \
+ (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
+#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTR_UPDATED)
+#define LP_PARTITION_ATTRIBUTE_MASK LP_PARTITION_ATTRIBUTE_MASK_V1
/* Default name of the physical partition that holds logical partition entries.
* The layout of this partition will look like:
diff --git a/fs_mgr/liblp/mock_property_fetcher.h b/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
similarity index 79%
rename from fs_mgr/liblp/mock_property_fetcher.h
rename to fs_mgr/liblp/include/liblp/mock_property_fetcher.h
index 4612c52..f15611b 100644
--- a/fs_mgr/liblp/mock_property_fetcher.h
+++ b/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
@@ -22,6 +22,7 @@
namespace android {
namespace fs_mgr {
+namespace testing {
class MockPropertyFetcher : public IPropertyFetcher {
public:
@@ -41,8 +42,15 @@
}
};
+static inline void ResetMockPropertyFetcher() {
+ IPropertyFetcher::OverrideForTesting(
+ std::make_unique<::testing::NiceMock<MockPropertyFetcher>>());
+}
+
+static inline MockPropertyFetcher* GetMockedPropertyFetcher() {
+ return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
+}
+
+} // namespace testing
} // namespace fs_mgr
} // namespace android
-
-android::fs_mgr::MockPropertyFetcher* GetMockedPropertyFetcher();
-void ResetPropertyFetcher();
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 4d00944..22f6746 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -27,7 +27,6 @@
#include "images.h"
#include "liblp_test.h"
-#include "mock_property_fetcher.h"
#include "reader.h"
#include "test_partition_opener.h"
#include "utility.h"
@@ -35,6 +34,7 @@
using namespace std;
using namespace android::fs_mgr;
+using namespace android::fs_mgr::testing;
using ::testing::_;
using ::testing::Return;
using unique_fd = android::base::unique_fd;
@@ -713,3 +713,32 @@
ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
}
+
+TEST_F(LiblpTest, UpdateVirtualAB) {
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+ .WillByDefault(Return(true));
+
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ DefaultPartitionOpener opener(fd);
+ auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+ ASSERT_NE(builder, nullptr);
+ auto updated = builder->Export();
+ ASSERT_NE(updated, nullptr);
+ ASSERT_TRUE(UpdatePartitionTable(opener, "super", *updated.get(), 1));
+
+ // Validate old slot.
+ auto metadata = ReadMetadata(opener, "super", 0);
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->header.minor_version, 0);
+ ASSERT_GE(metadata->partitions.size(), 1);
+ ASSERT_EQ(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
+
+ // Validate new slot.
+ metadata = ReadMetadata(opener, "super", 1);
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->header.minor_version, 1);
+ ASSERT_GE(metadata->partitions.size(), 1);
+ ASSERT_NE(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
+}
diff --git a/fs_mgr/liblp/liblp_test.h b/fs_mgr/liblp/liblp_test.h
index 892f43d..c61aaa5 100644
--- a/fs_mgr/liblp/liblp_test.h
+++ b/fs_mgr/liblp/liblp_test.h
@@ -18,10 +18,18 @@
#include <gtest/gtest.h>
-#include "mock_property_fetcher.h"
+#include <liblp/mock_property_fetcher.h>
+
+namespace android {
+namespace fs_mgr {
+namespace testing {
class LiblpTest : public ::testing::Test {
public:
- void SetUp() override { ResetPropertyFetcher(); }
- void TearDown() override { ResetPropertyFetcher(); }
+ void SetUp() override { ResetMockPropertyFetcher(); }
+ void TearDown() override { ResetMockPropertyFetcher(); }
};
+
+} // namespace testing
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/liblp_test.xml b/fs_mgr/liblp/liblp_test.xml
new file mode 100644
index 0000000..d9ee12e
--- /dev/null
+++ b/fs_mgr/liblp/liblp_test.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for liblp_test">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="vts-core" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="liblp_test" />
+ </test>
+</configuration>
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 8dbe955..aecf685 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -181,7 +181,7 @@
}
// Check that the version is compatible.
if (header.major_version != LP_METADATA_MAJOR_VERSION ||
- header.minor_version > LP_METADATA_MINOR_VERSION) {
+ header.minor_version > LP_METADATA_MINOR_VERSION_MAX) {
LERROR << "Logical partition metadata has incompatible version.";
return false;
}
@@ -245,6 +245,13 @@
return nullptr;
}
+ uint32_t valid_attributes = 0;
+ if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {
+ valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V1;
+ } else {
+ valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
+ }
+
// ValidateTableSize ensured that |cursor| is valid for the number of
// entries in the table.
uint8_t* cursor = buffer.get() + header.partitions.offset;
@@ -253,7 +260,7 @@
memcpy(&partition, cursor, sizeof(partition));
cursor += header.partitions.entry_size;
- if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
+ if (partition.attributes & ~valid_attributes) {
LERROR << "Logical partition has invalid attribute set.";
return nullptr;
}
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 17f05aa..afcce8f 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -269,6 +269,7 @@
<< "; this partition should not belong to this group!";
continue; // not adding to new_partition_ptrs
}
+ partition.attributes |= LP_PARTITION_ATTR_UPDATED;
partition.group_index = std::distance(new_group_ptrs.begin(), it);
new_partition_ptrs.push_back(&partition);
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 51f5c50..a54db58 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -28,6 +28,8 @@
],
static_libs: [
"libdm",
+ "libfs_mgr",
+ "liblp",
],
whole_static_libs: [
"libext2_uuid",
@@ -84,6 +86,7 @@
"libcutils",
"libcrypto",
"libfs_mgr",
+ "libgmock",
"liblp",
"libsnapshot",
],
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 1630ee5..b982b4b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -24,6 +24,7 @@
#include <android-base/unique_fd.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
@@ -37,6 +38,11 @@
class IImageManager;
} // namespace fiemap
+namespace fs_mgr {
+struct CreateLogicalPartitionParams;
+class IPartitionOpener;
+} // namespace fs_mgr
+
namespace snapshot {
enum class UpdateState : unsigned int {
@@ -64,6 +70,10 @@
};
class SnapshotManager final {
+ using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
+ using LpMetadata = android::fs_mgr::LpMetadata;
+ using IPartitionOpener = android::fs_mgr::IPartitionOpener;
+
public:
// Dependency injection for testing.
class IDeviceInfo {
@@ -72,6 +82,7 @@
virtual std::string GetGsidDir() const = 0;
virtual std::string GetMetadataDir() const = 0;
virtual std::string GetSlotSuffix() const = 0;
+ virtual const IPartitionOpener& GetPartitionOpener() const = 0;
};
~SnapshotManager();
@@ -81,6 +92,14 @@
// instance will be created.
static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
+ // This is similar to New(), except designed specifically for first-stage
+ // init.
+ static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
+
+ // Helper function for first-stage init to check whether a SnapshotManager
+ // might be needed to perform first-stage mounts.
+ static bool IsSnapshotManagerNeeded();
+
// Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None.
bool BeginUpdate();
@@ -126,13 +145,25 @@
// Other: 0
UpdateState GetUpdateState(double* progress = nullptr);
+ // If this returns true, first-stage mount must call
+ // CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
+ bool NeedSnapshotsInFirstStageMount();
+
+ // Perform first-stage mapping of snapshot targets. This replaces init's
+ // call to CreateLogicalPartitions when snapshots are present.
+ bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
+
private:
+ FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
FRIEND_TEST(SnapshotTest, CreateSnapshot);
- FRIEND_TEST(SnapshotTest, MapSnapshot);
+ FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback);
+ FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge);
+ FRIEND_TEST(SnapshotTest, FlashSuperDuringUpdate);
FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
- FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
+ FRIEND_TEST(SnapshotTest, MapSnapshot);
FRIEND_TEST(SnapshotTest, Merge);
FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
+ FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
friend class SnapshotTest;
using DmTargetSnapshot = android::dm::DmTargetSnapshot;
@@ -141,9 +172,12 @@
explicit SnapshotManager(IDeviceInfo* info);
- // This is created lazily since it connects via binder.
+ // This is created lazily since it can connect via binder.
bool EnsureImageManager();
+ // Helper for first-stage init.
+ bool ForceLocalImageManager();
+
// Helper function for tests.
IImageManager* image_manager() const { return images_.get(); }
@@ -278,6 +312,7 @@
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
std::unique_ptr<IImageManager> images_;
+ bool has_local_image_manager_ = false;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index fef5c06..ab1157b 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -20,6 +20,7 @@
#include <sys/unistd.h>
#include <thread>
+#include <unordered_set>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -27,9 +28,11 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr_dm_linear.h>
#include <fstab/fstab.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
namespace android {
namespace snapshot {
@@ -43,22 +46,30 @@
using android::dm::kSectorSize;
using android::dm::SnapshotStorageMode;
using android::fiemap::IImageManager;
+using android::fs_mgr::CreateLogicalPartition;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::GetPartitionName;
+using android::fs_mgr::LpMetadata;
+using android::fs_mgr::SlotNumberForSlotSuffix;
using namespace std::chrono_literals;
using namespace std::string_literals;
// Unit is sectors, this is a 4K chunk.
static constexpr uint32_t kSnapshotChunkSize = 8;
-
-static constexpr char kSnapshotBootIndicatorFile[] = "snapshot-boot";
+static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
class DeviceInfo final : public SnapshotManager::IDeviceInfo {
public:
std::string GetGsidDir() const override { return "ota"s; }
std::string GetMetadataDir() const override { return "/metadata/ota"s; }
std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
+ const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const { return opener_; }
+
+ private:
+ android::fs_mgr::PartitionOpener opener_;
};
-// Note: IIMageManager is an incomplete type in the header, so the default
+// Note: IImageManager is an incomplete type in the header, so the default
// destructor doesn't work.
SnapshotManager::~SnapshotManager() {}
@@ -69,6 +80,14 @@
return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
}
+std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
+ auto sm = New(info);
+ if (!sm || !sm->ForceLocalImageManager()) {
+ return nullptr;
+ }
+ return sm;
+}
+
SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
gsid_dir_ = device_->GetGsidDir();
metadata_dir_ = device_->GetMetadataDir();
@@ -78,6 +97,10 @@
return snapshot_name + "-cow";
}
+static std::string GetBaseDeviceName(const std::string& partition_name) {
+ return partition_name + "-base";
+}
+
bool SnapshotManager::BeginUpdate() {
auto file = LockExclusive();
if (!file) return false;
@@ -197,8 +220,8 @@
}
// Validate the block device size, as well as the requested snapshot size.
- // During this we also compute the linear sector region if any.
- {
+ // Note that during first-stage init, we don't have the device paths.
+ if (android::base::StartsWith(base_device, "/")) {
unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "open failed: " << base_device;
@@ -228,8 +251,17 @@
auto cow_name = GetCowName(name);
+ bool ok;
std::string cow_dev;
- if (!images_->MapImageDevice(cow_name, timeout_ms, &cow_dev)) {
+ if (has_local_image_manager_) {
+ // If we forced a local image manager, it means we don't have binder,
+ // which means first-stage init. We must use device-mapper.
+ const auto& opener = device_->GetPartitionOpener();
+ ok = images_->MapImageWithDeviceMapper(opener, cow_name, &cow_dev);
+ } else {
+ ok = images_->MapImageDevice(cow_name, timeout_ms, &cow_dev);
+ }
+ if (!ok) {
LOG(ERROR) << "Could not map image device: " << cow_name;
return false;
}
@@ -305,7 +337,7 @@
}
auto& dm = DeviceMapper::Instance();
- if (dm.GetState(name) != DmDeviceState::INVALID && !dm.DeleteDevice(name)) {
+ if (!dm.DeleteDeviceIfExists(name)) {
LOG(ERROR) << "Could not delete snapshot device: " << name;
return false;
}
@@ -313,8 +345,7 @@
// There may be an extra device, since the kernel doesn't let us have a
// snapshot and linear target in the same table.
auto dm_name = GetSnapshotDeviceName(name, status);
- if (name != dm_name && dm.GetState(dm_name) != DmDeviceState::INVALID &&
- !dm.DeleteDevice(dm_name)) {
+ if (name != dm_name && !dm.DeleteDeviceIfExists(dm_name)) {
LOG(ERROR) << "Could not delete inner snapshot device: " << dm_name;
return false;
}
@@ -705,7 +736,7 @@
}
std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
- return metadata_dir_ + "/" + kSnapshotBootIndicatorFile;
+ return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
}
void SnapshotManager::RemoveSnapshotBootIndicator() {
@@ -866,7 +897,7 @@
// the device, and device-mapper should have flushed remaining I/O. We
// could in theory replace with dm-zero (or re-use the table above), but
// for now it's better to know why this would fail.
- if (!dm.DeleteDevice(dm_name)) {
+ if (!dm.DeleteDeviceIfExists(dm_name)) {
LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
<< "reclaimed until after reboot.";
return false;
@@ -931,6 +962,132 @@
return true;
}
+bool SnapshotManager::IsSnapshotManagerNeeded() {
+ return access(kBootIndicatorPath, F_OK) == 0;
+}
+
+bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
+ // If we fail to read, we'll wind up using CreateLogicalPartitions, which
+ // will create devices that look like the old slot, except with extra
+ // content at the end of each device. This will confuse dm-verity, and
+ // ultimately we'll fail to boot. Why not make it a fatal error and have
+ // the reason be clearer? Because the indicator file still exists, and
+ // if this was FATAL, reverting to the old slot would be broken.
+ std::string old_slot;
+ auto boot_file = GetSnapshotBootIndicatorPath();
+ if (!android::base::ReadFileToString(boot_file, &old_slot)) {
+ PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
+ return false;
+ }
+ if (device_->GetSlotSuffix() == old_slot) {
+ LOG(INFO) << "Detected slot rollback, will not mount snapshots.";
+ return false;
+ }
+
+ // If we can't read the update state, it's unlikely anything else will
+ // succeed, so this is a fatal error. We'll eventually exhaust boot
+ // attempts and revert to the old slot.
+ auto lock = LockShared();
+ if (!lock) {
+ LOG(FATAL) << "Could not read update state to determine snapshot status";
+ return false;
+ }
+ switch (ReadUpdateState(lock.get())) {
+ case UpdateState::Unverified:
+ case UpdateState::Merging:
+ case UpdateState::MergeFailed:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SnapshotManager::CreateLogicalAndSnapshotPartitions(const std::string& super_device) {
+ LOG(INFO) << "Creating logical partitions with snapshots as needed";
+
+ auto lock = LockExclusive();
+ if (!lock) return false;
+
+ std::vector<std::string> snapshot_list;
+ if (!ListSnapshots(lock.get(), &snapshot_list)) {
+ return false;
+ }
+
+ std::unordered_set<std::string> live_snapshots;
+ for (const auto& snapshot : snapshot_list) {
+ SnapshotStatus status;
+ if (!ReadSnapshotStatus(lock.get(), snapshot, &status)) {
+ return false;
+ }
+ if (status.state != SnapshotState::MergeCompleted) {
+ live_snapshots.emplace(snapshot);
+ }
+ }
+
+ const auto& opener = device_->GetPartitionOpener();
+ uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+ auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
+ if (!metadata) {
+ LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
+ return false;
+ }
+
+ // Map logical partitions.
+ auto& dm = DeviceMapper::Instance();
+ for (const auto& partition : metadata->partitions) {
+ auto partition_name = GetPartitionName(partition);
+ if (!partition.num_extents) {
+ LOG(INFO) << "Skipping zero-length logical partition: " << partition_name;
+ continue;
+ }
+
+ if (!(partition.attributes & LP_PARTITION_ATTR_UPDATED)) {
+ LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
+ << partition_name;
+ live_snapshots.erase(partition_name);
+ }
+
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata = metadata.get(),
+ .partition = &partition,
+ .partition_opener = &opener,
+ };
+
+ if (auto iter = live_snapshots.find(partition_name); iter != live_snapshots.end()) {
+ // If the device has a snapshot, it'll need to be writable, and
+ // we'll need to create the logical partition with a marked-up name
+ // (since the snapshot will use the partition name).
+ params.force_writable = true;
+ params.device_name = GetBaseDeviceName(partition_name);
+ }
+
+ std::string ignore_path;
+ if (!CreateLogicalPartition(params, &ignore_path)) {
+ LOG(ERROR) << "Could not create logical partition " << partition_name << " as device "
+ << params.GetDeviceName();
+ return false;
+ }
+ if (!params.force_writable) {
+ // No snapshot.
+ continue;
+ }
+
+ // We don't have ueventd in first-stage init, so use device major:minor
+ // strings instead.
+ std::string base_device;
+ if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
+ LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
+ return false;
+ }
+ if (!MapSnapshot(lock.get(), partition_name, base_device, {}, &ignore_path)) {
+ LOG(ERROR) << "Could not map snapshot for partition: " << partition_name;
+ return false;
+ }
+ }
+ return true;
+}
+
auto SnapshotManager::OpenFile(const std::string& file, int open_flags, int lock_flags)
-> std::unique_ptr<LockedFile> {
unique_fd fd(open(file.c_str(), open_flags | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0660));
@@ -1173,5 +1330,15 @@
return true;
}
+bool SnapshotManager::ForceLocalImageManager() {
+ images_ = android::fiemap::ImageManager::Open(gsid_dir_);
+ if (!images_) {
+ LOG(ERROR) << "Could not open ImageManager";
+ return false;
+ }
+ has_local_image_manager_ = true;
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 518c619..acffe8c 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -31,6 +31,7 @@
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
+#include <liblp/mock_property_fetcher.h>
#include "test_helpers.h"
@@ -43,7 +44,10 @@
using android::fiemap::IImageManager;
using android::fs_mgr::BlockDeviceInfo;
using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::DestroyLogicalPartition;
using android::fs_mgr::MetadataBuilder;
+using namespace ::testing;
+using namespace android::fs_mgr::testing;
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -55,33 +59,20 @@
static constexpr uint64_t kSuperSize = 16 * 1024 * 1024;
-// Helper to remove stale partitions in fake super.
-void CleanupPartitions() {
- // These are hardcoded since we might abort in the middle of a test, and
- // can't recover the in-use list.
- static std::vector<std::string> kPartitionNames = {
- "base-device",
- };
-
- auto& dm = DeviceMapper::Instance();
- for (const auto& partition : kPartitionNames) {
- if (dm.GetState(partition) != DmDeviceState::INVALID) {
- dm.DeleteDevice(partition);
- }
- }
-}
-
class SnapshotTest : public ::testing::Test {
public:
SnapshotTest() : dm_(DeviceMapper::Instance()) {}
+ // This is exposed for main.
+ void Cleanup() {
+ InitializeState();
+ CleanupTestArtifacts();
+ }
+
protected:
void SetUp() override {
- ASSERT_TRUE(sm->EnsureImageManager());
- image_manager_ = sm->image_manager();
-
- test_device->set_slot_suffix("_a");
-
+ ResetMockPropertyFetcher();
+ InitializeState();
CleanupTestArtifacts();
FormatFakeSuper();
@@ -92,6 +83,14 @@
lock_ = nullptr;
CleanupTestArtifacts();
+ ResetMockPropertyFetcher();
+ }
+
+ void InitializeState() {
+ ASSERT_TRUE(sm->EnsureImageManager());
+ image_manager_ = sm->image_manager();
+
+ test_device->set_slot_suffix("_a");
}
void CleanupTestArtifacts() {
@@ -102,7 +101,8 @@
// get an accurate list to remove.
lock_ = nullptr;
- std::vector<std::string> snapshots = {"test-snapshot"};
+ std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
+ "test_partition_b"};
for (const auto& snapshot : snapshots) {
DeleteSnapshotDevice(snapshot);
DeleteBackingImage(image_manager_, snapshot + "-cow");
@@ -111,7 +111,15 @@
android::base::RemoveFileIfExists(status_file);
}
- CleanupPartitions();
+ // Remove stale partitions in fake super.
+ std::vector<std::string> partitions = {
+ "base-device",
+ "test_partition_b",
+ "test_partition_b-base",
+ };
+ for (const auto& partition : partitions) {
+ DeleteDevice(partition);
+ }
if (sm->GetUpdateState() != UpdateState::None) {
auto state_file = sm->GetStateFilePath();
@@ -124,6 +132,9 @@
return !!lock_;
}
+ // This is so main() can instantiate this to invoke Cleanup.
+ virtual void TestBody() override {}
+
void FormatFakeSuper() {
BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4096);
std::vector<BlockDeviceInfo> devices = {super_device};
@@ -150,6 +161,7 @@
return false;
}
+ // Update the source slot.
auto metadata = builder->Export();
if (!metadata) return false;
if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) {
@@ -168,12 +180,42 @@
return CreateLogicalPartition(params, path);
}
- void DeleteSnapshotDevice(const std::string& snapshot) {
- if (dm_.GetState(snapshot) != DmDeviceState::INVALID) {
- ASSERT_TRUE(dm_.DeleteDevice(snapshot));
+ bool MapUpdatePartitions() {
+ TestPartitionOpener opener(fake_super);
+ auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+ if (!builder) return false;
+
+ auto metadata = builder->Export();
+ if (!metadata) return false;
+
+ // Update the destination slot, mark it as updated.
+ if (!UpdatePartitionTable(opener, "super", *metadata.get(), 1)) {
+ return false;
}
- if (dm_.GetState(snapshot + "-inner") != DmDeviceState::INVALID) {
- ASSERT_TRUE(dm_.DeleteDevice(snapshot + "-inner"));
+
+ for (const auto& partition : metadata->partitions) {
+ CreateLogicalPartitionParams params = {
+ .block_device = fake_super,
+ .metadata = metadata.get(),
+ .partition = &partition,
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ std::string ignore_path;
+ if (!CreateLogicalPartition(params, &ignore_path)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void DeleteSnapshotDevice(const std::string& snapshot) {
+ DeleteDevice(snapshot);
+ DeleteDevice(snapshot + "-inner");
+ }
+ void DeleteDevice(const std::string& device) {
+ if (dm_.GetState(device) != DmDeviceState::INVALID) {
+ ASSERT_TRUE(dm_.DeleteDevice(device));
}
}
@@ -246,6 +288,25 @@
ASSERT_FALSE(sm->InitiateMerge());
}
+TEST_F(SnapshotTest, CleanFirstStageMount) {
+ // If there's no update in progress, there should be no first-stage mount
+ // needed.
+ TestDeviceInfo* info = new TestDeviceInfo(fake_super);
+ auto sm = SnapshotManager::NewForFirstStageMount(info);
+ ASSERT_NE(sm, nullptr);
+ ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
+}
+
+TEST_F(SnapshotTest, FirstStageMountAfterRollback) {
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+ // We didn't change the slot, so we shouldn't need snapshots.
+ TestDeviceInfo* info = new TestDeviceInfo(fake_super);
+ auto sm = SnapshotManager::NewForFirstStageMount(info);
+ ASSERT_NE(sm, nullptr);
+ ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
+}
+
TEST_F(SnapshotTest, Merge) {
ASSERT_TRUE(AcquireLock());
@@ -270,12 +331,12 @@
ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target));
ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
- // Set the state to Unverified, as if we finished an update.
- ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));
-
// Release the lock.
lock_ = nullptr;
+ // Done updating.
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
test_device->set_slot_suffix("_b");
ASSERT_TRUE(sm->InitiateMerge());
@@ -343,6 +404,86 @@
ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted);
}
+TEST_F(SnapshotTest, FirstStageMountAndMerge) {
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+ .WillByDefault(Return(true));
+
+ ASSERT_TRUE(AcquireLock());
+
+ static const uint64_t kDeviceSize = 1024 * 1024;
+
+ ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+ ASSERT_TRUE(MapUpdatePartitions());
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
+ kDeviceSize));
+
+ // Simulate a reboot into the new slot.
+ lock_ = nullptr;
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(DestroyLogicalPartition("test_partition_b"));
+
+ auto rebooted = new TestDeviceInfo(fake_super);
+ rebooted->set_slot_suffix("_b");
+
+ auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+ ASSERT_TRUE(AcquireLock());
+
+ // Validate that we have a snapshot device.
+ SnapshotManager::SnapshotStatus status;
+ ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
+ ASSERT_EQ(status.state, SnapshotManager::SnapshotState::Created);
+
+ DeviceMapper::TargetInfo target;
+ auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
+ ASSERT_TRUE(init->IsSnapshotDevice(dm_name, &target));
+ ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+}
+
+TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
+ ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
+ .WillByDefault(Return(true));
+
+ ASSERT_TRUE(AcquireLock());
+
+ static const uint64_t kDeviceSize = 1024 * 1024;
+
+ ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
+ ASSERT_TRUE(MapUpdatePartitions());
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
+ kDeviceSize));
+
+ // Simulate a reboot into the new slot.
+ lock_ = nullptr;
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+ ASSERT_TRUE(DestroyLogicalPartition("test_partition_b"));
+
+ // Reflash the super partition.
+ FormatFakeSuper();
+ ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
+
+ auto rebooted = new TestDeviceInfo(fake_super);
+ rebooted->set_slot_suffix("_b");
+
+ auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+ ASSERT_TRUE(AcquireLock());
+
+ SnapshotManager::SnapshotStatus status;
+ ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
+
+ // We should not get a snapshot device now.
+ DeviceMapper::TargetInfo target;
+ auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
+ ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target));
+}
+
} // namespace snapshot
} // namespace android
@@ -383,6 +524,9 @@
return 1;
}
+ // Clean up previous run.
+ SnapshotTest().Cleanup();
+
// Use a separate image manager for our fake super partition.
auto super_images = IImageManager::Open("ota/test/super", 10s);
if (!super_images) {
@@ -390,8 +534,7 @@
return 1;
}
- // Clean up previous run.
- CleanupPartitions();
+ // Clean up any old copy.
DeleteBackingImage(super_images.get(), "fake-super");
// Create and map the fake super partition.
@@ -405,11 +548,10 @@
std::cerr << "Could not map fake super partition\n";
return 1;
}
+ test_device->set_fake_super(fake_super);
auto result = RUN_ALL_TESTS();
- // Clean up again.
- CleanupPartitions();
DeleteBackingImage(super_images.get(), "fake-super");
return result;
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 17ffa4e..f67dd21 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -46,5 +46,12 @@
return PartitionOpener::GetInfo(partition_name, info);
}
+std::string TestPartitionOpener::GetDeviceString(const std::string& partition_name) const {
+ if (partition_name == "super") {
+ return fake_super_path_;
+ }
+ return PartitionOpener::GetDeviceString(partition_name);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index 9491be3..c87f118 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -25,18 +25,6 @@
using namespace std::string_literals;
-class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
- public:
- std::string GetGsidDir() const override { return "ota/test"s; }
- std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
- std::string GetSlotSuffix() const override { return slot_suffix_; }
-
- void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
-
- private:
- std::string slot_suffix_ = "_a";
-};
-
// Redirect requests for "super" to our fake super partition.
class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
public:
@@ -46,11 +34,33 @@
android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
bool GetInfo(const std::string& partition_name,
android::fs_mgr::BlockDeviceInfo* info) const override;
+ std::string GetDeviceString(const std::string& partition_name) const override;
private:
std::string fake_super_path_;
};
+class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
+ public:
+ TestDeviceInfo() {}
+ explicit TestDeviceInfo(const std::string& fake_super) { set_fake_super(fake_super); }
+ std::string GetGsidDir() const override { return "ota/test"s; }
+ std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
+ std::string GetSlotSuffix() const override { return slot_suffix_; }
+ const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
+ return *opener_.get();
+ }
+
+ void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
+ void set_fake_super(const std::string& path) {
+ opener_ = std::make_unique<TestPartitionOpener>(path);
+ }
+
+ private:
+ std::string slot_suffix_ = "_a";
+ std::unique_ptr<TestPartitionOpener> opener_;
+};
+
// Helper for error-spam-free cleanup.
void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);
diff --git a/healthd/api/charger_sysprop-current.txt b/healthd/api/charger_sysprop-current.txt
new file mode 100644
index 0000000..678c847
--- /dev/null
+++ b/healthd/api/charger_sysprop-current.txt
@@ -0,0 +1,29 @@
+props {
+ module: "android.sysprop.ChargerProperties"
+ prop {
+ api_name: "disable_init_blank"
+ scope: Internal
+ prop_name: "ro.charger.disable_init_blank"
+ }
+ prop {
+ api_name: "draw_split_offset"
+ type: Long
+ scope: Internal
+ prop_name: "ro.charger.draw_split_offset"
+ }
+ prop {
+ api_name: "draw_split_screen"
+ scope: Internal
+ prop_name: "ro.charger.draw_split_screen"
+ }
+ prop {
+ api_name: "enable_suspend"
+ scope: Internal
+ prop_name: "ro.charger.enable_suspend"
+ }
+ prop {
+ api_name: "no_ui"
+ scope: Internal
+ prop_name: "ro.charger.no_ui"
+ }
+}
diff --git a/healthd/api/charger_sysprop-latest.txt b/healthd/api/charger_sysprop-latest.txt
new file mode 100644
index 0000000..678c847
--- /dev/null
+++ b/healthd/api/charger_sysprop-latest.txt
@@ -0,0 +1,29 @@
+props {
+ module: "android.sysprop.ChargerProperties"
+ prop {
+ api_name: "disable_init_blank"
+ scope: Internal
+ prop_name: "ro.charger.disable_init_blank"
+ }
+ prop {
+ api_name: "draw_split_offset"
+ type: Long
+ scope: Internal
+ prop_name: "ro.charger.draw_split_offset"
+ }
+ prop {
+ api_name: "draw_split_screen"
+ scope: Internal
+ prop_name: "ro.charger.draw_split_screen"
+ }
+ prop {
+ api_name: "enable_suspend"
+ scope: Internal
+ prop_name: "ro.charger.enable_suspend"
+ }
+ prop {
+ api_name: "no_ui"
+ scope: Internal
+ prop_name: "ro.charger.no_ui"
+ }
+}
diff --git a/healthd/api/current.txt b/healthd/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/removed.txt b/healthd/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/system-current.txt b/healthd/api/system-current.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/system-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/system-removed.txt b/healthd/api/system-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/test-current.txt b/healthd/api/test-current.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/test-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/healthd/api/test-removed.txt b/healthd/api/test-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/healthd/api/test-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/init/Android.bp b/init/Android.bp
index 7dfb828..81e7dd9 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -70,6 +70,7 @@
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
+ "libsnapshot_nobinder",
],
shared_libs: [
"libbacktrace",
@@ -117,6 +118,7 @@
"firmware_handler.cpp",
"first_stage_init.cpp",
"first_stage_mount.cpp",
+ "fscrypt_init_extensions.cpp",
"import_parser.cpp",
"init.cpp",
"interface_utils.cpp",
@@ -209,6 +211,8 @@
cc_test {
name: "CtsInitTestCases",
defaults: ["init_defaults"],
+ require_root: true,
+
compile_multilib: "both",
multilib: {
lib32: {
diff --git a/init/Android.mk b/init/Android.mk
index 006e1bf..d7258a7 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -114,6 +114,7 @@
libbacktrace \
libmodprobe \
libext2_uuid \
+ libsnapshot_nobinder \
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 7076926..8c4d989 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -55,7 +55,6 @@
#include <cutils/android_reboot.h>
#include <fs_mgr.h>
#include <fscrypt/fscrypt.h>
-#include <fscrypt/fscrypt_init_extensions.h>
#include <libgsi/libgsi.h>
#include <selinux/android.h>
#include <selinux/label.h>
@@ -64,6 +63,7 @@
#include "action_manager.h"
#include "bootchart.h"
+#include "fscrypt_init_extensions.h"
#include "init.h"
#include "mount_namespace.h"
#include "parser.h"
@@ -357,51 +357,64 @@
// mkdir <path> [mode] [owner] [group]
static Result<void> do_mkdir(const BuiltinArguments& args) {
mode_t mode = 0755;
- if (args.size() >= 3) {
- mode = std::strtoul(args[2].c_str(), 0, 8);
- }
+ Result<uid_t> uid = -1;
+ Result<gid_t> gid = -1;
- if (!make_dir(args[1], mode)) {
- /* chmod in case the directory already exists */
- if (errno == EEXIST) {
- if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
- return ErrnoError() << "fchmodat() failed";
- }
- } else {
- return ErrnoErrorIgnoreEnoent() << "mkdir() failed";
- }
- }
-
- if (args.size() >= 4) {
- auto uid = DecodeUid(args[3]);
- if (!uid) {
- return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
- }
- Result<gid_t> gid = -1;
-
- if (args.size() == 5) {
+ switch (args.size()) {
+ case 5:
gid = DecodeUid(args[4]);
if (!gid) {
return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
}
- }
-
- if (lchown(args[1].c_str(), *uid, *gid) == -1) {
- return ErrnoError() << "lchown failed";
- }
-
- /* chown may have cleared S_ISUID and S_ISGID, chmod again */
- if (mode & (S_ISUID | S_ISGID)) {
- if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
- return ErrnoError() << "fchmodat failed";
+ FALLTHROUGH_INTENDED;
+ case 4:
+ uid = DecodeUid(args[3]);
+ if (!uid) {
+ return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
}
+ FALLTHROUGH_INTENDED;
+ case 3:
+ mode = std::strtoul(args[2].c_str(), 0, 8);
+ FALLTHROUGH_INTENDED;
+ case 2:
+ break;
+ default:
+ return Error() << "Unexpected argument count: " << args.size();
+ }
+ std::string target = args[1];
+ struct stat mstat;
+ if (lstat(target.c_str(), &mstat) != 0) {
+ if (errno != ENOENT) {
+ return ErrnoError() << "lstat() failed on " << target;
+ }
+ if (!make_dir(target, mode)) {
+ return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << target;
+ }
+ if (lstat(target.c_str(), &mstat) != 0) {
+ return ErrnoError() << "lstat() failed on new " << target;
}
}
-
+ if (!S_ISDIR(mstat.st_mode)) {
+ return Error() << "Not a directory on " << target;
+ }
+ bool needs_chmod = (mstat.st_mode & ~S_IFMT) != mode;
+ if ((*uid != static_cast<uid_t>(-1) && *uid != mstat.st_uid) ||
+ (*gid != static_cast<gid_t>(-1) && *gid != mstat.st_gid)) {
+ if (lchown(target.c_str(), *uid, *gid) == -1) {
+ return ErrnoError() << "lchown failed on " << target;
+ }
+ // chown may have cleared S_ISUID and S_ISGID, chmod again
+ needs_chmod = true;
+ }
+ if (needs_chmod) {
+ if (fchmodat(AT_FDCWD, target.c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+ return ErrnoError() << "fchmodat() failed on " << target;
+ }
+ }
if (fscrypt_is_native()) {
- if (fscrypt_set_directory_policy(args[1].c_str())) {
+ if (fscrypt_set_directory_policy(target)) {
return reboot_into_recovery(
- {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
+ {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + target});
}
}
return {};
@@ -689,11 +702,11 @@
static Result<void> do_setprop(const BuiltinArguments& args) {
if (StartsWith(args[1], "ctl.")) {
- return Error()
- << "Cannot set ctl. properties from init; call the Service functions directly";
+ return Error() << "InitPropertySet: Do not set ctl. properties from init; call the Service "
+ "functions directly";
}
if (args[1] == kRestoreconProperty) {
- return Error() << "Cannot set '" << kRestoreconProperty
+ return Error() << "InitPropertySet: Do not set '" << kRestoreconProperty
<< "' from init; use the restorecon builtin directly";
}
@@ -1012,20 +1025,7 @@
}
static Result<void> do_load_persist_props(const BuiltinArguments& args) {
- // Devices with FDE have load_persist_props called twice; the first time when the temporary
- // /data partition is mounted and then again once /data is truly mounted. We do not want to
- // read persistent properties from the temporary /data partition or mark persistent properties
- // as having been loaded during the first call, so we return in that case.
- std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
- std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
- if (crypto_state == "encrypted" && crypto_type == "block") {
- static size_t num_calls = 0;
- if (++num_calls == 1) return {};
- }
-
- SendLoadPersistentPropertiesMessage();
-
- start_waiting_for_property("ro.persistent_properties.ready", "true");
+ load_persist_props();
return {};
}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index dffd6af..6b4216f 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -36,6 +36,7 @@
#include <fs_mgr_overlayfs.h>
#include <libgsi/libgsi.h>
#include <liblp/liblp.h>
+#include <libsnapshot/snapshot.h>
#include "devices.h"
#include "switch_root.h"
@@ -55,6 +56,7 @@
using android::fs_mgr::ReadDefaultFstab;
using android::fs_mgr::ReadFstabFromDt;
using android::fs_mgr::SkipMountingPartitions;
+using android::snapshot::SnapshotManager;
using namespace std::literals;
@@ -244,8 +246,6 @@
if (!InitDevices()) return false;
- if (!CreateLogicalPartitions()) return false;
-
if (!MountPartitions()) return false;
return true;
@@ -366,6 +366,16 @@
return false;
}
+ if (SnapshotManager::IsSnapshotManagerNeeded()) {
+ auto sm = SnapshotManager::NewForFirstStageMount();
+ if (!sm) {
+ return false;
+ }
+ if (sm->NeedSnapshotsInFirstStageMount()) {
+ return sm->CreateLogicalAndSnapshotPartitions(lp_metadata_partition_);
+ }
+ }
+
auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
if (!metadata) {
LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
@@ -493,14 +503,7 @@
// this case, we mount system first then pivot to it. From that point on,
// we are effectively identical to a system-as-root device.
bool FirstStageMount::TrySwitchSystemAsRoot() {
- auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
- return entry.mount_point == "/metadata";
- });
- if (metadata_partition != fstab_.end()) {
- if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
- UseGsiIfPresent();
- }
- }
+ UseGsiIfPresent();
auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
return entry.mount_point == "/system";
@@ -523,6 +526,17 @@
}
bool FirstStageMount::MountPartitions() {
+ // Mount /metadata before creating logical partitions, since we need to
+ // know whether a snapshot merge is in progress.
+ auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+ return entry.mount_point == "/metadata";
+ });
+ if (metadata_partition != fstab_.end()) {
+ MountPartition(metadata_partition, true /* erase_same_mounts */);
+ }
+
+ if (!CreateLogicalPartitions()) return false;
+
if (!TrySwitchSystemAsRoot()) return false;
if (!SkipMountingPartitions(&fstab_)) return false;
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
new file mode 100644
index 0000000..0f5a864
--- /dev/null
+++ b/init/fscrypt_init_extensions.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2016 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 "fscrypt_init_extensions.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fts.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <fscrypt/fscrypt.h>
+#include <keyutils.h>
+#include <logwrap/logwrap.h>
+
+#define TAG "fscrypt"
+
+static int set_system_de_policy_on(const std::string& dir);
+
+int fscrypt_install_keyring() {
+ key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0, KEY_SPEC_SESSION_KEYRING);
+
+ if (device_keyring == -1) {
+ PLOG(ERROR) << "Failed to create keyring";
+ return -1;
+ }
+
+ LOG(INFO) << "Keyring created with id " << device_keyring << " in process " << getpid();
+
+ return 0;
+}
+
+// TODO(b/139378601): use a single central implementation of this.
+static void delete_dir_contents(const std::string& dir) {
+ char* const paths[2] = {const_cast<char*>(dir.c_str()), nullptr};
+ FTS* fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
+ FTSENT* cur;
+ while ((cur = fts_read(fts)) != nullptr) {
+ if (cur->fts_info == FTS_ERR) {
+ PLOG(ERROR) << "fts_read";
+ break;
+ }
+ if (dir == cur->fts_path) {
+ continue;
+ }
+ switch (cur->fts_info) {
+ case FTS_D:
+ break; // Ignore these
+ case FTS_DP:
+ if (rmdir(cur->fts_path) == -1) {
+ PLOG(ERROR) << "rmdir " << cur->fts_path;
+ }
+ break;
+ default:
+ PLOG(ERROR) << "FTS unexpected type " << cur->fts_info << " at " << cur->fts_path;
+ if (rmdir(cur->fts_path) != -1) break;
+ // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
+ FALLTHROUGH_INTENDED;
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (unlink(cur->fts_path) == -1) {
+ PLOG(ERROR) << "unlink " << cur->fts_path;
+ }
+ break;
+ }
+ }
+
+ if (fts_close(fts) != 0) {
+ PLOG(ERROR) << "fts_close";
+ }
+}
+
+int fscrypt_set_directory_policy(const std::string& dir) {
+ const std::string prefix = "/data/";
+
+ if (!android::base::StartsWith(dir, prefix)) {
+ return 0;
+ }
+
+ // Special-case /data/media/obb per b/64566063
+ if (dir == "/data/media/obb") {
+ // Try to set policy on this directory, but if it is non-empty this may fail.
+ set_system_de_policy_on(dir);
+ return 0;
+ }
+
+ // Only set policy on first level /data directories
+ // To make this less restrictive, consider using a policy file.
+ // However this is overkill for as long as the policy is simply
+ // to apply a global policy to all /data folders created via makedir
+ if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
+ return 0;
+ }
+
+ // Special case various directories that must not be encrypted,
+ // often because their subdirectories must be encrypted.
+ // This isn't a nice way to do this, see b/26641735
+ std::vector<std::string> directories_to_exclude = {
+ "lost+found",
+ "system_ce", "system_de",
+ "misc_ce", "misc_de",
+ "vendor_ce", "vendor_de",
+ "media",
+ "data", "user", "user_de",
+ "apex", "preloads", "app-staging",
+ "gsi",
+ };
+ for (const auto& d : directories_to_exclude) {
+ if ((prefix + d) == dir) {
+ LOG(INFO) << "Not setting policy on " << dir;
+ return 0;
+ }
+ }
+ int err = set_system_de_policy_on(dir);
+ if (err == 0) {
+ return 0;
+ }
+ // Empty these directories if policy setting fails.
+ std::vector<std::string> wipe_on_failure = {
+ "rollback", "rollback-observer", // b/139193659
+ };
+ for (const auto& d : wipe_on_failure) {
+ if ((prefix + d) == dir) {
+ LOG(ERROR) << "Setting policy failed, deleting: " << dir;
+ delete_dir_contents(dir);
+ err = set_system_de_policy_on(dir);
+ break;
+ }
+ }
+ return err;
+}
+
+static int set_system_de_policy_on(const std::string& dir) {
+ std::string ref_filename = std::string("/data") + fscrypt_key_ref;
+ std::string policy;
+ if (!android::base::ReadFileToString(ref_filename, &policy)) {
+ LOG(ERROR) << "Unable to read system policy to set on " << dir;
+ return -1;
+ }
+
+ auto type_filename = std::string("/data") + fscrypt_key_mode;
+ std::string modestring;
+ if (!android::base::ReadFileToString(type_filename, &modestring)) {
+ LOG(ERROR) << "Cannot read mode";
+ }
+
+ std::vector<std::string> modes = android::base::Split(modestring, ":");
+
+ if (modes.size() < 1 || modes.size() > 2) {
+ LOG(ERROR) << "Invalid encryption mode string: " << modestring;
+ return -1;
+ }
+
+ LOG(INFO) << "Setting policy on " << dir;
+ int result =
+ fscrypt_policy_ensure(dir.c_str(), policy.c_str(), policy.length(), modes[0].c_str(),
+ modes.size() >= 2 ? modes[1].c_str() : "aes-256-cts");
+ if (result) {
+ LOG(ERROR) << android::base::StringPrintf("Setting %02x%02x%02x%02x policy on %s failed!",
+ policy[0], policy[1], policy[2], policy[3],
+ dir.c_str());
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/init/fscrypt_init_extensions.h b/init/fscrypt_init_extensions.h
new file mode 100644
index 0000000..2163ef6
--- /dev/null
+++ b/init/fscrypt_init_extensions.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 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 <string>
+
+int fscrypt_install_keyring();
+int fscrypt_set_directory_policy(const std::string& dir);
diff --git a/init/init.cpp b/init/init.cpp
index 3432e24..742adee 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -28,6 +28,9 @@
#include <sys/types.h>
#include <unistd.h>
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
#include <functional>
#include <map>
#include <memory>
@@ -616,14 +619,6 @@
selinux_start_time_ns));
}
-void SendLoadPersistentPropertiesMessage() {
- auto init_message = InitMessage{};
- init_message.set_load_persistent_properties(true);
- if (auto result = SendMessage(property_fd, init_message); !result) {
- LOG(ERROR) << "Failed to send load persistent properties message: " << result.error();
- }
-}
-
static void HandlePropertyFd() {
auto message = ReadMessage(property_fd);
if (!message) {
@@ -640,16 +635,14 @@
switch (property_message.msg_case()) {
case PropertyMessage::kControlMessage: {
auto& control_message = property_message.control_message();
- bool response = HandleControlMessage(control_message.msg(), control_message.name(),
- control_message.pid());
+ bool success = HandleControlMessage(control_message.msg(), control_message.name(),
+ control_message.pid());
- auto init_message = InitMessage{};
- auto* control_response = init_message.mutable_control_response();
-
- control_response->set_result(response);
-
- if (auto result = SendMessage(property_fd, init_message); !result) {
- LOG(ERROR) << "Failed to send control response: " << result.error();
+ uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ if (control_message.has_fd()) {
+ int fd = control_message.fd();
+ TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));
+ close(fd);
}
break;
}
diff --git a/init/init.h b/init/init.h
index 832ce3f..624a3d4 100644
--- a/init/init.h
+++ b/init/init.h
@@ -37,8 +37,6 @@
void ResetWaitForProp();
-void SendLoadPersistentPropertiesMessage();
-
int SecondStageMain(int argc, char** argv);
} // namespace init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index b7d5743..c78b81b 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -96,12 +96,6 @@
static PropertyInfoAreaFile property_info_area;
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr, std::string* error);
-uint32_t InitPropertySet(const std::string& name, const std::string& value);
-
-uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;
-
void CreateSerializedPropertyInfo();
struct PropertyAuditData {
@@ -215,6 +209,11 @@
if (persistent_properties_loaded && StartsWith(name, "persist.")) {
WritePersistentProperty(name, value);
}
+ // If init sets ro.persistent_properties.ready to true, then it has finished writing persistent
+ // properties, and we should write future persistent properties to disk.
+ if (name == "ro.persistent_properties.ready" && value == "true") {
+ persistent_properties_loaded = true;
+ }
// If init hasn't started its main loop, then it won't be handling property changed messages
// anyway, so there's no need to try to send them.
if (init_socket != -1) {
@@ -259,18 +258,6 @@
bool thread_started_ = false;
};
-uint32_t InitPropertySet(const std::string& name, const std::string& value) {
- uint32_t result = 0;
- ucred cr = {.pid = 1, .uid = 0, .gid = 0};
- std::string error;
- result = HandlePropertySet(name, value, kInitContext.c_str(), cr, &error);
- if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
- }
-
- return result;
-}
-
class SocketConnection {
public:
SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}
@@ -311,6 +298,9 @@
}
bool SendUint32(uint32_t value) {
+ if (!socket_.ok()) {
+ return false;
+ }
int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
return result == sizeof(value);
}
@@ -325,7 +315,9 @@
return true;
}
- int socket() { return socket_; }
+ int Release() { return socket_.release(); }
+
+ int socket() { return socket_.get(); }
const ucred& cred() { return cred_; }
@@ -400,58 +392,30 @@
ucred cred_;
};
-// Init responds with whether or not the control message was successful. However, init may set
-// properties in the process of handling the control message, particularly when starting services.
-// Therefore we cannot block in SendControlMessage() to wait for init's response. Instead, we store
-// the SocketConnection for the socket that sent the control message. We then return to the main
-// poll loop and handle messages until we get the response from init.
-//
-// Note that this is a queue, since it is possible for more control messages to come while init is
-// handling the first. Both init and property service will handle these in order.
-//
-// Also note that the 1st version of the property service does not expect a result to be sent, so
-// we have a nullopt as a placeholder in the queue to keep track of which control messages have been
-// responded to.
-static std::queue<std::optional<SocketConnection>> pending_control_message_results;
-
static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
- std::string* error) {
+ SocketConnection* socket, std::string* error) {
auto property_msg = PropertyMessage{};
auto* control_message = property_msg.mutable_control_message();
control_message->set_msg(msg);
control_message->set_name(name);
control_message->set_pid(pid);
+ if (socket != nullptr) {
+ control_message->set_fd(socket->socket());
+ }
if (auto result = SendMessage(init_socket, property_msg); !result) {
*error = "Failed to send control message: " + result.error().message();
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
+ if (socket != nullptr) {
+ // We've successfully sent the fd to init, so release it here.
+ socket->Release();
+ }
+
return PROP_SUCCESS;
}
-void HandleControlResponse(const InitMessage& init_message) {
- if (pending_control_message_results.empty()) {
- LOG(ERROR) << "Got a control response without pending control messages";
- return;
- }
-
- if (!pending_control_message_results.front().has_value()) {
- pending_control_message_results.pop();
- return;
- }
-
- if (!pending_control_message_results.front().has_value()) {
- return;
- }
-
- auto& control_response = init_message.control_response();
- uint32_t response =
- control_response.result() ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
- pending_control_message_results.front()->SendUint32(response);
- pending_control_message_results.pop();
-}
-
bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr) {
// We check the legacy method first but these properties are dontaudit, so we only log an audit
@@ -519,13 +483,14 @@
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr, std::string* error) {
+ const std::string& source_context, const ucred& cr,
+ SocketConnection* socket, std::string* error) {
if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
return ret;
}
if (StartsWith(name, "ctl.")) {
- return SendControlMessage(name.c_str() + 4, value, cr.pid, error);
+ return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
}
// sys.powerctl is a special property that is used to make the device reboot. We want to log
@@ -556,6 +521,20 @@
return PropertySet(name, value, error);
}
+uint32_t InitPropertySet(const std::string& name, const std::string& value) {
+ uint32_t result = 0;
+ ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+ std::string error;
+ result = HandlePropertySet(name, value, kInitContext.c_str(), cr, nullptr, &error);
+ if (result != PROP_SUCCESS) {
+ LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
+ }
+
+ return result;
+}
+
+uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;
+
static void handle_property_set_fd() {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
@@ -604,16 +583,13 @@
const auto& cr = socket.cred();
std::string error;
- uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error);
+ uint32_t result =
+ HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
- if (result == PROP_SUCCESS && StartsWith(prop_name, "ctl.")) {
- pending_control_message_results.emplace(std::nullopt);
- }
-
break;
}
@@ -636,17 +612,13 @@
const auto& cr = socket.cred();
std::string error;
- uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
+ uint32_t result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
- if (result == PROP_SUCCESS && StartsWith(name, "ctl.")) {
- pending_control_message_results.emplace(std::move(socket));
- } else {
- socket.SendUint32(result);
- }
+ socket.SendUint32(result);
break;
}
@@ -805,6 +777,32 @@
}
}
+/* When booting an encrypted system, /data is not mounted when the
+ * property service is started, so any properties stored there are
+ * not loaded. Vold triggers init to load these properties once it
+ * has mounted /data.
+ */
+void load_persist_props(void) {
+ // Devices with FDE have load_persist_props called twice; the first time when the temporary
+ // /data partition is mounted and then again once /data is truly mounted. We do not want to
+ // read persistent properties from the temporary /data partition or mark persistent properties
+ // as having been loaded during the first call, so we return in that case.
+ std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
+ std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
+ if (crypto_state == "encrypted" && crypto_type == "block") {
+ static size_t num_calls = 0;
+ if (++num_calls == 1) return;
+ }
+
+ load_override_properties();
+ /* Read persistent properties after all default values have been loaded. */
+ auto persistent_properties = LoadPersistentProperties();
+ for (const auto& persistent_property_record : persistent_properties.properties()) {
+ property_set(persistent_property_record.name(), persistent_property_record.value());
+ }
+ property_set("ro.persistent_properties.ready", "true");
+}
+
// If the ro.product.[brand|device|manufacturer|model|name] properties have not been explicitly
// set, derive them from ro.product.${partition}.* properties
static void property_initialize_ro_product_props() {
@@ -1022,59 +1020,9 @@
selinux_android_restorecon(kPropertyInfosPath, 0);
}
-static void HandleInitSocket() {
- auto message = ReadMessage(init_socket);
- if (!message) {
- LOG(ERROR) << "Could not read message from init_dedicated_recv_socket: " << message.error();
- return;
- }
-
- auto init_message = InitMessage{};
- if (!init_message.ParseFromString(*message)) {
- LOG(ERROR) << "Could not parse message from init";
- return;
- }
-
- switch (init_message.msg_case()) {
- case InitMessage::kControlResponse: {
- HandleControlResponse(init_message);
- break;
- }
- case InitMessage::kLoadPersistentProperties: {
- load_override_properties();
- // Read persistent properties after all default values have been loaded.
- auto persistent_properties = LoadPersistentProperties();
- for (const auto& persistent_property_record : persistent_properties.properties()) {
- InitPropertySet(persistent_property_record.name(),
- persistent_property_record.value());
- }
- InitPropertySet("ro.persistent_properties.ready", "true");
- persistent_properties_loaded = true;
- break;
- }
- default:
- LOG(ERROR) << "Unknown message type from init: " << init_message.msg_case();
- }
-}
-
static void PropertyServiceThread() {
- Epoll epoll;
- if (auto result = epoll.Open(); !result) {
- LOG(FATAL) << result.error();
- }
-
- if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
- LOG(FATAL) << result.error();
- }
-
- if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result) {
- LOG(FATAL) << result.error();
- }
-
while (true) {
- if (auto result = epoll.Wait(std::nullopt); !result) {
- LOG(ERROR) << result.error();
- }
+ handle_property_set_fd();
}
}
diff --git a/init/property_service.h b/init/property_service.h
index 8f7d8d9..2dc92a5 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -33,6 +33,7 @@
void property_init();
void property_load_boot_defaults(bool load_debug_prop);
+void load_persist_props();
void StartPropertyService(int* epoll_socket);
} // namespace init
diff --git a/init/property_service.proto b/init/property_service.proto
index 894fa13..f711526 100644
--- a/init/property_service.proto
+++ b/init/property_service.proto
@@ -22,6 +22,7 @@
optional string msg = 1;
optional string name = 2;
optional int32 pid = 3;
+ optional int32 fd = 4;
}
message ChangedMessage {
@@ -34,12 +35,3 @@
ChangedMessage changed_message = 2;
};
}
-
-message InitMessage {
- message ControlResponse { optional bool result = 1; }
-
- oneof msg {
- ControlResponse control_response = 1;
- bool load_persistent_properties = 2;
- };
-}
diff --git a/init/selinux.cpp b/init/selinux.cpp
index fd42256..6842820 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -51,6 +51,8 @@
#include <android/api-level.h>
#include <fcntl.h>
+#include <linux/audit.h>
+#include <linux/netlink.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -446,6 +448,35 @@
}
}
+constexpr size_t kKlogMessageSize = 1024;
+
+void SelinuxAvcLog(char* buf, size_t buf_len) {
+ CHECK_GT(buf_len, 0u);
+
+ size_t str_len = strnlen(buf, buf_len);
+ // trim newline at end of string
+ if (buf[str_len - 1] == '\n') {
+ buf[str_len - 1] = '\0';
+ }
+
+ struct NetlinkMessage {
+ nlmsghdr hdr;
+ char buf[kKlogMessageSize];
+ } request = {};
+
+ request.hdr.nlmsg_flags = NLM_F_REQUEST;
+ request.hdr.nlmsg_type = AUDIT_USER_AVC;
+ request.hdr.nlmsg_len = sizeof(request);
+ strlcpy(request.buf, buf, sizeof(request.buf));
+
+ auto fd = unique_fd{socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT)};
+ if (!fd.ok()) {
+ return;
+ }
+
+ TEMP_FAILURE_RETRY(send(fd, &request, sizeof(request), 0));
+}
+
} // namespace
// The files and directories that were created before initial sepolicy load or
@@ -478,12 +509,19 @@
} else if (type == SELINUX_INFO) {
severity = android::base::INFO;
}
- char buf[1024];
+ char buf[kKlogMessageSize];
va_list ap;
va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
+ int length_written = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+ if (length_written <= 0) {
+ return 0;
+ }
+ if (type == SELINUX_AVC) {
+ SelinuxAvcLog(buf, sizeof(buf));
+ } else {
+ android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+ }
return 0;
}
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 8b2cf62..cffc1b9 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -17,12 +17,15 @@
#include "ueventd.h"
#include <ctype.h>
+#include <dirent.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
#include <sys/wait.h>
+#include <unistd.h>
#include <set>
#include <thread>
@@ -121,8 +124,9 @@
void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
void RegenerateUevents();
void ForkSubProcesses();
- void DoRestoreCon();
void WaitForSubProcesses();
+ void RestoreConHandler(unsigned int process_num, unsigned int total_processes);
+ void GenerateRestoreCon(const std::string& directory);
UeventListener& uevent_listener_;
std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;
@@ -131,6 +135,8 @@
std::vector<Uevent> uevent_queue_;
std::set<pid_t> subprocess_pids_;
+
+ std::vector<std::string> restorecon_queue_;
};
void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
@@ -141,9 +147,38 @@
uevent_handler->HandleUevent(uevent);
}
}
+}
+
+void ColdBoot::RestoreConHandler(unsigned int process_num, unsigned int total_processes) {
+ for (unsigned int i = process_num; i < restorecon_queue_.size(); i += total_processes) {
+ auto& dir = restorecon_queue_[i];
+
+ selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+ }
_exit(EXIT_SUCCESS);
}
+void ColdBoot::GenerateRestoreCon(const std::string& directory) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(directory.c_str()), &closedir);
+
+ if (!dir) return;
+
+ struct dirent* dent;
+ while ((dent = readdir(dir.get())) != NULL) {
+ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue;
+
+ struct stat st;
+ if (fstatat(dirfd(dir.get()), dent->d_name, &st, 0) == -1) continue;
+
+ if (S_ISDIR(st.st_mode)) {
+ std::string fullpath = directory + "/" + dent->d_name;
+ if (fullpath != "/sys/devices") {
+ restorecon_queue_.emplace_back(fullpath);
+ }
+ }
+ }
+}
+
void ColdBoot::RegenerateUevents() {
uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
uevent_queue_.emplace_back(uevent);
@@ -160,16 +195,13 @@
if (pid == 0) {
UeventHandlerMain(i, num_handler_subprocesses_);
+ RestoreConHandler(i, num_handler_subprocesses_);
}
subprocess_pids_.emplace(pid);
}
}
-void ColdBoot::DoRestoreCon() {
- selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-}
-
void ColdBoot::WaitForSubProcesses() {
// Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
// stuck.
@@ -208,9 +240,13 @@
RegenerateUevents();
- ForkSubProcesses();
+ selinux_android_restorecon("/sys", 0);
+ selinux_android_restorecon("/sys/devices", 0);
+ GenerateRestoreCon("/sys");
+ // takes long time for /sys/devices, parallelize it
+ GenerateRestoreCon("/sys/devices");
- DoRestoreCon();
+ ForkSubProcesses();
WaitForSubProcesses();
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 53d3ab3..c38d8cd 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,7 +15,6 @@
//
liblog_sources = [
- "config_read.cpp",
"config_write.cpp",
"log_event_list.cpp",
"log_event_write.cpp",
diff --git a/liblog/config_read.cpp b/liblog/config_read.cpp
deleted file mode 100644
index 3139f78..0000000
--- a/liblog/config_read.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2016 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 <log/log_transport.h>
-
-#include "config_read.h"
-#include "logger.h"
-
-struct listnode __android_log_transport_read = {&__android_log_transport_read,
- &__android_log_transport_read};
-struct listnode __android_log_persist_read = {&__android_log_persist_read,
- &__android_log_persist_read};
-
-static void __android_log_add_transport(struct listnode* list,
- struct android_log_transport_read* transport) {
- uint32_t i;
-
- /* Try to keep one functioning transport for each log buffer id */
- for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
- struct android_log_transport_read* transp;
-
- if (list_empty(list)) {
- if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
- list_add_tail(list, &transport->node);
- return;
- }
- } else {
- read_transport_for_each(transp, list) {
- if (!transp->available) {
- return;
- }
- if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
- (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
- list_add_tail(list, &transport->node);
- return;
- }
- }
- }
- }
-}
-
-void __android_log_config_read() {
-#if (FAKE_LOG_DEVICE == 0)
- if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
- extern struct android_log_transport_read logdLoggerRead;
- extern struct android_log_transport_read pmsgLoggerRead;
-
- __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead);
- __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead);
- }
-#endif
-}
-
-void __android_log_config_read_close() {
- struct android_log_transport_read* transport;
- struct listnode* n;
-
- read_transport_for_each_safe(transport, n, &__android_log_transport_read) {
- list_remove(&transport->node);
- }
- read_transport_for_each_safe(transport, n, &__android_log_persist_read) {
- list_remove(&transport->node);
- }
-}
diff --git a/liblog/config_read.h b/liblog/config_read.h
deleted file mode 100644
index 212b8a0..0000000
--- a/liblog/config_read.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 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 <cutils/list.h>
-
-#include "log_portability.h"
-
-__BEGIN_DECLS
-
-extern struct listnode __android_log_transport_read;
-extern struct listnode __android_log_persist_read;
-
-#define read_transport_for_each(transp, transports) \
- for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_read, node); \
- ((transp) != node_to_item((transports), \
- struct android_log_transport_read, node)) && \
- ((transp) != node_to_item((transp)->node.next, \
- struct android_log_transport_read, node)); \
- (transp) = node_to_item((transp)->node.next, \
- struct android_log_transport_read, node))
-
-#define read_transport_for_each_safe(transp, n, transports) \
- for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_read, node), \
- (n) = (transp)->node.next; \
- ((transp) != node_to_item((transports), \
- struct android_log_transport_read, node)) && \
- ((transp) != \
- node_to_item((n), struct android_log_transport_read, node)); \
- (transp) = node_to_item((n), struct android_log_transport_read, node), \
- (n) = (transp)->node.next)
-
-void __android_log_config_read();
-void __android_log_config_read_close();
-
-__END_DECLS
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index b7ba782..eba305f 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -35,7 +35,6 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_read.h"
#include "log_portability.h"
#include "logd_reader.h"
#include "logger.h"
diff --git a/liblog/logger.h b/liblog/logger.h
index accb6e7..4b4ef5f 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -95,9 +95,19 @@
size_t len);
};
+struct android_log_transport_context {
+ union android_log_context_union context; /* zero init per-transport context */
+
+ struct android_log_transport_read* transport;
+ unsigned logMask; /* mask of requested log buffers */
+ int ret; /* return value associated with following data */
+ struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
+};
+
struct android_log_logger_list {
struct listnode logger;
- struct listnode transport;
+ android_log_transport_context transport_context;
+ bool transport_initialized;
int mode;
unsigned int tail;
log_time start;
@@ -111,27 +121,7 @@
log_id_t logId;
};
-struct android_log_transport_context {
- struct listnode node;
- union android_log_context_union context; /* zero init per-transport context */
- struct android_log_logger_list* parent;
-
- struct android_log_transport_read* transport;
- unsigned logMask; /* mask of requested log buffers */
- int ret; /* return value associated with following data */
- struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
-};
-
/* assumes caller has structures read-locked, single threaded, or fenced */
-#define transport_context_for_each(transp, logger_list) \
- for ((transp) = node_to_item((logger_list)->transport.next, \
- struct android_log_transport_context, node); \
- ((transp) != node_to_item(&(logger_list)->transport, \
- struct android_log_transport_context, node)) && \
- ((transp)->parent == (logger_list)); \
- (transp) = node_to_item((transp)->node.next, \
- struct android_log_transport_context, node))
-
#define logger_for_each(logp, logger_list) \
for ((logp) = node_to_item((logger_list)->logger.next, \
struct android_log_logger, node); \
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 4cf0846..ff816b7 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -29,7 +29,6 @@
#include <cutils/list.h>
#include <private/android_filesystem_config.h>
-#include "config_read.h"
#include "log_portability.h"
#include "logger.h"
@@ -55,9 +54,6 @@
}
static int init_transport_context(struct android_log_logger_list* logger_list) {
- struct android_log_transport_read* transport;
- struct listnode* node;
-
if (!logger_list) {
return -EINVAL;
}
@@ -66,77 +62,63 @@
return -EINVAL;
}
- if (!list_empty(&logger_list->transport)) {
+ if (logger_list->transport_initialized) {
return 0;
}
- __android_log_lock();
- /* mini __write_to_log_initialize() to populate transports */
- if (list_empty(&__android_log_transport_read) && list_empty(&__android_log_persist_read)) {
- __android_log_config_read();
- }
- __android_log_unlock();
+#if (FAKE_LOG_DEVICE == 0)
+ extern struct android_log_transport_read logdLoggerRead;
+ extern struct android_log_transport_read pmsgLoggerRead;
- node = (logger_list->mode & ANDROID_LOG_PSTORE) ? &__android_log_persist_read
- : &__android_log_transport_read;
+ struct android_log_transport_read* transport;
+ transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
- read_transport_for_each(transport, node) {
- struct android_log_transport_context* transp;
- struct android_log_logger* logger;
- unsigned logMask = 0;
+ struct android_log_logger* logger;
+ unsigned logMask = 0;
- logger_for_each(logger, logger_list) {
- log_id_t logId = logger->logId;
+ logger_for_each(logger, logger_list) {
+ log_id_t logId = logger->logId;
- if ((logId == LOG_ID_SECURITY) && (__android_log_uid() != AID_SYSTEM)) {
- continue;
- }
- if (transport->read && (!transport->available || (transport->available(logId) >= 0))) {
- logMask |= 1 << logId;
- }
- }
- if (!logMask) {
+ if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) {
continue;
}
- transp = static_cast<android_log_transport_context*>(calloc(1, sizeof(*transp)));
- if (!transp) {
- return -ENOMEM;
+ if (transport->read && (!transport->available || transport->available(logId) >= 0)) {
+ logMask |= 1 << logId;
}
- transp->parent = logger_list;
- transp->transport = transport;
- transp->logMask = logMask;
- transp->ret = 1;
- list_add_tail(&logger_list->transport, &transp->node);
}
- if (list_empty(&logger_list->transport)) {
+ if (!logMask) {
return -ENODEV;
}
+
+ logger_list->transport_context.transport = transport;
+ logger_list->transport_context.logMask = logMask;
+ logger_list->transport_context.ret = 1;
+#endif
return 0;
}
-#define LOGGER_FUNCTION(logger, def, func, args...) \
- ssize_t ret = -EINVAL; \
- struct android_log_transport_context* transp; \
- struct android_log_logger* logger_internal = (struct android_log_logger*)(logger); \
- \
- if (!logger_internal) { \
- return ret; \
- } \
- ret = init_transport_context(logger_internal->parent); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- transport_context_for_each(transp, logger_internal->parent) { \
- if ((transp->logMask & (1 << logger_internal->logId)) && transp->transport && \
- transp->transport->func) { \
- ssize_t retval = (transp->transport->func)(logger_internal, transp, ##args); \
- if ((ret >= 0) || (ret == (def))) { \
- ret = retval; \
- } \
- } \
- } \
+#define LOGGER_FUNCTION(logger, def, func, args...) \
+ ssize_t ret = -EINVAL; \
+ android_log_logger* logger_internal = reinterpret_cast<android_log_logger*>(logger); \
+ \
+ if (!logger_internal) { \
+ return ret; \
+ } \
+ ret = init_transport_context(logger_internal->parent); \
+ if (ret < 0) { \
+ return ret; \
+ } \
+ \
+ ret = (def); \
+ android_log_transport_context* transport_context = &logger_internal->parent->transport_context; \
+ if (transport_context->logMask & (1 << logger_internal->logId) && \
+ transport_context->transport && transport_context->transport->func) { \
+ ssize_t retval = \
+ (transport_context->transport->func)(logger_internal, transport_context, ##args); \
+ if (ret >= 0 || ret == (def)) { \
+ ret = retval; \
+ } \
+ } \
return ret
int android_logger_clear(struct logger* logger) {
@@ -167,25 +149,24 @@
LOGGER_FUNCTION(logger, 4, version);
}
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...) \
- struct android_log_transport_context* transp; \
- struct android_log_logger_list* logger_list_internal = \
- (struct android_log_logger_list*)(logger_list); \
- \
- ssize_t ret = init_transport_context(logger_list_internal); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- transport_context_for_each(transp, logger_list_internal) { \
- if (transp->transport && (transp->transport->func)) { \
- ssize_t retval = (transp->transport->func)(logger_list_internal, transp, ##args); \
- if ((ret >= 0) || (ret == (def))) { \
- ret = retval; \
- } \
- } \
- } \
+#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...) \
+ android_log_logger_list* logger_list_internal = \
+ reinterpret_cast<android_log_logger_list*>(logger_list); \
+ \
+ ssize_t ret = init_transport_context(logger_list_internal); \
+ if (ret < 0) { \
+ return ret; \
+ } \
+ \
+ ret = (def); \
+ android_log_transport_context* transport_context = &logger_list_internal->transport_context; \
+ if (transport_context->transport && transport_context->transport->func) { \
+ ssize_t retval = \
+ (transport_context->transport->func)(logger_list_internal, transport_context, ##args); \
+ if (ret >= 0 || ret == (def)) { \
+ ret = retval; \
+ } \
+ } \
return ret
/*
@@ -212,7 +193,6 @@
}
list_init(&logger_list->logger);
- list_init(&logger_list->transport);
logger_list->mode = mode;
logger_list->tail = tail;
logger_list->pid = pid;
@@ -229,7 +209,6 @@
}
list_init(&logger_list->logger);
- list_init(&logger_list->transport);
logger_list->mode = mode;
logger_list->start = start;
logger_list->pid = pid;
@@ -247,38 +226,27 @@
struct android_log_logger* logger;
if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
- goto err;
+ return nullptr;
}
logger_for_each(logger, logger_list_internal) {
if (logger->logId == logId) {
- goto ok;
+ return reinterpret_cast<struct logger*>(logger);
}
}
logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
if (!logger) {
- goto err;
+ return nullptr;
}
logger->logId = logId;
list_add_tail(&logger_list_internal->logger, &logger->node);
logger->parent = logger_list_internal;
- /* Reset known transports to re-evaluate, we just added one */
- while (!list_empty(&logger_list_internal->transport)) {
- struct listnode* node = list_head(&logger_list_internal->transport);
- struct android_log_transport_context* transp =
- node_to_item(node, struct android_log_transport_context, node);
+ // Reset known transport to re-evaluate, since we added a new logger.
+ logger_list_internal->transport_initialized = false;
- list_remove(&transp->node);
- free(transp);
- }
- goto ok;
-
-err:
- logger = NULL;
-ok:
return (struct logger*)logger;
}
@@ -340,7 +308,6 @@
/* Read from the selected logs */
int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
- struct android_log_transport_context* transp;
struct android_log_logger_list* logger_list_internal =
(struct android_log_logger_list*)logger_list;
@@ -349,84 +316,8 @@
return ret;
}
- /* at least one transport */
- transp = node_to_item(logger_list_internal->transport.next, struct android_log_transport_context,
- node);
-
- /* more than one transport? */
- if (transp->node.next != &logger_list_internal->transport) {
- /* Poll and merge sort the entries if from multiple transports */
- struct android_log_transport_context* oldest = NULL;
- int ret;
- int polled = 0;
- do {
- if (polled) {
- sched_yield();
- }
- ret = -1000;
- polled = 0;
- do {
- int retval = transp->ret;
- if ((retval > 0) && !transp->logMsg.entry.len) {
- if (!transp->transport->read) {
- retval = transp->ret = 0;
- } else if ((logger_list_internal->mode & ANDROID_LOG_NONBLOCK) ||
- !transp->transport->poll) {
- retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
- } else {
- int pollval = (*transp->transport->poll)(logger_list_internal, transp);
- if (pollval <= 0) {
- sched_yield();
- pollval = (*transp->transport->poll)(logger_list_internal, transp);
- }
- polled = 1;
- if (pollval < 0) {
- if ((pollval == -EINTR) || (pollval == -EAGAIN)) {
- return -EAGAIN;
- }
- retval = transp->ret = pollval;
- } else if (pollval > 0) {
- retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
- }
- }
- }
- if (ret < retval) {
- ret = retval;
- }
- if ((transp->ret > 0) && transp->logMsg.entry.len &&
- (!oldest || (oldest->logMsg.entry.sec > transp->logMsg.entry.sec) ||
- ((oldest->logMsg.entry.sec == transp->logMsg.entry.sec) &&
- (oldest->logMsg.entry.nsec > transp->logMsg.entry.nsec)))) {
- oldest = transp;
- }
- transp = node_to_item(transp->node.next, struct android_log_transport_context, node);
- } while (transp != node_to_item(&logger_list_internal->transport,
- struct android_log_transport_context, node));
- if (!oldest && (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) {
- return (ret < 0) ? ret : -EAGAIN;
- }
- transp = node_to_item(logger_list_internal->transport.next,
- struct android_log_transport_context, node);
- } while (!oldest && (ret > 0));
- if (!oldest) {
- return ret;
- }
- // ret is a positive value less than sizeof(struct log_msg)
- ret = oldest->ret;
- if (ret < oldest->logMsg.entry.hdr_size) {
- // zero truncated header fields.
- memset(
- log_msg, 0,
- (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg) ? sizeof(oldest->logMsg)
- : oldest->logMsg.entry.hdr_size));
- }
- memcpy(log_msg, &oldest->logMsg, ret);
- oldest->logMsg.entry.len = 0; /* Mark it as copied */
- return ret;
- }
-
- /* if only one, no need to copy into transport_context and merge-sort */
- return android_transport_read(logger_list_internal, transp, log_msg);
+ android_log_transport_context* transport_context = &logger_list_internal->transport_context;
+ return android_transport_read(logger_list_internal, transport_context, log_msg);
}
/* Close all the logs */
@@ -438,16 +329,10 @@
return;
}
- while (!list_empty(&logger_list_internal->transport)) {
- struct listnode* node = list_head(&logger_list_internal->transport);
- struct android_log_transport_context* transp =
- node_to_item(node, struct android_log_transport_context, node);
+ android_log_transport_context* transport_context = &logger_list_internal->transport_context;
- if (transp->transport && transp->transport->close) {
- (*transp->transport->close)(logger_list_internal, transp);
- }
- list_remove(&transp->node);
- free(transp);
+ if (transport_context->transport && transport_context->transport->close) {
+ (*transport_context->transport->close)(logger_list_internal, transport_context);
}
while (!list_empty(&logger_list_internal->logger)) {
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index a4b3cd7..4fbab4b 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -29,7 +29,6 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_read.h" /* __android_log_config_read_close() definition */
#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
@@ -624,7 +623,6 @@
if (__android_log_transport != transport_flag) {
__android_log_transport = transport_flag;
__android_log_config_write_close();
- __android_log_config_read_close();
write_to_log = __write_to_log_init;
/* generically we only expect these two values for write_to_log */
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index eaac97f..81563bc 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -24,7 +24,6 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_read.h"
#include "logger.h"
static int pmsgAvailable(log_id_t logId);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 1f87b3e..45a9bc9 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1062,6 +1062,7 @@
when you depart from me, sorrow abides and happiness\n\
takes his leave.";
+#ifdef USING_LOGGER_DEFAULT
TEST(liblog, max_payload) {
#ifdef TEST_PREFIX
TEST_PREFIX
@@ -1130,6 +1131,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
TEST(liblog, __android_log_buf_print__maxtag) {
#ifdef TEST_PREFIX
@@ -1271,6 +1273,7 @@
#endif
}
+#ifdef USING_LOGGER_DEFAULT
TEST(liblog, dual_reader) {
#ifdef TEST_PREFIX
TEST_PREFIX
@@ -1334,6 +1337,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
#ifdef USING_LOGGER_DEFAULT // Do not retest logprint
static bool checkPriForTag(AndroidLogFormat* p_format, const char* tag,
@@ -3004,21 +3008,6 @@
EXPECT_LE(0, android_log_destroy(&ctx));
ASSERT_TRUE(NULL == ctx);
}
-
-TEST(liblog, android_log_write_list_buffer) {
- __android_log_event_list ctx(1005);
- ctx << 1005 << "tag_def"
- << "(tag|1),(name|3),(format|3)";
- std::string buffer(ctx);
- ctx.close();
-
- char msgBuf[1024];
- memset(msgBuf, 0, sizeof(msgBuf));
- EXPECT_EQ(android_log_buffer_to_string(buffer.data(), buffer.length(), msgBuf,
- sizeof(msgBuf)),
- 0);
- EXPECT_STREQ(msgBuf, "[1005,tag_def,(tag|1),(name|3),(format|3)]");
-}
#endif // USING_LOGGER_DEFAULT
#ifdef USING_LOGGER_DEFAULT // Do not retest pmsg functionality
diff --git a/liblog/tests/liblog_test_default.cpp b/liblog/tests/liblog_test_default.cpp
index 9fc443c..2edea27 100644
--- a/liblog/tests/liblog_test_default.cpp
+++ b/liblog/tests/liblog_test_default.cpp
@@ -2,4 +2,5 @@
#include <log/log_transport.h>
#define TEST_LOGGER LOGGER_DEFAULT
#endif
+#define USING_LOGGER_DEFAULT
#include "liblog_test.cpp"
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 62a7266..d864d1b 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -105,6 +105,8 @@
cc_test {
name: "memunreachable_binder_test",
defaults: ["libmemunreachable_defaults"],
+ require_root: true,
+
srcs: [
"tests/Binder_test.cpp",
],
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 15f03d0..0c9a2b8 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -34,6 +34,7 @@
host_supported: true,
srcs: [
"process.cpp",
+ "process_map.cpp",
],
local_include_dirs: ["include"],
@@ -58,6 +59,7 @@
name: "libprocinfo_test",
defaults: ["libprocinfo_defaults"],
host_supported: true,
+ isolated: true,
srcs: [
"process_test.cpp",
"process_map_test.cpp",
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index b6ec3cb..569a022 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -176,5 +176,9 @@
const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
}
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback);
+
} /* namespace procinfo */
} /* namespace android */
diff --git a/libprocinfo/process_map.cpp b/libprocinfo/process_map.cpp
new file mode 100644
index 0000000..5e240b9
--- /dev/null
+++ b/libprocinfo/process_map.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <procinfo/process_map.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <procinfo/process.h>
+
+namespace android {
+namespace procinfo {
+
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback) {
+ if (buffer == nullptr || buffer_size == 0) {
+ return false;
+ }
+
+ int fd = open(map_file, O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return false;
+ }
+
+ char* char_buffer = reinterpret_cast<char*>(buffer);
+ size_t start = 0;
+ size_t read_bytes = 0;
+ char* line = nullptr;
+ bool read_complete = false;
+ while (true) {
+ ssize_t bytes =
+ TEMP_FAILURE_RETRY(read(fd, char_buffer + read_bytes, buffer_size - read_bytes - 1));
+ if (bytes <= 0) {
+ if (read_bytes == 0) {
+ close(fd);
+ return bytes == 0;
+ }
+ // Treat the last piece of data as the last line.
+ char_buffer[start + read_bytes] = '\n';
+ bytes = 1;
+ read_complete = true;
+ }
+ read_bytes += bytes;
+
+ while (read_bytes > 0) {
+ char* newline = reinterpret_cast<char*>(memchr(&char_buffer[start], '\n', read_bytes));
+ if (newline == nullptr) {
+ break;
+ }
+ *newline = '\0';
+ line = &char_buffer[start];
+ start = newline - char_buffer + 1;
+ read_bytes -= newline - line + 1;
+
+ // Ignore the return code, errors are okay.
+ ReadMapFileContent(line, callback);
+ }
+
+ if (read_complete) {
+ close(fd);
+ return true;
+ }
+
+ if (start == 0 && read_bytes == buffer_size - 1) {
+ // The buffer provided is too small to contain this line, give up
+ // and indicate failure.
+ close(fd);
+ return false;
+ }
+
+ // Copy any leftover data to the front of the buffer.
+ if (start > 0) {
+ if (read_bytes > 0) {
+ memmove(char_buffer, &char_buffer[start], read_bytes);
+ }
+ start = 0;
+ }
+ }
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 562d864..b1bdc08 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -16,9 +16,14 @@
#include <procinfo/process_map.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+
#include <string>
+#include <vector>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
@@ -63,3 +68,215 @@
ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
ASSERT_GT(maps.size(), 0u);
}
+
+extern "C" void malloc_disable();
+extern "C" void malloc_enable();
+
+struct TestMapInfo {
+ TestMapInfo() = default;
+ TestMapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* new_name)
+ : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode) {
+ strcpy(name, new_name);
+ }
+ uint64_t start = 0;
+ uint64_t end = 0;
+ uint16_t flags = 0;
+ uint64_t pgoff = 0;
+ ino_t inode = 0;
+ char name[100] = {};
+};
+
+void VerifyReadMapFileAsyncSafe(const char* maps_data,
+ const std::vector<TestMapInfo>& expected_info) {
+ TemporaryFile tf;
+ ASSERT_TRUE(android::base::WriteStringToFd(maps_data, tf.fd));
+
+ std::vector<TestMapInfo> saved_info(expected_info.size());
+ size_t num_maps = 0;
+
+ auto callback = [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) {
+ if (num_maps != saved_info.size()) {
+ TestMapInfo& saved = saved_info[num_maps];
+ saved.start = start;
+ saved.end = end;
+ saved.flags = flags;
+ saved.pgoff = pgoff;
+ saved.inode = inode;
+ strcpy(saved.name, name);
+ }
+ num_maps++;
+ };
+
+ std::vector<char> buffer(64 * 1024);
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ bool parsed =
+ android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer.data(), buffer.size(), callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_TRUE(parsed) << "Parsing of data failed:\n" << maps_data;
+ ASSERT_EQ(expected_info.size(), num_maps);
+ for (size_t i = 0; i < expected_info.size(); i++) {
+ const TestMapInfo& expected = expected_info[i];
+ const TestMapInfo& saved = saved_info[i];
+ EXPECT_EQ(expected.start, saved.start);
+ EXPECT_EQ(expected.end, saved.end);
+ EXPECT_EQ(expected.flags, saved.flags);
+ EXPECT_EQ(expected.pgoff, saved.pgoff);
+ EXPECT_EQ(expected.inode, saved.inode);
+ EXPECT_STREQ(expected.name, saved.name);
+ }
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_invalid) {
+ std::vector<TestMapInfo> expected_info;
+
+ VerifyReadMapFileAsyncSafe("12c00000-2ac00000", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+ "/lib/fake.so");
+
+ VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so",
+ expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_with_newline) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+ "/lib/fake.so");
+
+ VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so\n",
+ expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_no_library) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 0xb00, 101, "");
+
+ VerifyReadMapFileAsyncSafe("a0000-c0000 rwxp 00000b00 00:05 101", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 1, 100, "");
+ expected_info.emplace_back(0xd0000, 0xe0000, PROT_READ, 2, 101, "/lib/libsomething1.so");
+ expected_info.emplace_back(0xf0000, 0x100000, PROT_WRITE, 3, 102, "/lib/libsomething2.so");
+ expected_info.emplace_back(0x110000, 0x120000, PROT_EXEC, 4, 103, "[anon:something or another]");
+
+ std::string map_data =
+ "0a0000-0c0000 rwxp 00000001 00:05 100\n"
+ "0d0000-0e0000 r--p 00000002 00:05 101 /lib/libsomething1.so\n"
+ "0f0000-100000 -w-p 00000003 00:05 102 /lib/libsomething2.so\n"
+ "110000-120000 --xp 00000004 00:05 103 [anon:something or another]\n";
+
+ VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple_reads) {
+ std::vector<TestMapInfo> expected_info;
+ std::string map_data;
+ uint64_t start = 0xa0000;
+ for (size_t i = 0; i < 10000; i++) {
+ map_data += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r--p %zx 01:20 %zu fake.so\n",
+ start, start + 0x1000, i, 1000 + i);
+ expected_info.emplace_back(start, start + 0x1000, PROT_READ, i, 1000 + i, "fake.so");
+ }
+
+ VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_nullptr) {
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", nullptr, 10, callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_size_zero) {
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ char buffer[10];
+ bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, 0, callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_no_calls) {
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ char buffer[10];
+ bool parsed =
+ android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_could_parse) {
+ TemporaryFile tf;
+ ASSERT_TRUE(android::base::WriteStringToFd(
+ "0a0000-0c0000 rwxp 00000001 00:05 100 /fake/lib.so\n", tf.fd));
+
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ char buffer[39];
+ bool parsed = android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index d0562d9..0cbcac5 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -165,8 +165,8 @@
}
}
-int GetInfo(const char* file, uint64_t pc) {
- Elf elf(Memory::CreateFileMemory(file, pc).release());
+int GetInfo(const char* file, uint64_t offset, uint64_t pc) {
+ Elf elf(Memory::CreateFileMemory(file, offset).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", file);
return 1;
@@ -243,12 +243,14 @@
} // namespace unwindstack
int main(int argc, char** argv) {
- if (argc != 3) {
- printf("Usage: unwind_reg_info ELF_FILE PC\n");
+ if (argc != 3 && argc != 4) {
+ printf("Usage: unwind_reg_info ELF_FILE PC [OFFSET]\n");
printf(" ELF_FILE\n");
printf(" The path to an elf file.\n");
printf(" PC\n");
printf(" The pc for which the register information should be obtained.\n");
+ printf(" OFFSET\n");
+ printf(" Use the offset into the ELF file as the beginning of the elf.\n");
return 1;
}
@@ -270,5 +272,15 @@
return 1;
}
- return unwindstack::GetInfo(argv[1], pc);
+ uint64_t offset = 0;
+ if (argc == 4) {
+ char* end;
+ offset = strtoull(argv[3], &end, 16);
+ if (*end != '\0') {
+ printf("Malformed OFFSET value: %s\n", argv[3]);
+ return 1;
+ }
+ }
+
+ return unwindstack::GetInfo(argv[1], offset, pc);
}
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 2fd9f95..82c4fb9 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -56,14 +56,7 @@
mLogId(elem.mLogId),
mDropped(elem.mDropped) {
if (mDropped) {
- if (elem.isBinary() && elem.mMsg != nullptr) {
- // for the following "len" value, refer to : setDropped(uint16_t value), getTag()
- const int len = sizeof(android_event_header_t);
- mMsg = new char[len];
- memcpy(mMsg, elem.mMsg, len);
- } else {
- mMsg = nullptr;
- }
+ mTag = elem.getTag();
} else {
mMsg = new char[mMsgLen];
memcpy(mMsg, elem.mMsg, mMsgLen);
@@ -71,31 +64,43 @@
}
LogBufferElement::~LogBufferElement() {
- delete[] mMsg;
+ if (!mDropped) {
+ delete[] mMsg;
+ }
}
uint32_t LogBufferElement::getTag() const {
- return (isBinary() &&
- ((mDropped && mMsg != nullptr) ||
- (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
- ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
- : 0;
+ // Binary buffers have no tag.
+ if (!isBinary()) {
+ return 0;
+ }
+
+ // Dropped messages store the tag in place of mMsg.
+ if (mDropped) {
+ return mTag;
+ }
+
+ // For non-dropped messages, we get the tag from the message header itself.
+ if (mMsgLen < sizeof(android_event_header_t)) {
+ return 0;
+ }
+
+ return reinterpret_cast<const android_event_header_t*>(mMsg)->tag;
}
uint16_t LogBufferElement::setDropped(uint16_t value) {
- // The tag information is saved in mMsg data, if the tag is non-zero
- // save only the information needed to get the tag.
- if (getTag() != 0) {
- if (mMsgLen > sizeof(android_event_header_t)) {
- char* truncated_msg = new char[sizeof(android_event_header_t)];
- memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
- delete[] mMsg;
- mMsg = truncated_msg;
- } // mMsgLen == sizeof(android_event_header_t), already at minimum.
- } else {
- delete[] mMsg;
- mMsg = nullptr;
+ if (mDropped) {
+ return mDroppedCount = value;
}
+
+ // The tag information is saved in mMsg data, which is in a union with mTag, used after mDropped
+ // is set to true. Therefore we save the tag value aside, delete mMsg, then set mTag to the tag
+ // value in its place.
+ auto old_tag = getTag();
+ delete[] mMsg;
+ mMsg = nullptr;
+
+ mTag = old_tag;
mDropped = true;
return mDroppedCount = value;
}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 57b0a95..da4991b 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -41,7 +41,10 @@
const uint32_t mPid;
const uint32_t mTid;
log_time mRealTime;
- char* mMsg;
+ union {
+ char* mMsg; // mDropped == false
+ int32_t mTag; // mDropped == true
+ };
union {
const uint16_t mMsgLen; // mDropped == false
uint16_t mDroppedCount; // mDropped == true
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9b0075d..0f61a61 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -618,6 +618,9 @@
mkdir /data/cache/backup_stage 0700 system system
mkdir /data/cache/backup 0700 system system
+ mkdir /data/rollback 0700 system system
+ mkdir /data/rollback-observer 0700 system system
+
# Wait for apexd to finish activating APEXes before starting more processes.
wait_for_prop apexd.status ready
parse_apex_configs
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index ca2421b..6668cf3 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -41,7 +41,6 @@
using android::hardware::health::V2_0::StorageInfo;
const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
-const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
const char* emmc_info_t::emmc_ver_str[9] = {
"4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
};
@@ -62,10 +61,8 @@
if (healthService != nullptr) {
return new health_storage_info_t(healthService);
}
- if (FileExists(emmc_info_t::emmc_sysfs) ||
- FileExists(emmc_info_t::emmc_debugfs)) {
- return new emmc_info_t;
- }
+ if (FileExists(emmc_info_t::emmc_sysfs)) return new emmc_info_t;
+
if (FileExists(ufs_info_t::health_file)) {
return new ufs_info_t;
}
@@ -241,8 +238,7 @@
void emmc_info_t::report()
{
- if (!report_sysfs() && !report_debugfs())
- return;
+ if (!report_sysfs()) return;
publish();
}
@@ -284,54 +280,6 @@
return true;
}
-namespace {
-
-const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
-/* 2 characters in string for each byte */
-const size_t EXT_CSD_REV_IDX = 192 * 2;
-const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
-const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
-const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
-
-} // namespace
-
-bool emmc_info_t::report_debugfs()
-{
- string buffer;
- uint16_t rev = 0;
-
- if (!ReadFileToString(emmc_debugfs, &buffer) ||
- buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
- return false;
- }
-
- string str = buffer.substr(EXT_CSD_REV_IDX, 2);
- if (!ParseUint(str, &rev) ||
- rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
- return false;
- }
-
- version = "emmc ";
- version += emmc_ver_str[rev];
-
- str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
- if (!ParseUint(str, &eol)) {
- return false;
- }
-
- str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
- if (!ParseUint(str, &lifetime_a)) {
- return false;
- }
-
- str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
- if (!ParseUint(str, &lifetime_b)) {
- return false;
- }
-
- return true;
-}
-
void ufs_info_t::report()
{
string buffer;