Merge "Revert "adb: detect some spin loops and abort.""
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 923442f..b0b4839 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -57,12 +57,15 @@
"libavb",
"libfstab",
"libdm",
+ "liblp",
],
export_static_lib_headers: [
"libfstab",
"libdm",
+ "liblp",
],
whole_static_libs: [
+ "liblp",
"liblogwrap",
"libdm",
"libfstab",
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index b2f3a68..ed42d40 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -37,6 +37,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <liblp/reader.h>
#include "fs_mgr_priv.h"
#include "fs_mgr_priv_dm_ioctl.h"
@@ -137,5 +138,30 @@
return nullptr;
}
+bool CreateLogicalPartitions(const std::string& block_device) {
+ uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+ auto metadata = ReadMetadata(block_device.c_str(), slot);
+ if (!metadata) {
+ LOG(ERROR) << "Could not read partition table.";
+ return true;
+ }
+
+ LogicalPartitionTable table;
+ for (const auto& partition : metadata->partitions) {
+ LogicalPartition new_partition;
+ new_partition.name = GetPartitionName(partition);
+ new_partition.attributes = partition.attributes;
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const auto& extent = metadata->extents[partition.first_extent_index + i];
+ new_partition.extents.emplace_back(new_partition.num_sectors, extent.target_data,
+ extent.num_sectors, block_device.c_str());
+ new_partition.num_sectors += extent.num_sectors;
+ }
+ table.partitions.push_back(new_partition);
+ }
+
+ return CreateLogicalPartitions(table);
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 7f928e5..9772c4b 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -92,6 +92,7 @@
// /dev/block/dm-<name> where |name| is the partition name.
//
bool CreateLogicalPartitions(const LogicalPartitionTable& table);
+bool CreateLogicalPartitions(const std::string& block_device);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 672d401..f1b18dd 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -16,6 +16,7 @@
cc_library_static {
name: "libdm",
+ defaults: ["fs_mgr_defaults"],
recovery_available: true,
export_include_dirs: ["include"],
@@ -35,3 +36,16 @@
"liblog_headers",
],
}
+
+cc_test {
+ name: "libdm_test",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libdm",
+ "libbase",
+ "liblog",
+ ],
+ srcs: [
+ "dm_test.cpp",
+ ]
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 57c1270..b96f4c1 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -14,23 +14,13 @@
* limitations under the License.
*/
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/dm-ioctl.h>
-#include <stdint.h>
+#include "libdm/dm.h"
+
#include <sys/ioctl.h>
+#include <sys/sysmacros.h>
#include <sys/types.h>
-#include <unistd.h>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/logging.h>
#include <android-base/macros.h>
-#include <android-base/unique_fd.h>
-
-#include "dm.h"
namespace android {
namespace dm {
@@ -51,23 +41,18 @@
return false;
}
- std::unique_ptr<struct dm_ioctl, decltype(&free)> io(
- static_cast<struct dm_ioctl*>(malloc(sizeof(struct dm_ioctl))), free);
- if (io == nullptr) {
- LOG(ERROR) << "Failed to allocate dm_ioctl";
- return false;
- }
- InitIo(io.get(), name);
+ struct dm_ioctl io;
+ InitIo(&io, name);
- if (ioctl(fd_, DM_DEV_CREATE, io.get())) {
- PLOG(ERROR) << "DM_DEV_CREATE failed to create [" << name << "]";
+ if (ioctl(fd_, DM_DEV_CREATE, &io)) {
+ PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
return false;
}
// Check to make sure the newly created device doesn't already have targets
// added or opened by someone
- CHECK(io->target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
- CHECK(io->open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
+ CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
+ CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
// Creates a new device mapper device with the name passed in
return true;
@@ -84,22 +69,17 @@
return false;
}
- std::unique_ptr<struct dm_ioctl, decltype(&free)> io(
- static_cast<struct dm_ioctl*>(malloc(sizeof(struct dm_ioctl))), free);
- if (io == nullptr) {
- LOG(ERROR) << "Failed to allocate dm_ioctl";
- return false;
- }
- InitIo(io.get(), name);
+ struct dm_ioctl io;
+ InitIo(&io, name);
- if (ioctl(fd_, DM_DEV_REMOVE, io.get())) {
- PLOG(ERROR) << "DM_DEV_REMOVE failed to create [" << name << "]";
+ if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
+ PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
return false;
}
// Check to make sure appropriate uevent is generated so ueventd will
// do the right thing and remove the corresponding device node and symlinks.
- CHECK(io->flags & DM_UEVENT_GENERATED_FLAG)
+ CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
<< "Didn't generate uevent for [" << name << "] removal";
return true;
@@ -115,13 +95,45 @@
return DmDeviceState::INVALID;
}
-bool DeviceMapper::LoadTableAndActivate(const std::string& /* name */, const DmTable& /* table */) {
- return false;
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
+ if (!CreateDevice(name)) {
+ return false;
+ }
+ if (!LoadTableAndActivate(name, table)) {
+ DeleteDevice(name);
+ return false;
+ }
+ return true;
+}
+
+bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
+ std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
+ ioctl_buffer += table.Serialize();
+
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
+ InitIo(io, name);
+ io->data_size = ioctl_buffer.size();
+ io->data_start = sizeof(struct dm_ioctl);
+ io->target_count = static_cast<uint32_t>(table.num_targets());
+ if (table.readonly()) {
+ io->flags |= DM_READONLY_FLAG;
+ }
+ if (ioctl(fd_, DM_TABLE_LOAD, io)) {
+ PLOG(ERROR) << "DM_TABLE_LOAD failed";
+ return false;
+ }
+
+ InitIo(io, name);
+ if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
+ PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
+ return false;
+ }
+ return true;
}
// Reads all the available device mapper targets and their corresponding
// versions from the kernel and returns in a vector
-bool DeviceMapper::GetAvailableTargets(std::vector<DmTarget>* targets) {
+bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
targets->clear();
// calculate the space needed to read a maximum of kMaxPossibleDmTargets
@@ -147,7 +159,7 @@
io->data_start = sizeof(*io);
if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
- PLOG(ERROR) << "Failed to get DM_LIST_VERSIONS from kernel";
+ PLOG(ERROR) << "DM_LIST_VERSIONS failed";
return false;
}
@@ -170,7 +182,7 @@
struct dm_target_versions* vers =
reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
while (next && data_size) {
- targets->emplace_back((vers));
+ targets->emplace_back(vers);
if (vers->next == 0) {
break;
}
@@ -209,7 +221,7 @@
io->data_start = sizeof(*io);
if (ioctl(fd_, DM_LIST_DEVICES, io)) {
- PLOG(ERROR) << "Failed to get DM_LIST_DEVICES from kernel";
+ PLOG(ERROR) << "DM_LIST_DEVICES failed";
return false;
}
@@ -247,8 +259,17 @@
// Accepts a device mapper device name (like system_a, vendor_b etc) and
// returns the path to it's device node (or symlink to the device node)
-std::string DeviceMapper::GetDmDevicePathByName(const std::string& /* name */) {
- return "";
+bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(ERROR) << "DM_DEV_STATUS failed for " << name;
+ return false;
+ }
+
+ uint32_t dev_num = minor(io.dev);
+ *path = "/dev/block/dm-" + std::to_string(dev_num);
+ return true;
}
// private methods of DeviceMapper
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
index 14b3932..cb6f210 100644
--- a/fs_mgr/libdm/dm_table.cpp
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -14,18 +14,16 @@
* limitations under the License.
*/
+#include "libdm/dm_table.h"
+
#include <android-base/logging.h>
#include <android-base/macros.h>
-#include <string>
-#include <vector>
-
-#include "dm_table.h"
-
namespace android {
namespace dm {
-bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& /* target */) {
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& target) {
+ targets_.push_back(std::move(target));
return true;
}
@@ -34,21 +32,37 @@
}
bool DmTable::valid() const {
+ if (targets_.empty()) {
+ LOG(ERROR) << "Device-mapper table must have at least one target.";
+ return "";
+ }
+ if (targets_[0]->start() != 0) {
+ LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
+ return "";
+ }
return true;
}
-uint64_t DmTable::size() const {
- return valid() ? size_ : 0;
+uint64_t DmTable::num_sectors() const {
+ return valid() ? num_sectors_ : 0;
}
-// Returns a string represnetation of the table that is ready to be passed
-// down to the kernel for loading
+// Returns a string representation of the table that is ready to be passed
+// down to the kernel for loading.
//
// Implementation must verify there are no gaps in the table, table starts
// with sector == 0, and iterate over each target to get its table
// serialized.
std::string DmTable::Serialize() const {
- return "";
+ if (!valid()) {
+ return "";
+ }
+
+ std::string table;
+ for (const auto& target : targets_) {
+ table += target->Serialize();
+ }
+ return table;
}
} // namespace dm
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 8bcd526..5934416 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -14,16 +14,46 @@
* limitations under the License.
*/
+#include "libdm/dm_target.h"
+
#include <android-base/logging.h>
#include <android-base/macros.h>
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "dm_target.h"
+#include <libdm/dm.h>
namespace android {
-namespace dm {} // namespace dm
+namespace dm {
+
+std::string DmTarget::Serialize() const {
+ // Create a string containing a dm_target_spec, parameter data, and an
+ // explicit null terminator.
+ std::string data(sizeof(dm_target_spec), '\0');
+ data += GetParameterString();
+ data.push_back('\0');
+
+ // The kernel expects each target to be 8-byte aligned.
+ size_t padding = DM_ALIGN(data.size()) - data.size();
+ for (size_t i = 0; i < padding; i++) {
+ data.push_back('\0');
+ }
+
+ // Finally fill in the dm_target_spec.
+ struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
+ spec->sector_start = start();
+ spec->length = size();
+ strlcpy(spec->target_type, name().c_str(), sizeof(spec->target_type));
+ spec->next = (uint32_t)data.size();
+ return data;
+}
+
+std::string DmTargetZero::GetParameterString() const {
+ // The zero target type has no additional parameters.
+ return "";
+}
+
+std::string DmTargetLinear::GetParameterString() const {
+ return block_device_ + " " + std::to_string(physical_sector_);
+}
+
+} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
new file mode 100644
index 0000000..adbe820
--- /dev/null
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+
+using namespace std;
+using namespace android::dm;
+
+TEST(libdm, HasMinimumTargets) {
+ DeviceMapper& dm = DeviceMapper::Instance();
+ vector<DmTargetTypeInfo> targets;
+ ASSERT_TRUE(dm.GetAvailableTargets(&targets));
+
+ map<string, DmTargetTypeInfo> by_name;
+ for (const auto& target : targets) {
+ by_name[target.name()] = target;
+ }
+
+ auto iter = by_name.find("linear");
+ EXPECT_NE(iter, by_name.end());
+}
diff --git a/fs_mgr/libdm/include/dm_target.h b/fs_mgr/libdm/include/dm_target.h
deleted file mode 100644
index 31b0cb6..0000000
--- a/fs_mgr/libdm/include/dm_target.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2018 Google, Inc
- *
- * 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.
- */
-
-#ifndef _LIBDM_DMTARGET_H_
-#define _LIBDM_DMTARGET_H_
-
-#include <linux/dm-ioctl.h>
-#include <stdint.h>
-
-#include <android-base/logging.h>
-
-#include <string>
-
-namespace android {
-namespace dm {
-
-class DmTarget {
- public:
- DmTarget(const std::string& name, uint64_t start = 0, uint64_t length = 0)
- : name_(name), v0_(0), v1_(0), v2_(0), start_(start), length_(length){};
-
- // Creates a DmTarget object from dm_target_version as read from kernel
- // with DM_LIST_VERSION ioctl.
- DmTarget(const struct dm_target_versions* vers) : start_(0), length_(0) {
- CHECK(vers != nullptr) << "Can't create DmTarget with dm_target_versions set to nullptr";
- v0_ = vers->version[0];
- v1_ = vers->version[1];
- v2_ = vers->version[2];
- name_ = vers->name;
- }
-
- virtual ~DmTarget() = default;
-
- // Returns name of the target.
- const std::string& name() const { return name_; }
-
- // Returns size in number of sectors when this target is part of
- // a DmTable, return 0 otherwise.
- uint64_t size() const { return length_; }
-
- // Return string representation of the device mapper target version.
- std::string version() const {
- return std::to_string(v0_) + "." + std::to_string(v1_) + "." + std::to_string(v2_);
- }
-
- // Function that converts this object to a string of arguments that can
- // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
- // must implement this, for it to be used on a device.
- virtual std::string Serialize() const { return ""; }
-
- private:
- // Name of the target.
- std::string name_;
- // Target version.
- uint32_t v0_, v1_, v2_;
- // logical sector number start and total length (in terms of 512-byte sectors) represented
- // by this target within a DmTable.
- uint64_t start_, length_;
-};
-
-} // namespace dm
-} // namespace android
-
-#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/libdm/include/dm.h b/fs_mgr/libdm/include/libdm/dm.h
similarity index 87%
rename from fs_mgr/libdm/include/dm.h
rename to fs_mgr/libdm/include/libdm/dm.h
index 52a9a11..60bceed 100644
--- a/fs_mgr/libdm/include/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -17,18 +17,20 @@
#ifndef _LIBDM_DM_H_
#define _LIBDM_DM_H_
-#include <errno.h>
#include <fcntl.h>
#include <linux/dm-ioctl.h>
#include <linux/kdev_t.h>
+#include <stdint.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <memory>
+#include <string>
+#include <vector>
#include <android-base/logging.h>
-#include <dm_table.h>
+#include "dm_table.h"
// The minimum expected device mapper major.minor version
#define DM_VERSION0 (4)
@@ -67,14 +69,6 @@
uint64_t dev_;
};
- // Creates a device mapper device with given name.
- // Return 'true' on success and 'false' on failure to
- // create OR if a device mapper device with the same name already
- // exists.
- // TODO(b/110035986): Make this method private and to be only
- // called through LoadTableAndActivate() below.
- bool CreateDevice(const std::string& name);
-
// Removes a device mapper device with the given name.
// Returns 'true' on success, false otherwise.
bool DeleteDevice(const std::string& name);
@@ -88,15 +82,20 @@
// One of INVALID, SUSPENDED or ACTIVE.
DmDeviceState state(const std::string& name) const;
- // Loads the device mapper table from parameter into the underlying
- // device mapper device with given name and activate / resumes the device in the process.
- // If a device mapper device with the 'name', doesn't exist, it will be created.
+ // Creates a device, loads the given table, and activates it. If the device
+ // is not able to be activated, it is destroyed, and false is returned.
+ bool CreateDevice(const std::string& name, const DmTable& table);
+
+ // Loads the device mapper table from parameter into the underlying device
+ // mapper device with given name and activate / resumes the device in the
+ // process. A device with the given name must already exist.
+ //
// Returns 'true' on success, false otherwise.
bool LoadTableAndActivate(const std::string& name, const DmTable& table);
// Returns true if a list of available device mapper targets registered in the kernel was
// successfully read and stored in 'targets'. Returns 'false' otherwise.
- bool GetAvailableTargets(std::vector<DmTarget>* targets);
+ bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
// Return 'true' if it can successfully read the list of device mapper block devices
// currently created. 'devices' will be empty if the kernel interactions
@@ -105,8 +104,9 @@
bool GetAvailableDevices(std::vector<DmBlockDevice>* devices);
// Returns the path to the device mapper device node in '/dev' corresponding to
- // 'name'.
- std::string GetDmDevicePathByName(const std::string& name);
+ // 'name'. If the device does not exist, false is returned, and the path
+ // parameter is not set.
+ bool GetDmDevicePathByName(const std::string& name, std::string* path);
// The only way to create a DeviceMapper object.
static DeviceMapper& Instance();
@@ -138,6 +138,12 @@
}
}
+ // Creates a device mapper device with given name.
+ // Return 'true' on success and 'false' on failure to
+ // create OR if a device mapper device with the same name already
+ // exists.
+ bool CreateDevice(const std::string& name);
+
int fd_;
// Non-copyable & Non-movable
DeviceMapper(const DeviceMapper&) = delete;
diff --git a/fs_mgr/libdm/include/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
similarity index 86%
rename from fs_mgr/libdm/include/dm_table.h
rename to fs_mgr/libdm/include/libdm/dm_table.h
index 0b1685d..5c639be 100644
--- a/fs_mgr/libdm/include/dm_table.h
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -30,7 +30,7 @@
class DmTable {
public:
- DmTable() : size_(0){};
+ DmTable() : num_sectors_(0), readonly_(false) {}
// Adds a target to the device mapper table for a range specified in the target object.
// The function will return 'true' if the target was successfully added and doesn't overlap with
@@ -48,14 +48,20 @@
// table is malformed.
bool valid() const;
+ // Returns the toatl number of targets.
+ size_t num_targets() const { return targets_.size(); }
+
// Returns the total size represented by the table in terms of number of 512-byte sectors.
// NOTE: This function will overlook if there are any gaps in the targets added in the table.
- uint64_t size() const;
+ uint64_t num_sectors() const;
// Returns the string represntation of the table that is ready to be passed into the kernel
// as part of the DM_TABLE_LOAD ioctl.
std::string Serialize() const;
+ void set_readonly(bool readonly) { readonly_ = readonly; }
+ bool readonly() const { return readonly_; }
+
~DmTable() = default;
private:
@@ -66,7 +72,10 @@
// Total size in terms of # of sectors, as calculated by looking at the last and the first
// target in 'target_'.
- uint64_t size_;
+ uint64_t num_sectors_;
+
+ // True if the device should be read-only; false otherwise.
+ bool readonly_;
};
} // namespace dm
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
new file mode 100644
index 0000000..31b6a70
--- /dev/null
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * 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.
+ */
+
+#ifndef _LIBDM_DMTARGET_H_
+#define _LIBDM_DMTARGET_H_
+
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace dm {
+
+class DmTargetTypeInfo {
+ public:
+ DmTargetTypeInfo() : major_(0), minor_(0), patch_(0) {}
+ DmTargetTypeInfo(const struct dm_target_versions* info)
+ : name_(info->name),
+ major_(info->version[0]),
+ minor_(info->version[1]),
+ patch_(info->version[2]) {}
+
+ const std::string& name() const { return name_; }
+ std::string version() const {
+ return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
+ }
+
+ private:
+ std::string name_;
+ uint32_t major_;
+ uint32_t minor_;
+ uint32_t patch_;
+};
+
+class DmTarget {
+ public:
+ DmTarget(uint64_t start, uint64_t length) : start_(start), length_(length) {}
+
+ virtual ~DmTarget() = default;
+
+ // Returns name of the target.
+ virtual std::string name() const = 0;
+
+ // Return the first logical sector represented by this target.
+ uint64_t start() const { return start_; }
+
+ // Returns size in number of sectors when this target is part of
+ // a DmTable, return 0 otherwise.
+ uint64_t size() const { return length_; }
+
+ // Function that converts this object to a string of arguments that can
+ // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
+ // must implement this, for it to be used on a device.
+ std::string Serialize() const;
+
+ protected:
+ // Get the parameter string that is passed to the end of the dm_target_spec
+ // for this target type.
+ virtual std::string GetParameterString() const = 0;
+
+ private:
+ // logical sector number start and total length (in terms of 512-byte sectors) represented
+ // by this target within a DmTable.
+ uint64_t start_, length_;
+};
+
+class DmTargetZero final : public DmTarget {
+ public:
+ DmTargetZero(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+ std::string name() const override { return "zero"; }
+ std::string GetParameterString() const override;
+};
+
+class DmTargetLinear final : public DmTarget {
+ public:
+ DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,
+ uint64_t physical_sector)
+ : DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}
+
+ std::string name() const override { return "linear"; }
+ std::string GetParameterString() const override;
+
+ private:
+ std::string block_device_;
+ uint64_t physical_sector_;
+};
+
+} // namespace dm
+} // namespace android
+
+#endif /* _LIBDM_DMTARGET_H_ */
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
new file mode 100644
index 0000000..f7086a8
--- /dev/null
+++ b/fs_mgr/liblp/Android.bp
@@ -0,0 +1,61 @@
+//
+// Copyright (C) 2018 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.
+//
+
+cc_library_static {
+ name: "liblp",
+ host_supported: true,
+ recovery_available: true,
+ defaults: ["fs_mgr_defaults"],
+ cppflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ srcs: [
+ "builder.cpp",
+ "reader.cpp",
+ "utility.cpp",
+ "writer.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libcrypto_utils",
+ ],
+ whole_static_libs: [
+ "libext2_uuid",
+ "libext4_utils",
+ "libsparse",
+ "libz",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "liblp_test",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libcrypto_utils",
+ "liblp",
+ ],
+ srcs: [
+ "builder_test.cpp",
+ "io_test.cpp",
+ "utility_test.cpp",
+ ],
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
new file mode 100644
index 0000000..0e4838c
--- /dev/null
+++ b/fs_mgr/liblp/builder.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "liblp/builder.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include <uuid/uuid.h>
+
+#include "liblp/metadata_format.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Align a byte count up to the nearest 512-byte sector.
+template <typename T>
+static inline T AlignToSector(T value) {
+ return (value + (LP_SECTOR_SIZE - 1)) & ~T(LP_SECTOR_SIZE - 1);
+}
+
+void LinearExtent::AddTo(LpMetadata* out) const {
+ out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_});
+}
+
+void ZeroExtent::AddTo(LpMetadata* out) const {
+ out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
+}
+
+Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
+ : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+
+void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
+ size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+ extents_.push_back(std::move(extent));
+}
+
+void Partition::RemoveExtents() {
+ size_ = 0;
+ extents_.clear();
+}
+
+void Partition::ShrinkTo(uint64_t requested_size) {
+ uint64_t aligned_size = AlignToSector(requested_size);
+ if (size_ <= aligned_size) {
+ return;
+ }
+ if (aligned_size == 0) {
+ RemoveExtents();
+ return;
+ }
+
+ // Remove or shrink extents of any kind until the total partition size is
+ // equal to the requested size.
+ uint64_t sectors_to_remove = (size_ - aligned_size) / LP_SECTOR_SIZE;
+ while (sectors_to_remove) {
+ Extent* extent = extents_.back().get();
+ if (extent->num_sectors() > sectors_to_remove) {
+ size_ -= sectors_to_remove * LP_SECTOR_SIZE;
+ extent->set_num_sectors(extent->num_sectors() - sectors_to_remove);
+ break;
+ }
+ size_ -= (extent->num_sectors() * LP_SECTOR_SIZE);
+ sectors_to_remove -= extent->num_sectors();
+ extents_.pop_back();
+ }
+ DCHECK(size_ == requested_size);
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(uint64_t blockdevice_size,
+ uint32_t metadata_max_size,
+ uint32_t metadata_slot_count) {
+ std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+ if (!builder->Init(blockdevice_size, metadata_max_size, metadata_slot_count)) {
+ return nullptr;
+ }
+ return builder;
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+ std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+ if (!builder->Init(metadata)) {
+ return nullptr;
+ }
+ return builder;
+}
+
+MetadataBuilder::MetadataBuilder() {
+ memset(&geometry_, 0, sizeof(geometry_));
+ geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
+ geometry_.struct_size = sizeof(geometry_);
+
+ memset(&header_, 0, sizeof(header_));
+ header_.magic = LP_METADATA_HEADER_MAGIC;
+ header_.major_version = LP_METADATA_MAJOR_VERSION;
+ header_.minor_version = LP_METADATA_MINOR_VERSION;
+ header_.header_size = sizeof(header_);
+ header_.partitions.entry_size = sizeof(LpMetadataPartition);
+ header_.extents.entry_size = sizeof(LpMetadataExtent);
+}
+
+bool MetadataBuilder::Init(const LpMetadata& metadata) {
+ geometry_ = metadata.geometry;
+
+ for (const auto& partition : metadata.partitions) {
+ Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
+ partition.attributes);
+ if (!builder) {
+ return false;
+ }
+
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+ auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_data);
+ builder->AddExtent(std::move(copy));
+ } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+ auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+ builder->AddExtent(std::move(copy));
+ }
+ }
+ }
+ return true;
+}
+
+bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size,
+ uint32_t metadata_slot_count) {
+ if (metadata_max_size < sizeof(LpMetadataHeader)) {
+ LERROR << "Invalid metadata maximum size.";
+ return false;
+ }
+ if (metadata_slot_count == 0) {
+ LERROR << "Invalid metadata slot count.";
+ return false;
+ }
+
+ // Align the metadata size up to the nearest sector.
+ metadata_max_size = AlignToSector(metadata_max_size);
+
+ // We reserve a geometry block (4KB) plus space for each copy of the
+ // maximum size of a metadata blob. Then, we double that space since
+ // we store a backup copy of everything.
+ uint64_t reserved =
+ LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
+ uint64_t total_reserved = reserved * 2;
+
+ if (blockdevice_size < total_reserved || blockdevice_size - total_reserved < LP_SECTOR_SIZE) {
+ LERROR << "Attempting to create metadata on a block device that is too small.";
+ return false;
+ }
+
+ // The last sector is inclusive. We subtract one to make sure that logical
+ // partitions won't overlap with the same sector as the backup metadata,
+ // which could happen if the block device was not aligned to LP_SECTOR_SIZE.
+ geometry_.first_logical_sector = reserved / LP_SECTOR_SIZE;
+ geometry_.last_logical_sector = ((blockdevice_size - reserved) / LP_SECTOR_SIZE) - 1;
+ geometry_.metadata_max_size = metadata_max_size;
+ geometry_.metadata_slot_count = metadata_slot_count;
+ DCHECK(geometry_.last_logical_sector >= geometry_.first_logical_sector);
+ return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+ uint32_t attributes) {
+ if (name.empty()) {
+ LERROR << "Partition must have a non-empty name.";
+ return nullptr;
+ }
+ if (FindPartition(name)) {
+ LERROR << "Attempting to create duplication partition with name: " << name;
+ return nullptr;
+ }
+ partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+ return partitions_.back().get();
+}
+
+Partition* MetadataBuilder::FindPartition(const std::string& name) {
+ for (const auto& partition : partitions_) {
+ if (partition->name() == name) {
+ return partition.get();
+ }
+ }
+ return nullptr;
+}
+
+void MetadataBuilder::RemovePartition(const std::string& name) {
+ for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
+ if ((*iter)->name() == name) {
+ partitions_.erase(iter);
+ return;
+ }
+ }
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
+ // Align the space needed up to the nearest sector.
+ uint64_t aligned_size = AlignToSector(requested_size);
+ if (partition->size() >= aligned_size) {
+ return true;
+ }
+
+ // Figure out how much we need to allocate.
+ uint64_t space_needed = aligned_size - partition->size();
+ uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
+ DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
+
+ struct Interval {
+ uint64_t start;
+ uint64_t end;
+
+ Interval(uint64_t start, uint64_t end) : start(start), end(end) {}
+ bool operator<(const Interval& other) const { return start < other.start; }
+ };
+ std::vector<Interval> intervals;
+
+ // Collect all extents in the partition table.
+ for (const auto& partition : partitions_) {
+ for (const auto& extent : partition->extents()) {
+ LinearExtent* linear = extent->AsLinearExtent();
+ if (!linear) {
+ continue;
+ }
+ intervals.emplace_back(linear->physical_sector(),
+ linear->physical_sector() + extent->num_sectors());
+ }
+ }
+
+ // Sort extents by starting sector.
+ std::sort(intervals.begin(), intervals.end());
+
+ // Find gaps that we can use for new extents. Note we store new extents in a
+ // temporary vector, and only commit them if we are guaranteed enough free
+ // space.
+ std::vector<std::unique_ptr<LinearExtent>> new_extents;
+ for (size_t i = 1; i < intervals.size(); i++) {
+ const Interval& previous = intervals[i - 1];
+ const Interval& current = intervals[i];
+
+ if (previous.end >= current.start) {
+ // There is no gap between these two extents, try the next one. Note that
+ // extents may never overlap, but just for safety, we ignore them if they
+ // do.
+ DCHECK(previous.end == current.start);
+ continue;
+ }
+
+ // This gap is enough to hold the remainder of the space requested, so we
+ // can allocate what we need and return.
+ if (current.start - previous.end >= sectors_needed) {
+ auto extent = std::make_unique<LinearExtent>(sectors_needed, previous.end);
+ sectors_needed -= extent->num_sectors();
+ new_extents.push_back(std::move(extent));
+ break;
+ }
+
+ // This gap is not big enough to fit the remainder of the space requested,
+ // so consume the whole thing and keep looking for more.
+ auto extent = std::make_unique<LinearExtent>(current.start - previous.end, previous.end);
+ sectors_needed -= extent->num_sectors();
+ new_extents.push_back(std::move(extent));
+ }
+
+ // If we still have more to allocate, take it from the remaining free space
+ // in the allocatable region.
+ if (sectors_needed) {
+ uint64_t first_sector;
+ if (intervals.empty()) {
+ first_sector = geometry_.first_logical_sector;
+ } else {
+ first_sector = intervals.back().end;
+ }
+ DCHECK(first_sector <= geometry_.last_logical_sector);
+
+ // Note: the last usable sector is inclusive.
+ if (geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
+ LERROR << "Not enough free space to expand partition: " << partition->name();
+ return false;
+ }
+ auto extent = std::make_unique<LinearExtent>(sectors_needed, first_sector);
+ new_extents.push_back(std::move(extent));
+ }
+
+ for (auto& extent : new_extents) {
+ partition->AddExtent(std::move(extent));
+ }
+ return true;
+}
+
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t requested_size) {
+ partition->ShrinkTo(requested_size);
+}
+
+std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+ std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+ metadata->header = header_;
+ metadata->geometry = geometry_;
+
+ // Flatten the partition and extent structures into an LpMetadata, which
+ // makes it very easy to validate, serialize, or pass on to device-mapper.
+ for (const auto& partition : partitions_) {
+ LpMetadataPartition part;
+ memset(&part, 0, sizeof(part));
+
+ if (partition->name().size() > sizeof(part.name)) {
+ LERROR << "Partition name is too long: " << partition->name();
+ return nullptr;
+ }
+ if (partition->attributes() & ~(LP_PARTITION_ATTRIBUTE_MASK)) {
+ LERROR << "Partition " << partition->name() << " has unsupported attribute.";
+ return nullptr;
+ }
+
+ strncpy(part.name, partition->name().c_str(), sizeof(part.name));
+ if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
+ LERROR << "Could not parse guid " << partition->guid() << " for partition "
+ << partition->name();
+ return nullptr;
+ }
+
+ part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
+ part.num_extents = static_cast<uint32_t>(partition->extents().size());
+ part.attributes = partition->attributes();
+
+ for (const auto& extent : partition->extents()) {
+ extent->AddTo(metadata.get());
+ }
+ metadata->partitions.push_back(part);
+ }
+
+ metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
+ metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+ return metadata;
+}
+
+uint64_t MetadataBuilder::AllocatableSpace() const {
+ return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
new file mode 100644
index 0000000..2983f0f
--- /dev/null
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+
+using namespace std;
+using namespace android::fs_mgr;
+
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303";
+
+TEST(liblp, BuildBasic) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ EXPECT_EQ(partition->name(), "system");
+ EXPECT_EQ(partition->guid(), TEST_GUID);
+ EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(partition->size(), 0);
+ EXPECT_EQ(builder->FindPartition("system"), partition);
+
+ builder->RemovePartition("system");
+ EXPECT_EQ(builder->FindPartition("system"), nullptr);
+}
+
+TEST(liblp, ResizePartition) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(system->size(), 65536);
+ ASSERT_EQ(system->extents().size(), 1);
+
+ LinearExtent* extent = system->extents()[0]->AsLinearExtent();
+ ASSERT_NE(extent, nullptr);
+ EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ // The first logical sector will be (4096+1024*2)/512 = 12.
+ EXPECT_EQ(extent->physical_sector(), 12);
+
+ // Test growing to the same size.
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents().size(), 1);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ // Test growing to a smaller size.
+ EXPECT_EQ(builder->GrowPartition(system, 0), true);
+ EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents().size(), 1);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ // Test shrinking to a greater size.
+ builder->ShrinkPartition(system, 131072);
+ EXPECT_EQ(system->size(), 65536);
+ EXPECT_EQ(system->extents().size(), 1);
+ EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);
+
+ // Test shrinking within the same extent.
+ builder->ShrinkPartition(system, 32768);
+ EXPECT_EQ(system->size(), 32768);
+ EXPECT_EQ(system->extents().size(), 1);
+ extent = system->extents()[0]->AsLinearExtent();
+ ASSERT_NE(extent, nullptr);
+ EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(extent->physical_sector(), 12);
+
+ // Test shrinking to 0.
+ builder->ShrinkPartition(system, 0);
+ EXPECT_EQ(system->size(), 0);
+ EXPECT_EQ(system->extents().size(), 0);
+}
+
+TEST(liblp, PartitionAlignment) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ // Test that we align up to one sector.
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 10000), true);
+ EXPECT_EQ(system->size(), 10240);
+ EXPECT_EQ(system->extents().size(), 1);
+
+ builder->ShrinkPartition(system, 9000);
+ EXPECT_EQ(system->size(), 9216);
+ EXPECT_EQ(system->extents().size(), 1);
+}
+
+TEST(liblp, DiskAlignment) {
+ static const uint64_t kDiskSize = 1000000;
+ static const uint32_t kMetadataSize = 1024;
+ static const uint32_t kMetadataSlots = 2;
+
+ // If the disk size is not aligned to 512 bytes, make sure it still leaves
+ // space at the end for backup metadata, and that it doesn't overlap with
+ // the space for logical partitions.
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ static const size_t kMetadataSpace =
+ (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
+ uint64_t space_at_end =
+ kDiskSize - (exported->geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
+ EXPECT_GE(space_at_end, kMetadataSpace);
+}
+
+TEST(liblp, MetadataAlignment) {
+ // Make sure metadata sizes get aligned up.
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
+}
+
+TEST(liblp, UseAllDiskSpace) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ EXPECT_EQ(builder->AllocatableSpace(), 1036288);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 1036288), true);
+ EXPECT_EQ(system->size(), 1036288);
+ EXPECT_EQ(builder->GrowPartition(system, 1036289), false);
+}
+
+TEST(liblp, BuildComplex) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+ EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+ EXPECT_EQ(system->size(), 98304);
+ EXPECT_EQ(vendor->size(), 32768);
+
+ // We now expect to have 3 extents total: 2 for system, 1 for vendor, since
+ // our allocation strategy is greedy/first-fit.
+ ASSERT_EQ(system->extents().size(), 2);
+ ASSERT_EQ(vendor->extents().size(), 1);
+
+ LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+ LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+ LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+ ASSERT_NE(system1, nullptr);
+ ASSERT_NE(system2, nullptr);
+ ASSERT_NE(vendor1, nullptr);
+ EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system1->physical_sector(), 12);
+ EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system2->physical_sector(), 204);
+ EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(vendor1->physical_sector(), 140);
+ EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());
+ EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
+}
+
+TEST(liblp, AddInvalidPartition) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+
+ // Duplicate name.
+ partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(partition, nullptr);
+
+ // Empty name.
+ partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(partition, nullptr);
+}
+
+TEST(liblp, BuilderExport) {
+ static const uint64_t kDiskSize = 1024 * 1024;
+ static const uint32_t kMetadataSize = 1024;
+ static const uint32_t kMetadataSlots = 2;
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+ EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ EXPECT_NE(exported, nullptr);
+
+ // Verify geometry. Some details of this may change if we change the
+ // metadata structures. So in addition to checking the exact values, we
+ // also check that they are internally consistent after.
+ const LpMetadataGeometry& geometry = exported->geometry;
+ EXPECT_EQ(geometry.magic, LP_METADATA_GEOMETRY_MAGIC);
+ EXPECT_EQ(geometry.struct_size, sizeof(geometry));
+ EXPECT_EQ(geometry.metadata_max_size, 1024);
+ EXPECT_EQ(geometry.metadata_slot_count, 2);
+ EXPECT_EQ(geometry.first_logical_sector, 12);
+ EXPECT_EQ(geometry.last_logical_sector, 2035);
+
+ static const size_t kMetadataSpace =
+ (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE;
+ uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE;
+ EXPECT_GE(space_at_end, kMetadataSpace);
+ EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
+
+ // Verify header.
+ const LpMetadataHeader& header = exported->header;
+ EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
+ EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
+ EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
+
+ ASSERT_EQ(exported->partitions.size(), 2);
+ ASSERT_EQ(exported->extents.size(), 3);
+
+ for (const auto& partition : exported->partitions) {
+ Partition* original = builder->FindPartition(GetPartitionName(partition));
+ ASSERT_NE(original, nullptr);
+ EXPECT_EQ(original->guid(), GetPartitionGuid(partition));
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ const auto& extent = exported->extents[partition.first_extent_index + i];
+ LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();
+ EXPECT_EQ(extent.num_sectors, original_extent->num_sectors());
+ EXPECT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
+ EXPECT_EQ(extent.target_data, original_extent->physical_sector());
+ }
+ EXPECT_EQ(partition.attributes, original->attributes());
+ }
+}
+
+TEST(liblp, BuilderImport) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->GrowPartition(system, 65536), true);
+ EXPECT_EQ(builder->GrowPartition(vendor, 32768), true);
+ EXPECT_EQ(builder->GrowPartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ builder = MetadataBuilder::New(*exported.get());
+ ASSERT_NE(builder, nullptr);
+ system = builder->FindPartition("system");
+ ASSERT_NE(system, nullptr);
+ vendor = builder->FindPartition("vendor");
+ ASSERT_NE(vendor, nullptr);
+
+ EXPECT_EQ(system->size(), 98304);
+ ASSERT_EQ(system->extents().size(), 2);
+ EXPECT_EQ(system->guid(), TEST_GUID);
+ EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);
+ EXPECT_EQ(vendor->size(), 32768);
+ ASSERT_EQ(vendor->extents().size(), 1);
+ EXPECT_EQ(vendor->guid(), TEST_GUID2);
+ EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);
+
+ LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
+ LinearExtent* system2 = system->extents()[1]->AsLinearExtent();
+ LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();
+ EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system1->physical_sector(), 12);
+ EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);
+ EXPECT_EQ(system2->physical_sector(), 204);
+ EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
+}
+
+TEST(liblp, ExportNameTooLong) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
+ Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ EXPECT_NE(system, nullptr);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, ExportInvalidGuid) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+
+ Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY);
+ EXPECT_NE(system, nullptr);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ EXPECT_EQ(exported, nullptr);
+}
+
+TEST(liblp, MetadataTooLarge) {
+ static const size_t kDiskSize = 128 * 1024;
+ static const size_t kMetadataSize = 64 * 1024;
+
+ // No space to store metadata + geometry.
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize, kMetadataSize, 1);
+ EXPECT_EQ(builder, nullptr);
+
+ // No space to store metadata + geometry + one free sector.
+ builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2, kMetadataSize, 1);
+ EXPECT_EQ(builder, nullptr);
+
+ // Space for metadata + geometry + one free sector.
+ builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2 + LP_SECTOR_SIZE,
+ kMetadataSize, 1);
+ EXPECT_NE(builder, nullptr);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
new file mode 100644
index 0000000..671a3bd
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -0,0 +1,172 @@
+//
+// Copyright (C) 2018 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.
+//
+
+#ifndef LIBLP_METADATA_BUILDER_H
+#define LIBLP_METADATA_BUILDER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+class LinearExtent;
+
+// Abstraction around dm-targets that can be encoded into logical partition tables.
+class Extent {
+ public:
+ explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
+ virtual ~Extent() {}
+
+ virtual void AddTo(LpMetadata* out) const = 0;
+ virtual LinearExtent* AsLinearExtent() { return nullptr; }
+
+ uint64_t num_sectors() const { return num_sectors_; }
+ void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
+
+ protected:
+ uint64_t num_sectors_;
+};
+
+// This corresponds to a dm-linear target.
+class LinearExtent final : public Extent {
+ public:
+ LinearExtent(uint64_t num_sectors, uint64_t physical_sector)
+ : Extent(num_sectors), physical_sector_(physical_sector) {}
+
+ void AddTo(LpMetadata* metadata) const override;
+ LinearExtent* AsLinearExtent() override { return this; }
+
+ uint64_t physical_sector() const { return physical_sector_; }
+
+ private:
+ uint64_t physical_sector_;
+};
+
+// This corresponds to a dm-zero target.
+class ZeroExtent final : public Extent {
+ public:
+ explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
+
+ void AddTo(LpMetadata* out) const override;
+};
+
+class Partition final {
+ public:
+ Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+ // Add a raw extent.
+ void AddExtent(std::unique_ptr<Extent>&& extent);
+
+ // Remove all extents from this partition.
+ void RemoveExtents();
+
+ // Remove and/or shrink extents until the partition is the requested size.
+ // See MetadataBuilder::ShrinkPartition for more information.
+ void ShrinkTo(uint64_t requested_size);
+
+ const std::string& name() const { return name_; }
+ uint32_t attributes() const { return attributes_; }
+ const std::string& guid() const { return guid_; }
+ const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
+ uint64_t size() const { return size_; }
+
+ private:
+ std::string name_;
+ std::string guid_;
+ std::vector<std::unique_ptr<Extent>> extents_;
+ uint32_t attributes_;
+ uint64_t size_;
+};
+
+class MetadataBuilder {
+ public:
+ // Construct an empty logical partition table builder. The block device size
+ // and maximum metadata size must be specified, as this will determine which
+ // areas of the physical partition can be flashed for metadata vs for logical
+ // partitions.
+ //
+ // If the parameters would yield invalid metadata, nullptr is returned. This
+ // could happen if the block device size is too small to store the metadata
+ // and backup copies.
+ static std::unique_ptr<MetadataBuilder> New(uint64_t blockdevice_size,
+ uint32_t metadata_max_size,
+ uint32_t metadata_slot_count);
+
+ // Import an existing table for modification. If the table is not valid, for
+ // example it contains duplicate partition names, then nullptr is returned.
+ static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+
+ // Export metadata so it can be serialized to an image, to disk, or mounted
+ // via device-mapper.
+ std::unique_ptr<LpMetadata> Export();
+
+ // Add a partition, returning a handle so it can be sized as needed. If a
+ // partition with the given name already exists, nullptr is returned.
+ Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+
+ // Delete a partition by name if it exists.
+ void RemovePartition(const std::string& name);
+
+ // Find a partition by name. If no partition is found, nullptr is returned.
+ Partition* FindPartition(const std::string& name);
+
+ // Grow a partition to the requested size. If the partition's size is already
+ // greater or equal to the requested size, this will return true and the
+ // partition table will not be changed. Otherwise, a greedy algorithm is
+ // used to find free gaps in the partition table and allocate them for this
+ // partition. If not enough space can be allocated, false is returned, and
+ // the parition table will not be modified.
+ //
+ // The size will be rounded UP to the nearest sector.
+ //
+ // Note, this is an in-memory operation, and it does not alter the
+ // underlying filesystem or contents of the partition on disk.
+ bool GrowPartition(Partition* partition, uint64_t requested_size);
+
+ // Shrink a partition to the requested size. If the partition is already
+ // smaller than the given size, this will return and the partition table
+ // will not be changed. Otherwise, extents will be removed and/or shrunk
+ // from the end of the partition until it is the requested size.
+ //
+ // The size will be rounded UP to the nearest sector.
+ //
+ // Note, this is an in-memory operation, and it does not alter the
+ // underlying filesystem or contents of the partition on disk.
+ void ShrinkPartition(Partition* partition, uint64_t requested_size);
+
+ // Amount of space that can be allocated to logical partitions.
+ uint64_t AllocatableSpace() const;
+
+ private:
+ MetadataBuilder();
+ bool Init(uint64_t blockdevice_size, uint32_t metadata_max_size, uint32_t metadata_slot_count);
+ bool Init(const LpMetadata& metadata);
+
+ LpMetadataGeometry geometry_;
+ LpMetadataHeader header_;
+ std::vector<std::unique_ptr<Partition>> partitions_;
+};
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif /* LIBLP_METADATA_BUILDER_H */
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
new file mode 100644
index 0000000..8522435
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef LOGICAL_PARTITION_METADATA_FORMAT_H_
+#define LOGICAL_PARTITION_METADATA_FORMAT_H_
+
+#ifdef __cplusplus
+#include <string>
+#include <vector>
+#endif
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic signature for LpMetadataGeometry. */
+#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
+
+/* Space reserved for geometry information. */
+#define LP_METADATA_GEOMETRY_SIZE 4096
+
+/* Magic signature for LpMetadataHeader. */
+#define LP_METADATA_HEADER_MAGIC 0x414C5030
+
+/* Current metadata version. */
+#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MINOR_VERSION 0
+
+/* Attributes for the LpMetadataPartition::attributes field.
+ *
+ * READONLY - The partition should not be considered writable. When used with
+ * device mapper, the block device will be created as read-only.
+ */
+#define LP_PARTITION_ATTR_NONE 0x0
+#define LP_PARTITION_ATTR_READONLY 0x1
+
+/* Mask that defines all valid attributes. */
+#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY)
+
+/* Default name of the physical partition that holds logical partition entries.
+ * The layout of this partition will look like:
+ *
+ * +--------------------+
+ * | Disk Geometry |
+ * +--------------------+
+ * | Metadata |
+ * +--------------------+
+ * | Logical Partitions |
+ * +--------------------+
+ * | Backup Metadata |
+ * +--------------------+
+ * | Geometry Backup |
+ * +--------------------+
+ */
+#define LP_METADATA_PARTITION_NAME "android"
+
+/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
+#define LP_SECTOR_SIZE 512
+
+/* This structure is stored at sector 0 in the first 4096 bytes of the
+ * partition, and again in the very last 4096 bytes. It is never modified and
+ * describes how logical partition information can be located.
+ */
+typedef struct LpMetadataGeometry {
+ /* 0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
+ uint32_t magic;
+
+ /* 4: Size of the LpMetadataGeometry struct. */
+ uint32_t struct_size;
+
+ /* 8: SHA256 checksum of this struct, with this field set to 0. */
+ uint8_t checksum[32];
+
+ /* 40: Maximum amount of space a single copy of the metadata can use. */
+ uint32_t metadata_max_size;
+
+ /* 44: Number of copies of the metadata to keep. For A/B devices, this
+ * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
+ * it will be 1. A backup copy of each slot is kept, so if this is "2",
+ * there will be four copies total.
+ */
+ uint32_t metadata_slot_count;
+
+ /* 48: First usable sector for allocating logical partitions. this will be
+ * the first sector after the initial 4096 geometry block, followed by the
+ * space consumed by metadata_max_size*metadata_slot_count.
+ */
+ uint64_t first_logical_sector;
+
+ /* 56: Last usable sector, inclusive, for allocating logical partitions.
+ * At the end of this sector will follow backup metadata slots and the
+ * backup geometry block at the very end.
+ */
+ uint64_t last_logical_sector;
+} __attribute__((packed)) LpMetadataGeometry;
+
+/* The logical partition metadata has a number of tables; they are described
+ * in the header via the following structure.
+ *
+ * The size of the table can be computed by multiplying entry_size by
+ * num_entries, and the result must not overflow a 32-bit signed integer.
+ */
+typedef struct LpMetadataTableDescriptor {
+ /* 0: Location of the table, relative to the metadata header. */
+ uint32_t offset;
+ /* 4: Number of entries in the table. */
+ uint32_t num_entries;
+ /* 8: Size of each entry in the table, in bytes. */
+ uint32_t entry_size;
+} __attribute__((packed)) LpMetadataTableDescriptor;
+
+/* Binary format for the header of the logical partition metadata format.
+ *
+ * The format has three sections. The header must occur first, and the
+ * proceeding tables may be placed in any order after.
+ *
+ * +-----------------------------------------+
+ * | Header data - fixed size |
+ * +-----------------------------------------+
+ * | Partition table - variable size |
+ * +-----------------------------------------+
+ * | Partition table extents - variable size |
+ * +-----------------------------------------+
+ *
+ * The "Header" portion is described by LpMetadataHeader. It will always
+ * precede the other three blocks.
+ *
+ * All fields are stored in little-endian byte order when serialized.
+ *
+ * This struct is versioned; see the |major_version| and |minor_version|
+ * fields.
+ */
+typedef struct LpMetadataHeader {
+ /* 0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
+ uint32_t magic;
+
+ /* 4: Version number required to read this metadata. If the version is not
+ * equal to the library version, the metadata should be considered
+ * incompatible.
+ */
+ uint16_t major_version;
+
+ /* 6: Minor version. A library supporting newer features should be able to
+ * read metadata with an older minor version. However, an older library
+ * should not support reading metadata if its minor version is higher.
+ */
+ uint16_t minor_version;
+
+ /* 8: The size of this header struct. */
+ uint32_t header_size;
+
+ /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
+ * if this field were set to 0.
+ */
+ uint8_t header_checksum[32];
+
+ /* 44: The total size of all tables. This size is contiguous; tables may not
+ * have gaps in between, and they immediately follow the header.
+ */
+ uint32_t tables_size;
+
+ /* 48: SHA256 checksum of all table contents. */
+ uint8_t tables_checksum[32];
+
+ /* 80: Partition table descriptor. */
+ LpMetadataTableDescriptor partitions;
+ /* 92: Extent table descriptor. */
+ LpMetadataTableDescriptor extents;
+} __attribute__((packed)) LpMetadataHeader;
+
+/* This struct defines a logical partition entry, similar to what would be
+ * present in a GUID Partition Table.
+ */
+typedef struct LpMetadataPartition {
+ /* 0: Name of this partition in ASCII characters. Any unused characters in
+ * the buffer must be set to 0. Characters may only be alphanumeric or _.
+ * The name must include at least one ASCII character, and it must be unique
+ * across all partition names. The length (36) is the same as the maximum
+ * length of a GPT partition name.
+ */
+ char name[36];
+
+ /* 36: Globally unique identifier (GUID) of this partition. */
+ uint8_t guid[16];
+
+ /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+ uint32_t attributes;
+
+ /* 56: Index of the first extent owned by this partition. The extent will
+ * start at logical sector 0. Gaps between extents are not allowed.
+ */
+ uint32_t first_extent_index;
+
+ /* 60: Number of extents in the partition. Every partition must have at
+ * least one extent.
+ */
+ uint32_t num_extents;
+} __attribute__((packed)) LpMetadataPartition;
+
+/* This extent is a dm-linear target, and the index is an index into the
+ * LinearExtent table.
+ */
+#define LP_TARGET_TYPE_LINEAR 0
+
+/* This extent is a dm-zero target. The index is ignored and must be 0. */
+#define LP_TARGET_TYPE_ZERO 1
+
+/* This struct defines an extent entry in the extent table block. */
+typedef struct LpMetadataExtent {
+ /* 0: Length of this extent, in 512-byte sectors. */
+ uint64_t num_sectors;
+
+ /* 8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
+ uint32_t target_type;
+
+ /* 12: Contents depends on target_type.
+ *
+ * LINEAR: The sector on the physical partition that this extent maps onto.
+ * ZERO: This field must be 0.
+ */
+ uint64_t target_data;
+} __attribute__((packed)) LpMetadataExtent;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#ifdef __cplusplus
+namespace android {
+namespace fs_mgr {
+
+// Helper structure for easily interpreting deserialized metadata, or
+// re-serializing metadata.
+struct LpMetadata {
+ LpMetadataGeometry geometry;
+ LpMetadataHeader header;
+ std::vector<LpMetadataPartition> partitions;
+ std::vector<LpMetadataExtent> extents;
+};
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGuid(const LpMetadataPartition& partition);
+
+// Helper to return a slot number for a slot suffix.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+
+} // namespace fs_mgr
+} // namespace android
+#endif
+
+#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/fs_mgr/liblp/include/liblp/reader.h b/fs_mgr/liblp/include/liblp/reader.h
new file mode 100644
index 0000000..982fe65
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/reader.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef LIBLP_READER_H_
+#define LIBLP_READER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Read logical partition metadata from its predetermined location on a block
+// device. If readback fails, we also attempt to load from a backup copy.
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+
+// Read and validate the logical partition geometry from a block device.
+bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry);
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+
+// Read logical partition metadata from an image file that was created with
+// WriteToImageFile().
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif /* LIBLP_READER_H_ */
diff --git a/fs_mgr/liblp/include/liblp/writer.h b/fs_mgr/liblp/include/liblp/writer.h
new file mode 100644
index 0000000..efa409d
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/writer.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef LIBLP_WRITER_H
+#define LIBLP_WRITER_H
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// When flashing the initial logical partition layout, we also write geometry
+// information at the start and end of the big physical partition. This helps
+// locate metadata and backup metadata in the case of corruption or a failed
+// update. For normal changes to the metadata, we never modify the geometry.
+enum class SyncMode {
+ // Write geometry information.
+ Flash,
+ // Normal update of a single slot.
+ Update
+};
+
+// Write the given partition table to the given block device, writing only
+// copies according to the given sync mode.
+//
+// This will perform some verification, such that the device has enough space
+// to store the metadata as well as all of its extents.
+//
+// The slot number indicates which metadata slot to use.
+bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number);
+bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number);
+
+// Helper function to serialize geometry and metadata to a normal file, for
+// flashing or debugging.
+bool WriteToImageFile(const char* file, const LpMetadata& metadata);
+bool WriteToImageFile(int fd, const LpMetadata& metadata);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif /* LIBLP_WRITER_H */
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
new file mode 100644
index 0000000..2595654
--- /dev/null
+++ b/fs_mgr/liblp/io_test.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <liblp/reader.h>
+#include <liblp/writer.h>
+
+#include "utility.h"
+
+using namespace std;
+using namespace android::fs_mgr;
+using unique_fd = android::base::unique_fd;
+
+// Our tests assume a 128KiB disk with two 512 byte metadata slots.
+static const size_t kDiskSize = 131072;
+static const size_t kMetadataSize = 512;
+static const size_t kMetadataSlots = 2;
+static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
+static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
+
+// Helper function for creating an in-memory file descriptor. This lets us
+// simulate read/writing logical partition metadata as if we had a block device
+// for a physical partition.
+static unique_fd CreateFakeDisk(off_t size) {
+ unique_fd fd(syscall(__NR_memfd_create, "fake_disk", MFD_ALLOW_SEALING));
+ if (fd < 0) {
+ perror("memfd_create");
+ return {};
+ }
+ if (ftruncate(fd, size) < 0) {
+ perror("ftruncate");
+ return {};
+ }
+ // Prevent anything from accidentally growing/shrinking the file, as it
+ // would not be allowed on an actual partition.
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+ perror("fcntl");
+ return {};
+ }
+ // Write garbage to the "disk" so we can tell what has been zeroed or not.
+ unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(size);
+ memset(buffer.get(), 0xcc, size);
+ if (!android::base::WriteFully(fd, buffer.get(), size)) {
+ return {};
+ }
+ return fd;
+}
+
+// Create a disk of the default size.
+static unique_fd CreateFakeDisk() {
+ return CreateFakeDisk(kDiskSize);
+}
+
+// Create a MetadataBuilder around some default sizes.
+static unique_ptr<MetadataBuilder> CreateDefaultBuilder() {
+ unique_ptr<MetadataBuilder> builder =
+ MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
+ return builder;
+}
+
+static bool AddDefaultPartitions(MetadataBuilder* builder) {
+ Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ if (!system) {
+ return false;
+ }
+ return builder->GrowPartition(system, 24 * 1024);
+}
+
+// Create a temporary disk and flash it with the default partition setup.
+static unique_fd CreateFlashedDisk() {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ if (!builder || !AddDefaultPartitions(builder.get())) {
+ return {};
+ }
+ unique_fd fd = CreateFakeDisk();
+ if (fd < 0) {
+ return {};
+ }
+ // Export and flash.
+ unique_ptr<LpMetadata> exported = builder->Export();
+ if (!exported) {
+ return {};
+ }
+ if (!WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0)) {
+ return {};
+ }
+ return fd;
+}
+
+// Test that our CreateFakeDisk() function works.
+TEST(liblp, CreateFakeDisk) {
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ uint64_t size;
+ ASSERT_TRUE(GetDescriptorSize(fd, &size));
+ ASSERT_EQ(size, kDiskSize);
+}
+
+// Flashing metadata should not work if the metadata was created for a larger
+// disk than the destination disk.
+TEST(liblp, ExportDiskTooSmall) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
+ ASSERT_NE(builder, nullptr);
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // A larger geometry should fail to flash, since there won't be enough
+ // space to store the logical partition range that was specified.
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ EXPECT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+}
+
+// Test the basics of flashing a partition and reading it back.
+TEST(liblp, FlashAndReadback) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Export and flash.
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+
+ // Read back. Note that some fields are only filled in during
+ // serialization, so exported and imported will not be identical. For
+ // example, table sizes and checksums are computed in WritePartitionTable.
+ // Therefore we check on a field-by-field basis.
+ unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+
+ // Check geometry and header.
+ EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
+ EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
+ EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
+ EXPECT_EQ(exported->geometry.last_logical_sector, imported->geometry.last_logical_sector);
+ EXPECT_EQ(exported->header.major_version, imported->header.major_version);
+ EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
+ EXPECT_EQ(exported->header.header_size, imported->header.header_size);
+
+ // Check partition tables.
+ ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
+ EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
+ EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
+ EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
+ EXPECT_EQ(exported->partitions[0].first_extent_index,
+ imported->partitions[0].first_extent_index);
+ EXPECT_EQ(exported->partitions[0].num_extents, imported->partitions[0].num_extents);
+
+ // Check extent tables.
+ ASSERT_EQ(exported->extents.size(), imported->extents.size());
+ EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
+ EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
+ EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
+}
+
+// Test that we can update metadata slots without disturbing others.
+TEST(liblp, UpdateAnyMetadataSlot) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_EQ(imported->partitions.size(), 1);
+ EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+ // Verify that we can't read unwritten metadata.
+ ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
+
+ // Change the name before writing to the next slot.
+ strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
+ ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ // Read back the original slot, make sure it hasn't changed.
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_EQ(imported->partitions.size(), 1);
+ EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
+
+ // Now read back the new slot, and verify that it has a different name.
+ imported = ReadMetadata(fd, 1);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_EQ(imported->partitions.size(), 1);
+ EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
+
+ // Verify that we didn't overwrite anything in the logical paritition area.
+ // We expect the disk to be filled with 0xcc on creation so we can read
+ // this back and compare it.
+ char expected[LP_SECTOR_SIZE];
+ memset(expected, 0xcc, sizeof(expected));
+ for (uint64_t i = imported->geometry.first_logical_sector;
+ i <= imported->geometry.last_logical_sector; i++) {
+ char buffer[LP_SECTOR_SIZE];
+ ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+ ASSERT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+ }
+}
+
+TEST(liblp, InvalidMetadataSlot) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ // Make sure all slots are filled.
+ unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ ASSERT_NE(metadata, nullptr);
+ for (uint32_t i = 1; i < kMetadataSlots; i++) {
+ ASSERT_TRUE(WritePartitionTable(fd, *metadata.get(), SyncMode::Update, i));
+ }
+
+ // Verify that we can't read unavailable slots.
+ EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
+}
+
+// Test that updating a metadata slot does not allow it to be computed based
+// on mismatching geometry.
+TEST(liblp, NoChangingGeometry) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ imported->geometry.metadata_slot_count++;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ imported->geometry.first_logical_sector++;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+
+ imported = ReadMetadata(fd, 0);
+ ASSERT_NE(imported, nullptr);
+ imported->geometry.last_logical_sector--;
+ ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+}
+
+// Test that changing one bit of metadata is enough to break the checksum.
+TEST(liblp, BitFlipGeometry) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ LpMetadataGeometry geometry;
+ ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
+
+ LpMetadataGeometry bad_geometry = geometry;
+ bad_geometry.metadata_slot_count++;
+ ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
+
+ unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ ASSERT_NE(metadata, nullptr);
+ EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
+}
+
+TEST(liblp, ReadBackupGeometry) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ char corruption[LP_METADATA_GEOMETRY_SIZE];
+ memset(corruption, 0xff, sizeof(corruption));
+
+ // Corrupt the first 4096 bytes of the disk.
+ ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+ // Corrupt the last 4096 bytes too.
+ ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+TEST(liblp, ReadBackupMetadata) {
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+
+ char corruption[kMetadataSize];
+ memset(corruption, 0xff, sizeof(corruption));
+
+ ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_NE(ReadMetadata(fd, 0), nullptr);
+
+ off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2;
+
+ // Corrupt the backup metadata.
+ ASSERT_GE(lseek(fd, -offset, SEEK_END), 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
+ EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
+}
+
+// Test that we don't attempt to write metadata if it would overflow its
+// reserved space.
+TEST(liblp, TooManyPartitions) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+
+ // Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
+ size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
+ EXPECT_LT(max_partitions, 10);
+
+ // Add this number of partitions.
+ Partition* partition = nullptr;
+ for (size_t i = 0; i < max_partitions; i++) {
+ std::string guid = std::string(TEST_GUID) + to_string(i);
+ partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
+ ASSERT_NE(partition, nullptr);
+ }
+ ASSERT_NE(partition, nullptr);
+ // Add one extent to any partition to fill up more space - we're at 508
+ // bytes after this, out of 512.
+ ASSERT_TRUE(builder->GrowPartition(partition, 1024));
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ unique_fd fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Check that we are able to write our table.
+ ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+ ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+
+ // Check that adding one more partition overflows the metadata allotment.
+ partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ EXPECT_NE(partition, nullptr);
+
+ exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // The new table should be too large to be written.
+ ASSERT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+
+ // Check that the first and last logical sectors weren't touched when we
+ // wrote this almost-full metadata.
+ char expected[LP_SECTOR_SIZE];
+ memset(expected, 0xcc, sizeof(expected));
+ char buffer[LP_SECTOR_SIZE];
+ ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+ EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+ ASSERT_GE(lseek(fd, exported->geometry.last_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
+ ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
+ EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
+}
+
+// Test that we can read and write image files.
+TEST(liblp, ImageFiles) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+ unique_ptr<LpMetadata> exported = builder->Export();
+
+ unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
+ ASSERT_GE(fd, 0);
+ ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));
+
+ unique_ptr<LpMetadata> imported = ReadFromImageFile(fd);
+ ASSERT_NE(imported, nullptr);
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
new file mode 100644
index 0000000..7938186
--- /dev/null
+++ b/fs_mgr/liblp/reader.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "liblp/reader.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
+// LP_METADATA_GEOMETRY_SIZE bytes in size.
+static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
+ static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
+ memcpy(geometry, buffer, sizeof(*geometry));
+
+ // Check the magic signature.
+ if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
+ LERROR << "Logical partition metadata has invalid geometry magic signature.";
+ return false;
+ }
+ // Recompute and check the CRC32.
+ {
+ LpMetadataGeometry temp = *geometry;
+ memset(&temp.checksum, 0, sizeof(temp.checksum));
+ SHA256(&temp, sizeof(temp), temp.checksum);
+ if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
+ LERROR << "Logical partition metadata has invalid geometry checksum.";
+ return false;
+ }
+ }
+ // Check that the struct size is equal (this will have to change if we ever
+ // change the struct size in a release).
+ if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
+ LERROR << "Logical partition metadata has invalid struct size.";
+ return false;
+ }
+ if (geometry->metadata_slot_count == 0) {
+ LERROR << "Logical partition metadata has invalid slot count.";
+ return false;
+ }
+
+ // Check that the metadata area and logical partition areas don't overlap.
+ int64_t end_of_metadata =
+ GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
+ geometry->metadata_max_size;
+ if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << "Logical partition metadata overlaps with logical partition contents.";
+ return false;
+ }
+ return true;
+}
+
+// Read and validate geometry information from a block device that holds
+// logical partitions. If the information is corrupted, this will attempt
+// to read it from a secondary backup location.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+ // Read the first 4096 bytes.
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+ if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+ return false;
+ }
+ if (ParseGeometry(buffer.get(), geometry)) {
+ return true;
+ }
+
+ // Try the backup copy in the last 4096 bytes.
+ if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
+ return false;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << "backup read " << LP_METADATA_GEOMETRY_SIZE
+ << " bytes failed";
+ return false;
+ }
+ return ParseGeometry(buffer.get(), geometry);
+}
+
+// Helper function to read geometry from a device without an open descriptor.
+bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry) {
+ android::base::unique_fd fd(open(block_device, O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+ return false;
+ }
+ return ReadLogicalPartitionGeometry(fd, geometry);
+}
+
+static bool ValidateTableBounds(const LpMetadataHeader& header,
+ const LpMetadataTableDescriptor& table) {
+ if (table.offset > header.tables_size) {
+ return false;
+ }
+ uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;
+ if (header.tables_size - table.offset < table_size) {
+ return false;
+ }
+ return true;
+}
+
+static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
+ // To compute the header's checksum, we have to temporarily set its checksum
+ // field to 0.
+ {
+ LpMetadataHeader temp = header;
+ memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
+ SHA256(&temp, sizeof(temp), temp.header_checksum);
+ if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != 0) {
+ LERROR << "Logical partition metadata has invalid checksum.";
+ return false;
+ }
+ }
+
+ // Do basic validation of key metadata bits.
+ if (header.magic != LP_METADATA_HEADER_MAGIC) {
+ LERROR << "Logical partition metadata has invalid magic value.";
+ return false;
+ }
+ // Check that the version is compatible.
+ if (header.major_version != LP_METADATA_MAJOR_VERSION ||
+ header.minor_version > LP_METADATA_MINOR_VERSION) {
+ LERROR << "Logical partition metadata has incompatible version.";
+ return false;
+ }
+ if (!ValidateTableBounds(header, header.partitions) ||
+ !ValidateTableBounds(header, header.extents)) {
+ LERROR << "Logical partition metadata has invalid table bounds.";
+ return false;
+ }
+ // Check that table entry sizes can accomodate their respective structs. If
+ // table sizes change, these checks will have to be adjusted.
+ if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {
+ LERROR << "Logical partition metadata has invalid partition table entry size.";
+ return false;
+ }
+ if (header.extents.entry_size != sizeof(LpMetadataExtent)) {
+ LERROR << "Logical partition metadata has invalid extent table entry size.";
+ return false;
+ }
+ return true;
+}
+
+using ReadMetadataFn = std::function<bool(void* buffer, size_t num_bytes)>;
+
+// Parse and validate all metadata at the current position in the given file
+// descriptor.
+static std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
+ // First read and validate the header.
+ std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+ if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
+ PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
+ return nullptr;
+ }
+ if (!ValidateMetadataHeader(metadata->header)) {
+ return nullptr;
+ }
+
+ LpMetadataHeader& header = metadata->header;
+
+ // Read the metadata payload. Allocation is fallible in case the metadata is
+ // corrupt and has some huge value.
+ std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
+ if (!buffer) {
+ LERROR << "Out of memory reading logical partition tables.";
+ return nullptr;
+ }
+ if (!android::base::ReadFully(fd, buffer.get(), header.tables_size)) {
+ PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
+ return nullptr;
+ }
+
+ uint8_t checksum[32];
+ SHA256(buffer.get(), header.tables_size, checksum);
+ if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {
+ LERROR << "Logical partition metadata has invalid table checksum.";
+ return nullptr;
+ }
+
+ // ValidateTableSize ensured that |cursor| is valid for the number of
+ // entries in the table.
+ uint8_t* cursor = buffer.get() + header.partitions.offset;
+ for (size_t i = 0; i < header.partitions.num_entries; i++) {
+ LpMetadataPartition partition;
+ memcpy(&partition, cursor, sizeof(partition));
+ cursor += header.partitions.entry_size;
+
+ if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
+ LERROR << "Logical partition has invalid attribute set.";
+ return nullptr;
+ }
+ if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
+ LERROR << "Logical partition has invalid extent list.";
+ return nullptr;
+ }
+
+ metadata->partitions.push_back(partition);
+ }
+
+ cursor = buffer.get() + header.extents.offset;
+ for (size_t i = 0; i < header.extents.num_entries; i++) {
+ LpMetadataExtent extent;
+ memcpy(&extent, cursor, sizeof(extent));
+ cursor += header.extents.entry_size;
+
+ metadata->extents.push_back(extent);
+ }
+
+ return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+ LpMetadataGeometry geometry;
+ if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+ return nullptr;
+ }
+
+ if (slot_number >= geometry.metadata_slot_count) {
+ LERROR << __PRETTY_FUNCTION__ << "invalid metadata slot number";
+ return nullptr;
+ }
+
+ // First try the primary copy.
+ int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
+ if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+ return nullptr;
+ }
+ std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+
+ // If the primary copy failed, try the backup copy.
+ if (!metadata) {
+ offset = GetBackupMetadataOffset(geometry, slot_number);
+ if (SeekFile64(fd, offset, SEEK_END) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
+ return nullptr;
+ }
+ metadata = ParseMetadata(fd);
+ }
+
+ if (metadata) {
+ metadata->geometry = geometry;
+ }
+ return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
+ android::base::unique_fd fd(open(block_device, O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+ return nullptr;
+ }
+ return ReadMetadata(fd, slot_number);
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
+ LpMetadataGeometry geometry;
+ if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+ return nullptr;
+ }
+ if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
+ return nullptr;
+ }
+ std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
+ if (!metadata) {
+ return nullptr;
+ }
+ metadata->geometry = geometry;
+ return metadata;
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
+ android::base::unique_fd fd(open(file, O_RDONLY));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+ return nullptr;
+ }
+ return ReadFromImageFile(fd);
+}
+
+static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
+ // If the end of the buffer has a null character, it's safe to assume the
+ // buffer is null terminated. Otherwise, we cap the string to the input
+ // buffer size.
+ if (name[buffer_size - 1] == '\0') {
+ return std::string(name);
+ }
+ return std::string(name, buffer_size);
+}
+
+std::string GetPartitionName(const LpMetadataPartition& partition) {
+ return NameFromFixedArray(partition.name, sizeof(partition.name));
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
new file mode 100644
index 0000000..5310cab
--- /dev/null
+++ b/fs_mgr/liblp/utility.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <openssl/sha.h>
+#include <uuid/uuid.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+bool GetDescriptorSize(int fd, uint64_t* size) {
+ struct stat s;
+ if (fstat(fd, &s) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "fstat failed";
+ return false;
+ }
+
+ if (S_ISBLK(s.st_mode)) {
+ return get_block_device_size(fd);
+ }
+
+ int64_t result = SeekFile64(fd, 0, SEEK_END);
+ if (result == -1) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed";
+ return false;
+ }
+
+ *size = result;
+ return true;
+}
+
+int64_t SeekFile64(int fd, int64_t offset, int whence) {
+ static_assert(sizeof(off_t) == sizeof(int64_t), "Need 64-bit lseek");
+ return lseek(fd, offset, whence);
+}
+
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+ CHECK(slot_number < geometry.metadata_slot_count);
+
+ int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number;
+ CHECK(offset + geometry.metadata_max_size <=
+ int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
+ return offset;
+}
+
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+ CHECK(slot_number < geometry.metadata_slot_count);
+ int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) -
+ int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+ return start + int64_t(geometry.metadata_max_size * slot_number);
+}
+
+void SHA256(const void* data, size_t length, uint8_t out[32]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+std::string GetPartitionGuid(const LpMetadataPartition& partition) {
+ // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
+ // macro to assist with buffer sizing.
+ static const size_t kGuidLen = 36;
+ char buffer[kGuidLen + 1];
+ uuid_unparse_upper(partition.guid, buffer);
+ return buffer;
+}
+
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
+ if (suffix.empty()) {
+ return 0;
+ }
+ if (suffix.size() != 2 || suffix[0] != '_' || suffix[1] < 'a') {
+ LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
+ << "' does not have a recognized format.";
+ return 0;
+ }
+ return suffix[1] - 'a';
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
new file mode 100644
index 0000000..09ed314
--- /dev/null
+++ b/fs_mgr/liblp/utility.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef LIBLP_UTILITY_H
+#define LIBLP_UTILITY_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+
+#include "liblp/metadata_format.h"
+
+#define LP_TAG "[liblp]"
+#define LERROR LOG(ERROR) << LP_TAG
+#define PERROR PLOG(ERROR) << LP_TAG
+
+namespace android {
+namespace fs_mgr {
+
+// Determine the size of a block device (or file). Logs and returns false on
+// error. After calling this, the position of |fd| may have changed.
+bool GetDescriptorSize(int fd, uint64_t* size);
+
+// Return the offset of a primary metadata slot, relative to the start of the
+// device.
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Return the offset of a backup metadata slot, relative to the end of the
+// device.
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Cross-platform helper for lseek64().
+int64_t SeekFile64(int fd, int64_t offset, int whence);
+
+// Compute a SHA256 hash.
+void SHA256(const void* data, size_t length, uint8_t out[32]);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif // LIBLP_UTILITY_H
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
new file mode 100644
index 0000000..25e8a25
--- /dev/null
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utility.h"
+#include <gtest/gtest.h>
+
+using namespace android;
+using namespace android::fs_mgr;
+
+TEST(liblp, SlotNumberForSlotSuffix) {
+ EXPECT_EQ(SlotNumberForSlotSuffix(""), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_a"), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 2);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 3);
+}
+
+TEST(liblp, GetMetadataOffset) {
+ LpMetadataGeometry geometry = {
+ LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000};
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
+ EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3);
+
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
+ EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
new file mode 100644
index 0000000..89cbabd
--- /dev/null
+++ b/fs_mgr/liblp/writer.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+
+#include "liblp/reader.h"
+#include "liblp/writer.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+static std::string SerializeGeometry(const LpMetadataGeometry& input) {
+ LpMetadataGeometry geometry = input;
+ memset(geometry.checksum, 0, sizeof(geometry.checksum));
+ SHA256(&geometry, sizeof(geometry), geometry.checksum);
+ return std::string(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+}
+
+static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
+ return g1.metadata_max_size == g2.metadata_max_size &&
+ g1.metadata_slot_count == g2.metadata_slot_count &&
+ g1.first_logical_sector == g2.first_logical_sector &&
+ g1.last_logical_sector == g2.last_logical_sector;
+}
+
+static std::string SerializeMetadata(const LpMetadata& input) {
+ LpMetadata metadata = input;
+ LpMetadataHeader& header = metadata.header;
+
+ // Serialize individual tables.
+ std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),
+ metadata.partitions.size() * sizeof(LpMetadataPartition));
+ std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
+ metadata.extents.size() * sizeof(LpMetadataExtent));
+
+ // Compute positions of tables.
+ header.partitions.offset = 0;
+ header.extents.offset = header.partitions.offset + partitions.size();
+ header.tables_size = header.extents.offset + extents.size();
+
+ // Compute payload checksum.
+ std::string tables = partitions + extents;
+ SHA256(tables.data(), tables.size(), header.tables_checksum);
+
+ // Compute header checksum.
+ memset(header.header_checksum, 0, sizeof(header.header_checksum));
+ SHA256(&header, sizeof(header), header.header_checksum);
+
+ std::string header_blob =
+ std::string(reinterpret_cast<const char*>(&metadata.header), sizeof(metadata.header));
+ return header_blob + tables;
+}
+
+// Perform sanity checks so we don't accidentally overwrite valid metadata
+// with potentially invalid metadata, or random partition data with metadata.
+static bool ValidateGeometryAndMetadata(const LpMetadata& metadata, uint64_t blockdevice_size,
+ uint64_t metadata_size) {
+ const LpMetadataHeader& header = metadata.header;
+ const LpMetadataGeometry& geometry = metadata.geometry;
+ // Validate the usable sector range.
+ if (geometry.first_logical_sector > geometry.last_logical_sector) {
+ LERROR << "Logical partition metadata has invalid sector range.";
+ return false;
+ }
+ // Make sure we're writing within the space reserved.
+ if (metadata_size > geometry.metadata_max_size) {
+ LERROR << "Logical partition metadata is too large.";
+ return false;
+ }
+
+ // Make sure the device has enough space to store two backup copies of the
+ // metadata.
+ uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
+ uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+ if (reserved_size > blockdevice_size ||
+ reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) {
+ LERROR << "Not enough space to store all logical partition metadata slots.";
+ return false;
+ }
+ if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) {
+ LERROR << "Not enough space to backup all logical partition metadata slots.";
+ return false;
+ }
+
+ // Make sure all partition entries reference valid extents.
+ for (const auto& partition : metadata.partitions) {
+ if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {
+ LERROR << "Partition references invalid extent.";
+ return false;
+ }
+ }
+
+ // Make sure all linear extents have a valid range.
+ for (const auto& extent : metadata.extents) {
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+ uint64_t physical_sector = extent.target_data;
+ if (physical_sector < geometry.first_logical_sector ||
+ physical_sector + extent.num_sectors > geometry.last_logical_sector) {
+ LERROR << "Extent table entry is out of bounds.";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number) {
+ uint64_t size;
+ if (!GetDescriptorSize(fd, &size)) {
+ return false;
+ }
+
+ const LpMetadataGeometry& geometry = metadata.geometry;
+ if (sync_mode != SyncMode::Flash) {
+ // Verify that the old geometry is identical. If it's not, then we've
+ // based this new metadata on invalid assumptions.
+ LpMetadataGeometry old_geometry;
+ if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
+ return false;
+ }
+ if (!CompareGeometry(geometry, old_geometry)) {
+ LERROR << "Incompatible geometry in new logical partition metadata";
+ return false;
+ }
+ }
+
+ // Make sure we're writing to a valid metadata slot.
+ if (slot_number >= geometry.metadata_slot_count) {
+ LERROR << "Invalid logical partition metadata slot number.";
+ return false;
+ }
+
+ // Before writing geometry and/or logical partition tables, perform some
+ // basic checks that the geometry and tables are coherent, and will fit
+ // on the given block device.
+ std::string blob = SerializeMetadata(metadata);
+ if (!ValidateGeometryAndMetadata(metadata, size, blob.size())) {
+ return false;
+ }
+
+ // First write geometry if this is a flash operation. It gets written to
+ // the first and last 4096-byte regions of the device.
+ if (sync_mode == SyncMode::Flash) {
+ std::string blob = SerializeGeometry(metadata.geometry);
+ if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+ return false;
+ }
+ if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+ return false;
+ }
+ }
+
+ // Write the primary copy of the metadata.
+ int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
+ if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset;
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+ return false;
+ }
+
+ // Write the backup copy of the metadata.
+ int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
+ int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END);
+ if (abs_offset == (int64_t)-1) {
+ PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset;
+ return false;
+ }
+ if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) {
+ PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset
+ << " is within logical partition bounds, sector " << geometry.last_logical_sector;
+ return false;
+ }
+ if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+ return false;
+ }
+ return true;
+}
+
+bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+ uint32_t slot_number) {
+ android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+ return false;
+ }
+ return WritePartitionTable(fd, metadata, sync_mode, slot_number);
+}
+
+bool WriteToImageFile(int fd, const LpMetadata& input) {
+ std::string geometry = SerializeGeometry(input.geometry);
+ std::string padding(LP_METADATA_GEOMETRY_SIZE - geometry.size(), '\0');
+ std::string metadata = SerializeMetadata(input);
+
+ std::string everything = geometry + padding + metadata;
+
+ if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
+ PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
+ return false;
+ }
+ return true;
+}
+
+bool WriteToImageFile(const char* file, const LpMetadata& input) {
+ android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+ if (fd < 0) {
+ PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
+ return false;
+ }
+ return WriteToImageFile(fd, input);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index b40b83a..9d48b8c 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -22,63 +22,154 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
-#include <dm.h>
+#include <libdm/dm.h>
#include <functional>
#include <iomanip>
#include <ios>
#include <iostream>
#include <map>
+#include <sstream>
#include <string>
#include <vector>
using DeviceMapper = ::android::dm::DeviceMapper;
+using DmTable = ::android::dm::DmTable;
using DmTarget = ::android::dm::DmTarget;
+using DmTargetLinear = ::android::dm::DmTargetLinear;
+using DmTargetZero = ::android::dm::DmTargetZero;
+using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
static int Usage(void) {
std::cerr << "usage: dmctl <command> [command options]" << std::endl;
std::cerr << "commands:" << std::endl;
- std::cerr << " create <dm-name> [<dm-target> [-lo <filename>] <dm-target-args>]" << std::endl;
+ std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets>" << std::endl;
+ std::cerr << " getpath <dm-name>" << std::endl;
std::cerr << " help" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << "Target syntax:" << std::endl;
+ std::cerr << " <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
return -EINVAL;
}
+class TargetParser final {
+ public:
+ TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
+
+ bool More() const { return arg_index_ < argc_; }
+ std::unique_ptr<DmTarget> Next() {
+ if (!HasArgs(3)) {
+ std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
+ return nullptr;
+ }
+
+ std::string target_type = NextArg();
+ uint64_t start_sector, num_sectors;
+ if (!android::base::ParseUint(NextArg(), &start_sector)) {
+ std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+ if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
+ std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+
+ if (target_type == "zero") {
+ return std::make_unique<DmTargetZero>(start_sector, num_sectors);
+ } else if (target_type == "linear") {
+ if (!HasArgs(2)) {
+ std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
+ return nullptr;
+ }
+
+ std::string block_device = NextArg();
+ uint64_t physical_sector;
+ if (!android::base::ParseUint(NextArg(), &physical_sector)) {
+ std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
+ return nullptr;
+ }
+ return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
+ physical_sector);
+ } else {
+ std::cerr << "Unrecognized target type: " << target_type << std::endl;
+ return nullptr;
+ }
+ }
+
+ private:
+ bool HasArgs(int count) { return arg_index_ + count <= argc_; }
+ const char* NextArg() {
+ CHECK(arg_index_ < argc_);
+ return argv_[arg_index_++];
+ }
+ const char* PreviousArg() {
+ CHECK(arg_index_ >= 0);
+ return argv_[arg_index_ - 1];
+ }
+
+ private:
+ int arg_index_;
+ int argc_;
+ char** argv_;
+};
+
static int DmCreateCmdHandler(int argc, char** argv) {
if (argc < 1) {
- std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device";
+ std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
+ return -EINVAL;
+ }
+ std::string name = argv[0];
+
+ // Parse extended options first.
+ DmTable table;
+ int arg_index = 1;
+ while (arg_index < argc && argv[arg_index][0] == '-') {
+ if (strcmp(argv[arg_index], "-ro") == 0) {
+ table.set_readonly(true);
+ } else {
+ std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
+ return -EINVAL;
+ }
+ arg_index++;
+ }
+
+ // Parse everything else as target information.
+ TargetParser parser(argc - arg_index, argv + arg_index);
+ while (parser.More()) {
+ std::unique_ptr<DmTarget> target = parser.Next();
+ if (!target || !table.AddTarget(std::move(target))) {
+ return -EINVAL;
+ }
+ }
+
+ if (table.num_targets() == 0) {
+ std::cerr << "Must define at least one target." << std::endl;
return -EINVAL;
}
- std::string name = argv[0];
DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.CreateDevice(name)) {
- std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+ if (!dm.CreateDevice(name, table)) {
+ std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
return -EIO;
}
-
- // if we also have target specified
- if (argc > 1) {
- // fall through for now. This will eventually create a DmTarget() based on the target name
- // passing it the table that is specified at the command line
- }
-
return 0;
}
static int DmDeleteCmdHandler(int argc, char** argv) {
if (argc < 1) {
- std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device";
+ std::cerr << "Usage: dmctl delete <name>" << std::endl;
return -EINVAL;
}
std::string name = argv[0];
DeviceMapper& dm = DeviceMapper::Instance();
if (!dm.DeleteDevice(name)) {
- std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+ std::cerr << "Failed to delete [" << name << "]" << std::endl;
return -EIO;
}
@@ -86,7 +177,7 @@
}
static int DmListTargets(DeviceMapper& dm) {
- std::vector<DmTarget> targets;
+ std::vector<DmTargetTypeInfo> targets;
if (!dm.GetAvailableTargets(&targets)) {
std::cerr << "Failed to read available device mapper targets" << std::endl;
return -errno;
@@ -151,11 +242,30 @@
return 0;
}
+static int GetPathCmdHandler(int argc, char** argv) {
+ if (argc != 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string path;
+ if (!dm.GetDmDevicePathByName(argv[0], &path)) {
+ std::cerr << "Could not query path of device \"" << argv[0] << "\"." << std::endl;
+ return -EINVAL;
+ }
+ std::cout << path << std::endl;
+ return 0;
+}
+
static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
+ // clang-format off
{"create", DmCreateCmdHandler},
{"delete", DmDeleteCmdHandler},
{"list", DmListCmdHandler},
{"help", HelpCmdHandler},
+ {"getpath", GetPathCmdHandler},
+ // clang-format on
};
int main(int argc, char** argv) {
diff --git a/init/Android.bp b/init/Android.bp
index 7d863c8..cf7637f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -76,7 +76,6 @@
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
- "libselinux",
],
shared_libs: [
"libcutils",
@@ -87,6 +86,7 @@
"libc++",
"libdl",
"libz",
+ "libselinux",
],
}
diff --git a/init/Android.mk b/init/Android.mk
index da27a73..a81a0f6 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -61,7 +61,6 @@
libseccomp_policy \
libcrypto_utils \
libsparse \
- libselinux \
libprocessgroup \
libavb \
libkeyutils \
@@ -76,6 +75,7 @@
libcrypto \
libdl \
libz \
+ libselinux \
ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
# init is static executable for non-system-as-root devices, because the dynamic linker
diff --git a/init/README.md b/init/README.md
index 550ef05..b0a73b9 100644
--- a/init/README.md
+++ b/init/README.md
@@ -752,3 +752,22 @@
kill -SIGCONT 4343
> strace runs
+
+Host Init Script Verification
+-----------------------------
+
+Init scripts are checked for correctness during build time. Specifically the below is checked.
+
+1) Well formatted action, service and import sections, e.g. no actions without a preceding 'on'
+line, and no extraneous lines after an 'import' statement.
+2) All commands map to a valid keyword and the argument count is within the correct range.
+3) All service options are valid. This is stricter than how commands are checked as the service
+options' arguments are fully parsed, e.g. UIDs and GIDs must resolve.
+
+There are other parts of init scripts that are only parsed at runtime and therefore not checked
+during build time, among them are the below.
+
+1) The validity of the arguments of commands, e.g. no checking if file paths actually exist, if
+SELinux would permit the operation, or if the UIDs and GIDs resolve.
+2) No checking if a service exists or has a valid SELinux domain defined
+3) No checking if a service has not been previously defined in a different init script.
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 7e93b44..8407729 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -146,7 +146,7 @@
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
- if (!parser.ParseConfig(argv[1])) {
+ if (!parser.ParseConfigFileInsecure(argv[1])) {
LOG(ERROR) << "Failed to open init rc script '" << argv[1] << "'";
return EXIT_FAILURE;
}
diff --git a/init/init.cpp b/init/init.cpp
index b494bcc..77c4fc4 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -352,21 +352,23 @@
}
static void export_kernel_boot_props() {
+ constexpr const char* UNSET = "";
struct {
const char *src_prop;
const char *dst_prop;
const char *default_value;
} prop_map[] = {
- { "ro.boot.serialno", "ro.serialno", "", },
+ { "ro.boot.serialno", "ro.serialno", UNSET, },
{ "ro.boot.mode", "ro.bootmode", "unknown", },
{ "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
{ "ro.boot.hardware", "ro.hardware", "unknown", },
{ "ro.boot.revision", "ro.revision", "0", },
};
- for (size_t i = 0; i < arraysize(prop_map); i++) {
- std::string value = GetProperty(prop_map[i].src_prop, "");
- property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
+ for (const auto& prop : prop_map) {
+ std::string value = GetProperty(prop.src_prop, prop.default_value);
+ if (value != UNSET)
+ property_set(prop.dst_prop, value);
}
}
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index db60ce1..2bc9f3a 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -29,6 +29,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
+#include <liblp/metadata_format.h>
#include "devices.h"
#include "fs_mgr.h"
@@ -75,6 +76,7 @@
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
+ std::string lp_metadata_partition_;
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
std::unique_ptr<DeviceHandler> device_handler_;
@@ -120,13 +122,17 @@
}
static inline bool IsDmLinearEnabled() {
- bool enabled = false;
- import_kernel_cmdline(
- false, [&enabled](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.logical_partitions" && value == "1") {
- enabled = true;
- }
- });
+ static bool checked = false;
+ static bool enabled = false;
+ if (checked) {
+ return enabled;
+ }
+ import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.logical_partitions" && value == "1") {
+ enabled = true;
+ }
+ });
+ checked = true;
return enabled;
}
@@ -163,7 +169,7 @@
}
bool FirstStageMount::DoFirstStageMount() {
- if (!dm_linear_table_ && mount_fstab_recs_.empty()) {
+ if (!IsDmLinearEnabled() && mount_fstab_recs_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
@@ -184,14 +190,18 @@
bool FirstStageMount::GetBackingDmLinearDevices() {
// Add any additional devices required for dm-linear mappings.
- if (!dm_linear_table_) {
+ if (!IsDmLinearEnabled()) {
return true;
}
- for (const auto& partition : dm_linear_table_->partitions) {
- for (const auto& extent : partition.extents) {
- const std::string& partition_name = android::base::Basename(extent.block_device());
- required_devices_partition_names_.emplace(partition_name);
+ required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
+
+ if (dm_linear_table_) {
+ for (const auto& partition : dm_linear_table_->partitions) {
+ for (const auto& extent : partition.extents) {
+ const std::string& partition_name = android::base::Basename(extent.block_device());
+ required_devices_partition_names_.emplace(partition_name);
+ }
}
}
return true;
@@ -205,7 +215,7 @@
return true;
}
- if (dm_linear_table_ || need_dm_verity_) {
+ if (IsDmLinearEnabled() || need_dm_verity_) {
const std::string dm_path = "/devices/virtual/misc/device-mapper";
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
@@ -253,10 +263,21 @@
}
bool FirstStageMount::CreateLogicalPartitions() {
- if (!dm_linear_table_) {
+ if (!IsDmLinearEnabled()) {
return true;
}
- return android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get());
+
+ if (lp_metadata_partition_.empty()) {
+ LOG(ERROR) << "Could not locate logical partition tables in partition "
+ << LP_METADATA_PARTITION_NAME;
+ return false;
+ }
+ if (dm_linear_table_) {
+ if (!android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get())) {
+ return false;
+ }
+ }
+ return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
}
ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
@@ -266,6 +287,10 @@
auto iter = required_devices_partition_names_.find(name);
if (iter != required_devices_partition_names_.end()) {
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
+ if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+ std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
+ lp_metadata_partition_ = links[0];
+ }
required_devices_partition_names_.erase(iter);
device_handler_->HandleDeviceEvent(uevent);
if (required_devices_partition_names_.empty()) {
diff --git a/init/parser.cpp b/init/parser.cpp
index 4f1cac4..fa0fd11 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -19,6 +19,7 @@
#include <dirent.h>
#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -39,14 +40,13 @@
line_callbacks_.emplace_back(prefix, callback);
}
-void Parser::ParseData(const std::string& filename, const std::string& data) {
- // TODO: Use a parser with const input and remove this copy
- std::vector<char> data_copy(data.begin(), data.end());
- data_copy.push_back('\0');
+void Parser::ParseData(const std::string& filename, std::string* data) {
+ data->push_back('\n'); // TODO: fix tokenizer
+ data->push_back('\0');
parse_state state;
state.line = 0;
- state.ptr = &data_copy[0];
+ state.ptr = data->data();
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
@@ -69,6 +69,11 @@
switch (next_token(&state)) {
case T_EOF:
end_section();
+
+ for (const auto& [section_name, section_parser] : section_parsers_) {
+ section_parser->EndFile();
+ }
+
return;
case T_NEWLINE: {
state.line++;
@@ -118,6 +123,16 @@
}
}
+bool Parser::ParseConfigFileInsecure(const std::string& path) {
+ std::string config_contents;
+ if (!android::base::ReadFileToString(path, &config_contents)) {
+ return false;
+ }
+
+ ParseData(path, &config_contents);
+ return true;
+}
+
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
@@ -127,11 +142,7 @@
return false;
}
- config_contents->push_back('\n'); // TODO: fix parse_config.
- ParseData(path, *config_contents);
- for (const auto& [section_name, section_parser] : section_parsers_) {
- section_parser->EndFile();
- }
+ ParseData(path, &config_contents.value());
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
diff --git a/init/parser.h b/init/parser.h
index 3501d8c..2454b6a 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -75,10 +75,13 @@
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ // Host init verifier check file permissions.
+ bool ParseConfigFileInsecure(const std::string& path);
+
size_t parse_error_count() const { return parse_error_count_; }
private:
- void ParseData(const std::string& filename, const std::string& data);
+ void ParseData(const std::string& filename, std::string* data);
bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
diff --git a/init/service.cpp b/init/service.cpp
index 565cae7..95b37ab 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -787,9 +787,9 @@
flags_ |= SVC_EXEC;
is_exec_service_running_ = true;
- LOG(INFO) << "SVC_EXEC pid " << pid_ << " (uid " << uid_ << " gid " << gid_ << "+"
- << supp_gids_.size() << " context " << (!seclabel_.empty() ? seclabel_ : "default")
- << ") started; waiting...";
+ LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid_ << " gid "
+ << gid_ << "+" << supp_gids_.size() << " context "
+ << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
return Success();
}
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 329ba85..6d78ed6 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -21,6 +21,7 @@
TEST(libc, __pstore_append) {
#ifdef __ANDROID__
+#ifndef NO_PSTORE
FILE* fp;
ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
static const char message[] = "libc.__pstore_append\n";
@@ -43,6 +44,9 @@
"Reboot, ensure string libc.__pstore_append is in "
"/sys/fs/pstore/pmsg-ramoops-0\n");
}
+#else /* NO_PSTORE */
+ GTEST_LOG_(INFO) << "This test does nothing because of NO_PSTORE.\n";
+#endif /* NO_PSTORE */
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 597a6bb..a8a9a12 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -3180,113 +3180,6 @@
}
#endif // USING_LOGGER_DEFAULT
-#ifdef USING_LOGGER_DEFAULT // Do not retest event mapping functionality
-#ifdef __ANDROID__
-// must be: '<needle:> 0 kB'
-static bool isZero(const std::string& content, std::string::size_type pos,
- const char* needle) {
- std::string::size_type offset = content.find(needle, pos);
- return (offset != std::string::npos) &&
- ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
- std::string::npos) &&
- (content.find_first_not_of('0', offset) != offset);
-}
-
-// must not be: '<needle:> 0 kB'
-static bool isNotZero(const std::string& content, std::string::size_type pos,
- const char* needle) {
- std::string::size_type offset = content.find(needle, pos);
- return (offset != std::string::npos) &&
- ((offset = content.find_first_not_of(" \t", offset + strlen(needle))) !=
- std::string::npos) &&
- (content.find_first_not_of("123456789", offset) != offset);
-}
-
-static void event_log_tags_test_smap(pid_t pid) {
- std::string filename = android::base::StringPrintf("/proc/%d/smaps", pid);
-
- std::string content;
- if (!android::base::ReadFileToString(filename, &content)) return;
-
- bool shared_ok = false;
- bool private_ok = false;
- bool anonymous_ok = false;
- bool pass_ok = false;
-
- static const char event_log_tags[] = "event-log-tags";
- std::string::size_type pos = 0;
- while ((pos = content.find(event_log_tags, pos)) != std::string::npos) {
- pos += strlen(event_log_tags);
-
- // must not be: 'Shared_Clean: 0 kB'
- bool ok =
- isNotZero(content, pos, "Shared_Clean:") ||
- // If not /etc/event-log-tags, thus r/w, then half points
- // back for not 'Shared_Dirty: 0 kB'
- ((content.substr(pos - 5 - strlen(event_log_tags), 5) != "/etc/") &&
- isNotZero(content, pos, "Shared_Dirty:"));
- if (ok && !pass_ok) {
- shared_ok = true;
- } else if (!ok) {
- shared_ok = false;
- }
-
- // must be: 'Private_Dirty: 0 kB' and 'Private_Clean: 0 kB'
- ok = isZero(content, pos, "Private_Dirty:") ||
- isZero(content, pos, "Private_Clean:");
- if (ok && !pass_ok) {
- private_ok = true;
- } else if (!ok) {
- private_ok = false;
- }
-
- // must be: 'Anonymous: 0 kB'
- ok = isZero(content, pos, "Anonymous:");
- if (ok && !pass_ok) {
- anonymous_ok = true;
- } else if (!ok) {
- anonymous_ok = false;
- }
-
- pass_ok = true;
- }
- content = "";
-
- if (!pass_ok) return;
- if (shared_ok && anonymous_ok && private_ok) return;
-
- filename = android::base::StringPrintf("/proc/%d/comm", pid);
- android::base::ReadFileToString(filename, &content);
- content = android::base::StringPrintf(
- "%d:%s", pid, content.substr(0, content.find('\n')).c_str());
-
- EXPECT_TRUE(IsOk(shared_ok, content));
- EXPECT_TRUE(IsOk(private_ok, content));
- EXPECT_TRUE(IsOk(anonymous_ok, content));
-}
-#endif // __ANDROID__
-
-TEST(liblog, event_log_tags) {
-#ifdef __ANDROID__
- std::unique_ptr<DIR, int (*)(DIR*)> proc_dir(opendir("/proc"), closedir);
- ASSERT_FALSE(!proc_dir);
-
- dirent* e;
- while ((e = readdir(proc_dir.get()))) {
- if (e->d_type != DT_DIR) continue;
- if (!isdigit(e->d_name[0])) continue;
- long long id = atoll(e->d_name);
- if (id <= 0) continue;
- pid_t pid = id;
- if (id != pid) continue;
- event_log_tags_test_smap(pid);
- }
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-#endif // USING_LOGGER_DEFAULT
-
#ifdef USING_LOGGER_DEFAULT // Do not retest ratelimit
TEST(liblog, __android_log_ratelimit) {
time_t state = 0;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 70b6a3e..478b8d0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -201,6 +201,7 @@
$$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $$@
$$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
$$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
+ $$(hide) sed -i -e 's?%PRODUCT%?$$(TARGET_COPY_OUT_PRODUCT)?g' $$@
llndk_libraries_list :=
vndksp_libraries_list :=
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index eebad2b..42dc7ab 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,7 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
-dir.system = /product/bin/
+dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
@@ -39,7 +39,7 @@
namespace.default.isolated = true
namespace.default.search.paths = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
# We can't have entire /system/${LIB} as permitted paths because doing so
# makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -51,8 +51,7 @@
namespace.default.permitted.paths = /system/${LIB}/drm
namespace.default.permitted.paths += /system/${LIB}/extractors
namespace.default.permitted.paths += /system/${LIB}/hw
-namespace.default.permitted.paths += /product/${LIB}
-namespace.default.permitted.paths += /system/product/${LIB}
+namespace.default.permitted.paths += /%PRODUCT%/${LIB}
# These are where odex files are located. libart has to be able to dlopen the files
namespace.default.permitted.paths += /system/framework
namespace.default.permitted.paths += /system/app
@@ -64,12 +63,9 @@
namespace.default.permitted.paths += /odm/app
namespace.default.permitted.paths += /odm/priv-app
namespace.default.permitted.paths += /oem/app
-namespace.default.permitted.paths += /product/framework
-namespace.default.permitted.paths += /product/app
-namespace.default.permitted.paths += /product/priv-app
-namespace.default.permitted.paths += /system/product/framework
-namespace.default.permitted.paths += /system/product/app
-namespace.default.permitted.paths += /system/product/priv-app
+namespace.default.permitted.paths += /%PRODUCT%/framework
+namespace.default.permitted.paths += /%PRODUCT%/app
+namespace.default.permitted.paths += /%PRODUCT%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
@@ -92,14 +88,10 @@
namespace.default.asan.permitted.paths += /odm/app
namespace.default.asan.permitted.paths += /odm/priv-app
namespace.default.asan.permitted.paths += /oem/app
-namespace.default.asan.permitted.paths += /product/${LIB}
-namespace.default.asan.permitted.paths += /product/framework
-namespace.default.asan.permitted.paths += /product/app
-namespace.default.asan.permitted.paths += /product/priv-app
-namespace.default.asan.permitted.paths += /system/product/${LIB}
-namespace.default.asan.permitted.paths += /system/product/framework
-namespace.default.asan.permitted.paths += /system/product/app
-namespace.default.asan.permitted.paths += /system/product/priv-app
+namespace.default.asan.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCT%/framework
+namespace.default.asan.permitted.paths += /%PRODUCT%/app
+namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
###############################################################################
@@ -335,7 +327,7 @@
namespace.system.isolated = false
namespace.system.search.paths = /system/${LIB}
-namespace.system.search.paths += /product/${LIB}
+namespace.system.search.paths += /%PRODUCT%/${LIB}
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
@@ -353,4 +345,4 @@
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 6438e3d..7834dd5 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,24 +1,46 @@
phony {
name: "shell_and_utilities",
required: [
+ "shell_and_utilities_system",
+ "shell_and_utilities_recovery",
+ "shell_and_utilities_vendor",
+ ],
+}
+
+phony {
+ name: "shell_and_utilities_system",
+ required: [
"awk",
- "awk_vendor",
"bzip2",
"grep",
- "grep_vendor",
"logwrapper",
- "logwrapper_vendor",
"mkshrc",
- "mkshrc_vendor",
+ "newfs_msdos",
"reboot",
"sh",
- "sh.recovery",
- "sh_vendor",
"toolbox",
- "toolbox.recovery",
- "toolbox_vendor",
"toybox",
+ ],
+}
+
+phony {
+ name: "shell_and_utilities_recovery",
+ required: [
+ "sh.recovery",
+ "toolbox.recovery",
"toybox.recovery",
+ ],
+}
+
+phony {
+ name: "shell_and_utilities_vendor",
+ required: [
+ "awk_vendor",
+ "grep_vendor",
+ "logwrapper_vendor",
+ "mkshrc_vendor",
+ "sh_vendor",
+ "toolbox_vendor",
"toybox_vendor",
],
}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index b15be1f..e310e6b 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -193,13 +193,15 @@
Android Q
---------
+BSD: fsck\_msdos newfs\_msdos
+
bzip2: bzcat bzip2 bunzip2
one-true-awk: awk
PCRE: egrep fgrep grep
-toolbox: getevent getprop newfs\_msdos
+toolbox: getevent getprop
toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date dd df diff dirname
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index f12c810..fc51705 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -26,7 +26,6 @@
"toolbox.c",
"getevent.c",
"getprop.cpp",
- "newfs_msdos.c",
],
generated_headers: [
"toolbox_input_labels",
@@ -39,7 +38,6 @@
symlinks: [
"getevent",
"getprop",
- "newfs_msdos",
],
}
@@ -62,10 +60,3 @@
defaults: ["toolbox_defaults"],
srcs: ["r.c"],
}
-
-cc_binary_host {
- name: "newfs_msdos",
- defaults: ["toolbox_defaults"],
- srcs: ["newfs_msdos.c"],
- cflags: ["-Dnewfs_msdos_main=main"]
-}
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_APACHE2
similarity index 100%
rename from toolbox/MODULE_LICENSE_BSD
rename to toolbox/MODULE_LICENSE_APACHE2
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
index e9ab58d..8e8a91c 100644
--- a/toolbox/NOTICE
+++ b/toolbox/NOTICE
@@ -1,4 +1,4 @@
-Copyright (C) 2010 The Android Open Source Project
+Copyright (C) 2017 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.
@@ -14,974 +14,3 @@
-------------------------------------------------------------------
-Copyright (C) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1987, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Jeffrey Mogul.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-David Hitz of Auspex Systems Inc.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1988, 1993, 1994, 2003
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Kevin Fall.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Chris Newcomb.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Ken Smith of The State University of New York at Buffalo.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1990, 1993, 1994, 2003
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1991, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Keith Muller of the University of California, San Diego and Lance
-Visser of Convex Computer Corporation.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1992, 1993, 1994
- The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
-NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1998 Robert Nordier
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
-Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
-Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2007 The NetBSD Foundation, Inc.
-All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation
-by Luke Mewburn.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2008, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2009, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010 The NetBSD Foundation, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2010, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2012, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2013, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 2014, The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- * Neither the name of Google, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this
- software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
diff --git a/toolbox/getprop.cpp b/toolbox/getprop.cpp
index 9e324a0..ca345cb 100644
--- a/toolbox/getprop.cpp
+++ b/toolbox/getprop.cpp
@@ -1,18 +1,18 @@
-//
-// Copyright (C) 2017 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.
-//
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#include <getopt.h>
#include <sys/system_properties.h>
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
deleted file mode 100644
index 5fc8b02..0000000
--- a/toolbox/newfs_msdos.c
+++ /dev/null
@@ -1,1108 +0,0 @@
-/*
- * Copyright (c) 1998 Robert Nordier
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $";
-#endif /* not lint */
-
-#include <sys/param.h>
-
-#ifdef __APPLE__
-#elif defined(ANDROID)
-#include <linux/fs.h>
-#include <linux/hdreg.h>
-#include <stdarg.h>
-#include <sys/ioctl.h>
-#else
-#include <sys/fdcio.h>
-#include <sys/disk.h>
-#include <sys/disklabel.h>
-#include <sys/mount.h>
-#endif
-
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <paths.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
-#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */
-#define BPN 4 /* bits per nibble */
-#define NPB 2 /* nibbles per byte */
-
-#define DOSMAGIC 0xaa55 /* DOS magic number */
-#define MINBPS 512 /* minimum bytes per sector */
-#define MAXSPC 128 /* maximum sectors per cluster */
-#define MAXNFT 16 /* maximum number of FATs */
-#define DEFBLK 4096 /* default block size */
-#define DEFBLK16 2048 /* default block size FAT16 */
-#define DEFRDE 512 /* default root directory entries */
-#define RESFTE 2 /* reserved FAT entries */
-#define MINCLS12 1 /* minimum FAT12 clusters */
-#define MINCLS16 0x1000 /* minimum FAT16 clusters */
-#define MINCLS32 2 /* minimum FAT32 clusters */
-#define MAXCLS12 0xfed /* maximum FAT12 clusters */
-#define MAXCLS16 0xfff5 /* maximum FAT16 clusters */
-#define MAXCLS32 0xffffff5 /* maximum FAT32 clusters */
-
-#define mincls(fat) ((fat) == 12 ? MINCLS12 : \
- (fat) == 16 ? MINCLS16 : \
- MINCLS32)
-
-#define maxcls(fat) ((fat) == 12 ? MAXCLS12 : \
- (fat) == 16 ? MAXCLS16 : \
- MAXCLS32)
-
-#define mk1(p, x) \
- (p) = (u_int8_t)(x)
-
-#define mk2(p, x) \
- (p)[0] = (u_int8_t)(x), \
- (p)[1] = (u_int8_t)((x) >> 010)
-
-#define mk4(p, x) \
- (p)[0] = (u_int8_t)(x), \
- (p)[1] = (u_int8_t)((x) >> 010), \
- (p)[2] = (u_int8_t)((x) >> 020), \
- (p)[3] = (u_int8_t)((x) >> 030)
-
-#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg)
-#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg)
-#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg)
-#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg)
-
-struct bs {
- u_int8_t jmp[3]; /* bootstrap entry point */
- u_int8_t oem[8]; /* OEM name and version */
-};
-
-struct bsbpb {
- u_int8_t bps[2]; /* bytes per sector */
- u_int8_t spc; /* sectors per cluster */
- u_int8_t res[2]; /* reserved sectors */
- u_int8_t nft; /* number of FATs */
- u_int8_t rde[2]; /* root directory entries */
- u_int8_t sec[2]; /* total sectors */
- u_int8_t mid; /* media descriptor */
- u_int8_t spf[2]; /* sectors per FAT */
- u_int8_t spt[2]; /* sectors per track */
- u_int8_t hds[2]; /* drive heads */
- u_int8_t hid[4]; /* hidden sectors */
- u_int8_t bsec[4]; /* big total sectors */
-};
-
-struct bsxbpb {
- u_int8_t bspf[4]; /* big sectors per FAT */
- u_int8_t xflg[2]; /* FAT control flags */
- u_int8_t vers[2]; /* file system version */
- u_int8_t rdcl[4]; /* root directory start cluster */
- u_int8_t infs[2]; /* file system info sector */
- u_int8_t bkbs[2]; /* backup boot sector */
- u_int8_t rsvd[12]; /* reserved */
-};
-
-struct bsx {
- u_int8_t drv; /* drive number */
- u_int8_t rsvd; /* reserved */
- u_int8_t sig; /* extended boot signature */
- u_int8_t volid[4]; /* volume ID number */
- u_int8_t label[11]; /* volume label */
- u_int8_t type[8]; /* file system type */
-};
-
-struct de {
- u_int8_t namext[11]; /* name and extension */
- u_int8_t attr; /* attributes */
- u_int8_t rsvd[10]; /* reserved */
- u_int8_t time[2]; /* creation time */
- u_int8_t date[2]; /* creation date */
- u_int8_t clus[2]; /* starting cluster */
- u_int8_t size[4]; /* size */
-};
-
-struct bpb {
- u_int bps; /* bytes per sector */
- u_int spc; /* sectors per cluster */
- u_int res; /* reserved sectors */
- u_int nft; /* number of FATs */
- u_int rde; /* root directory entries */
- u_int sec; /* total sectors */
- u_int mid; /* media descriptor */
- u_int spf; /* sectors per FAT */
- u_int spt; /* sectors per track */
- u_int hds; /* drive heads */
- u_int hid; /* hidden sectors */
- u_int bsec; /* big total sectors */
- u_int bspf; /* big sectors per FAT */
- u_int rdcl; /* root directory start cluster */
- u_int infs; /* file system info sector */
- u_int bkbs; /* backup boot sector */
-};
-
-#define BPBGAP 0, 0, 0, 0, 0, 0
-
-static struct {
- const char *name;
- struct bpb bpb;
-} const stdfmt[] = {
- {"160", {512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1, BPBGAP}},
- {"180", {512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1, BPBGAP}},
- {"320", {512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2, BPBGAP}},
- {"360", {512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, BPBGAP}},
- {"640", {512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2, BPBGAP}},
- {"720", {512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, BPBGAP}},
- {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
- {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2, BPBGAP}},
- {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
- {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
-};
-
-static const u_int8_t bootcode[] = {
- 0xfa, /* cli */
- 0x31, 0xc0, /* xor ax,ax */
- 0x8e, 0xd0, /* mov ss,ax */
- 0xbc, 0x00, 0x7c, /* mov sp,7c00h */
- 0xfb, /* sti */
- 0x8e, 0xd8, /* mov ds,ax */
- 0xe8, 0x00, 0x00, /* call $ + 3 */
- 0x5e, /* pop si */
- 0x83, 0xc6, 0x19, /* add si,+19h */
- 0xbb, 0x07, 0x00, /* mov bx,0007h */
- 0xfc, /* cld */
- 0xac, /* lodsb */
- 0x84, 0xc0, /* test al,al */
- 0x74, 0x06, /* jz $ + 8 */
- 0xb4, 0x0e, /* mov ah,0eh */
- 0xcd, 0x10, /* int 10h */
- 0xeb, 0xf5, /* jmp $ - 9 */
- 0x30, 0xe4, /* xor ah,ah */
- 0xcd, 0x16, /* int 16h */
- 0xcd, 0x19, /* int 19h */
- 0x0d, 0x0a,
- 'N', 'o', 'n', '-', 's', 'y', 's', 't',
- 'e', 'm', ' ', 'd', 'i', 's', 'k',
- 0x0d, 0x0a,
- 'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
- 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
- ' ', 'r', 'e', 'b', 'o', 'o', 't',
- 0x0d, 0x0a,
- 0
-};
-
-static void check_mounted(const char *, mode_t);
-static void getstdfmt(const char *, struct bpb *);
-static void getdiskinfo(int, const char *, const char *, int, struct bpb *);
-static void print_bpb(struct bpb *);
-static u_int ckgeom(const char *, u_int, const char *);
-static u_int argtou(const char *, u_int, u_int, const char *);
-static off_t argtooff(const char *, const char *);
-static int oklabel(const char *);
-static void mklabel(u_int8_t *, const char *);
-static void setstr(u_int8_t *, const char *, size_t);
-static void usage(void);
-
-/*
- * Construct a FAT12, FAT16, or FAT32 file system.
- */
-int newfs_msdos_main(int argc, char *argv[])
-{
- static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
- const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
- u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
- u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
- u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
- u_int opt_A = 0;
- int opt_N = 0;
- int Iflag = 0, mflag = 0, oflag = 0;
- char buf[MAXPATHLEN];
- struct stat sb;
- struct timeval tv;
- struct bpb bpb;
- struct tm *tm;
- struct bs *bs;
- struct bsbpb *bsbpb;
- struct bsxbpb *bsxbpb;
- struct bsx *bsx;
- struct de *de;
- u_int8_t *img;
- const char *fname, *dtype, *bname;
- ssize_t n;
- time_t now;
- u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
- u_int extra_res, alignment=0, set_res, set_spf, set_spc, tempx, attempts=0;
- int ch, fd, fd1;
- off_t opt_create = 0, opt_ofs = 0;
-
- while ((ch = getopt(argc, argv, opts)) != -1)
- switch (ch) {
- case '@':
- opt_ofs = argtooff(optarg, "offset");
- break;
- case 'N':
- opt_N = 1;
- break;
- case 'A':
- opt_A = 1;
- break;
- case 'B':
- opt_B = optarg;
- break;
- case 'C':
- opt_create = argtooff(optarg, "create size");
- break;
- case 'F':
- if (strcmp(optarg, "12") && strcmp(optarg, "16") && strcmp(optarg, "32"))
- errx(1, "%s: bad FAT type", optarg);
- opt_F = atoi(optarg);
- break;
- case 'I':
- opt_I = argto4(optarg, 0, "volume ID");
- Iflag = 1;
- break;
- case 'L':
- if (!oklabel(optarg))
- errx(1, "%s: bad volume label", optarg);
- opt_L = optarg;
- break;
- case 'O':
- if (strlen(optarg) > 8)
- errx(1, "%s: bad OEM string", optarg);
- opt_O = optarg;
- break;
- case 'S':
- opt_S = argto2(optarg, 1, "bytes/sector");
- break;
- case 'a':
- opt_a = argto4(optarg, 1, "sectors/FAT");
- break;
- case 'b':
- opt_b = argtox(optarg, 1, "block size");
- opt_c = 0;
- break;
- case 'c':
- opt_c = argto1(optarg, 1, "sectors/cluster");
- opt_b = 0;
- break;
- case 'e':
- opt_e = argto2(optarg, 1, "directory entries");
- break;
- case 'f':
- opt_f = optarg;
- break;
- case 'h':
- opt_h = argto2(optarg, 1, "drive heads");
- break;
- case 'i':
- opt_i = argto2(optarg, 1, "info sector");
- break;
- case 'k':
- opt_k = argto2(optarg, 1, "backup sector");
- break;
- case 'm':
- opt_m = argto1(optarg, 0, "media descriptor");
- mflag = 1;
- break;
- case 'n':
- opt_n = argto1(optarg, 1, "number of FATs");
- break;
- case 'o':
- opt_o = argto4(optarg, 0, "hidden sectors");
- oflag = 1;
- break;
- case 'r':
- opt_r = argto2(optarg, 1, "reserved sectors");
- break;
- case 's':
- opt_s = argto4(optarg, 1, "file system size");
- break;
- case 'u':
- opt_u = argto2(optarg, 1, "sectors/track");
- break;
- default:
- usage();
- }
- argc -= optind;
- argv += optind;
- if (argc < 1 || argc > 2)
- usage();
- fname = *argv++;
- if (!opt_create && !strchr(fname, '/')) {
- snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
- if (!(fname = strdup(buf)))
- err(1, "%s", buf);
- }
- dtype = *argv;
- if (opt_A) {
- if (opt_r)
- errx(1, "align (-A) is incompatible with -r");
- if (opt_N)
- errx(1, "align (-A) is incompatible with -N");
- }
- if (opt_create) {
- if (opt_N)
- errx(1, "create (-C) is incompatible with -N");
- fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
- if (fd == -1)
- errx(1, "failed to create %s", fname);
- if (ftruncate(fd, opt_create))
- errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
- } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
- err(1, "%s", fname);
- if (fstat(fd, &sb))
- err(1, "%s", fname);
- if (opt_create) {
- if (!S_ISREG(sb.st_mode))
- warnx("warning, %s is not a regular file", fname);
- } else {
- if (!S_ISCHR(sb.st_mode))
- warnx("warning, %s is not a character device", fname);
- }
- if (!opt_N)
- check_mounted(fname, sb.st_mode);
- if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
- errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
- memset(&bpb, 0, sizeof(bpb));
- if (opt_f) {
- getstdfmt(opt_f, &bpb);
- bpb.bsec = bpb.sec;
- bpb.sec = 0;
- bpb.bspf = bpb.spf;
- bpb.spf = 0;
- }
- if (opt_h)
- bpb.hds = opt_h;
- if (opt_u)
- bpb.spt = opt_u;
- if (opt_S)
- bpb.bps = opt_S;
- if (opt_s)
- bpb.bsec = opt_s;
- if (oflag)
- bpb.hid = opt_o;
- if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
- off_t delta;
- getdiskinfo(fd, fname, dtype, oflag, &bpb);
- if (opt_s) {
- bpb.bsec = opt_s;
- }
- bpb.bsec -= (opt_ofs / bpb.bps);
- delta = bpb.bsec % bpb.spt;
- if (delta != 0) {
- warnx("trim %d sectors from %d to adjust to a multiple of %d",
- (int)delta, bpb.bsec, bpb.spt);
- bpb.bsec -= delta;
- }
- if (bpb.spc == 0) { /* set defaults */
- if (bpb.bsec <= 6000) /* about 3MB -> 512 bytes */
- bpb.spc = 1;
- else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */
- bpb.spc = 8;
- else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */
- bpb.spc = 16;
- else if (bpb.bsec <= (1<<22)) /* 2G -> 16k, some versions of windows
- require a minimum of 65527 clusters */
- bpb.spc = 32;
- else
- bpb.spc = 64; /* otherwise 32k */
- }
- }
- if (!powerof2(bpb.bps))
- errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps);
- if (bpb.bps < MINBPS)
- errx(1, "bytes/sector (%u) is too small; minimum is %u",
- bpb.bps, MINBPS);
- if (!(fat = opt_F)) {
- if (opt_f)
- fat = 12;
- else if (!opt_e && (opt_i || opt_k))
- fat = 32;
- }
- if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
- errx(1, "-%c is not a legal FAT%s option",
- fat == 32 ? 'e' : opt_i ? 'i' : 'k',
- fat == 32 ? "32" : "12/16");
- if (opt_f && fat == 32)
- bpb.rde = 0;
- if (opt_b) {
- if (!powerof2(opt_b))
- errx(1, "block size (%u) is not a power of 2", opt_b);
- if (opt_b < bpb.bps)
- errx(1, "block size (%u) is too small; minimum is %u",
- opt_b, bpb.bps);
- if (opt_b > bpb.bps * MAXSPC)
- errx(1, "block size (%u) is too large; maximum is %u", opt_b, bpb.bps * MAXSPC);
- bpb.spc = opt_b / bpb.bps;
- }
- if (opt_c) {
- if (!powerof2(opt_c))
- errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
- bpb.spc = opt_c;
- }
- if (opt_r)
- bpb.res = opt_r;
- if (opt_n) {
- if (opt_n > MAXNFT)
- errx(1, "number of FATs (%u) is too large; maximum is %u", opt_n, MAXNFT);
- bpb.nft = opt_n;
- }
- if (opt_e)
- bpb.rde = opt_e;
- if (mflag) {
- if (opt_m < 0xf0)
- errx(1, "illegal media descriptor (%#x)", opt_m);
- bpb.mid = opt_m;
- }
- if (opt_a)
- bpb.bspf = opt_a;
- if (opt_i)
- bpb.infs = opt_i;
- if (opt_k)
- bpb.bkbs = opt_k;
- bss = 1;
- bname = NULL;
- fd1 = -1;
- if (opt_B) {
- bname = opt_B;
- if (!strchr(bname, '/')) {
- snprintf(buf, sizeof(buf), "/boot/%s", bname);
- if (!(bname = strdup(buf)))
- err(1, "%s", buf);
- }
- if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
- err(1, "%s", bname);
- if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
- sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
- errx(1, "%s: inappropriate file type or format", bname);
- bss = sb.st_size / bpb.bps;
- }
- if (!bpb.nft)
- bpb.nft = 2;
- if (!fat) {
- if (bpb.bsec < (bpb.res ? bpb.res : bss) +
- howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
- ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
- bpb.nft +
- howmany(bpb.rde ? bpb.rde : DEFRDE,
- bpb.bps / sizeof(struct de)) +
- (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
- (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
- fat = 12;
- else if (bpb.rde || bpb.bsec <
- (bpb.res ? bpb.res : bss) +
- howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
- howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
- (MAXCLS16 + 1) *
- (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
- fat = 16;
- else
- fat = 32;
- }
- x = bss;
- if (fat == 32) {
- if (!bpb.infs) {
- if (x == MAXU16 || x == bpb.bkbs)
- errx(1, "no room for info sector");
- bpb.infs = x;
- }
- if (bpb.infs != MAXU16 && x <= bpb.infs)
- x = bpb.infs + 1;
- if (!bpb.bkbs) {
- if (x == MAXU16)
- errx(1, "no room for backup sector");
- bpb.bkbs = x;
- } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
- errx(1, "backup sector would overwrite info sector");
- if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
- x = bpb.bkbs + 1;
- }
-
- extra_res = 0;
- set_res = !bpb.res;
- set_spf = !bpb.bspf;
- set_spc = !bpb.spc;
- tempx = x;
- /*
- * Attempt to align if opt_A is set. This is done by increasing the number
- * of reserved blocks. This can cause other factors to change, which can in
- * turn change the alignment. This should take at most 2 iterations, as
- * increasing the reserved amount may cause the FAT size to decrease by 1,
- * requiring another nft reserved blocks. If spc changes, it will
- * be half of its previous size, and thus will not throw off alignment.
- */
- do {
- x = tempx;
- if (set_res)
- bpb.res = (fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x) + extra_res;
- else if (bpb.res < x)
- errx(1, "too few reserved sectors");
- if (fat != 32 && !bpb.rde)
- bpb.rde = DEFRDE;
- rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
- if (set_spc)
- for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
- bpb.spc < MAXSPC &&
- bpb.res +
- howmany((RESFTE + maxcls(fat)) * (fat / BPN),
- bpb.bps * NPB) * bpb.nft +
- rds +
- (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
- bpb.spc <<= 1);
- if (fat != 32 && bpb.bspf > MAXU16)
- errx(1, "too many sectors/FAT for FAT12/16");
- x1 = bpb.res + rds;
- x = bpb.bspf ? bpb.bspf : 1;
- if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
- errx(1, "meta data exceeds file system size");
- x1 += x * bpb.nft;
- x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
- (bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
- x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN), bpb.bps * NPB);
- if (set_spf) {
- if (!bpb.bspf) {
- bpb.bspf = x2;
- }
- x1 += (bpb.bspf - 1) * bpb.nft;
- }
- if(set_res) {
- /* attempt to align root directory */
- alignment = (bpb.res + bpb.bspf * bpb.nft) % bpb.spc;
- extra_res += bpb.spc - alignment;
- }
- attempts++;
- } while(opt_A && alignment != 0 && attempts < 2);
- if (alignment != 0)
- warnx("warning: Alignment failed.");
-
- cls = (bpb.bsec - x1) / bpb.spc;
- x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
- if (cls > x)
- cls = x;
- if (bpb.bspf < x2)
- warnx("warning: sectors/FAT limits file system to %u clusters", cls);
- if (cls < mincls(fat))
- errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat, mincls(fat));
- if (cls > maxcls(fat)) {
- cls = maxcls(fat);
- bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
- warnx("warning: FAT type limits file system to %u sectors", bpb.bsec);
- }
- printf("%s: %u sector%s in %u FAT%u cluster%s (%u bytes/cluster)\n",
- fname, cls * bpb.spc, cls * bpb.spc == 1 ? "" : "s", cls, fat,
- cls == 1 ? "" : "s", bpb.bps * bpb.spc);
- if (!bpb.mid)
- bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
- if (fat == 32)
- bpb.rdcl = RESFTE;
- if (bpb.hid + bpb.bsec <= MAXU16) {
- bpb.sec = bpb.bsec;
- bpb.bsec = 0;
- }
- if (fat != 32) {
- bpb.spf = bpb.bspf;
- bpb.bspf = 0;
- }
- print_bpb(&bpb);
- if (!opt_N) {
- gettimeofday(&tv, NULL);
- now = tv.tv_sec;
- tm = localtime(&now);
- if (!(img = malloc(bpb.bps)))
- err(1, "%u", bpb.bps);
- dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
- for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
- x = lsn;
- if (opt_B && fat == 32 && bpb.bkbs != MAXU16 && bss <= bpb.bkbs && x >= bpb.bkbs) {
- x -= bpb.bkbs;
- if (!x && lseek(fd1, opt_ofs, SEEK_SET))
- err(1, "%s", bname);
- }
- if (opt_B && x < bss) {
- if ((n = read(fd1, img, bpb.bps)) == -1)
- err(1, "%s", bname);
- if ((unsigned)n != bpb.bps)
- errx(1, "%s: can't read sector %u", bname, x);
- } else
- memset(img, 0, bpb.bps);
- if (!lsn || (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
- x1 = sizeof(struct bs);
- bsbpb = (struct bsbpb *)(img + x1);
- mk2(bsbpb->bps, bpb.bps);
- mk1(bsbpb->spc, bpb.spc);
- mk2(bsbpb->res, bpb.res);
- mk1(bsbpb->nft, bpb.nft);
- mk2(bsbpb->rde, bpb.rde);
- mk2(bsbpb->sec, bpb.sec);
- mk1(bsbpb->mid, bpb.mid);
- mk2(bsbpb->spf, bpb.spf);
- mk2(bsbpb->spt, bpb.spt);
- mk2(bsbpb->hds, bpb.hds);
- mk4(bsbpb->hid, bpb.hid);
- mk4(bsbpb->bsec, bpb.bsec);
- x1 += sizeof(struct bsbpb);
- if (fat == 32) {
- bsxbpb = (struct bsxbpb *)(img + x1);
- mk4(bsxbpb->bspf, bpb.bspf);
- mk2(bsxbpb->xflg, 0);
- mk2(bsxbpb->vers, 0);
- mk4(bsxbpb->rdcl, bpb.rdcl);
- mk2(bsxbpb->infs, bpb.infs);
- mk2(bsxbpb->bkbs, bpb.bkbs);
- x1 += sizeof(struct bsxbpb);
- }
- bsx = (struct bsx *)(img + x1);
- mk1(bsx->sig, 0x29);
- if (Iflag)
- x = opt_I;
- else
- x = (((u_int)(1 + tm->tm_mon) << 8 |
- (u_int)tm->tm_mday) +
- ((u_int)tm->tm_sec << 8 |
- (u_int)(tv.tv_usec / 10))) << 16 |
- ((u_int)(1900 + tm->tm_year) +
- ((u_int)tm->tm_hour << 8 |
- (u_int)tm->tm_min));
- mk4(bsx->volid, x);
- mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
- snprintf(buf, sizeof(buf), "FAT%u", fat);
- setstr(bsx->type, buf, sizeof(bsx->type));
- if (!opt_B) {
- x1 += sizeof(struct bsx);
- bs = (struct bs *)img;
- mk1(bs->jmp[0], 0xeb);
- mk1(bs->jmp[1], x1 - 2);
- mk1(bs->jmp[2], 0x90);
- setstr(bs->oem, opt_O ? opt_O : "BSD 4.4",
- sizeof(bs->oem));
- memcpy(img + x1, bootcode, sizeof(bootcode));
- mk2(img + MINBPS - 2, DOSMAGIC);
- }
- } else if (fat == 32 && bpb.infs != MAXU16 &&
- (lsn == bpb.infs || (bpb.bkbs != MAXU16 &&
- lsn == bpb.bkbs + bpb.infs))) {
- mk4(img, 0x41615252);
- mk4(img + MINBPS - 28, 0x61417272);
- mk4(img + MINBPS - 24, 0xffffffff);
- mk4(img + MINBPS - 20, bpb.rdcl);
- mk2(img + MINBPS - 2, DOSMAGIC);
- } else if (lsn >= bpb.res && lsn < dir &&
- !((lsn - bpb.res) % (bpb.spf ? bpb.spf : bpb.bspf))) {
- mk1(img[0], bpb.mid);
- for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
- mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
- } else if (lsn == dir && opt_L) {
- de = (struct de *)img;
- mklabel(de->namext, opt_L);
- mk1(de->attr, 050);
- x = (u_int)tm->tm_hour << 11 |
- (u_int)tm->tm_min << 5 |
- (u_int)tm->tm_sec >> 1;
- mk2(de->time, x);
- x = (u_int)(tm->tm_year - 80) << 9 |
- (u_int)(tm->tm_mon + 1) << 5 |
- (u_int)tm->tm_mday;
- mk2(de->date, x);
- }
- if ((n = write(fd, img, bpb.bps)) == -1)
- err(1, "%s", fname);
- if ((unsigned)n != bpb.bps) {
- errx(1, "%s: can't write sector %u", fname, lsn);
- exit(1);
- }
- }
- free(img);
- }
- return 0;
-}
-
-/*
- * Exit with error if file system is mounted.
- */
-static void check_mounted(const char *fname, mode_t mode)
-{
-#ifdef ANDROID
- warnx("Skipping mount checks");
-#else
- struct statfs *mp;
- const char *s1, *s2;
- size_t len;
- int n, r;
-
- if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
- err(1, "getmntinfo");
- len = strlen(_PATH_DEV);
- s1 = fname;
- if (!strncmp(s1, _PATH_DEV, len))
- s1 += len;
- r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
- for (; n--; mp++) {
- s2 = mp->f_mntfromname;
- if (!strncmp(s2, _PATH_DEV, len))
- s2 += len;
- if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || !strcmp(s1, s2))
- errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
- }
-#endif
-}
-
-/*
- * Get a standard format.
- */
-static void getstdfmt(const char *fmt, struct bpb *bpb)
-{
- u_int x, i;
-
- x = sizeof(stdfmt) / sizeof(stdfmt[0]);
- for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
- if (i == x)
- errx(1, "%s: unknown standard format", fmt);
- *bpb = stdfmt[i].bpb;
-}
-
-/*
- * Get disk slice, partition, and geometry information.
- */
-
-#ifdef __APPLE__
-static void getdiskinfo(__unused int fd, __unused const char* fname, __unused const char* dtype,
- __unused int oflag, __unused struct bpb* bpb) {}
-#elif ANDROID
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
- __unused int oflag,struct bpb *bpb)
-{
- struct hd_geometry geom;
- u_long block_size;
-
- if (ioctl(fd, BLKSSZGET, &bpb->bps)) {
- fprintf(stderr, "Error getting bytes / sector (%s)\n", strerror(errno));
- exit(1);
- }
-
- ckgeom(fname, bpb->bps, "bytes/sector");
-
- if (ioctl(fd, BLKGETSIZE, &block_size)) {
- fprintf(stderr, "Error getting blocksize (%s)\n", strerror(errno));
- exit(1);
- }
-
- if (block_size > UINT32_MAX) {
- fprintf(stderr, "Error blocksize too large: %lu\n", block_size);
- exit(1);
- }
-
- bpb->bsec = (u_int)block_size;
-
- if (ioctl(fd, HDIO_GETGEO, &geom)) {
- fprintf(stderr, "Error getting gemoetry (%s) - trying sane values\n", strerror(errno));
- geom.heads = 64;
- geom.sectors = 63;
- }
-
- if (!geom.heads) {
- printf("Bogus heads from kernel - setting sane value\n");
- geom.heads = 64;
- }
-
- if (!geom.sectors) {
- printf("Bogus sectors from kernel - setting sane value\n");
- geom.sectors = 63;
- }
-
- bpb->spt = geom.sectors;
- ckgeom(fname, bpb->spt, "sectors/track");
-
- bpb->hds = geom.heads;
- ckgeom(fname, bpb->hds, "drive heads");
-}
-
-#else
-
-static void getdiskinfo(int fd, const char *fname, const char *dtype,
- __unused int oflag, struct bpb *bpb)
-{
- struct disklabel *lp, dlp;
- struct fd_type type;
- off_t ms, hs = 0;
-
- lp = NULL;
-
- /* If the user specified a disk type, try to use that */
- if (dtype != NULL) {
- lp = getdiskbyname(dtype);
- }
-
- /* Maybe it's a floppy drive */
- if (lp == NULL) {
- if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
- struct stat st;
-
- if (fstat(fd, &st))
- err(1, "Cannot get disk size");
- /* create a fake geometry for a file image */
- ms = st.st_size;
- dlp.d_secsize = 512;
- dlp.d_nsectors = 63;
- dlp.d_ntracks = 255;
- dlp.d_secperunit = ms / dlp.d_secsize;
- lp = &dlp;
- } else if (ioctl(fd, FD_GTYPE, &type) != -1) {
- dlp.d_secsize = 128 << type.secsize;
- dlp.d_nsectors = type.sectrac;
- dlp.d_ntracks = type.heads;
- dlp.d_secperunit = ms / dlp.d_secsize;
- lp = &dlp;
- }
- }
-
- /* Maybe it's a fixed drive */
- if (lp == NULL) {
- if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
- if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1)
- errx(1, "Cannot get sector size, %s", strerror(errno));
-
- /* XXX Should we use bpb->bps if it's set? */
- dlp.d_secperunit = ms / dlp.d_secsize;
-
- if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) {
- warnx("Cannot get number of sectors per track, %s", strerror(errno));
- dlp.d_nsectors = 63;
- }
- if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
- warnx("Cannot get number of heads, %s", strerror(errno));
- if (dlp.d_secperunit <= 63*1*1024)
- dlp.d_ntracks = 1;
- else if (dlp.d_secperunit <= 63*16*1024)
- dlp.d_ntracks = 16;
- else
- dlp.d_ntracks = 255;
- }
- }
-
- hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
- lp = &dlp;
- }
-
- if (bpb->bps == 0)
- bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector");
- if (bpb->spt == 0)
- bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track");
- if (bpb->hds == 0)
- bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads");
- if (bpb->bsec == 0)
- bpb->bsec = lp->d_secperunit;
- if (bpb->hid == 0)
- bpb->hid = hs;
-}
-#endif
-
-/*
- * Print out BPB values.
- */
-static void print_bpb(struct bpb *bpb)
-{
- printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
- bpb->nft);
- if (bpb->rde)
- printf(" rde=%u", bpb->rde);
- if (bpb->sec)
- printf(" sec=%u", bpb->sec);
- printf(" mid=%#x", bpb->mid);
- if (bpb->spf)
- printf(" spf=%u", bpb->spf);
- printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
- if (bpb->bsec)
- printf(" bsec=%u", bpb->bsec);
- if (!bpb->spf) {
- printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
- printf(" infs=");
- printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
- printf(" bkbs=");
- printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
- }
- printf("\n");
-}
-
-/*
- * Check a disk geometry value.
- */
-static u_int ckgeom(const char *fname, u_int val, const char *msg)
-{
- if (!val)
- errx(1, "%s: no default %s", fname, msg);
- if (val > MAXU16)
- errx(1, "%s: illegal %s %d", fname, msg, val);
- return val;
-}
-
-/*
- * Convert and check a numeric option argument.
- */
-static u_int argtou(const char *arg, u_int lo, u_int hi, const char *msg)
-{
- char *s;
- u_long x;
-
- errno = 0;
- x = strtoul(arg, &s, 0);
- if (errno || !*arg || *s || x < lo || x > hi)
- errx(1, "%s: bad %s", arg, msg);
- return x;
-}
-
-/*
- * Same for off_t, with optional skmgpP suffix
- */
-static off_t argtooff(const char *arg, const char *msg)
-{
- char *s;
- off_t x;
-
- x = strtoll(arg, &s, 0);
- /* allow at most one extra char */
- if (errno || x < 0 || (s[0] && s[1]) )
- errx(1, "%s: bad %s", arg, msg);
- if (*s) { /* the extra char is the multiplier */
- switch (*s) {
- default:
- errx(1, "%s: bad %s", arg, msg);
- /* notreached */
-
- case 's': /* sector */
- case 'S':
- x <<= 9; /* times 512 */
- break;
-
- case 'k': /* kilobyte */
- case 'K':
- x <<= 10; /* times 1024 */
- break;
-
- case 'm': /* megabyte */
- case 'M':
- x <<= 20; /* times 1024*1024 */
- break;
-
- case 'g': /* gigabyte */
- case 'G':
- x <<= 30; /* times 1024*1024*1024 */
- break;
-
- case 'p': /* partition start */
- case 'P': /* partition start */
- case 'l': /* partition length */
- case 'L': /* partition length */
- errx(1, "%s: not supported yet %s", arg, msg);
- /* notreached */
- }
- }
- return x;
-}
-
-/*
- * Check a volume label.
- */
-static int oklabel(const char *src)
-{
- int c, i;
-
- for (i = 0; i <= 11; i++) {
- c = (u_char)*src++;
- if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
- break;
- }
- return i && !c;
-}
-
-/*
- * Make a volume label.
- */
-static void mklabel(u_int8_t *dest, const char *src)
-{
- int c, i;
-
- for (i = 0; i < 11; i++) {
- c = *src ? toupper(*src++) : ' ';
- *dest++ = !i && c == '\xe5' ? 5 : c;
- }
-}
-
-/*
- * Copy string, padding with spaces.
- */
-static void setstr(u_int8_t *dest, const char *src, size_t len)
-{
- while (len--)
- *dest++ = *src ? *src++ : ' ';
-}
-
-/*
- * Print usage message.
- */
-static void usage(void)
-{
- fprintf(stderr,
- "usage: newfs_msdos [ -options ] special [disktype]\n"
- "where the options are:\n"
- "\t-@ create file system at specified offset\n"
- "\t-A Attempt to cluster align root directory\n"
- "\t-B get bootstrap from file\n"
- "\t-C create image file with specified size\n"
- "\t-F FAT type (12, 16, or 32)\n"
- "\t-I volume ID\n"
- "\t-L volume label\n"
- "\t-N don't create file system: just print out parameters\n"
- "\t-O OEM string\n"
- "\t-S bytes/sector\n"
- "\t-a sectors/FAT\n"
- "\t-b block size\n"
- "\t-c sectors/cluster\n"
- "\t-e root directory entries\n"
- "\t-f standard format\n"
- "\t-h drive heads\n"
- "\t-i file system info sector\n"
- "\t-k backup boot sector\n"
- "\t-m media descriptor\n"
- "\t-n number of FATs\n"
- "\t-o hidden sectors\n"
- "\t-r reserved sectors\n"
- "\t-s file system size (sectors)\n"
- "\t-u sectors/track\n");
- exit(1);
-}
diff --git a/toolbox/tools.h b/toolbox/tools.h
index 3d4bc3e..abeb3ef 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,4 +1,3 @@
TOOL(getevent)
TOOL(getprop)
-TOOL(newfs_msdos)
TOOL(toolbox)