Merge "AVB: allow no metadata in the generic system.img for project Treble"
This commit is contained in:
commit
c70bf5836c
|
@ -839,7 +839,8 @@ int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
|
|||
return FS_MGR_MNTALL_FAIL;
|
||||
}
|
||||
}
|
||||
if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
|
||||
if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
|
||||
SetUpAvbHashtreeResult::kFail) {
|
||||
LERROR << "Failed to set up AVB on partition: "
|
||||
<< fstab->recs[i].mount_point << ", skipping!";
|
||||
/* Skips mounting the device. */
|
||||
|
@ -1055,7 +1056,8 @@ int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
|
|||
return FS_MGR_DOMNT_FAILED;
|
||||
}
|
||||
}
|
||||
if (!avb_handle->SetUpAvb(&fstab->recs[i], true /* wait_for_verity_dev */)) {
|
||||
if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
|
||||
SetUpAvbHashtreeResult::kFail) {
|
||||
LERROR << "Failed to set up AVB on partition: "
|
||||
<< fstab->recs[i].mount_point << ", skipping!";
|
||||
/* Skips mounting the device. */
|
||||
|
|
|
@ -114,7 +114,6 @@ static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& ver
|
|||
|
||||
// Reads the following values from kernel cmdline and provides the
|
||||
// VerifyVbmetaImages() to verify AvbSlotVerifyData.
|
||||
// - androidboot.vbmeta.device_state
|
||||
// - androidboot.vbmeta.hash_alg
|
||||
// - androidboot.vbmeta.size
|
||||
// - androidboot.vbmeta.digest
|
||||
|
@ -123,7 +122,6 @@ class FsManagerAvbVerifier {
|
|||
// The factory method to return a unique_ptr<FsManagerAvbVerifier>
|
||||
static std::unique_ptr<FsManagerAvbVerifier> Create();
|
||||
bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
|
||||
bool IsDeviceUnlocked() { return is_device_unlocked_; }
|
||||
|
||||
protected:
|
||||
FsManagerAvbVerifier() = default;
|
||||
|
@ -138,7 +136,6 @@ class FsManagerAvbVerifier {
|
|||
HashAlgorithm hash_alg_;
|
||||
uint8_t digest_[SHA512_DIGEST_LENGTH];
|
||||
size_t vbmeta_size_;
|
||||
bool is_device_unlocked_;
|
||||
};
|
||||
|
||||
std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
|
||||
|
@ -161,9 +158,7 @@ std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
|
|||
const std::string& key = pieces[0];
|
||||
const std::string& value = pieces[1];
|
||||
|
||||
if (key == "androidboot.vbmeta.device_state") {
|
||||
avb_verifier->is_device_unlocked_ = (value == "unlocked");
|
||||
} else if (key == "androidboot.vbmeta.hash_alg") {
|
||||
if (key == "androidboot.vbmeta.hash_alg") {
|
||||
hash_alg = value;
|
||||
} else if (key == "androidboot.vbmeta.size") {
|
||||
if (!android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
|
||||
|
@ -478,6 +473,16 @@ static bool get_hashtree_descriptor(const std::string& partition_name,
|
|||
return true;
|
||||
}
|
||||
|
||||
// Orange state means the device is unlocked, see the following link for details.
|
||||
// https://source.android.com/security/verifiedboot/verified-boot#device_state
|
||||
static inline bool IsDeviceUnlocked() {
|
||||
std::string verified_boot_state;
|
||||
if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
|
||||
return verified_boot_state == "orange";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) {
|
||||
FsManagerAvbOps avb_ops(fstab);
|
||||
return DoOpen(&avb_ops);
|
||||
|
@ -493,12 +498,7 @@ FsManagerAvbUniquePtr FsManagerAvbHandle::Open(ByNameSymlinkMap&& by_name_symlin
|
|||
}
|
||||
|
||||
FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
|
||||
// Gets the expected hash value of vbmeta images from kernel cmdline.
|
||||
std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
|
||||
if (!avb_verifier) {
|
||||
LERROR << "Failed to create FsManagerAvbVerifier";
|
||||
return nullptr;
|
||||
}
|
||||
bool is_device_unlocked = IsDeviceUnlocked();
|
||||
|
||||
FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
|
||||
if (!avb_handle) {
|
||||
|
@ -506,9 +506,8 @@ FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
AvbSlotVerifyFlags flags = avb_verifier->IsDeviceUnlocked()
|
||||
? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
|
||||
: AVB_SLOT_VERIFY_FLAGS_NONE;
|
||||
AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
|
||||
: AVB_SLOT_VERIFY_FLAGS_NONE;
|
||||
AvbSlotVerifyResult verify_result =
|
||||
avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
|
||||
|
||||
|
@ -526,62 +525,81 @@ FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
|
|||
// for more details.
|
||||
switch (verify_result) {
|
||||
case AVB_SLOT_VERIFY_RESULT_OK:
|
||||
avb_handle->status_ = kFsManagerAvbHandleSuccess;
|
||||
avb_handle->status_ = kAvbHandleSuccess;
|
||||
break;
|
||||
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
|
||||
if (!avb_verifier->IsDeviceUnlocked()) {
|
||||
if (!is_device_unlocked) {
|
||||
LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
|
||||
return nullptr;
|
||||
}
|
||||
avb_handle->status_ = kFsManagerAvbHandleErrorVerification;
|
||||
avb_handle->status_ = kAvbHandleVerificationError;
|
||||
break;
|
||||
default:
|
||||
LERROR << "avb_slot_verify failed, result: " << verify_result;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Verifies vbmeta images against the digest passed from bootloader.
|
||||
if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
|
||||
LERROR << "VerifyVbmetaImages failed";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
|
||||
avb_handle->avb_version_ =
|
||||
android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
|
||||
|
||||
// Checks whether FLAGS_HASHTREE_DISABLED is set.
|
||||
// Checks whether FLAGS_VERIFICATION_DISABLED is set:
|
||||
// - Only the top-level vbmeta struct is read.
|
||||
// - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
|
||||
// and AVB HASHTREE descriptor(s).
|
||||
AvbVBMetaImageHeader vbmeta_header;
|
||||
avb_vbmeta_image_header_to_host_byte_order(
|
||||
(AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
|
||||
&vbmeta_header);
|
||||
bool verification_disabled =
|
||||
((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
|
||||
|
||||
bool hashtree_disabled =
|
||||
((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
|
||||
if (hashtree_disabled) {
|
||||
avb_handle->status_ = kFsManagerAvbHandleHashtreeDisabled;
|
||||
if (verification_disabled) {
|
||||
avb_handle->status_ = kAvbHandleVerificationDisabled;
|
||||
} else {
|
||||
// Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
|
||||
std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
|
||||
if (!avb_verifier) {
|
||||
LERROR << "Failed to create FsManagerAvbVerifier";
|
||||
return nullptr;
|
||||
}
|
||||
if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
|
||||
LERROR << "VerifyVbmetaImages failed";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Checks whether FLAGS_HASHTREE_DISABLED is set.
|
||||
bool hashtree_disabled =
|
||||
((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
|
||||
if (hashtree_disabled) {
|
||||
avb_handle->status_ = kAvbHandleHashtreeDisabled;
|
||||
}
|
||||
}
|
||||
|
||||
LINFO << "Returning avb_handle with status: " << avb_handle->status_;
|
||||
return avb_handle;
|
||||
}
|
||||
|
||||
bool FsManagerAvbHandle::SetUpAvb(struct fstab_rec* fstab_entry, bool wait_for_verity_dev) {
|
||||
if (!fstab_entry) return false;
|
||||
if (!avb_slot_data_ || avb_slot_data_->num_vbmeta_images < 1) {
|
||||
return false;
|
||||
SetUpAvbHashtreeResult FsManagerAvbHandle::SetUpAvbHashtree(struct fstab_rec* fstab_entry,
|
||||
bool wait_for_verity_dev) {
|
||||
if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
|
||||
avb_slot_data_->num_vbmeta_images < 1) {
|
||||
return SetUpAvbHashtreeResult::kFail;
|
||||
}
|
||||
|
||||
if (status_ == kFsManagerAvbHandleUninitialized) return false;
|
||||
if (status_ == kFsManagerAvbHandleHashtreeDisabled) {
|
||||
LINFO << "AVB HASHTREE disabled on:" << fstab_entry->mount_point;
|
||||
return true;
|
||||
if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
|
||||
LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
|
||||
return SetUpAvbHashtreeResult::kDisabled;
|
||||
}
|
||||
|
||||
std::string partition_name(basename(fstab_entry->mount_point));
|
||||
if (!avb_validate_utf8((const uint8_t*)partition_name.c_str(), partition_name.length())) {
|
||||
LERROR << "Partition name: " << partition_name.c_str() << " is not valid UTF-8.";
|
||||
return false;
|
||||
// Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
|
||||
// to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
|
||||
std::string partition_name(basename(fstab_entry->blk_device));
|
||||
if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
|
||||
auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
|
||||
if (ab_suffix != std::string::npos) {
|
||||
partition_name.erase(ab_suffix);
|
||||
}
|
||||
}
|
||||
|
||||
AvbHashtreeDescriptor hashtree_descriptor;
|
||||
|
@ -589,13 +607,14 @@ bool FsManagerAvbHandle::SetUpAvb(struct fstab_rec* fstab_entry, bool wait_for_v
|
|||
std::string root_digest;
|
||||
if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
|
||||
&root_digest)) {
|
||||
return false;
|
||||
return SetUpAvbHashtreeResult::kFail;
|
||||
}
|
||||
|
||||
// Converts HASHTREE descriptor to verity_table_params.
|
||||
if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
|
||||
wait_for_verity_dev)) {
|
||||
return false;
|
||||
return SetUpAvbHashtreeResult::kFail;
|
||||
}
|
||||
return true;
|
||||
|
||||
return SetUpAvbHashtreeResult::kSuccess;
|
||||
}
|
||||
|
|
|
@ -89,6 +89,15 @@ static AvbIOResult dummy_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNU
|
|||
return AVB_IO_RESULT_OK;
|
||||
}
|
||||
|
||||
static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
|
||||
const char* partition ATTRIBUTE_UNUSED,
|
||||
uint64_t* out_size_num_byte) {
|
||||
// The function is for bootloader to load entire content of AVB HASH partitions.
|
||||
// In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
|
||||
*out_size_num_byte = 0;
|
||||
return AVB_IO_RESULT_OK;
|
||||
}
|
||||
|
||||
void FsManagerAvbOps::InitializeAvbOps() {
|
||||
// We only need to provide the implementation of read_from_partition()
|
||||
// operation since that's all what is being used by the avb_slot_verify().
|
||||
|
@ -101,6 +110,7 @@ void FsManagerAvbOps::InitializeAvbOps() {
|
|||
avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
|
||||
avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
|
||||
avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
|
||||
avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
|
||||
|
||||
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
|
||||
avb_ops_.user_data = this;
|
||||
|
|
|
@ -25,11 +25,10 @@
|
|||
|
||||
#include "fs_mgr.h"
|
||||
|
||||
enum FsManagerAvbHandleStatus {
|
||||
kFsManagerAvbHandleUninitialized = -1,
|
||||
kFsManagerAvbHandleSuccess = 0,
|
||||
kFsManagerAvbHandleHashtreeDisabled = 1,
|
||||
kFsManagerAvbHandleErrorVerification = 2,
|
||||
enum class SetUpAvbHashtreeResult {
|
||||
kSuccess = 0,
|
||||
kFail,
|
||||
kDisabled,
|
||||
};
|
||||
|
||||
class FsManagerAvbOps;
|
||||
|
@ -40,8 +39,8 @@ using FsManagerAvbUniquePtr = std::unique_ptr<FsManagerAvbHandle>;
|
|||
using ByNameSymlinkMap = std::map<std::string, std::string>;
|
||||
|
||||
// Provides a factory method to return a unique_ptr pointing to itself and the
|
||||
// SetUpAvb() function to extract dm-verity parameters from AVB metadata to
|
||||
// load verity table into kernel through ioctl.
|
||||
// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
|
||||
// descriptors to load verity table into kernel through ioctl.
|
||||
class FsManagerAvbHandle {
|
||||
public:
|
||||
// The factory method to return a FsManagerAvbUniquePtr that holds
|
||||
|
@ -65,12 +64,22 @@ class FsManagerAvbHandle {
|
|||
// - nullptr: any error when reading and verifying the metadata,
|
||||
// e.g., I/O error, digest value mismatch, size mismatch, etc.
|
||||
//
|
||||
// - a valid unique_ptr with status kFsMgrAvbHandleHashtreeDisabled:
|
||||
// - a valid unique_ptr with status kAvbHandleHashtreeDisabled:
|
||||
// to support the existing 'adb disable-verity' feature in Android.
|
||||
// It's very helpful for developers to make the filesystem writable to
|
||||
// allow replacing binaries on the device.
|
||||
//
|
||||
// - a valid unique_ptr with status kFsMgrAvbHandleSuccess: the metadata
|
||||
// - a valid unique_ptr with status kAvbHandleVerificationDisabled:
|
||||
// to support 'avbctl disable-verification': only the top-level
|
||||
// vbmeta is read, vbmeta structs in other partitions are not processed.
|
||||
// It's needed to bypass AVB when using the generic system.img to run
|
||||
// VTS for project Treble.
|
||||
//
|
||||
// - a valid unique_ptr with status kAvbHandleVerificationError:
|
||||
// there is verification error when libavb loads vbmeta from each
|
||||
// partition. This is only allowed when the device is unlocked.
|
||||
//
|
||||
// - a valid unique_ptr with status kAvbHandleSuccess: the metadata
|
||||
// is verified and can be trusted.
|
||||
//
|
||||
static FsManagerAvbUniquePtr Open(const fstab& fstab);
|
||||
|
@ -79,14 +88,15 @@ class FsManagerAvbHandle {
|
|||
// Sets up dm-verity on the given fstab entry.
|
||||
// The 'wait_for_verity_dev' parameter makes this function wait for the
|
||||
// verity device to get created before return.
|
||||
// Returns true if the mount point is eligible to mount, it includes:
|
||||
// - status_ is kFsMgrAvbHandleHashtreeDisabled or
|
||||
// - status_ is kFsMgrAvbHandleSuccess and sending ioctl DM_TABLE_LOAD
|
||||
// to load verity table is success.
|
||||
// Otherwise, returns false.
|
||||
bool SetUpAvb(fstab_rec* fstab_entry, bool wait_for_verity_dev);
|
||||
//
|
||||
// Return value:
|
||||
// - kSuccess: successfully loads dm-verity table into kernel.
|
||||
// - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,
|
||||
// failed to get the HASHTREE descriptor, runtime error when set up
|
||||
// device-mapper, etc.
|
||||
// - kDisabled: hashtree is disabled.
|
||||
SetUpAvbHashtreeResult SetUpAvbHashtree(fstab_rec* fstab_entry, bool wait_for_verity_dev);
|
||||
|
||||
bool hashtree_disabled() const { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
|
||||
const std::string& avb_version() const { return avb_version_; }
|
||||
|
||||
FsManagerAvbHandle(const FsManagerAvbHandle&) = delete; // no copy
|
||||
|
@ -102,11 +112,19 @@ class FsManagerAvbHandle {
|
|||
};
|
||||
|
||||
private:
|
||||
FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kFsManagerAvbHandleUninitialized) {}
|
||||
enum AvbHandleStatus {
|
||||
kAvbHandleSuccess = 0,
|
||||
kAvbHandleUninitialized,
|
||||
kAvbHandleHashtreeDisabled,
|
||||
kAvbHandleVerificationDisabled,
|
||||
kAvbHandleVerificationError,
|
||||
};
|
||||
|
||||
FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
|
||||
static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
|
||||
|
||||
AvbSlotVerifyData* avb_slot_data_;
|
||||
FsManagerAvbHandleStatus status_;
|
||||
AvbHandleStatus status_;
|
||||
std::string avb_version_;
|
||||
};
|
||||
|
||||
|
|
|
@ -310,17 +310,17 @@ bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
|
|||
if (fs_mgr_is_verified(fstab_rec)) {
|
||||
int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
|
||||
switch (ret) {
|
||||
case FS_MGR_SETUP_VERITY_SKIPPED:
|
||||
case FS_MGR_SETUP_VERITY_DISABLED:
|
||||
LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
|
||||
break;
|
||||
case FS_MGR_SETUP_VERITY_SUCCESS:
|
||||
// The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
|
||||
// Needs to create it because ueventd isn't started in init first stage.
|
||||
return InitVerityDevice(fstab_rec->blk_device);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
case FS_MGR_SETUP_VERITY_SKIPPED:
|
||||
case FS_MGR_SETUP_VERITY_DISABLED:
|
||||
LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
|
||||
return true;
|
||||
case FS_MGR_SETUP_VERITY_SUCCESS:
|
||||
// The exact block device name (fstab_rec->blk_device) is changed to
|
||||
// "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
|
||||
// first stage.
|
||||
return InitVerityDevice(fstab_rec->blk_device);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true; // Returns true to mount the partition.
|
||||
|
@ -409,14 +409,18 @@ ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
|
|||
bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
|
||||
if (fs_mgr_is_avb(fstab_rec)) {
|
||||
if (!InitAvbHandle()) return false;
|
||||
if (avb_handle_->hashtree_disabled()) {
|
||||
LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
|
||||
} else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
|
||||
// The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
|
||||
// Needs to create it because ueventd isn't started in init first stage.
|
||||
InitVerityDevice(fstab_rec->blk_device);
|
||||
} else {
|
||||
return false;
|
||||
SetUpAvbHashtreeResult hashtree_result =
|
||||
avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
|
||||
switch (hashtree_result) {
|
||||
case SetUpAvbHashtreeResult::kDisabled:
|
||||
return true; // Returns true to mount the partition.
|
||||
case SetUpAvbHashtreeResult::kSuccess:
|
||||
// The exact block device name (fstab_rec->blk_device) is changed to
|
||||
// "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
|
||||
// first stage.
|
||||
return InitVerityDevice(fstab_rec->blk_device);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true; // Returns true to mount the partition.
|
||||
|
|
Loading…
Reference in New Issue