Merge "Avoid odd behavior when clearing nonexistent SID"
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 4eb7382..4043a6e 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -159,7 +159,8 @@
}
noinline void fprintf_null() {
- fprintf(nullptr, "oops");
+ FILE* sneaky_null = nullptr;
+ fprintf(sneaky_null, "oops");
}
noinline void readdir_null() {
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 5dac3f5..430ff14 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -128,25 +128,6 @@
int64_t image_size;
};
-enum class ImageType {
- // Must be flashed for device to boot into the kernel.
- BootCritical,
- // Normal partition to be flashed during "flashall".
- Normal,
- // Partition that is never flashed during "flashall".
- Extra
-};
-
-struct Image {
- std::string nickname;
- std::string img_name;
- std::string sig_name;
- std::string part_name;
- bool optional_if_no_image;
- ImageType type;
- bool IsSecondary() const { return nickname.empty(); }
-};
-
static std::vector<Image> images = {
// clang-format off
{ "boot", "boot.img", "boot.sig", "boot", false, ImageType::BootCritical },
@@ -336,28 +317,7 @@
return -1;
}
-struct NetworkSerial {
- Socket::Protocol protocol;
- std::string address;
- int port;
-};
-
-class ParseNetworkAddressError {
- public:
- enum Type { WRONG_PREFIX = 1, WRONG_ADDRESS = 2 };
-
- ParseNetworkAddressError(Type&& type) : type_(std::forward<Type>(type)) {}
-
- Type value() const { return type_; }
- operator Type() const { return value(); }
- std::string print() const { return ""; }
-
- private:
- Type type_;
-};
-
-static Result<NetworkSerial, ParseNetworkAddressError> ParseNetworkSerial(
- const std::string& serial) {
+Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial) {
Socket::Protocol protocol;
const char* net_address = nullptr;
int port = 0;
@@ -371,7 +331,7 @@
net_address = serial.c_str() + strlen("udp:");
port = udp::kDefaultPort;
} else {
- return Error<ParseNetworkAddressError>(ParseNetworkAddressError::Type::WRONG_PREFIX)
+ return Error<FastbootError>(FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX)
<< "protocol prefix ('tcp:' or 'udp:') is missed: " << serial << ". "
<< "Expected address format:\n"
<< "<protocol>:<address>:<port> (tcp:localhost:5554)";
@@ -380,7 +340,7 @@
std::string error;
std::string host;
if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
- return Error<ParseNetworkAddressError>(ParseNetworkAddressError::Type::WRONG_ADDRESS)
+ return Error<FastbootError>(FastbootError::Type::NETWORK_SERIAL_WRONG_ADDRESS)
<< "invalid network address '" << net_address << "': " << error;
}
@@ -399,8 +359,7 @@
// object, and the caller should not attempt to delete the returned Transport.
static Transport* open_device(const char* local_serial, bool wait_for_device = true,
bool announce = true) {
- const Result<NetworkSerial, ParseNetworkAddressError> network_serial =
- ParseNetworkSerial(local_serial);
+ const Result<NetworkSerial, FastbootError> network_serial = ParseNetworkSerial(local_serial);
Transport* transport = nullptr;
while (true) {
@@ -417,7 +376,8 @@
if (transport == nullptr && announce) {
LOG(ERROR) << "error: " << error;
}
- } else if (network_serial.error().code() == ParseNetworkAddressError::Type::WRONG_PREFIX) {
+ } else if (network_serial.error().code() ==
+ FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX) {
// WRONG_PREFIX is special because it happens when user wants to communicate with USB
// device
transport = usb_open(match_fastboot(local_serial));
@@ -1604,8 +1564,7 @@
class FlashAllTool {
public:
- FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary,
- bool wipe, bool force_flash);
+ FlashAllTool(FlashingPlan* fp);
void Flash();
@@ -1625,25 +1584,12 @@
std::string GetPartitionName(const ImageEntry& entry);
- const ImageSource& source_;
- std::string slot_override_;
- bool skip_secondary_;
- bool wipe_;
- bool force_flash_;
- std::string current_slot_;
- std::string secondary_slot_;
-
std::vector<ImageEntry> boot_images_;
std::vector<ImageEntry> os_images_;
+ FlashingPlan* fp_;
};
-FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override,
- bool skip_secondary, bool wipe, bool force_flash)
- : source_(source),
- slot_override_(slot_override),
- skip_secondary_(skip_secondary),
- wipe_(wipe),
- force_flash_(force_flash) {}
+FlashAllTool::FlashAllTool(FlashingPlan* fp) : fp_(fp) {}
void FlashAllTool::Flash() {
DumpInfo();
@@ -1651,10 +1597,10 @@
// Change the slot first, so we boot into the correct recovery image when
// using fastbootd.
- if (slot_override_ == "all") {
+ if (fp_->slot == "all") {
set_active("a");
} else {
- set_active(slot_override_);
+ set_active(fp_->slot);
}
DetermineSlot();
@@ -1691,13 +1637,13 @@
LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
return false;
}
- if (slot_override_ == "all") {
+ if (fp_->slot == "all") {
LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
return false;
}
// Does this device use dynamic partitions at all?
- unique_fd fd = source_.OpenFile("super_empty.img");
+ unique_fd fd = fp_->source->OpenFile("super_empty.img");
if (fd < 0) {
LOG(VERBOSE) << "could not open super_empty.img";
return false;
@@ -1714,7 +1660,7 @@
return false;
}
- SuperFlashHelper helper(source_);
+ SuperFlashHelper helper(*fp_->source);
if (!helper.Open(fd)) {
return false;
}
@@ -1754,43 +1700,43 @@
void FlashAllTool::CheckRequirements() {
std::vector<char> contents;
- if (!source_.ReadFile("android-info.txt", &contents)) {
+ if (!fp_->source->ReadFile("android-info.txt", &contents)) {
die("could not read android-info.txt");
}
- ::CheckRequirements({contents.data(), contents.size()}, force_flash_);
+ ::CheckRequirements({contents.data(), contents.size()}, fp_->force_flash);
}
void FlashAllTool::DetermineSlot() {
- if (slot_override_.empty()) {
- current_slot_ = get_current_slot();
+ if (fp_->slot.empty()) {
+ fp_->current_slot = get_current_slot();
} else {
- current_slot_ = slot_override_;
+ fp_->current_slot = fp_->slot;
}
- if (skip_secondary_) {
+ if (fp_->skip_secondary) {
return;
}
- if (slot_override_ != "" && slot_override_ != "all") {
- secondary_slot_ = get_other_slot(slot_override_);
+ if (fp_->slot != "" && fp_->slot != "all") {
+ fp_->secondary_slot = get_other_slot(fp_->slot);
} else {
- secondary_slot_ = get_other_slot();
+ fp_->secondary_slot = get_other_slot();
}
- if (secondary_slot_ == "") {
+ if (fp_->secondary_slot == "") {
if (supports_AB()) {
fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
}
- skip_secondary_ = true;
+ fp_->skip_secondary = true;
}
}
void FlashAllTool::CollectImages() {
for (size_t i = 0; i < images.size(); ++i) {
- std::string slot = slot_override_;
+ std::string slot = fp_->slot;
if (images[i].IsSecondary()) {
- if (skip_secondary_) {
+ if (fp_->skip_secondary) {
continue;
}
- slot = secondary_slot_;
+ slot = fp_->secondary_slot;
}
if (images[i].type == ImageType::BootCritical) {
boot_images_.emplace_back(&images[i], slot);
@@ -1803,7 +1749,7 @@
void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
for (const auto& [image, slot] : images) {
fastboot_buffer buf;
- unique_fd fd = source_.OpenFile(image->img_name);
+ unique_fd fd = fp_->source->OpenFile(image->img_name);
if (fd < 0 || !load_buf_fd(std::move(fd), &buf)) {
if (image->optional_if_no_image) {
continue;
@@ -1817,7 +1763,7 @@
void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
auto flash = [&, this](const std::string& partition_name) {
std::vector<char> signature_data;
- if (source_.ReadFile(image.sig_name, &signature_data)) {
+ if (fp_->source->ReadFile(image.sig_name, &signature_data)) {
fb->Download("signature", signature_data);
fb->RawCommand("signature", "installing signature");
}
@@ -1831,7 +1777,7 @@
}
void FlashAllTool::UpdateSuperPartition() {
- unique_fd fd = source_.OpenFile("super_empty.img");
+ unique_fd fd = fp_->source->OpenFile("super_empty.img");
if (fd < 0) {
return;
}
@@ -1846,7 +1792,7 @@
fb->Download(super_name, fd, get_file_size(fd));
std::string command = "update-super:" + super_name;
- if (wipe_) {
+ if (fp_->wants_wipe) {
command += ":wipe";
}
fb->RawCommand(command, "Updating super partition");
@@ -1868,7 +1814,7 @@
std::string FlashAllTool::GetPartitionName(const ImageEntry& entry) {
auto slot = entry.second;
if (slot.empty()) {
- slot = current_slot_;
+ slot = fp_->current_slot;
}
if (slot.empty()) {
return entry.first->part_name;
@@ -1897,15 +1843,16 @@
return unzip_to_file(zip_, name.c_str());
}
-static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary,
- bool force_flash) {
+static void do_update(const char* filename, FlashingPlan* fp) {
ZipArchiveHandle zip;
int error = OpenArchive(filename, &zip);
if (error != 0) {
die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
}
-
- FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false, force_flash);
+ ZipImageSource zp = ZipImageSource(zip);
+ fp->source = &zp;
+ fp->wants_wipe = false;
+ FlashAllTool tool(fp);
tool.Flash();
CloseArchive(zip);
@@ -1930,9 +1877,10 @@
return unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));
}
-static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe,
- bool force_flash) {
- FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe, force_flash);
+static void do_flashall(FlashingPlan* fp) {
+ LocalImageSource s = LocalImageSource();
+ fp->source = &s;
+ FlashAllTool tool(fp);
tool.Flash();
}
@@ -2167,12 +2115,8 @@
int FastBootTool::Main(int argc, char* argv[]) {
android::base::InitLogging(argv, FastbootLogger, FastbootAborter);
+ std::unique_ptr<FlashingPlan> fp = std::make_unique<FlashingPlan>();
- bool wants_wipe = false;
- bool skip_reboot = false;
- bool wants_set_active = false;
- bool skip_secondary = false;
- bool force_flash = false;
unsigned fs_options = 0;
int longindex;
std::string slot_override;
@@ -2225,7 +2169,7 @@
} else if (name == "disable-verity") {
g_disable_verity = true;
} else if (name == "force") {
- force_flash = true;
+ fp->force_flash = true;
} else if (name == "fs-options") {
fs_options = ParseFsOption(optarg);
} else if (name == "header-version") {
@@ -2244,9 +2188,9 @@
} else if (name == "ramdisk-offset") {
g_boot_img_hdr.ramdisk_addr = strtoul(optarg, 0, 16);
} else if (name == "skip-reboot") {
- skip_reboot = true;
+ fp->skip_reboot = true;
} else if (name == "skip-secondary") {
- skip_secondary = true;
+ fp->skip_secondary = true;
} else if (name == "slot") {
slot_override = optarg;
} else if (name == "dtb-offset") {
@@ -2267,7 +2211,7 @@
} else {
switch (c) {
case 'a':
- wants_set_active = true;
+ fp->wants_set_active = true;
if (optarg) next_active = optarg;
break;
case 'h':
@@ -2287,7 +2231,7 @@
set_verbose();
break;
case 'w':
- wants_wipe = true;
+ fp->wants_wipe = true;
break;
case '?':
return 1;
@@ -2300,7 +2244,7 @@
argc -= optind;
argv += optind;
- if (argc == 0 && !wants_wipe && !wants_set_active) syntax_error("no command");
+ if (argc == 0 && !fp->wants_wipe && !fp->wants_set_active) syntax_error("no command");
if (argc > 0 && !strcmp(*argv, "devices")) {
list_devices();
@@ -2336,13 +2280,14 @@
fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
fb = &fastboot_driver;
+ fp->fb = &fastboot_driver;
const double start = now();
if (slot_override != "") slot_override = verify_slot(slot_override);
if (next_active != "") next_active = verify_slot(next_active, false);
- if (wants_set_active) {
+ if (fp->wants_set_active) {
if (next_active == "") {
if (slot_override == "") {
std::string current_slot;
@@ -2350,7 +2295,7 @@
if (current_slot[0] == '_') current_slot.erase(0, 1);
next_active = verify_slot(current_slot, false);
} else {
- wants_set_active = false;
+ fp->wants_set_active = false;
}
} else {
next_active = verify_slot(slot_override, false);
@@ -2411,17 +2356,17 @@
} else if (command == FB_CMD_REBOOT) {
if (args.size() == 1) {
std::string reboot_target = next_arg(&args);
- reboot_task = std::make_unique<RebootTask>(fb, reboot_target);
+ reboot_task = std::make_unique<RebootTask>(fp.get(), reboot_target);
} else {
- reboot_task = std::make_unique<RebootTask>(fb);
+ reboot_task = std::make_unique<RebootTask>(fp.get());
}
if (!args.empty()) syntax_error("junk after reboot command");
} else if (command == FB_CMD_REBOOT_BOOTLOADER) {
- reboot_task = std::make_unique<RebootTask>(fb, "bootloader");
+ reboot_task = std::make_unique<RebootTask>(fp.get(), "bootloader");
} else if (command == FB_CMD_REBOOT_RECOVERY) {
- reboot_task = std::make_unique<RebootTask>(fb, "recovery");
+ reboot_task = std::make_unique<RebootTask>(fp.get(), "recovery");
} else if (command == FB_CMD_REBOOT_FASTBOOT) {
- reboot_task = std::make_unique<RebootTask>(fb, "fastboot");
+ reboot_task = std::make_unique<RebootTask>(fp.get(), "fastboot");
} else if (command == FB_CMD_CONTINUE) {
fb->Continue();
} else if (command == FB_CMD_BOOT) {
@@ -2442,7 +2387,7 @@
fname = find_item(pname);
}
if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
- FlashTask task(slot_override, force_flash, pname, fname);
+ FlashTask task(slot_override, pname, fname);
task.Run();
} else if (command == "flash:raw") {
std::string partition = next_arg(&args);
@@ -2461,11 +2406,12 @@
if (slot_override == "all") {
fprintf(stderr,
"Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
- do_flashall(slot_override, true, wants_wipe, force_flash);
+ fp->skip_secondary = true;
+ do_flashall(fp.get());
} else {
- do_flashall(slot_override, skip_secondary, wants_wipe, force_flash);
+ do_flashall(fp.get());
}
- reboot_task = std::make_unique<RebootTask>(fb);
+ reboot_task = std::make_unique<RebootTask>(fp.get());
} else if (command == "update") {
bool slot_all = (slot_override == "all");
if (slot_all) {
@@ -2476,8 +2422,8 @@
if (!args.empty()) {
filename = next_arg(&args);
}
- do_update(filename.c_str(), slot_override, skip_secondary || slot_all, force_flash);
- reboot_task = std::make_unique<RebootTask>(fb);
+ do_update(filename.c_str(), fp.get());
+ reboot_task = std::make_unique<RebootTask>(fp.get());
} else if (command == FB_CMD_SET_ACTIVE) {
std::string slot = verify_slot(next_arg(&args), false);
fb->SetActive(slot);
@@ -2549,8 +2495,8 @@
syntax_error("unknown command %s", command.c_str());
}
}
- if (wants_wipe) {
- if (force_flash) {
+ if (fp->wants_wipe) {
+ if (fp->force_flash) {
CancelSnapshotIfNeeded();
}
std::vector<std::string> partitions = {"userdata", "cache", "metadata"};
@@ -2564,10 +2510,10 @@
fb_perform_format(partition, 1, partition_type, "", fs_options);
}
}
- if (wants_set_active) {
+ if (fp->wants_set_active) {
fb->SetActive(next_active);
}
- if (reboot_task && !skip_reboot) {
+ if (reboot_task && !fp->skip_reboot) {
reboot_task->Run();
}
fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index b5fb8c0..09666aa 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -28,9 +28,15 @@
#pragma once
#include <string>
+#include "fastboot_driver.h"
+#include "util.h"
#include <bootimg.h>
+#include "result.h"
+#include "socket.h"
+#include "util.h"
+
class FastBootTool {
public:
int Main(int argc, char* argv[]);
@@ -40,6 +46,45 @@
unsigned ParseFsOption(const char*);
};
+enum class ImageType {
+ // Must be flashed for device to boot into the kernel.
+ BootCritical,
+ // Normal partition to be flashed during "flashall".
+ Normal,
+ // Partition that is never flashed during "flashall".
+ Extra
+};
+
+struct Image {
+ std::string nickname;
+ std::string img_name;
+ std::string sig_name;
+ std::string part_name;
+ bool optional_if_no_image;
+ ImageType type;
+ bool IsSecondary() const { return nickname.empty(); }
+};
+
+using ImageEntry = std::pair<const Image*, std::string>;
+
+struct FlashingPlan {
+ // If the image uses the default slot, or the user specified "all", then
+ // the paired string will be empty. If the image requests a specific slot
+ // (for example, system_other) it is specified instead.
+ ImageSource* source;
+ bool wants_wipe = false;
+ bool skip_reboot = false;
+ bool wants_set_active = false;
+ bool skip_secondary = false;
+ bool force_flash = false;
+
+ std::string slot;
+ std::string current_slot;
+ std::string secondary_slot;
+ fastboot::FastBootDriver* fb;
+
+};
+
bool should_flash_in_userspace(const std::string& partition_name);
bool is_userspace_fastboot();
void do_flash(const char* pname, const char* fname);
@@ -48,3 +93,11 @@
std::string find_item(const std::string& item);
void reboot_to_userspace_fastboot();
void syntax_error(const char* fmt, ...);
+
+struct NetworkSerial {
+ Socket::Protocol protocol;
+ std::string address;
+ int port;
+};
+
+Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial);
\ No newline at end of file
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 79f37fd..1863e95 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -203,6 +203,54 @@
ParseRequirementLineTestMalformed("require-for-product :");
}
+static void ParseNetworkSerialTest(const std::string& description, const std::string& serial,
+ const std::string& expected_address,
+ const Socket::Protocol expected_protocol,
+ const int expected_port) {
+ const Result<NetworkSerial, FastbootError> parsed = ParseNetworkSerial(serial);
+
+ ASSERT_RESULT_OK(parsed) << description;
+
+ const NetworkSerial network_serial = parsed.value();
+ EXPECT_EQ(network_serial.address, expected_address) << description;
+ EXPECT_EQ(network_serial.protocol, expected_protocol) << description;
+ EXPECT_EQ(network_serial.port, expected_port) << description;
+}
+
+static void ParseNetworkSerialNegativeTest(const std::string& description,
+ const std::string& serial,
+ const FastbootError::Type expected_error) {
+ const Result<NetworkSerial, FastbootError> parsed = ParseNetworkSerial(serial);
+
+ EXPECT_FALSE(parsed.ok()) << description;
+ EXPECT_EQ(parsed.error().code(), expected_error) << description;
+}
+
+TEST(FastBoot, ParseNetworkSerial) {
+ ParseNetworkSerialTest("tcp IPv4 parsed", "tcp:192.168.1.0", "192.168.1.0",
+ Socket::Protocol::kTcp, 5554);
+
+ ParseNetworkSerialTest("udp IPv4 parsed", "udp:192.168.1.0", "192.168.1.0",
+ Socket::Protocol::kUdp, 5554);
+
+ ParseNetworkSerialTest("port parsed", "udp:192.168.1.0:9999", "192.168.1.0",
+ Socket::Protocol::kUdp, 9999);
+
+ ParseNetworkSerialTest("IPv6 parsed", "tcp:2001:db8:3333:4444:5555:6666:7777:8888",
+ "2001:db8:3333:4444:5555:6666:7777:8888", Socket::Protocol::kTcp, 5554);
+
+ ParseNetworkSerialTest("empty IPv6 parsed", "tcp:::", "::", Socket::Protocol::kTcp, 5554);
+
+ ParseNetworkSerialNegativeTest("wrong prefix", "tcpa:192.168.1.0",
+ FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX);
+
+ ParseNetworkSerialNegativeTest("no prefix", "192.168.1.0",
+ FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX);
+
+ ParseNetworkSerialNegativeTest("wrong port", "tcp:192.168.1.0:-1",
+ FastbootError::Type::NETWORK_SERIAL_WRONG_ADDRESS);
+}
+
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
android::base::InitLogging(argv);
diff --git a/fastboot/result.h b/fastboot/result.h
new file mode 100644
index 0000000..dfa5e10
--- /dev/null
+++ b/fastboot/result.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 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/result.h>
+
+#include "util.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::ResultError;
+
+class FastbootError {
+ public:
+ enum Type { NETWORK_SERIAL_WRONG_PREFIX = 1, NETWORK_SERIAL_WRONG_ADDRESS = 2 };
+
+ FastbootError(Type&& type) : type_(std::forward<Type>(type)) {}
+
+ Type value() const { return type_; }
+ operator Type() const { return value(); }
+ std::string print() const { return ""; }
+
+ private:
+ Type type_;
+};
+
+template <typename T, typename U>
+inline T Expect(Result<T, U> r) {
+ if (r.ok()) {
+ return r.value();
+ }
+
+ die(r.error().message());
+
+ return r.value();
+}
\ No newline at end of file
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index 94dd5c3..a4aa637 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -20,20 +20,16 @@
#include "fastboot.h"
#include "util.h"
-FlashTask::FlashTask(const std::string& _slot) : slot_(_slot){};
-FlashTask::FlashTask(const std::string& _slot, bool _force_flash)
- : slot_(_slot), force_flash_(_force_flash) {}
-FlashTask::FlashTask(const std::string& _slot, bool _force_flash, const std::string& _pname)
- : pname_(_pname), fname_(find_item(_pname)), slot_(_slot), force_flash_(_force_flash) {
+FlashTask::FlashTask(const std::string& _slot, const std::string& _pname)
+ : pname_(_pname), fname_(find_item(_pname)), slot_(_slot) {
if (fname_.empty()) die("cannot determine image filename for '%s'", pname_.c_str());
}
-FlashTask::FlashTask(const std::string& _slot, bool _force_flash, const std::string& _pname,
- const std::string& _fname)
- : pname_(_pname), fname_(_fname), slot_(_slot), force_flash_(_force_flash) {}
+FlashTask::FlashTask(const std::string& _slot, const std::string& _pname, const std::string& _fname)
+ : pname_(_pname), fname_(_fname), slot_(_slot) {}
void FlashTask::Run() {
auto flash = [&](const std::string& partition) {
- if (should_flash_in_userspace(partition) && !is_userspace_fastboot() && !force_flash_) {
+ if (should_flash_in_userspace(partition) && !is_userspace_fastboot()) {
die("The partition you are trying to flash is dynamic, and "
"should be flashed via fastbootd. Please run:\n"
"\n"
@@ -47,25 +43,25 @@
do_for_partitions(pname_, slot_, flash, true);
}
-RebootTask::RebootTask(fastboot::FastBootDriver* _fb) : fb_(_fb){};
-RebootTask::RebootTask(fastboot::FastBootDriver* _fb, std::string _reboot_target)
- : reboot_target_(std::move(_reboot_target)), fb_(_fb){};
+RebootTask::RebootTask(FlashingPlan* _fp) : fp_(_fp){};
+RebootTask::RebootTask(FlashingPlan* _fp, const std::string& _reboot_target)
+ : reboot_target_(_reboot_target), fp_(_fp){};
void RebootTask::Run() {
if ((reboot_target_ == "userspace" || reboot_target_ == "fastboot")) {
if (!is_userspace_fastboot()) {
reboot_to_userspace_fastboot();
- fb_->WaitForDisconnect();
+ fp_->fb->WaitForDisconnect();
}
} else if (reboot_target_ == "recovery") {
- fb_->RebootTo("recovery");
- fb_->WaitForDisconnect();
+ fp_->fb->RebootTo("recovery");
+ fp_->fb->WaitForDisconnect();
} else if (reboot_target_ == "bootloader") {
- fb_->RebootTo("bootloader");
- fb_->WaitForDisconnect();
+ fp_->fb->RebootTo("bootloader");
+ fp_->fb->WaitForDisconnect();
} else if (reboot_target_ == "") {
- fb_->Reboot();
- fb_->WaitForDisconnect();
+ fp_->fb->Reboot();
+ fp_->fb->WaitForDisconnect();
} else {
syntax_error("unknown reboot target %s", reboot_target_.c_str());
}
diff --git a/fastboot/task.h b/fastboot/task.h
index 582fa2f..d8b9e21 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -18,6 +18,7 @@
#include <sstream>
#include <string>
+#include "fastboot.h"
#include "fastboot_driver.h"
class Task {
@@ -29,11 +30,8 @@
class FlashTask : public Task {
public:
- FlashTask(const std::string& _slot);
- FlashTask(const std::string& _slot, bool _force_flash);
- FlashTask(const std::string& _slot, bool _force_flash, const std::string& _pname);
- FlashTask(const std::string& _slot, bool _force_flash, const std::string& _pname,
- const std::string& _fname);
+ FlashTask(const std::string& _slot, const std::string& _pname);
+ FlashTask(const std::string& _slot, const std::string& _pname, const std::string& _fname);
void Run() override;
~FlashTask() {}
@@ -42,17 +40,16 @@
const std::string pname_;
const std::string fname_;
const std::string slot_;
- bool force_flash_ = false;
};
class RebootTask : public Task {
public:
- RebootTask(fastboot::FastBootDriver* _fb);
- RebootTask(fastboot::FastBootDriver* _fb, const std::string _reboot_target);
+ RebootTask(FlashingPlan* _fp);
+ RebootTask(FlashingPlan* _fp, const std::string& _reboot_target);
void Run() override;
~RebootTask() {}
private:
const std::string reboot_target_ = "";
- fastboot::FastBootDriver* fb_;
+ FlashingPlan* fp_;
};
\ No newline at end of file
diff --git a/fastboot/util.h b/fastboot/util.h
index 8a79e13..290d0d5 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -6,29 +6,11 @@
#include <string>
#include <vector>
-#include <android-base/logging.h>
-#include <android-base/result.h>
#include <android-base/unique_fd.h>
#include <bootimg.h>
#include <liblp/liblp.h>
#include <sparse/sparse.h>
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-using android::base::ResultError;
-
-template <typename T, typename U>
-inline T Expect(Result<T, U> r) {
- if (r.ok()) {
- return r.value();
- }
-
- LOG(FATAL) << r.error().message();
-
- return r.value();
-}
-
using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
/* util stuff */
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 15f025c..f655522 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -3216,6 +3216,8 @@
vabc_disable_reason = "recovery";
} else if (!cow_format_support) {
vabc_disable_reason = "cow format not supported";
+ } else if (!KernelSupportsCompressedSnapshots()) {
+ vabc_disable_reason = "kernel missing userspace block device support";
}
if (!vabc_disable_reason.empty()) {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 13314da..460d49d 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -124,6 +124,10 @@
SKIP_IF_NON_VIRTUAL_AB();
SetupProperties();
+ if (!DeviceSupportsMode()) {
+ GTEST_SKIP() << "Mode not supported on this device";
+ }
+
InitializeState();
CleanupTestArtifacts();
FormatFakeSuper();
@@ -159,7 +163,13 @@
IPropertyFetcher::OverrideForTesting(std::move(fetcher));
if (GetLegacyCompressionEnabledProperty() || CanUseUserspaceSnapshots()) {
- snapuserd_required_ = true;
+ // If we're asked to test the device's actual configuration, then it
+ // may be misconfigured, so check for kernel support as libsnapshot does.
+ if (FLAGS_force_mode.empty()) {
+ snapuserd_required_ = KernelSupportsCompressedSnapshots();
+ } else {
+ snapuserd_required_ = true;
+ }
}
}
@@ -176,6 +186,16 @@
LOG(INFO) << "Teardown complete for test: " << test_name_;
}
+ bool DeviceSupportsMode() {
+ if (FLAGS_force_mode.empty()) {
+ return true;
+ }
+ if (snapuserd_required_ && !KernelSupportsCompressedSnapshots()) {
+ return false;
+ }
+ return true;
+ }
+
void InitializeState() {
ASSERT_TRUE(sm->EnsureImageManager());
image_manager_ = sm->image_manager();
@@ -193,6 +213,11 @@
// get an accurate list to remove.
lock_ = nullptr;
+ // If there is no image manager, the test was skipped.
+ if (!image_manager_) {
+ return;
+ }
+
std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
"test_partition_b"};
for (const auto& snapshot : snapshots) {
@@ -946,6 +971,11 @@
SKIP_IF_NON_VIRTUAL_AB();
SnapshotTest::SetUp();
+ if (!image_manager_) {
+ // Test was skipped.
+ return;
+ }
+
Cleanup();
// Cleanup() changes slot suffix, so initialize it again.
@@ -2680,6 +2710,9 @@
CleanUp();
}
void CleanUp() {
+ if (!image_manager_) {
+ return;
+ }
EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
image_manager_->DeleteBackingImage(kImageName));
}
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index a98bf0e..1ffa89c 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -29,6 +29,7 @@
#include <fs_mgr/roots.h>
#include <liblp/property_fetcher.h>
+using android::dm::DeviceMapper;
using android::dm::kSectorSize;
using android::fiemap::FiemapStatus;
using android::fs_mgr::EnsurePathMounted;
@@ -251,7 +252,10 @@
LOG(INFO) << "Userspace snapshots disabled for testing";
return false;
}
-
+ if (!KernelSupportsCompressedSnapshots()) {
+ LOG(ERROR) << "Userspace snapshots requested, but no kernel support is available.";
+ return false;
+ }
return true;
}
@@ -278,5 +282,10 @@
return fetcher->GetBoolProperty("snapuserd.test.dm.snapshots", false);
}
+bool KernelSupportsCompressedSnapshots() {
+ auto& dm = DeviceMapper::Instance();
+ return dm.GetTargetByName("user", nullptr);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 8c4c7c6..370f3c4 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -127,6 +127,8 @@
void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,
uint64_t start_block, uint64_t num_blocks);
+bool KernelSupportsCompressedSnapshots();
+
bool GetLegacyCompressionEnabledProperty();
bool GetUserspaceSnapshotsEnabledProperty();
bool GetIouringEnabledProperty();
diff --git a/init/init.cpp b/init/init.cpp
index c965fe6..be1ebee 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -247,48 +247,21 @@
WakeMainInitThread();
}
- std::optional<std::string> CheckShutdown() {
+ std::optional<std::string> CheckShutdown() __attribute__((warn_unused_result)) {
auto lock = std::lock_guard{shutdown_command_lock_};
if (do_shutdown_ && !IsShuttingDown()) {
+ do_shutdown_ = false;
return shutdown_command_;
}
return {};
}
- bool do_shutdown() const { return do_shutdown_; }
- void set_do_shutdown(bool value) { do_shutdown_ = value; }
-
private:
std::mutex shutdown_command_lock_;
std::string shutdown_command_ GUARDED_BY(shutdown_command_lock_);
bool do_shutdown_ = false;
} shutdown_state;
-static void UnwindMainThreadStack() {
- unwindstack::AndroidLocalUnwinder unwinder;
- unwindstack::AndroidUnwinderData data;
- if (!unwinder.Unwind(data)) {
- LOG(ERROR) << __FUNCTION__
- << "sys.powerctl: Failed to unwind callstack: " << data.GetErrorString();
- }
- for (const auto& frame : data.frames) {
- LOG(ERROR) << "sys.powerctl: " << unwinder.FormatFrame(frame);
- }
-}
-
-void DebugRebootLogging() {
- LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
- << " IsShuttingDown: " << IsShuttingDown();
- if (shutdown_state.do_shutdown()) {
- LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
- UnwindMainThreadStack();
- }
- if (IsShuttingDown()) {
- LOG(ERROR) << "sys.powerctl set while init is already shutting down";
- UnwindMainThreadStack();
- }
-}
-
void DumpState() {
ServiceList::GetInstance().DumpState();
ActionManager::GetInstance().DumpState();
@@ -1109,36 +1082,43 @@
// Restore prio before main loop
setpriority(PRIO_PROCESS, 0, 0);
while (true) {
- // By default, sleep until something happens.
- std::optional<std::chrono::milliseconds> epoll_timeout;
+ // By default, sleep until something happens. Do not convert far_future into
+ // std::chrono::milliseconds because that would trigger an overflow. The unit of boot_clock
+ // is 1ns.
+ const boot_clock::time_point far_future = boot_clock::time_point::max();
+ boot_clock::time_point next_action_time = far_future;
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
- shutdown_state.set_do_shutdown(false);
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
+ // If there's more work to do, wake up again immediately.
+ if (am.HasMoreCommands()) {
+ next_action_time = boot_clock::now();
+ }
}
+ // Since the above code examined pending actions, no new actions must be
+ // queued by the code between this line and the Epoll::Wait() call below
+ // without calling WakeMainInitThread().
if (!IsShuttingDown()) {
auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
if (next_process_action_time) {
- epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
- *next_process_action_time - boot_clock::now());
- if (epoll_timeout < 0ms) epoll_timeout = 0ms;
+ next_action_time = std::min(next_action_time, *next_process_action_time);
}
}
- if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
- // If there's more work to do, wake up again immediately.
- if (am.HasMoreCommands()) epoll_timeout = 0ms;
+ std::optional<std::chrono::milliseconds> epoll_timeout;
+ if (next_action_time != far_future) {
+ epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
+ std::max(next_action_time - boot_clock::now(), 0ns));
}
-
auto epoll_result = epoll.Wait(epoll_timeout);
if (!epoll_result.ok()) {
LOG(ERROR) << epoll_result.error();
diff --git a/init/init.h b/init/init.h
index 063632a..9c7e918 100644
--- a/init/init.h
+++ b/init/init.h
@@ -42,8 +42,6 @@
void PropertyChanged(const std::string& name, const std::string& value);
bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd);
-void DebugRebootLogging();
-
int SecondStageMain(int argc, char** argv);
int StopServicesFromApex(const std::string& apex_name);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 87ffdb9..8da6982 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -550,9 +550,6 @@
}
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
<< process_log_string;
- if (!value.empty()) {
- DebugRebootLogging();
- }
if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
*error = "Userspace reboot is not supported by this device";
return {PROP_ERROR_INVALID_VALUE};
diff --git a/init/service.cpp b/init/service.cpp
index cce24c3..8456d1e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -447,6 +447,15 @@
return {};
}
+ // On newer kernels, /dev/console will always exist because
+ // "console=ttynull" is hard-coded in CONFIG_CMDLINE. This new boot
+ // property should be set via "androidboot.serialconsole=0" to explicitly
+ // disable services requiring the console. For older kernels and boot
+ // images, not setting this at all will fall back to the old behavior
+ if (GetProperty("ro.boot.serialconsole", "") == "0") {
+ return {};
+ }
+
if (proc_attr_.console.empty()) {
proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
}
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 384a8f0..38f19ff 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -372,6 +372,7 @@
std::set<pid_t> pgids;
pgids.emplace(initialPid);
std::set<pid_t> pids;
+ int processes = 0;
std::unique_ptr<FILE, decltype(&fclose)> fd(nullptr, fclose);
@@ -390,6 +391,7 @@
pid_t pid;
bool file_is_empty = true;
while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) {
+ processes++;
file_is_empty = false;
if (pid == 0) {
// Should never happen... but if it does, trying to kill this
@@ -419,15 +421,12 @@
}
}
- int processes = 0;
// Kill all process groups.
for (const auto pgid : pgids) {
LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
<< " as part of process cgroup " << initialPid;
- if (kill(-pgid, signal) == 0) {
- processes++;
- } else if (errno != ESRCH) {
+ if (kill(-pgid, signal) == -1 && errno != ESRCH) {
PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
}
}
@@ -437,9 +436,7 @@
LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
<< initialPid;
- if (kill(pid, signal) == 0) {
- processes++;
- } else if (errno != ESRCH) {
+ if (kill(pid, signal) == -1 && errno != ESRCH) {
PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
}
}
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 35adf36..4db7372 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -815,11 +815,11 @@
profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
}
if (!profile->ExecuteForProcess(uid, pid)) {
- PLOG(WARNING) << "Failed to apply " << name << " process profile";
+ LOG(WARNING) << "Failed to apply " << name << " process profile";
success = false;
}
} else {
- PLOG(WARNING) << "Failed to find " << name << " process profile";
+ LOG(WARNING) << "Failed to find " << name << " process profile";
success = false;
}
}
@@ -836,11 +836,11 @@
profile->EnableResourceCaching(ProfileAction::RCT_TASK);
}
if (!profile->ExecuteForTask(tid)) {
- PLOG(WARNING) << "Failed to apply " << name << " task profile";
+ LOG(WARNING) << "Failed to apply " << name << " task profile";
success = false;
}
} else {
- PLOG(WARNING) << "Failed to find " << name << " task profile";
+ LOG(WARNING) << "Failed to find " << name << " task profile";
success = false;
}
}
diff --git a/libutils/ProcessCallStack_fuzz.cpp b/libutils/ProcessCallStack_fuzz.cpp
index 30136cd..552a11e 100644
--- a/libutils/ProcessCallStack_fuzz.cpp
+++ b/libutils/ProcessCallStack_fuzz.cpp
@@ -44,7 +44,7 @@
dataProvider->ConsumeRandomLengthString(MAX_NAME_SIZE).append(std::to_string(i));
std::thread th = std::thread(loop);
pthread_setname_np(th.native_handle(), threadName.c_str());
- threads.push_back(move(th));
+ threads.push_back(std::move(th));
}
// Collect thread information
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/RefBase_fuzz.cpp
index 69288b3..8291be9 100644
--- a/libutils/RefBase_fuzz.cpp
+++ b/libutils/RefBase_fuzz.cpp
@@ -177,7 +177,7 @@
uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, kMaxOperations);
std::vector<uint8_t> threadOperations = dataProvider->ConsumeBytes<uint8_t>(opCount);
std::thread tmpThread = std::thread(loop, threadOperations);
- threads.push_back(move(tmpThread));
+ threads.push_back(std::move(tmpThread));
}
for (auto& th : threads) {
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index ca522b7..9a733bb 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -18,8 +18,11 @@
in Marshmallow we changed direction and started the move to toybox.
Not everything is provided by toybox, though. For the bzip2 command-line tools
-we use the ones that are part of the bzip2 distribution. The awk added in
-Android P is Brian Kernighan's "one true" awk.
+we use the ones that are part of the bzip2 distribution.
+The awk added in Android P is the
+["one true" awk](https://github.com/onetrueawk/awk).
+The bc added in Android Q is
+[Gavin Howard's bc](https://github.com/gavinhoward/bc).
The lists below show what tools were provided and where they came from in
each release starting with Gingerbread. This doesn't tell the full story,
@@ -34,6 +37,40 @@
full list for a release by running `toybox` directly.
+## Android 14 ("U")
+
+BSD: fsck\_msdos newfs\_msdos
+
+bzip2: bzcat bzip2 bunzip2
+
+gavinhoward/bc: bc
+
+one-true-awk: awk
+
+toolbox: getevent getprop setprop start stop
+
+toybox ([0.8.9](http://landley.net/toybox/#10-01-2023)-ish):
+[ acpi base64 basename blkdiscard blkid blockdev **brctl** cal cat chattr
+chcon chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut
+date dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
+expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
+fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
+help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
+inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
+log **logger** logname losetup ls lsattr lsmod lsof lspci lsusb makedevs
+md5sum microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe
+more mount mountpoint mv nbd-client nc netcat netstat nice nl nohup
+nproc nsenter od partprobe paste patch pgrep pidof ping ping6 pivot\_root
+pkill pmap printenv printf prlimit ps pwd pwdx readelf readlink realpath
+renice restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent
+seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee test time timeout top touch tr traceroute
+traceroute6 true truncate tty tunctl uclampset ulimit umount uname
+uniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen
+vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
+
+
## Android 13 ("T")
BSD: fsck\_msdos newfs\_msdos
@@ -46,7 +83,8 @@
toolbox: getevent getprop setprop start stop
-toybox (0.8.6-ish): [ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon
+toybox ([0.8.6](http://landley.net/toybox/#30-11-2021)-ish):
+[ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon
chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
@@ -79,7 +117,8 @@
toolbox: getevent getprop setprop start stop
-toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
+toybox ([0.8.4](http://landley.net/toybox/#24-10-2020)-ish):
+**[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
@@ -112,7 +151,8 @@
toolbox: getevent getprop setprop start stop
-toybox (0.8.3-ish): acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
+toybox ([0.8.3](http://landley.net/toybox/#11-05-2020)-ish):
+acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
chown chroot chrt cksum clear cmp comm cp cpio cut date dd **devmem**
df diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
false fgrep file find flock fmt free freeramdisk fsfreeze **fsync** getconf
@@ -143,7 +183,8 @@
toolbox: getevent getprop
-toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
+toybox ([0.8.0](http://landley.net/toybox/#08-02-2019)-ish):
+acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
@@ -174,7 +215,8 @@
toolbox: getevent getprop newfs\_msdos
-toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox ([0.7.6](http://landley.net/toybox/#24-02-2018)-ish):
+acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
dos2unix du echo env expand expr fallocate false file find flock **fmt** free
getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
@@ -198,7 +240,8 @@
toolbox: getevent newfs\_msdos
-toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox ([0.7.3](http://landley.net/toybox/#21-02-2017)-ish):
+acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
dos2unix du echo env expand expr fallocate false **file** find flock free
getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
@@ -221,7 +264,8 @@
toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
sendevent start stop top
-toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
+toybox ([0.7.0](http://landley.net/toybox/#02-02-2016)-ish):
+acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
dos2unix **du** echo env expand expr fallocate false find **flock** free
getenforce getprop groups head hostname hwclock id ifconfig inotifyd
@@ -242,7 +286,8 @@
toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
-toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+toybox ([0.5.2](http://landley.net/toybox/#25-02-2015)-ish):
+acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
env expand expr fallocate false find free getenforce getprop groups
head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln