blob: 75d0a53bed7e032f03255b7b28587346e5cbdc92 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "factory_reset.h"
#include <dirent.h>
#include <errno.h>
#include <fbl/string_buffer.h>
#include <fbl/string_piece.h>
#include <fbl/unique_ptr.h>
#include <fcntl.h>
#include <fs-management/mount.h>
#include <fuchsia/device/c/fidl.h>
#include <fuchsia/hardware/block/c/fidl.h>
#include <fuchsia/sysinfo/c/fidl.h>
#include <lib/fdio/fdio.h>
#include <lib/fzl/fdio.h>
#include <lib/zx/channel.h>
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/system.h>
namespace factory_reset {
const char* kBlockPath = "class/block";
const int kNumZxcryptSuperblocks = 3;
zx_status_t WriteRandomBlock(int fd, ssize_t block_size) {
std::unique_ptr<uint8_t[]> buf(new uint8_t[block_size]);
zx_cprng_draw(buf.get(), block_size);
ssize_t res;
if ((res = write(fd, buf.get(), block_size)) < block_size) {
fprintf(stderr, "write(%d, %p, %zu) failed: %s\n ", fd, buf.get(), block_size, strerror(errno));
return ZX_ERR_IO;
}
return ZX_OK;
}
// Determines the block size of the passed in fd.
zx_status_t FindBlockSize(int fd, ssize_t* block_size) {
zx_status_t rc, call_status;
fzl::UnownedFdioCaller caller(fd);
if (!caller) {
return ZX_ERR_BAD_STATE;
}
fuchsia_hardware_block_BlockInfo block_info;
if ((rc = fuchsia_hardware_block_BlockGetInfo(caller.borrow_channel(), &call_status,
&block_info)) != ZX_OK) {
return rc;
}
if (call_status != ZX_OK) {
return call_status;
}
*block_size = block_info.block_size;
return ZX_OK;
}
zx_status_t ShredBlockDevice(fbl::unique_fd fd, int num_blocks) {
zx_status_t status;
if (lseek(fd.get(), 0, SEEK_SET) != 0) {
return ZX_ERR_IO;
}
ssize_t block_size = 0;
status = FindBlockSize(fd.get(), &block_size);
if (status != ZX_OK) {
return status;
}
for (int i = 0; i < num_blocks; ++i) {
if ((status = WriteRandomBlock(fd.get(), block_size)) != ZX_OK) {
fprintf(stderr, "Couldn't write to %d block: %d (%s)\n", i, status,
zx_status_get_string(status));
return status;
}
}
return ZX_OK;
}
FactoryReset::FactoryReset(fbl::unique_fd dev_fd,
fuchsia::device::manager::AdministratorPtr admin) {
dev_fd_ = std::move(dev_fd);
admin_ = std::move(admin);
}
zx_status_t FactoryReset::Shred() const {
fbl::unique_fd block_dir(openat(dev_fd_.get(), kBlockPath, O_RDONLY | O_DIRECTORY));
if (!block_dir) {
fprintf(stderr, "Error opening %s\n", kBlockPath);
return ZX_ERR_NOT_FOUND;
}
struct dirent* de;
DIR* dir = fdopendir(block_dir.get());
// Attempts to shred every zxcrypt volume found.
while ((de = readdir(dir)) != nullptr) {
fbl::unique_fd block_fd(openat(dirfd(dir), de->d_name, O_RDWR));
if (!block_fd || detect_disk_format(block_fd.get()) != DISK_FORMAT_ZXCRYPT) {
continue;
}
zx_status_t status = ShredBlockDevice(std::move(block_fd), kNumZxcryptSuperblocks);
if (status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
void FactoryReset::Reset(ResetCallback callback) {
zx_status_t status = Shred();
if (status != ZX_OK) {
fprintf(stderr, "FactoryReset: Shred failed: %d (%s)\n", status, zx_status_get_string(status));
callback(std::move(status));
return;
}
// Reboot to initiate the recovery.
admin_->Suspend(fuchsia::device::manager::SUSPEND_FLAG_REBOOT, std::move(callback));
}
} // namespace factory_reset