Commit b5e820d7 authored by haven@chromium.org's avatar haven@chromium.org

Unmounts volumes before writing to a drive.

When writing to a drive with mounted volumes, Chrome OS will recreate the partition table if it is observe to be missing and the volumes are accessed after writing.  This only occurs when erasing just the partition table because enough of the volume information is still present.

We also expand the amount of data erased when destroying partitions to 8k to account for 4k block sizes.  This has the side effect of overwriting the LBA for 512-byte-block drives, which only increases the effect of destroying partitions.

BUG=284834
BUG=335390

Review URL: https://codereview.chromium.org/282853003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271551 0039d316-1c4b-4281-b951-d872f2087c98
parent 1f7926e4
...@@ -10,10 +10,10 @@ ...@@ -10,10 +10,10 @@
namespace extensions { namespace extensions {
namespace image_writer { namespace image_writer {
// Number of bytes for the maximum partition table size. By wiping this many // Number of bytes for the maximum partition table size. GUID partition tables
// bytes we can essentially guarantee the header and associated information will // reside in the second sector of the disk. Disks can have up to 4k sectors.
// be wiped. See http://crbug.com/328246 for more information. // See http://crbug.com/328246 for more information.
const int kPartitionTableSize = 1 * 1024; const int kPartitionTableSize = 2 * 4096;
DestroyPartitionsOperation::DestroyPartitionsOperation( DestroyPartitionsOperation::DestroyPartitionsOperation(
base::WeakPtr<OperationManager> manager, base::WeakPtr<OperationManager> manager,
......
...@@ -45,6 +45,7 @@ const char kImageNotFound[] = "IMAGE_NOT_FOUND"; ...@@ -45,6 +45,7 @@ const char kImageNotFound[] = "IMAGE_NOT_FOUND";
const char kImageOpenError[] = "IMAGE_OPEN_ERROR"; const char kImageOpenError[] = "IMAGE_OPEN_ERROR";
const char kImageReadError[] = "IMAGE_READ_ERROR"; const char kImageReadError[] = "IMAGE_READ_ERROR";
const char kImageSizeError[] = "IMAGE_STAT_ERROR"; const char kImageSizeError[] = "IMAGE_STAT_ERROR";
const char kUnmountVolumesError[] = "UNMOUNT_VOLUMES_ERROR";
// Verification Errors // Verification Errors
const char kHashReadError[] = "HASH_READ_ERROR"; const char kHashReadError[] = "HASH_READ_ERROR";
......
...@@ -46,6 +46,7 @@ extern const char kImageNotFound[]; ...@@ -46,6 +46,7 @@ extern const char kImageNotFound[];
extern const char kImageOpenError[]; extern const char kImageOpenError[];
extern const char kImageReadError[]; extern const char kImageReadError[];
extern const char kImageSizeError[]; extern const char kImageSizeError[];
extern const char kUnmountVolumesError[];
// Verification Errors // Verification Errors
extern const char kHashReadError[]; extern const char kHashReadError[];
......
...@@ -164,8 +164,14 @@ class Operation : public base::RefCountedThreadSafe<Operation> { ...@@ -164,8 +164,14 @@ class Operation : public base::RefCountedThreadSafe<Operation> {
#endif #endif
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
void StartWriteOnUIThread(const base::Closure& continuation); // Unmounts all volumes on |device_path_|.
void UnmountVolumes(const base::Closure& continuation);
// Starts the write after unmounting.
void UnmountVolumesCallback(const base::Closure& continuation, bool success);
// Starts the ImageBurner write. Note that target_path is the file path of
// the device where device_path has been a system device path.
void StartWriteOnUIThread(const std::string& target_path,
const base::Closure& continuation);
void OnBurnFinished(const base::Closure& continuation, void OnBurnFinished(const base::Closure& continuation,
const std::string& target_path, const std::string& target_path,
bool success, bool success,
......
...@@ -6,11 +6,13 @@ ...@@ -6,11 +6,13 @@
#include "chrome/browser/extensions/api/image_writer_private/operation.h" #include "chrome/browser/extensions/api/image_writer_private/operation.h"
#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/image_burner_client.h" #include "chromeos/dbus/image_burner_client.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
namespace extensions { namespace extensions {
namespace image_writer { namespace image_writer {
using chromeos::disks::DiskMountManager;
using chromeos::ImageBurnerClient; using chromeos::ImageBurnerClient;
using content::BrowserThread; using content::BrowserThread;
...@@ -35,12 +37,13 @@ void Operation::Write(const base::Closure& continuation) { ...@@ -35,12 +37,13 @@ void Operation::Write(const base::Closure& continuation) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE); DCHECK_CURRENTLY_ON(BrowserThread::FILE);
SetStage(image_writer_api::STAGE_WRITE); SetStage(image_writer_api::STAGE_WRITE);
// Note this has to be run on the FILE thread to avoid concurrent access.
AddCleanUpFunction(base::Bind(&ClearImageBurner));
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::UI, BrowserThread::UI,
FROM_HERE, FROM_HERE,
base::Bind(&Operation::StartWriteOnUIThread, this, continuation)); base::Bind(&Operation::UnmountVolumes, this, continuation));
AddCleanUpFunction(base::Bind(&ClearImageBurner));
} }
void Operation::VerifyWrite(const base::Closure& continuation) { void Operation::VerifyWrite(const base::Closure& continuation) {
...@@ -50,9 +53,42 @@ void Operation::VerifyWrite(const base::Closure& continuation) { ...@@ -50,9 +53,42 @@ void Operation::VerifyWrite(const base::Closure& continuation) {
continuation.Run(); continuation.Run();
} }
void Operation::StartWriteOnUIThread(const base::Closure& continuation) { void Operation::UnmountVolumes(const base::Closure& continuation) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DiskMountManager::GetInstance()->UnmountDeviceRecursively(
device_path_.value(),
base::Bind(&Operation::UnmountVolumesCallback, this, continuation));
}
void Operation::UnmountVolumesCallback(const base::Closure& continuation,
bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!success) {
LOG(ERROR) << "Volume unmounting failed.";
Error(error::kUnmountVolumesError);
return;
}
const DiskMountManager::DiskMap& disks =
DiskMountManager::GetInstance()->disks();
DiskMountManager::DiskMap::const_iterator iter =
disks.find(device_path_.value());
if (iter == disks.end()) {
LOG(ERROR) << "Disk not found in disk list after unmounting volumes.";
Error(error::kUnmountVolumesError);
return;
}
StartWriteOnUIThread(iter->second->file_path(), continuation);
}
void Operation::StartWriteOnUIThread(const std::string& target_path,
const base::Closure& continuation) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
// TODO(haven): Image Burner cannot handle multiple burns. crbug.com/373575
ImageBurnerClient* burner = ImageBurnerClient* burner =
chromeos::DBusThreadManager::Get()->GetImageBurnerClient(); chromeos::DBusThreadManager::Get()->GetImageBurnerClient();
...@@ -61,7 +97,7 @@ void Operation::StartWriteOnUIThread(const base::Closure& continuation) { ...@@ -61,7 +97,7 @@ void Operation::StartWriteOnUIThread(const base::Closure& continuation) {
base::Bind(&Operation::OnBurnProgress, this)); base::Bind(&Operation::OnBurnProgress, this));
burner->BurnImage(image_path_.value(), burner->BurnImage(image_path_.value(),
device_path_.value(), target_path,
base::Bind(&Operation::OnBurnError, this)); base::Bind(&Operation::OnBurnError, this));
} }
......
...@@ -64,9 +64,14 @@ void OperationManager::StartWriteFromUrl( ...@@ -64,9 +64,14 @@ void OperationManager::StartWriteFromUrl(
const std::string& hash, const std::string& hash,
const std::string& device_path, const std::string& device_path,
const Operation::StartWriteCallback& callback) { const Operation::StartWriteCallback& callback) {
#if defined(OS_CHROMEOS)
// Chrome OS can only support a single operation at a time.
if (operations_.size() > 0) {
#else
OperationMap::iterator existing_operation = operations_.find(extension_id); OperationMap::iterator existing_operation = operations_.find(extension_id);
if (existing_operation != operations_.end()) { if (existing_operation != operations_.end()) {
#endif
return callback.Run(false, error::kOperationAlreadyInProgress); return callback.Run(false, error::kOperationAlreadyInProgress);
} }
...@@ -89,9 +94,14 @@ void OperationManager::StartWriteFromFile( ...@@ -89,9 +94,14 @@ void OperationManager::StartWriteFromFile(
const base::FilePath& path, const base::FilePath& path,
const std::string& device_path, const std::string& device_path,
const Operation::StartWriteCallback& callback) { const Operation::StartWriteCallback& callback) {
#if defined(OS_CHROMEOS)
// Chrome OS can only support a single operation at a time.
if (operations_.size() > 0) {
#else
OperationMap::iterator existing_operation = operations_.find(extension_id); OperationMap::iterator existing_operation = operations_.find(extension_id);
if (existing_operation != operations_.end()) { if (existing_operation != operations_.end()) {
#endif
return callback.Run(false, error::kOperationAlreadyInProgress); return callback.Run(false, error::kOperationAlreadyInProgress);
} }
......
...@@ -31,7 +31,7 @@ bool RemovableStorageProvider::PopulateDeviceList( ...@@ -31,7 +31,7 @@ bool RemovableStorageProvider::PopulateDeviceList(
disk.device_type() == chromeos::DEVICE_TYPE_SD)) { disk.device_type() == chromeos::DEVICE_TYPE_SD)) {
linked_ptr<api::image_writer_private::RemovableStorageDevice> device( linked_ptr<api::image_writer_private::RemovableStorageDevice> device(
new api::image_writer_private::RemovableStorageDevice()); new api::image_writer_private::RemovableStorageDevice());
device->storage_unit_id = disk.file_path(); device->storage_unit_id = disk.device_path();
device->capacity = disk.total_size_in_bytes(); device->capacity = disk.total_size_in_bytes();
device->vendor = disk.vendor_name(); device->vendor = disk.vendor_name();
device->model = disk.product_name(); device->model = disk.product_name();
......
...@@ -55,6 +55,17 @@ MockOperationManager::MockOperationManager(content::BrowserContext* context) ...@@ -55,6 +55,17 @@ MockOperationManager::MockOperationManager(content::BrowserContext* context)
: OperationManager(context) {} : OperationManager(context) {}
MockOperationManager::~MockOperationManager() {} MockOperationManager::~MockOperationManager() {}
#if defined(OS_CHROMEOS)
FakeDiskMountManager::FakeDiskMountManager() {}
FakeDiskMountManager::~FakeDiskMountManager() {}
void FakeDiskMountManager::UnmountDeviceRecursively(
const std::string& device_path,
const UnmountDeviceRecursivelyCallbackType& callback) {
base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(callback, true));
}
#endif
FakeImageWriterClient::FakeImageWriterClient() {} FakeImageWriterClient::FakeImageWriterClient() {}
FakeImageWriterClient::~FakeImageWriterClient() {} FakeImageWriterClient::~FakeImageWriterClient() {}
...@@ -131,12 +142,33 @@ void ImageWriterUnitTestBase::SetUp() { ...@@ -131,12 +142,33 @@ void ImageWriterUnitTestBase::SetUp() {
fake_dbus_thread_manager->SetImageBurnerClient(image_burner_fake.Pass()); fake_dbus_thread_manager->SetImageBurnerClient(image_burner_fake.Pass());
chromeos::DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); chromeos::DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager);
} }
FakeDiskMountManager* disk_manager = new FakeDiskMountManager();
chromeos::disks::DiskMountManager::InitializeForTesting(disk_manager);
// Adds a disk entry for test_device_path_ with the same device and file path.
disk_manager->CreateDiskEntryForMountDevice(
chromeos::disks::DiskMountManager::MountPointInfo(
test_device_path_.value(),
"/dummy/mount",
chromeos::MOUNT_TYPE_DEVICE,
chromeos::disks::MOUNT_CONDITION_NONE),
"device_id",
"device_label",
"Vendor",
"Product",
chromeos::DEVICE_TYPE_USB,
kTestFileSize,
true,
true,
false);
disk_manager->SetupDefaultReplies();
#endif #endif
} }
void ImageWriterUnitTestBase::TearDown() { void ImageWriterUnitTestBase::TearDown() {
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
chromeos::DBusThreadManager::Shutdown(); chromeos::DBusThreadManager::Shutdown();
chromeos::disks::DiskMountManager::Shutdown();
#endif #endif
} }
......
...@@ -16,6 +16,11 @@ ...@@ -16,6 +16,11 @@
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_CHROMEOS)
#include "chromeos/disks/disk_mount_manager.h"
#include "chromeos/disks/mock_disk_mount_manager.h"
#endif
namespace extensions { namespace extensions {
namespace image_writer { namespace image_writer {
...@@ -49,6 +54,37 @@ class MockOperationManager : public OperationManager { ...@@ -49,6 +54,37 @@ class MockOperationManager : public OperationManager {
const std::string& error_message)); const std::string& error_message));
}; };
#if defined(OS_CHROMEOS)
// A fake for the DiskMountManager that will successfully call the unmount
// callback.
class FakeDiskMountManager : public chromeos::disks::MockDiskMountManager {
public:
FakeDiskMountManager();
virtual ~FakeDiskMountManager();
virtual void UnmountDeviceRecursively(
const std::string& device_path,
const UnmountDeviceRecursivelyCallbackType& callback) OVERRIDE;
/*
MOCK_METHOD1(AddObserver, void(chromeos::disks::DiskMountManager::Observer*));
MOCK_METHOD1(RemoveObserver,
void(chromeos::disks::DiskMountManager::Observer*));
MOCK_CONST_METHOD0(disks, const DiskMap&());
MOCK_CONST_METHOD1(FindDiskBySourcePath, const Disk*(const std::string&));
MOCK_CONST_METHOD0(mount_points, const MountPointMap&());
MOCK_METHOD0(RequestMountInfoRefresh, void());
MOCK_METHOD4(MountPath, void(const std::string&, const std::string&, const
std::string&, chromeos::MountType));
MOCK_METHOD3(UnmountPath, void(const std::string&, chromeos::UnmountOptions,
const UnmountPathCallback&));
MOCK_METHOD1(FormatMountedDevice, void(const std::string&));
*/
private:
DiskMap disks_;
};
#endif
class FakeImageWriterClient : public ImageWriterUtilityClient { class FakeImageWriterClient : public ImageWriterUtilityClient {
public: public:
FakeImageWriterClient(); FakeImageWriterClient();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment