Merge "logcat: modernize the code"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cc1978d..375207b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,7 +10,7 @@
"name": "debuggerd_test"
},
{
- "name": "fs_mgr_unit_test"
+ "name": "CtsFsMgrTestCases"
},
{
"name": "fs_mgr_vendor_overlay_test"
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index bb63df8..f579078 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -32,9 +32,9 @@
Then enter one of the following sequences:
- $ adb stop
+ $ adb shell stop
$ adb sync
- $ adb start
+ $ adb shell start
$ adb reboot
*or*
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 2ff5243..d216458 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -99,71 +99,9 @@
return false;
}
-const std::array<const char*, 3> kFileContentsEncryptionMode = {
- "aes-256-xts",
- "adiantum",
- "ice",
-};
-
-const std::array<const char*, 3> kFileNamesEncryptionMode = {
- "aes-256-cts",
- "aes-256-heh",
- "adiantum",
-};
-
void ParseFileEncryption(const std::string& arg, FstabEntry* entry) {
- // The fileencryption flag is followed by an = and 1 to 3 colon-separated fields:
- //
- // 1. Contents encryption mode
- // 2. Filenames encryption mode (defaults to "aes-256-cts" or "adiantum"
- // depending on the contents encryption mode)
- // 3. Encryption policy version (defaults to "v1". Use "v2" on new devices.)
entry->fs_mgr_flags.file_encryption = true;
-
- auto parts = Split(arg, ":");
- if (parts.empty() || parts.size() > 3) {
- LWARNING << "Warning: fileencryption= flag malformed: " << arg;
- return;
- }
-
- // Alias for backwards compatibility.
- if (parts[0] == "software") {
- parts[0] = "aes-256-xts";
- }
-
- if (std::find(kFileContentsEncryptionMode.begin(), kFileContentsEncryptionMode.end(),
- parts[0]) == kFileContentsEncryptionMode.end()) {
- LWARNING << "fileencryption= flag malformed, file contents encryption mode not found: "
- << arg;
- return;
- }
-
- entry->file_contents_mode = parts[0];
-
- if (parts.size() >= 2) {
- if (std::find(kFileNamesEncryptionMode.begin(), kFileNamesEncryptionMode.end(), parts[1]) ==
- kFileNamesEncryptionMode.end()) {
- LWARNING << "fileencryption= flag malformed, file names encryption mode not found: "
- << arg;
- return;
- }
-
- entry->file_names_mode = parts[1];
- } else if (entry->file_contents_mode == "adiantum") {
- entry->file_names_mode = "adiantum";
- } else {
- entry->file_names_mode = "aes-256-cts";
- }
-
- if (parts.size() >= 3) {
- if (!android::base::StartsWith(parts[2], 'v') ||
- !android::base::ParseInt(&parts[2][1], &entry->file_policy_version)) {
- LWARNING << "fileencryption= flag malformed, unknown options: " << arg;
- return;
- }
- } else {
- entry->file_policy_version = 1;
- }
+ entry->encryption_options = arg;
}
bool SetMountFlag(const std::string& flag, FstabEntry* entry) {
@@ -299,9 +237,7 @@
// return it.
entry->fs_mgr_flags.force_fde_or_fbe = true;
entry->key_loc = arg;
- entry->file_contents_mode = "aes-256-xts";
- entry->file_names_mode = "aes-256-cts";
- entry->file_policy_version = 1;
+ entry->encryption_options = "aes-256-xts:aes-256-cts";
} else if (StartsWith(flag, "max_comp_streams=")) {
if (!ParseInt(arg, &entry->max_comp_streams)) {
LWARNING << "Warning: max_comp_streams= flag malformed: " << arg;
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 3c517dc..c6a16e3 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -45,9 +45,7 @@
int max_comp_streams = 0;
off64_t zram_size = 0;
off64_t reserved_size = 0;
- std::string file_contents_mode;
- std::string file_names_mode;
- int file_policy_version = 0;
+ std::string encryption_options;
off64_t erase_blk_size = 0;
off64_t logical_blk_size = 0;
std::string sysfs_path;
diff --git a/fs_mgr/libsnapshot/dm_snapshot_internals.h b/fs_mgr/libsnapshot/dm_snapshot_internals.h
index 4903de1..fef256d 100644
--- a/fs_mgr/libsnapshot/dm_snapshot_internals.h
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#pragma once
+
#include <stdint.h>
#include <vector>
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
new file mode 100644
index 0000000..d5ceb0e
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <string>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace snapshot {
+
+// An abstract "device" that will be cleaned up (unmapped, unmounted, etc.) upon
+// destruction.
+struct AutoDevice {
+ virtual ~AutoDevice(){};
+ void Release();
+
+ protected:
+ AutoDevice(const std::string& name) : name_(name) {}
+ std::string name_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoDevice);
+ AutoDevice(AutoDevice&& other) = delete;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 120340c..431fea1 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -33,6 +33,8 @@
#include <liblp/liblp.h>
#include <update_engine/update_metadata.pb.h>
+#include <libsnapshot/auto_device.h>
+
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
friend class test_set_name##_##individual_test##_Test
@@ -120,6 +122,7 @@
virtual const IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+ virtual bool IsRecovery() const = 0;
};
~SnapshotManager();
@@ -209,6 +212,22 @@
// Dump debug information.
bool Dump(std::ostream& os);
+ // Ensure metadata directory is mounted in recovery. When the returned
+ // AutoDevice is destroyed, the metadata directory is automatically
+ // unmounted.
+ // Return nullptr if any failure.
+ // In Android mode, Return an AutoDevice that does nothing
+ // In recovery, return an AutoDevice that does nothing if metadata entry
+ // is not found in fstab.
+ // Note: if this function is called the second time before the AutoDevice returned from the
+ // first call is destroyed, the device will be unmounted when any of these AutoDevices is
+ // destroyed. FOr example:
+ // auto a = mgr->EnsureMetadataMounted(); // mounts
+ // auto b = mgr->EnsureMetadataMounted(); // does nothing
+ // b.reset() // unmounts
+ // a.reset() // does nothing
+ std::unique_ptr<AutoDevice> EnsureMetadataMounted();
+
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
FRIEND_TEST(SnapshotTest, CreateSnapshot);
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index eedc1cd..77315b4 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -17,8 +17,9 @@
#include <math.h>
#include <android-base/logging.h>
-
#include <android/snapshot/snapshot.pb.h>
+
+#include "dm_snapshot_internals.h"
#include "utility.h"
using android::dm::kSectorSize;
@@ -33,13 +34,6 @@
namespace android {
namespace snapshot {
-// Round |d| up to a multiple of |block_size|.
-static uint64_t RoundUp(double d, uint64_t block_size) {
- uint64_t ret = ((uint64_t)ceil(d) + block_size - 1) / block_size * block_size;
- CHECK(ret >= d) << "Can't round " << d << " up to a multiple of " << block_size;
- return ret;
-}
-
// Intersect two linear extents. If no intersection, return an extent with length 0.
static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {
// Convert target_extent and existing_extent to linear extents. Zero extents
@@ -68,33 +62,58 @@
return false;
}
-std::optional<uint64_t> PartitionCowCreator::GetCowSize(uint64_t snapshot_size) {
- // TODO: Use |operations|. to determine a minimum COW size.
- // kCowEstimateFactor is good for prototyping but we can't use that in production.
- static constexpr double kCowEstimateFactor = 1.05;
- auto cow_size = RoundUp(snapshot_size * kCowEstimateFactor, kDefaultBlockSize);
- return cow_size;
+uint64_t PartitionCowCreator::GetCowSize() {
+ // WARNING: The origin partition should be READ-ONLY
+ const uint64_t logical_block_size = current_metadata->logical_block_size();
+ const unsigned int sectors_per_block = logical_block_size / kSectorSize;
+ DmSnapCowSizeCalculator sc(kSectorSize, kSnapshotChunkSize);
+
+ if (operations == nullptr) return sc.cow_size_bytes();
+
+ for (const auto& iop : *operations) {
+ for (const auto& de : iop.dst_extents()) {
+ // Skip if no blocks are written
+ if (de.num_blocks() == 0) continue;
+
+ // Flag all the blocks that were written
+ const auto block_boundary = de.start_block() + de.num_blocks();
+ for (auto b = de.start_block(); b < block_boundary; ++b) {
+ for (unsigned int s = 0; s < sectors_per_block; ++s) {
+ const auto sector_id = b * sectors_per_block + s;
+ sc.WriteSector(sector_id);
+ }
+ }
+ }
+ }
+
+ return sc.cow_size_bytes();
}
std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
- uint64_t logical_block_size = current_metadata->logical_block_size();
+ const uint64_t logical_block_size = current_metadata->logical_block_size();
CHECK(logical_block_size != 0 && !(logical_block_size & (logical_block_size - 1)))
<< "logical_block_size is not power of 2";
Return ret;
ret.snapshot_status.set_name(target_partition->name());
ret.snapshot_status.set_device_size(target_partition->size());
-
- // TODO(b/141889746): Optimize by using a smaller snapshot. Some ranges in target_partition
- // may be written directly.
ret.snapshot_status.set_snapshot_size(target_partition->size());
- auto cow_size = GetCowSize(ret.snapshot_status.snapshot_size());
- if (!cow_size.has_value()) return std::nullopt;
-
+ // Being the COW partition virtual, its size doesn't affect the storage
+ // memory that will be occupied by the target.
+ // The actual storage space is affected by the COW file, whose size depends
+ // on the chunks that diverged between |current| and |target|.
+ // If the |target| partition is bigger than |current|, the data that is
+ // modified outside of |current| can be written directly to |current|.
+ // This because the data that will be written outside of |current| would
+ // not invalidate any useful information of |current|, thus:
+ // - if the snapshot is accepted for merge, this data would be already at
+ // the right place and should not be copied;
+ // - in the unfortunate case of the snapshot to be discarded, the regions
+ // modified by this data can be set as free regions and reused.
// Compute regions that are free in both current and target metadata. These are the regions
// we can use for COW partition.
auto target_free_regions = target_metadata->GetFreeRegions();
@@ -102,13 +121,15 @@
auto free_regions = Interval::Intersect(target_free_regions, current_free_regions);
uint64_t free_region_length = 0;
for (const auto& interval : free_regions) {
- free_region_length += interval.length() * kSectorSize;
+ free_region_length += interval.length();
}
+ free_region_length *= kSectorSize;
LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
+ auto cow_size = GetCowSize();
// Compute the COW partition size.
- uint64_t cow_partition_size = std::min(*cow_size, free_region_length);
+ uint64_t cow_partition_size = std::min(cow_size, free_region_length);
// Round it down to the nearest logical block. Logical partitions must be a multiple
// of logical blocks.
cow_partition_size &= ~(logical_block_size - 1);
@@ -116,8 +137,7 @@
// Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.
ret.cow_partition_usable_regions = std::move(free_regions);
- // The rest of the COW space is allocated on ImageManager.
- uint64_t cow_file_size = (*cow_size) - ret.snapshot_status.cow_partition_size();
+ auto cow_file_size = cow_size - cow_partition_size;
// Round it up to the nearest sector.
cow_file_size += kSectorSize - 1;
cow_file_size &= ~(kSectorSize - 1);
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 8888f78..d3d186b 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -60,7 +60,7 @@
private:
bool HasExtent(Partition* p, Extent* e);
- std::optional<uint64_t> GetCowSize(uint64_t snapshot_size);
+ uint64_t GetCowSize();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index f683f5b..eae6c35 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -14,12 +14,14 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <libdm/dm.h>
#include <liblp/builder.h>
#include <liblp/property_fetcher.h>
#include "dm_snapshot_internals.h"
#include "partition_cow_creator.h"
#include "test_helpers.h"
+#include "utility.h"
using namespace android::fs_mgr;
@@ -100,6 +102,90 @@
ASSERT_TRUE(ret.has_value());
}
+TEST_F(PartitionCowCreatorTest, CowSize) {
+ using InstallOperation = chromeos_update_engine::InstallOperation;
+ using RepeatedInstallOperationPtr = google::protobuf::RepeatedPtrField<InstallOperation>;
+ using Extent = chromeos_update_engine::Extent;
+
+ constexpr uint64_t initial_size = 50_MiB;
+ constexpr uint64_t final_size = 40_MiB;
+
+ auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ ASSERT_NE(builder_a, nullptr);
+ auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_a, nullptr);
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+
+ auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ ASSERT_NE(builder_b, nullptr);
+ auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_b, nullptr);
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+
+ const uint64_t block_size = builder_b->logical_block_size();
+ const uint64_t chunk_size = kSnapshotChunkSize * dm::kSectorSize;
+ ASSERT_EQ(chunk_size, block_size);
+
+ auto cow_device_size = [](const std::vector<InstallOperation>& iopv, MetadataBuilder* builder_a,
+ MetadataBuilder* builder_b, Partition* system_b) {
+ RepeatedInstallOperationPtr riop(iopv.begin(), iopv.end());
+ PartitionCowCreator creator{.target_metadata = builder_b,
+ .target_suffix = "_b",
+ .target_partition = system_b,
+ .current_metadata = builder_a,
+ .current_suffix = "_a",
+ .operations = &riop};
+
+ auto ret = creator.Run();
+
+ if (ret.has_value()) {
+ return ret->snapshot_status.cow_file_size() + ret->snapshot_status.cow_partition_size();
+ }
+ return std::numeric_limits<uint64_t>::max();
+ };
+
+ std::vector<InstallOperation> iopv;
+ InstallOperation iop;
+ Extent* e;
+
+ // No data written, no operations performed
+ ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ // No data written
+ e = iop.add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(0);
+ iopv.push_back(iop);
+ ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ e = iop.add_dst_extents();
+ e->set_start_block(1);
+ e->set_num_blocks(0);
+ iopv.push_back(iop);
+ ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ // Fill the first block
+ e = iop.add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(1);
+ iopv.push_back(iop);
+ ASSERT_EQ(3 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ // Fill the second block
+ e = iop.add_dst_extents();
+ e->set_start_block(1);
+ e->set_num_blocks(1);
+ iopv.push_back(iop);
+ ASSERT_EQ(4 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+
+ // Jump to 5th block and write 2
+ e = iop.add_dst_extents();
+ e->set_start_block(5);
+ e->set_num_blocks(2);
+ iopv.push_back(iop);
+ ASSERT_EQ(6 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
+}
+
TEST(DmSnapshotInternals, CowSizeCalculator) {
DmSnapCowSizeCalculator cc(512, 8);
unsigned long int b;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 758f69b..63d97d0 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -77,6 +77,12 @@
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
+#ifdef __ANDROID_RECOVERY__
+constexpr bool kIsRecovery = true;
+#else
+constexpr bool kIsRecovery = false;
+#endif
+
class DeviceInfo final : public SnapshotManager::IDeviceInfo {
public:
std::string GetGsidDir() const override { return "ota"s; }
@@ -89,6 +95,7 @@
}
bool IsOverlayfsSetup() const override { return fs_mgr_overlayfs_is_setup(); }
bool SetBootControlMergeStatus(MergeStatus status) override;
+ bool IsRecovery() const override { return kIsRecovery; }
private:
android::fs_mgr::PartitionOpener opener_;
@@ -1786,6 +1793,14 @@
auto target_metadata =
MetadataBuilder::NewForUpdate(opener, current_super, current_slot, target_slot);
+ // Delete partitions with target suffix in |current_metadata|. Otherwise,
+ // partition_cow_creator recognizes these left-over partitions as used space.
+ for (const auto& group_name : current_metadata->ListGroups()) {
+ if (android::base::EndsWith(group_name, target_suffix)) {
+ current_metadata->RemoveGroupAndPartitions(group_name);
+ }
+ }
+
SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
if (!metadata_updater.Update()) {
LOG(ERROR) << "Cannot calculate new metadata.";
@@ -2065,5 +2080,14 @@
return ok;
}
+std::unique_ptr<AutoDevice> SnapshotManager::EnsureMetadataMounted() {
+ if (!device_->IsRecovery()) {
+ // No need to mount anything in recovery.
+ LOG(INFO) << "EnsureMetadataMounted does nothing in Android mode.";
+ return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice());
+ }
+ return AutoUnmountDevice::New(device_->GetMetadataDir());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index f6a4722..5728582 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -26,6 +26,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr/roots.h>
#include <fs_mgr_dm_linear.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
@@ -47,12 +48,16 @@
using android::fs_mgr::BlockDeviceInfo;
using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::EnsurePathMounted;
+using android::fs_mgr::EnsurePathUnmounted;
using android::fs_mgr::Extent;
+using android::fs_mgr::Fstab;
using android::fs_mgr::GetPartitionGroupName;
using android::fs_mgr::GetPartitionName;
using android::fs_mgr::Interval;
using android::fs_mgr::MetadataBuilder;
using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::DynamicPartitionGroup;
using chromeos_update_engine::PartitionUpdate;
using namespace ::testing;
using namespace android::storage_literals;
@@ -656,12 +661,12 @@
// Not using full name "system", "vendor", "product" because these names collide with the
// mapped partitions on the running device.
// Each test modifies manifest_ slightly to indicate changes to the partition layout.
- auto group = manifest_.mutable_dynamic_partition_metadata()->add_groups();
- group->set_name("group");
- group->set_size(kGroupSize);
- group->add_partition_names("sys");
- group->add_partition_names("vnd");
- group->add_partition_names("prd");
+ group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups();
+ group_->set_name("group");
+ group_->set_size(kGroupSize);
+ group_->add_partition_names("sys");
+ group_->add_partition_names("vnd");
+ group_->add_partition_names("prd");
sys_ = manifest_.add_partitions();
sys_->set_partition_name("sys");
SetSize(sys_, 3_MiB);
@@ -765,6 +770,7 @@
PartitionUpdate* sys_ = nullptr;
PartitionUpdate* vnd_ = nullptr;
PartitionUpdate* prd_ = nullptr;
+ DynamicPartitionGroup* group_ = nullptr;
};
// Test full update flow executed by update_engine. Some partitions uses super empty space,
@@ -778,9 +784,17 @@
}
// Grow all partitions.
- SetSize(sys_, 3788_KiB);
- SetSize(vnd_, 3788_KiB);
- SetSize(prd_, 3788_KiB);
+ constexpr uint64_t partition_size = 3788_KiB;
+ SetSize(sys_, partition_size);
+ SetSize(vnd_, partition_size);
+ SetSize(prd_, partition_size);
+
+ // Create fake install operations to grow the COW device size.
+ for (auto& partition : {sys_, vnd_, prd_}) {
+ auto e = partition->add_operations()->add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(GetSize(partition) / manifest_.block_size());
+ }
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
@@ -943,6 +957,13 @@
ASSERT_TRUE(sm->BeginUpdate());
ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
+ // Create fake install operations to grow the COW device size.
+ for (auto& partition : {sys_, vnd_, prd_}) {
+ auto e = partition->add_operations()->add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(GetSize(partition) / manifest_.block_size());
+ }
+
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
// Write some data to target partitions.
@@ -1056,6 +1077,149 @@
}
}
+TEST_F(SnapshotUpdateTest, RetrofitAfterRegularAb) {
+ constexpr auto kRetrofitGroupSize = kGroupSize / 2;
+
+ // Initialize device-mapper / disk
+ ASSERT_TRUE(UnmapAll());
+ FormatFakeSuper();
+
+ // Setup source partition metadata to have both _a and _b partitions.
+ src_ = MetadataBuilder::New(*opener_, "super", 0);
+ ASSERT_NE(nullptr, src_);
+ for (const auto& suffix : {"_a"s, "_b"s}) {
+ ASSERT_TRUE(src_->AddGroup(group_->name() + suffix, kRetrofitGroupSize));
+ for (const auto& name : {"sys"s, "vnd"s, "prd"s}) {
+ auto partition = src_->AddPartition(name + suffix, group_->name() + suffix, 0);
+ ASSERT_NE(nullptr, partition);
+ ASSERT_TRUE(src_->ResizePartition(partition, 2_MiB));
+ }
+ }
+ auto metadata = src_->Export();
+ ASSERT_NE(nullptr, metadata);
+ ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
+
+ // Flash source partitions
+ std::string path;
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(CreateLogicalPartition(
+ CreateLogicalPartitionParams{
+ .block_device = fake_super,
+ .metadata_slot = 0,
+ .partition_name = name,
+ .timeout_ms = 1s,
+ .partition_opener = opener_.get(),
+ },
+ &path));
+ ASSERT_TRUE(WriteRandomData(path));
+ auto hash = GetHash(path);
+ ASSERT_TRUE(hash.has_value());
+ hashes_[name] = *hash;
+ }
+
+ // Setup manifest.
+ group_->set_size(kRetrofitGroupSize);
+ for (auto* partition : {sys_, vnd_, prd_}) {
+ SetSize(partition, 2_MiB);
+ auto* e = partition->add_operations()->add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(2_MiB / manifest_.block_size());
+ }
+
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Test that COW image should not be created for retrofit devices; super
+ // should be big enough.
+ ASSERT_FALSE(image_manager_->BackingImageExists("sys_b-cow-img"));
+ ASSERT_FALSE(image_manager_->BackingImageExists("vnd_b-cow-img"));
+ ASSERT_FALSE(image_manager_->BackingImageExists("prd_b-cow-img"));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ std::string path;
+ ASSERT_TRUE(sm->MapUpdateSnapshot(
+ CreateLogicalPartitionParams{
+ .block_device = fake_super,
+ .metadata_slot = 1,
+ .partition_name = name,
+ .timeout_ms = 10s,
+ .partition_opener = opener_.get(),
+ },
+ &path))
+ << name;
+ ASSERT_TRUE(WriteRandomData(path));
+ auto hash = GetHash(path);
+ ASSERT_TRUE(hash.has_value());
+ hashes_[name] = *hash;
+ }
+
+ // Assert that source partitions aren't affected.
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+}
+
+class MetadataMountedTest : public SnapshotUpdateTest {
+ public:
+ void SetUp() override {
+ metadata_dir_ = test_device->GetMetadataDir();
+ ASSERT_TRUE(ReadDefaultFstab(&fstab_));
+ }
+ void TearDown() override {
+ SetUp();
+ // Remount /metadata
+ test_device->set_recovery(false);
+ EXPECT_TRUE(android::fs_mgr::EnsurePathMounted(&fstab_, metadata_dir_));
+ }
+ AssertionResult IsMetadataMounted() {
+ Fstab mounted_fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+ ADD_FAILURE() << "Failed to scan mounted volumes";
+ return AssertionFailure() << "Failed to scan mounted volumes";
+ }
+
+ auto entry = GetEntryForPath(&fstab_, metadata_dir_);
+ if (entry == nullptr) {
+ return AssertionFailure() << "No mount point found in fstab for path " << metadata_dir_;
+ }
+
+ auto mv = GetEntryForMountPoint(&mounted_fstab, entry->mount_point);
+ if (mv == nullptr) {
+ return AssertionFailure() << metadata_dir_ << " is not mounted";
+ }
+ return AssertionSuccess() << metadata_dir_ << " is mounted";
+ }
+ std::string metadata_dir_;
+ Fstab fstab_;
+};
+
+TEST_F(MetadataMountedTest, Android) {
+ auto device = sm->EnsureMetadataMounted();
+ EXPECT_NE(nullptr, device);
+ device.reset();
+
+ EXPECT_TRUE(IsMetadataMounted());
+ EXPECT_TRUE(sm->CancelUpdate()) << "Metadata dir should never be unmounted in Android mode";
+}
+
+TEST_F(MetadataMountedTest, Recovery) {
+ test_device->set_recovery(true);
+ metadata_dir_ = test_device->GetMetadataDir();
+
+ EXPECT_TRUE(android::fs_mgr::EnsurePathUnmounted(&fstab_, metadata_dir_));
+ EXPECT_FALSE(IsMetadataMounted());
+
+ auto device = sm->EnsureMetadataMounted();
+ EXPECT_NE(nullptr, device);
+ EXPECT_TRUE(IsMetadataMounted());
+
+ device.reset();
+ EXPECT_FALSE(IsMetadataMounted());
+}
+
} // namespace snapshot
} // namespace android
@@ -1097,6 +1261,7 @@
}
// Clean up previous run.
+ MetadataMountedTest().TearDown();
SnapshotUpdateTest().Cleanup();
SnapshotTest().Cleanup();
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 539c5c5..312fa3e 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -140,5 +140,9 @@
partition_update->mutable_new_partition_info()->set_size(size);
}
+uint64_t GetSize(PartitionUpdate* partition_update) {
+ return partition_update->mutable_new_partition_info()->size();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index ea2c5b6..0f70afe 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -88,17 +88,20 @@
return true;
}
bool IsOverlayfsSetup() const override { return false; }
+ bool IsRecovery() const override { return recovery_; }
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);
}
+ void set_recovery(bool value) { recovery_ = value; }
MergeStatus merge_status() const { return merge_status_; }
private:
std::string slot_suffix_ = "_a";
std::unique_ptr<TestPartitionOpener> opener_;
MergeStatus merge_status_;
+ bool recovery_ = false;
};
class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
@@ -138,5 +141,8 @@
// In the update package metadata, set a partition with the given size.
void SetSize(PartitionUpdate* partition_update, uint64_t size);
+// Get partition size from update package metadata.
+uint64_t GetSize(PartitionUpdate* partition_update);
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 66629e8..615cbca 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -17,9 +17,15 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
+#include <fs_mgr/roots.h>
+using android::fs_mgr::EnsurePathMounted;
+using android::fs_mgr::EnsurePathUnmounted;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::GetEntryForPath;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::Partition;
+using android::fs_mgr::ReadDefaultFstab;
namespace android {
namespace snapshot {
@@ -109,5 +115,31 @@
return true;
}
+std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ LOG(ERROR) << "Cannot read default fstab";
+ return nullptr;
+ }
+
+ if (GetEntryForPath(&fstab, path) == nullptr) {
+ LOG(INFO) << "EnsureMetadataMounted can't find entry for " << path << ", skipping";
+ return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice("", {}));
+ }
+
+ if (!EnsurePathMounted(&fstab, path)) {
+ LOG(ERROR) << "Cannot mount " << path;
+ return nullptr;
+ }
+ return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice(path, std::move(fstab)));
+}
+
+AutoUnmountDevice::~AutoUnmountDevice() {
+ if (name_.empty()) return;
+ if (!EnsurePathUnmounted(&fstab_, name_)) {
+ LOG(ERROR) << "Cannot unmount " << name_;
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 3051184..0c08ed2 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -18,31 +18,21 @@
#include <string>
#include <android-base/macros.h>
+#include <fstab/fstab.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
#include <libsnapshot/snapshot.h>
#include <update_engine/update_metadata.pb.h>
+#include <libsnapshot/auto_device.h>
+
namespace android {
namespace snapshot {
// Unit is sectors, this is a 4K chunk.
static constexpr uint32_t kSnapshotChunkSize = 8;
-struct AutoDevice {
- virtual ~AutoDevice(){};
- void Release();
-
- protected:
- AutoDevice(const std::string& name) : name_(name) {}
- std::string name_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AutoDevice);
- AutoDevice(AutoDevice&& other) = delete;
-};
-
// A list of devices we created along the way.
// - Whenever a device is created that is subject to GC'ed at the end of
// this function, add it to this list.
@@ -103,6 +93,18 @@
SnapshotManager::LockedFile* lock_ = nullptr;
};
+struct AutoUnmountDevice : AutoDevice {
+ // Empty object that does nothing.
+ AutoUnmountDevice() : AutoDevice("") {}
+ static std::unique_ptr<AutoUnmountDevice> New(const std::string& path);
+ ~AutoUnmountDevice();
+
+ private:
+ AutoUnmountDevice(const std::string& path, android::fs_mgr::Fstab&& fstab)
+ : AutoDevice(path), fstab_(std::move(fstab)) {}
+ android::fs_mgr::Fstab fstab_;
+};
+
// Return a list of partitions in |builder| with the name ending in |suffix|.
std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 83668e9..4f6ec5a 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -13,8 +13,21 @@
// limitations under the License.
cc_test {
- name: "fs_mgr_unit_test",
- test_suites: ["device-tests"],
+ name: "CtsFsMgrTestCases",
+ test_suites: [
+ "cts",
+ "device-tests",
+ "vts",
+ ],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
shared_libs: [
"libbase",
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
new file mode 100644
index 0000000..91c3fb9
--- /dev/null
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?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 CTS fs_mgr test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="systems" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsFsMgrTestCases" />
+ <option name="runtime-hint" value="65s" />
+ </test>
+</configuration>
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index a7ea817..1cbaf45 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -179,6 +179,7 @@
{"nodiratime", MS_NODIRATIME},
{"ro", MS_RDONLY},
{"rw", 0},
+ {"sync", MS_SYNCHRONOUS},
{"remount", MS_REMOUNT},
{"bind", MS_BIND},
{"rec", MS_REC},
@@ -197,7 +198,7 @@
if (!(entry.flags & MS_RDONLY)) {
fs_options.emplace("rw");
}
- EXPECT_EQ(mnt_opts, fs_options);
+ EXPECT_EQ(mnt_opts, fs_options) << "At line " << i;
++i;
}
EXPECT_EQ(i, fstab.size());
@@ -420,8 +421,7 @@
EXPECT_EQ(0, entry->max_comp_streams);
EXPECT_EQ(0, entry->zram_size);
EXPECT_EQ(0, entry->reserved_size);
- EXPECT_EQ("", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
+ EXPECT_EQ("", entry->encryption_options);
EXPECT_EQ(0, entry->erase_blk_size);
EXPECT_EQ(0, entry->logical_blk_size);
EXPECT_EQ("", entry->sysfs_path);
@@ -448,8 +448,7 @@
EXPECT_EQ(0, entry->max_comp_streams);
EXPECT_EQ(0, entry->zram_size);
EXPECT_EQ(0, entry->reserved_size);
- EXPECT_EQ("", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
+ EXPECT_EQ("", entry->encryption_options);
EXPECT_EQ(0, entry->erase_blk_size);
EXPECT_EQ(0, entry->logical_blk_size);
EXPECT_EQ("", entry->sysfs_path);
@@ -458,16 +457,14 @@
EXPECT_EQ("", entry->zram_backing_dev_path);
entry++;
- // forcefdeorfbe sets file_contents_mode and file_names_mode by default, so test it separately.
+ // forcefdeorfbe has its own encryption_options defaults, so test it separately.
EXPECT_EQ("none2", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
flags.force_fde_or_fbe = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
+ EXPECT_EQ("aes-256-xts:aes-256-cts", entry->encryption_options);
EXPECT_EQ("", entry->key_loc);
}
@@ -681,37 +678,21 @@
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
EXPECT_EQ("/dir/key", entry->key_loc);
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
+ EXPECT_EQ("aes-256-xts:aes-256-cts", entry->encryption_options);
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FileEncryption) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
-source none0 swap defaults fileencryption=blah
-source none1 swap defaults fileencryption=software
-source none2 swap defaults fileencryption=aes-256-xts
-source none3 swap defaults fileencryption=adiantum
-source none4 swap defaults fileencryption=adiantum:aes-256-heh
-source none5 swap defaults fileencryption=ice
-source none6 swap defaults fileencryption=ice:blah
-source none7 swap defaults fileencryption=ice:aes-256-cts
-source none8 swap defaults fileencryption=ice:aes-256-heh
-source none9 swap defaults fileencryption=ice:adiantum
-source none10 swap defaults fileencryption=aes-256-xts:aes-256-cts:v1
-source none11 swap defaults fileencryption=aes-256-xts:aes-256-cts:v2
-source none12 swap defaults fileencryption=aes-256-xts:aes-256-cts:v2:
-source none13 swap defaults fileencryption=aes-256-xts:aes-256-cts:blah
-source none14 swap defaults fileencryption=aes-256-xts:aes-256-cts:vblah
+source none0 swap defaults fileencryption=aes-256-xts:aes-256-cts:v1
)fs";
ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
Fstab fstab;
EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
- ASSERT_EQ(15U, fstab.size());
+ ASSERT_EQ(1U, fstab.size());
FstabEntry::FsMgrFlags flags = {};
flags.file_encryption = true;
@@ -719,107 +700,7 @@
auto entry = fstab.begin();
EXPECT_EQ("none0", entry->mount_point);
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none1", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none2", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none3", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("adiantum", entry->file_contents_mode);
- EXPECT_EQ("adiantum", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none4", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("adiantum", entry->file_contents_mode);
- EXPECT_EQ("aes-256-heh", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none5", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none6", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none7", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none8", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("aes-256-heh", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none9", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("ice", entry->file_contents_mode);
- EXPECT_EQ("adiantum", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none10", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(1, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none11", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(2, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none12", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("", entry->file_contents_mode);
- EXPECT_EQ("", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none13", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
-
- entry++;
- EXPECT_EQ("none14", entry->mount_point);
- EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
- EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
- EXPECT_EQ("aes-256-cts", entry->file_names_mode);
- EXPECT_EQ(0, entry->file_policy_version);
+ EXPECT_EQ("aes-256-xts:aes-256-cts:v1", entry->encryption_options);
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MaxCompStreams) {
@@ -1081,3 +962,10 @@
EXPECT_EQ("none5", entry->mount_point);
EXPECT_EQ("/dev/path2", entry->zram_backing_dev_path);
}
+
+TEST(fs_mgr, DefaultFstabContainsUserdata) {
+ Fstab fstab;
+ ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
+ ASSERT_NE(nullptr, GetEntryForMountPoint(&fstab, "/data"))
+ << "Default fstab doesn't contain /data entry";
+}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 4f89bfb..e04f70f 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -17,6 +17,10 @@
shared_libs: [
"libutils",
"libbase",
+
+ // Need latest HealthInfo definition from headers of this shared
+ // library. Clients don't need to link to this.
+ "android.hardware.health@2.1",
],
header_libs: ["libhealthd_headers"],
export_header_lib_headers: ["libhealthd_headers"],
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 57ed362..9e168e9 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -29,10 +29,12 @@
#include <algorithm>
#include <memory>
+#include <optional>
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <android/hardware/health/2.1/types.h>
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
#include <cutils/properties.h>
@@ -47,97 +49,93 @@
#define MILLION 1.0e6
#define DEFAULT_VBUS_VOLTAGE 5000000
+using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
+using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
+using android::hardware::health::V1_0::BatteryHealth;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V2_1::BatteryCapacityLevel;
+
namespace android {
-struct sysfsStringEnumMap {
+template <typename T>
+struct SysfsStringEnumMap {
const char* s;
- int val;
+ T val;
};
-static int mapSysfsString(const char* str,
- struct sysfsStringEnumMap map[]) {
+template <typename T>
+static std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {
for (int i = 0; map[i].s; i++)
if (!strcmp(str, map[i].s))
return map[i].val;
- return -1;
-}
-
-static void initBatteryProperties(BatteryProperties* props) {
- props->chargerAcOnline = false;
- props->chargerUsbOnline = false;
- props->chargerWirelessOnline = false;
- props->maxChargingCurrent = 0;
- props->maxChargingVoltage = 0;
- props->batteryStatus = BATTERY_STATUS_UNKNOWN;
- props->batteryHealth = BATTERY_HEALTH_UNKNOWN;
- props->batteryPresent = false;
- props->batteryLevel = 0;
- props->batteryVoltage = 0;
- props->batteryTemperature = 0;
- props->batteryCurrent = 0;
- props->batteryCycleCount = 0;
- props->batteryFullCharge = 0;
- props->batteryChargeCounter = 0;
- props->batteryTechnology.clear();
+ return std::nullopt;
}
BatteryMonitor::BatteryMonitor()
: mHealthdConfig(nullptr),
mBatteryDevicePresent(false),
mBatteryFixedCapacity(0),
- mBatteryFixedTemperature(0) {
- initBatteryProperties(&props);
+ mBatteryFixedTemperature(0),
+ mHealthInfo(std::make_unique<HealthInfo_2_1>()) {}
+
+BatteryMonitor::~BatteryMonitor() {}
+
+const HealthInfo_1_0& BatteryMonitor::getHealthInfo_1_0() const {
+ return getHealthInfo_2_0().legacy;
}
-struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {
- return batteryMonitor->props;
+const HealthInfo_2_0& BatteryMonitor::getHealthInfo_2_0() const {
+ return getHealthInfo_2_1().legacy;
}
-int BatteryMonitor::getBatteryStatus(const char* status) {
- int ret;
- struct sysfsStringEnumMap batteryStatusMap[] = {
- { "Unknown", BATTERY_STATUS_UNKNOWN },
- { "Charging", BATTERY_STATUS_CHARGING },
- { "Discharging", BATTERY_STATUS_DISCHARGING },
- { "Not charging", BATTERY_STATUS_NOT_CHARGING },
- { "Full", BATTERY_STATUS_FULL },
- { NULL, 0 },
+const HealthInfo_2_1& BatteryMonitor::getHealthInfo_2_1() const {
+ return *mHealthInfo;
+}
+
+BatteryStatus getBatteryStatus(const char* status) {
+ static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {
+ {"Unknown", BatteryStatus::UNKNOWN},
+ {"Charging", BatteryStatus::CHARGING},
+ {"Discharging", BatteryStatus::DISCHARGING},
+ {"Not charging", BatteryStatus::NOT_CHARGING},
+ {"Full", BatteryStatus::FULL},
+ {NULL, BatteryStatus::UNKNOWN},
};
- ret = mapSysfsString(status, batteryStatusMap);
- if (ret < 0) {
+ auto ret = mapSysfsString(status, batteryStatusMap);
+ if (!ret) {
KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
- ret = BATTERY_STATUS_UNKNOWN;
+ *ret = BatteryStatus::UNKNOWN;
}
- return ret;
+ return *ret;
}
-int BatteryMonitor::getBatteryHealth(const char* status) {
- int ret;
- struct sysfsStringEnumMap batteryHealthMap[] = {
- { "Unknown", BATTERY_HEALTH_UNKNOWN },
- { "Good", BATTERY_HEALTH_GOOD },
- { "Overheat", BATTERY_HEALTH_OVERHEAT },
- { "Dead", BATTERY_HEALTH_DEAD },
- { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
- { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
- { "Cold", BATTERY_HEALTH_COLD },
- // battery health values from JEITA spec
- { "Warm", BATTERY_HEALTH_GOOD },
- { "Cool", BATTERY_HEALTH_GOOD },
- { "Hot", BATTERY_HEALTH_OVERHEAT },
- { NULL, 0 },
+BatteryHealth getBatteryHealth(const char* status) {
+ static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
+ {"Unknown", BatteryHealth::UNKNOWN},
+ {"Good", BatteryHealth::GOOD},
+ {"Overheat", BatteryHealth::OVERHEAT},
+ {"Dead", BatteryHealth::DEAD},
+ {"Over voltage", BatteryHealth::OVER_VOLTAGE},
+ {"Unspecified failure", BatteryHealth::UNSPECIFIED_FAILURE},
+ {"Cold", BatteryHealth::COLD},
+ // battery health values from JEITA spec
+ {"Warm", BatteryHealth::GOOD},
+ {"Cool", BatteryHealth::GOOD},
+ {"Hot", BatteryHealth::OVERHEAT},
+ {NULL, BatteryHealth::UNKNOWN},
};
- ret = mapSysfsString(status, batteryHealthMap);
- if (ret < 0) {
+ auto ret = mapSysfsString(status, batteryHealthMap);
+ if (!ret) {
KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
- ret = BATTERY_HEALTH_UNKNOWN;
+ *ret = BatteryHealth::UNKNOWN;
}
- return ret;
+ return *ret;
}
int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
@@ -148,35 +146,34 @@
}
BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
- std::string buf;
- int ret;
- struct sysfsStringEnumMap supplyTypeMap[] = {
- { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
- { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
- { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB", ANDROID_POWER_SUPPLY_TYPE_USB },
- { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
- { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
- { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
- { NULL, 0 },
+ static SysfsStringEnumMap<int> supplyTypeMap[] = {
+ {"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
+ {"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY},
+ {"UPS", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"Mains", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB", ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+ {NULL, 0},
};
+ std::string buf;
if (readFromFile(path, &buf) <= 0)
return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
- ret = mapSysfsString(buf.c_str(), supplyTypeMap);
+ auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
if (ret < 0) {
KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
- ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
}
- return static_cast<BatteryMonitor::PowerSupplyType>(ret);
+ return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
}
bool BatteryMonitor::getBooleanField(const String8& path) {
@@ -201,7 +198,9 @@
}
void BatteryMonitor::updateValues(void) {
- initBatteryProperties(&props);
+ *mHealthInfo = HealthInfo_2_1{};
+
+ HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
if (!mHealthdConfig->batteryPresentPath.isEmpty())
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -225,6 +224,15 @@
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
+ if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+ mHealthInfo->legacy.batteryCurrentAverage =
+ getIntField(mHealthdConfig->batteryCurrentAvgPath);
+
+ // TODO(b/142260281): Retrieve these values correctly.
+ mHealthInfo->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
+ mHealthInfo->batteryChargeTimeToFullNowSeconds = 0;
+ mHealthInfo->batteryFullCapacityUah = props.batteryFullCharge;
+
props.batteryTemperature = mBatteryFixedTemperature ?
mBatteryFixedTemperature :
getIntField(mHealthdConfig->batteryTemperaturePath);
@@ -292,6 +300,7 @@
void BatteryMonitor::logValues(void) {
char dmesgline[256];
size_t len;
+ const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
if (props.batteryPresent) {
snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
@@ -325,18 +334,19 @@
}
bool BatteryMonitor::isChargerOnline() {
+ const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
return props.chargerAcOnline | props.chargerUsbOnline |
props.chargerWirelessOnline;
}
int BatteryMonitor::getChargeStatus() {
- int result = BATTERY_STATUS_UNKNOWN;
+ BatteryStatus result = BatteryStatus::UNKNOWN;
if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
std::string buf;
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
result = getBatteryStatus(buf.c_str());
}
- return result;
+ return static_cast<int>(result);
}
status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
@@ -409,6 +419,7 @@
void BatteryMonitor::dumpState(int fd) {
int v;
char vs[128];
+ const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
props.chargerAcOnline, props.chargerUsbOnline,
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 0fd3824..d41a374 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -17,6 +17,8 @@
#ifndef HEALTHD_BATTERYMONITOR_H
#define HEALTHD_BATTERYMONITOR_H
+#include <memory>
+
#include <batteryservice/BatteryService.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -24,6 +26,19 @@
#include <healthd/healthd.h>
namespace android {
+namespace hardware {
+namespace health {
+namespace V1_0 {
+struct HealthInfo;
+} // namespace V1_0
+namespace V2_0 {
+struct HealthInfo;
+} // namespace V2_0
+namespace V2_1 {
+struct HealthInfo;
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
class BatteryMonitor {
public:
@@ -37,11 +52,15 @@
};
BatteryMonitor();
+ ~BatteryMonitor();
void init(struct healthd_config *hc);
int getChargeStatus();
status_t getProperty(int id, struct BatteryProperty *val);
void dumpState(int fd);
- friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
+
+ const android::hardware::health::V1_0::HealthInfo& getHealthInfo_1_0() const;
+ const android::hardware::health::V2_0::HealthInfo& getHealthInfo_2_0() const;
+ const android::hardware::health::V2_1::HealthInfo& getHealthInfo_2_1() const;
void updateValues(void);
void logValues(void);
@@ -53,10 +72,8 @@
bool mBatteryDevicePresent;
int mBatteryFixedCapacity;
int mBatteryFixedTemperature;
- struct BatteryProperties props;
+ std::unique_ptr<android::hardware::health::V2_1::HealthInfo> mHealthInfo;
- int getBatteryStatus(const char* status);
- int getBatteryHealth(const char* status);
int readFromFile(const String8& path, std::string* buf);
PowerSupplyType readPowerSupplyType(const String8& path);
bool getBooleanField(const String8& path);
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 2251479..667bddc 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -175,7 +175,7 @@
}
cc_binary {
- name: "unzip",
+ name: "ziptool",
defaults: ["libziparchive_flags"],
srcs: ["unzip.cpp"],
shared_libs: [
@@ -183,6 +183,12 @@
"libziparchive",
],
recovery_available: true,
+ host_supported: true,
+ target: {
+ android: {
+ symlinks: ["unzip", "zipinfo"],
+ },
+ },
}
cc_fuzz {
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 391cff9..047af90 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -40,8 +40,8 @@
* Represents information about a zip entry in a zip file.
*/
struct ZipEntry {
- // Compression method: One of kCompressStored or
- // kCompressDeflated.
+ // Compression method. One of kCompressStored or kCompressDeflated.
+ // See also `gpbf` for deflate subtypes.
uint16_t method;
// Modification time. The zipfile format specifies
@@ -55,7 +55,7 @@
struct tm GetModificationTime() const;
// Suggested Unix mode for this entry, from the zip archive if created on
- // Unix, or a default otherwise.
+ // Unix, or a default otherwise. See also `external_file_attributes`.
mode_t unix_mode;
// 1 if this entry contains a data descriptor segment, 0
@@ -79,6 +79,18 @@
// The offset to the start of data for this ZipEntry.
off64_t offset;
+
+ // The version of zip and the host file system this came from (for zipinfo).
+ uint16_t version_made_by;
+
+ // The raw attributes, whose interpretation depends on the host
+ // file system in `version_made_by` (for zipinfo). See also `unix_mode`.
+ uint32_t external_file_attributes;
+
+ // Specifics about the deflation (for zipinfo).
+ uint16_t gpbf;
+ // Whether this entry is believed to be text or binary (for zipinfo).
+ bool is_text;
};
struct ZipArchive;
@@ -125,6 +137,19 @@
*/
void CloseArchive(ZipArchiveHandle archive);
+/** See GetArchiveInfo(). */
+struct ZipArchiveInfo {
+ /** The size in bytes of the archive itself. Used by zipinfo. */
+ off64_t archive_size;
+ /** The number of entries in the archive. */
+ size_t entry_count;
+};
+
+/**
+ * Returns information about the given archive.
+ */
+ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive);
+
/*
* Find an entry in the Zip archive, by name. |data| must be non-null.
*
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 426325e..6c2221e 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -34,18 +34,29 @@
#include <android-base/strings.h>
#include <ziparchive/zip_archive.h>
+using android::base::EndsWith;
+using android::base::StartsWith;
+
enum OverwriteMode {
kAlways,
kNever,
kPrompt,
};
+enum Role {
+ kUnzip,
+ kZipinfo,
+};
+
+static Role role;
static OverwriteMode overwrite_mode = kPrompt;
+static bool flag_1 = false;
static const char* flag_d = nullptr;
static bool flag_l = false;
static bool flag_p = false;
static bool flag_q = false;
static bool flag_v = false;
+static bool flag_x = false;
static const char* archive_name = nullptr;
static std::set<std::string> includes;
static std::set<std::string> excludes;
@@ -88,32 +99,51 @@
return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed);
}
-static void MaybeShowHeader() {
- if (!flag_q) printf("Archive: %s\n", archive_name);
- if (flag_v) {
- printf(
- " Length Method Size Cmpr Date Time CRC-32 Name\n"
- "-------- ------ ------- ---- ---------- ----- -------- ----\n");
- } else if (flag_l) {
- printf(
- " Length Date Time Name\n"
- "--------- ---------- ----- ----\n");
+static void MaybeShowHeader(ZipArchiveHandle zah) {
+ if (role == kUnzip) {
+ // unzip has three formats.
+ if (!flag_q) printf("Archive: %s\n", archive_name);
+ if (flag_v) {
+ printf(
+ " Length Method Size Cmpr Date Time CRC-32 Name\n"
+ "-------- ------ ------- ---- ---------- ----- -------- ----\n");
+ } else if (flag_l) {
+ printf(
+ " Length Date Time Name\n"
+ "--------- ---------- ----- ----\n");
+ }
+ } else {
+ // zipinfo.
+ if (!flag_1 && includes.empty() && excludes.empty()) {
+ ZipArchiveInfo info{GetArchiveInfo(zah)};
+ printf("Archive: %s\n", archive_name);
+ printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size,
+ info.entry_count);
+ }
}
}
static void MaybeShowFooter() {
- if (flag_v) {
- printf(
- "-------- ------- --- -------\n"
- "%8" PRId64 " %8" PRId64 " %3d%% %zu file%s\n",
- total_uncompressed_length, total_compressed_length,
- CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
- (file_count == 1) ? "" : "s");
- } else if (flag_l) {
- printf(
- "--------- -------\n"
- "%9" PRId64 " %zu file%s\n",
- total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+ if (role == kUnzip) {
+ if (flag_v) {
+ printf(
+ "-------- ------- --- -------\n"
+ "%8" PRId64 " %8" PRId64 " %3d%% %zu file%s\n",
+ total_uncompressed_length, total_compressed_length,
+ CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+ (file_count == 1) ? "" : "s");
+ } else if (flag_l) {
+ printf(
+ "--------- -------\n"
+ "%9" PRId64 " %zu file%s\n",
+ total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
+ }
+ } else {
+ if (!flag_1 && includes.empty() && excludes.empty()) {
+ printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed: %3d%%\n",
+ file_count, total_uncompressed_length, total_compressed_length,
+ CompressionRatio(total_uncompressed_length, total_compressed_length));
+ }
}
}
@@ -163,8 +193,7 @@
static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
// Bad filename?
- if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") ||
- name.find("/../") != std::string::npos) {
+ if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
error(1, 0, "bad filename %s", name.c_str());
}
@@ -172,7 +201,7 @@
std::string dst;
if (flag_d) {
dst = flag_d;
- if (!android::base::EndsWith(dst, "/")) dst += '/';
+ if (!EndsWith(dst, "/")) dst += '/';
}
dst += name;
@@ -182,7 +211,7 @@
}
// An entry in a zip file can just be a directory itself.
- if (android::base::EndsWith(name, "/")) {
+ if (EndsWith(name, "/")) {
if (mkdir(name.c_str(), entry.unix_mode) == -1) {
// If the directory already exists, that's fine.
if (errno == EEXIST) {
@@ -226,17 +255,82 @@
}
}
+static void InfoOne(const ZipEntry& entry, const std::string& name) {
+ if (flag_1) {
+ // "android-ndk-r19b/sources/android/NOTICE"
+ printf("%s\n", name.c_str());
+ return;
+ }
+
+ int version = entry.version_made_by & 0xff;
+ int os = (entry.version_made_by >> 8) & 0xff;
+
+ // TODO: Support suid/sgid? Non-Unix/non-FAT host file system attributes?
+ const char* src_fs = "???";
+ char mode[] = "??? ";
+ if (os == 0) {
+ src_fs = "fat";
+ // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+ int attrs = entry.external_file_attributes & 0xff;
+ mode[0] = (attrs & 0x10) ? 'd' : '-';
+ mode[1] = 'r';
+ mode[2] = (attrs & 0x01) ? '-' : 'w';
+ // The man page also mentions ".btm", but that seems to be obsolete?
+ mode[3] = EndsWith(name, ".exe") || EndsWith(name, ".com") || EndsWith(name, ".bat") ||
+ EndsWith(name, ".cmd")
+ ? 'x'
+ : '-';
+ mode[4] = (attrs & 0x20) ? 'a' : '-';
+ mode[5] = (attrs & 0x02) ? 'h' : '-';
+ mode[6] = (attrs & 0x04) ? 's' : '-';
+ } else if (os == 3) {
+ src_fs = "unx";
+ mode[0] = S_ISDIR(entry.unix_mode) ? 'd' : (S_ISREG(entry.unix_mode) ? '-' : '?');
+ mode[1] = entry.unix_mode & S_IRUSR ? 'r' : '-';
+ mode[2] = entry.unix_mode & S_IWUSR ? 'w' : '-';
+ mode[3] = entry.unix_mode & S_IXUSR ? 'x' : '-';
+ mode[4] = entry.unix_mode & S_IRGRP ? 'r' : '-';
+ mode[5] = entry.unix_mode & S_IWGRP ? 'w' : '-';
+ mode[6] = entry.unix_mode & S_IXGRP ? 'x' : '-';
+ mode[7] = entry.unix_mode & S_IROTH ? 'r' : '-';
+ mode[8] = entry.unix_mode & S_IWOTH ? 'w' : '-';
+ mode[9] = entry.unix_mode & S_IXOTH ? 'x' : '-';
+ }
+
+ char method[5] = "stor";
+ if (entry.method == kCompressDeflated) {
+ snprintf(method, sizeof(method), "def%c", "NXFS"[(entry.gpbf >> 1) & 0x3]);
+ }
+
+ // TODO: zipinfo (unlike unzip) sometimes uses time zone?
+ // TODO: this uses 4-digit years because we're not barbarians unless interoperability forces it.
+ tm t = entry.GetModificationTime();
+ char time[32];
+ snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
+ t.tm_mday, t.tm_hour, t.tm_min);
+
+ // "-rw-r--r-- 3.0 unx 577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
+ printf("%s %2d.%d %s %8d %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs,
+ entry.uncompressed_length, entry.is_text ? 't' : 'b',
+ entry.has_data_descriptor ? 'X' : 'x', method, time, name.c_str());
+}
+
static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
- if (flag_l || flag_v) {
- // -l or -lv or -lq or -v.
- ListOne(entry, name);
- } else {
- // Actually extract.
- if (flag_p) {
- ExtractToPipe(zah, entry, name);
+ if (role == kUnzip) {
+ if (flag_l || flag_v) {
+ // -l or -lv or -lq or -v.
+ ListOne(entry, name);
} else {
- ExtractOne(zah, entry, name);
+ // Actually extract.
+ if (flag_p) {
+ ExtractToPipe(zah, entry, name);
+ } else {
+ ExtractOne(zah, entry, name);
+ }
}
+ } else {
+ // zipinfo or zipinfo -1.
+ InfoOne(entry, name);
}
total_uncompressed_length += entry.uncompressed_length;
total_compressed_length += entry.compressed_length;
@@ -244,7 +338,7 @@
}
static void ProcessAll(ZipArchiveHandle zah) {
- MaybeShowHeader();
+ MaybeShowHeader(zah);
// libziparchive iteration order doesn't match the central directory.
// We could sort, but that would cost extra and wouldn't match either.
@@ -267,73 +361,120 @@
}
static void ShowHelp(bool full) {
- fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
- if (!full) exit(EXIT_FAILURE);
+ if (role == kUnzip) {
+ fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+ if (!full) exit(EXIT_FAILURE);
- printf(
- "\n"
- "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
- "exclude (-x) lists use shell glob patterns.\n"
- "\n"
- "-d DIR Extract into DIR\n"
- "-l List contents (-lq excludes archive name, -lv is verbose)\n"
- "-n Never overwrite files (default: prompt)\n"
- "-o Always overwrite files\n"
- "-p Pipe to stdout\n"
- "-q Quiet\n"
- "-v List contents verbosely\n"
- "-x FILE Exclude files\n");
+ printf(
+ "\n"
+ "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
+ "exclude (-x) lists use shell glob patterns.\n"
+ "\n"
+ "-d DIR Extract into DIR\n"
+ "-l List contents (-lq excludes archive name, -lv is verbose)\n"
+ "-n Never overwrite files (default: prompt)\n"
+ "-o Always overwrite files\n"
+ "-p Pipe to stdout\n"
+ "-q Quiet\n"
+ "-v List contents verbosely\n"
+ "-x FILE Exclude files\n");
+ } else {
+ fprintf(full ? stdout : stderr, "usage: zipinfo [-1] ZIP [FILE...] [-x FILE...]\n");
+ if (!full) exit(EXIT_FAILURE);
+
+ printf(
+ "\n"
+ "Show information about FILEs from ZIP archive. Default is all files.\n"
+ "Both the include and exclude (-x) lists use shell glob patterns.\n"
+ "\n"
+ "-1 Show filenames only, one per line\n"
+ "-x FILE Exclude files\n");
+ }
exit(EXIT_SUCCESS);
}
+static void HandleCommonOption(int opt) {
+ switch (opt) {
+ case 'h':
+ ShowHelp(true);
+ break;
+ case 'x':
+ flag_x = true;
+ break;
+ case 1:
+ // -x swallows all following arguments, so we use '-' in the getopt
+ // string and collect files here.
+ if (!archive_name) {
+ archive_name = optarg;
+ } else if (flag_x) {
+ excludes.insert(optarg);
+ } else {
+ includes.insert(optarg);
+ }
+ break;
+ default:
+ ShowHelp(false);
+ break;
+ }
+}
+
int main(int argc, char* argv[]) {
- static struct option opts[] = {
+ // Who am I, and what am I doing?
+ const char* base = basename(argv[0]);
+ if (!strcmp(base, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
+ if (!strcmp(base, "unzip")) {
+ role = kUnzip;
+ } else if (!strcmp(base, "zipinfo")) {
+ role = kZipinfo;
+ } else {
+ error(1, 0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
+ }
+
+ static const struct option opts[] = {
{"help", no_argument, 0, 'h'},
};
- bool saw_x = false;
- int opt;
- while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
- switch (opt) {
- case 'd':
- flag_d = optarg;
- break;
- case 'h':
- ShowHelp(true);
- break;
- case 'l':
- flag_l = true;
- break;
- case 'n':
- overwrite_mode = kNever;
- break;
- case 'o':
- overwrite_mode = kAlways;
- break;
- case 'p':
- flag_p = flag_q = true;
- break;
- case 'q':
- flag_q = true;
- break;
- case 'v':
- flag_v = true;
- break;
- case 'x':
- saw_x = true;
- break;
- case 1:
- // -x swallows all following arguments, so we use '-' in the getopt
- // string and collect files here.
- if (!archive_name) {
- archive_name = optarg;
- } else if (saw_x) {
- excludes.insert(optarg);
- } else {
- includes.insert(optarg);
- }
- break;
- default:
- ShowHelp(false);
+
+ if (role == kUnzip) {
+ int opt;
+ while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+ switch (opt) {
+ case 'd':
+ flag_d = optarg;
+ break;
+ case 'l':
+ flag_l = true;
+ break;
+ case 'n':
+ overwrite_mode = kNever;
+ break;
+ case 'o':
+ overwrite_mode = kAlways;
+ break;
+ case 'p':
+ flag_p = flag_q = true;
+ break;
+ case 'q':
+ flag_q = true;
+ break;
+ case 'v':
+ flag_v = true;
+ break;
+ default:
+ HandleCommonOption(opt);
+ break;
+ }
+ }
+ } else {
+ int opt;
+ while ((opt = getopt_long(argc, argv, "-1hx", opts, nullptr)) != -1) {
+ switch (opt) {
+ case '1':
+ flag_1 = true;
+ break;
+ default:
+ HandleCommonOption(opt);
+ break;
+ }
}
}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 3a552d8..ef29188 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -478,6 +478,13 @@
return OpenArchiveInternal(archive, debug_file_name);
}
+ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
+ ZipArchiveInfo result;
+ result.archive_size = archive->mapped_zip.GetFileLength();
+ result.entry_count = archive->num_entries;
+ return result;
+}
+
/*
* Close a ZipArchive, closing the file and freeing the contents.
*/
@@ -614,12 +621,21 @@
}
// 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
- if ((cdr->version_made_by >> 8) == 3) {
+ data->version_made_by = cdr->version_made_by;
+ data->external_file_attributes = cdr->external_file_attributes;
+ if ((data->version_made_by >> 8) == 3) {
data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
} else {
data->unix_mode = 0777;
}
+ // 4.4.4: general purpose bit flags.
+ data->gpbf = lfh->gpb_flags;
+
+ // 4.4.14: the lowest bit of the internal file attributes field indicates text.
+ // Currently only needed to implement zipinfo.
+ data->is_text = (cdr->internal_file_attributes & 1);
+
// Check that the local file header name matches the declared
// name in the central directory.
if (lfh->file_name_length != nameLen) {
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index e1bb02f..5241730 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -326,7 +326,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_SAMEPROCESS_LIBRARIES),.vendor)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_SAMEPROCESS_LIBRARIES),.com.android.vndk.current)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
@@ -342,7 +342,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_CORE_LIBRARIES),.vendor)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_CORE_LIBRARIES),.com.android.vndk.current)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
@@ -358,7 +358,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_PRIVATE_LIBRARIES),.vendor)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_PRIVATE_LIBRARIES),.com.android.vndk.current)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 694b50e..ec4f6ab 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -25,7 +25,7 @@
"tcpdump",
"toolbox",
"toybox",
- "unzip",
+ "ziptool",
],
}