Adds ImageWriterPrivate.destroyPartitions operation.

BUG=328246

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242452 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc
new file mode 100644
index 0000000..127b3b0
--- /dev/null
+++ b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc
@@ -0,0 +1,51 @@
+// Copyright 2013 The Chromium 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 "base/file_util.h"
+#include "chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h"
+#include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
+
+namespace extensions {
+namespace image_writer {
+
+// Number of bytes for the maximum partition table size.  By wiping this many
+// bytes we can essentially guarantee the header and associated information will
+// be wiped. See http://crbug.com/328246 for more information.
+const int kPartitionTableSize = 1 * 1024;
+
+DestroyPartitionsOperation::DestroyPartitionsOperation(
+    base::WeakPtr<OperationManager> manager,
+    const ExtensionId& extension_id,
+    const std::string& storage_unit_id)
+    : Operation(manager, extension_id, storage_unit_id) {
+  verify_write_ = false;
+}
+
+DestroyPartitionsOperation::~DestroyPartitionsOperation() {}
+
+void DestroyPartitionsOperation::Start() {
+  if (!temp_dir_.CreateUniqueTempDir()) {
+    Error(error::kTempDirError);
+    return;
+  }
+
+  if (!base::CreateTemporaryFileInDir(temp_dir_.path(), &image_path_)) {
+    Error(error::kTempFileError);
+    return;
+  }
+
+  scoped_ptr<char[]> buffer(new char[kPartitionTableSize]);
+  memset(buffer.get(), 0, kPartitionTableSize);
+
+  if (file_util::WriteFile(image_path_, buffer.get(), kPartitionTableSize) !=
+      kPartitionTableSize) {
+    Error(error::kTempFileError);
+    return;
+  }
+
+  WriteStart();
+}
+
+}  // namespace image_writer
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h
new file mode 100644
index 0000000..1da35da
--- /dev/null
+++ b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h
@@ -0,0 +1,33 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_DESTROY_PARTITIONS_OPERATION_H_
+#define CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_DESTROY_PARTITIONS_OPERATION_H_
+
+#include "base/files/scoped_temp_dir.h"
+#include "chrome/browser/extensions/api/image_writer_private/operation.h"
+
+namespace extensions {
+namespace image_writer {
+
+extern const int kPartitionTableSize;
+
+// Encapsulates an operation for destroying partitions.  This is achieved by
+// creating a dummy blank image which is then burned to the disk.
+class DestroyPartitionsOperation : public Operation {
+ public:
+  DestroyPartitionsOperation(base::WeakPtr<OperationManager> manager,
+                             const ExtensionId& extension_id,
+                             const std::string& storage_unit_id);
+  virtual void Start() OVERRIDE;
+
+ private:
+  virtual ~DestroyPartitionsOperation();
+  base::ScopedTempDir temp_dir_;
+};
+
+}  // namespace image_writer
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_DESTROY_PARTITIONS_OPERATION_H_
diff --git a/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation_unittest.cc
new file mode 100644
index 0000000..1e1e48e
--- /dev/null
+++ b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright 2013 The Chromium 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 "base/run_loop.h"
+#include "chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h"
+#include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
+#include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
+
+namespace extensions {
+namespace image_writer {
+
+using testing::_;
+using testing::AnyNumber;
+
+namespace {
+
+class ImageWriterDestroyPartitionsOperationTest
+    : public ImageWriterUnitTestBase {
+};
+
+// Tests that the DestroyPartitionsOperation can successfully zero the first
+// kPartitionTableSize bytes of an image.
+TEST_F(ImageWriterDestroyPartitionsOperationTest, DestroyPartitions) {
+  MockOperationManager manager;
+  base::RunLoop loop;
+
+  scoped_refptr<DestroyPartitionsOperation> operation(
+      new DestroyPartitionsOperation(manager.AsWeakPtr(),
+                                     kDummyExtensionId,
+                                     test_device_path_.AsUTF8Unsafe()));
+
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+  EXPECT_CALL(manager, OnProgress(kDummyExtensionId, _, _)).Times(0);
+  EXPECT_CALL(manager, OnProgress(kDummyExtensionId,
+                                  image_writer_api::STAGE_WRITE,
+                                  _)).Times(AnyNumber());
+  EXPECT_CALL(manager, OnComplete(kDummyExtensionId)).Times(1);
+  EXPECT_CALL(manager, OnError(kDummyExtensionId, _, _, _)).Times(0);
+#else
+  EXPECT_CALL(manager, OnProgress(kDummyExtensionId, _, _)).Times(0);
+  EXPECT_CALL(manager, OnComplete(kDummyExtensionId)).Times(0);
+  EXPECT_CALL(manager, OnError(kDummyExtensionId,
+                               _,
+                               _,
+                               error::kUnsupportedOperation)).Times(1);
+#endif
+
+  operation->Start();
+
+  loop.RunUntilIdle();
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  scoped_ptr<char[]> image_data(new char[kPartitionTableSize]);
+  scoped_ptr<char[]> zeroes(new char[kPartitionTableSize]);
+  memset(zeroes.get(), 0, kPartitionTableSize);
+  ASSERT_EQ(kPartitionTableSize, base::ReadFile(test_device_path_,
+                                                image_data.get(),
+                                                kPartitionTableSize));
+  EXPECT_EQ(0, memcmp(image_data.get(), zeroes.get(), kPartitionTableSize));
+#endif
+}
+
+} // namespace
+} // namespace image_writer
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.cc b/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.cc
index fffd1ac..3d7fd42 100644
--- a/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.cc
+++ b/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.cc
@@ -151,10 +151,25 @@
       image_writer_api::DestroyPartitions::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  SendResponse(true);
+  image_writer::OperationManager::Get(GetProfile())->DestroyPartitions(
+      extension_id(),
+      params->storage_unit_id,
+      base::Bind(
+          &ImageWriterPrivateDestroyPartitionsFunction::OnDestroyComplete,
+          this));
   return true;
 }
 
+void ImageWriterPrivateDestroyPartitionsFunction::OnDestroyComplete(
+    bool success,
+    const std::string& error) {
+  if (!success) {
+    error_ = error;
+  }
+
+  SendResponse(success);
+}
+
 ImageWriterPrivateListRemovableStorageDevicesFunction::
   ImageWriterPrivateListRemovableStorageDevicesFunction() {
 }
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.h b/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.h
index 4789d18..9cccd0fc 100644
--- a/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.h
+++ b/chrome/browser/extensions/api/image_writer_private/image_writer_private_api.h
@@ -59,6 +59,7 @@
  private:
   virtual ~ImageWriterPrivateDestroyPartitionsFunction();
   virtual bool RunImpl() OVERRIDE;
+  void OnDestroyComplete(bool success, const std::string& error);
 };
 
 class ImageWriterPrivateListRemovableStorageDevicesFunction
diff --git a/chrome/browser/extensions/api/image_writer_private/operation.cc b/chrome/browser/extensions/api/image_writer_private/operation.cc
index 743f797..4a57c7d2 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation.cc
@@ -32,6 +32,7 @@
     : manager_(manager),
       extension_id_(extension_id),
       storage_unit_id_(storage_unit_id),
+      verify_write_(true),
       stage_(image_writer_api::STAGE_UNKNOWN),
       progress_(0) {
 }
diff --git a/chrome/browser/extensions/api/image_writer_private/operation.h b/chrome/browser/extensions/api/image_writer_private/operation.h
index 7e45fe1..ce62f65d 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation.h
+++ b/chrome/browser/extensions/api/image_writer_private/operation.h
@@ -110,6 +110,9 @@
   base::FilePath image_path_;
   const std::string storage_unit_id_;
 
+  // Whether or not to run the final verification step.
+  bool verify_write_;
+
  private:
   friend class base::RefCountedThreadSafe<Operation>;
 
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_linux.cc b/chrome/browser/extensions/api/image_writer_private/operation_linux.cc
index 798dea3..df34834b 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_linux.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_linux.cc
@@ -138,11 +138,15 @@
   DVLOG(2) << "Completed write of " << image_path_.value();
   SetProgress(kProgressComplete);
 
-  BrowserThread::PostTask(
-    BrowserThread::FILE,
-    FROM_HERE,
-    base::Bind(&Operation::VerifyWriteStart,
-               this));
+  if (verify_write_) {
+    BrowserThread::PostTask(BrowserThread::FILE,
+                            FROM_HERE,
+                            base::Bind(&Operation::VerifyWriteStart, this));
+  } else {
+    BrowserThread::PostTask(BrowserThread::FILE,
+                            FROM_HERE,
+                            base::Bind(&Operation::Finish, this));
+  }
 }
 
 void Operation::VerifyWriteStart() {
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager.cc b/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
index b1b1c44..2b525e56 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
@@ -5,6 +5,7 @@
 #include "base/lazy_instance.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h"
 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
 #include "chrome/browser/extensions/api/image_writer_private/operation.h"
 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
@@ -63,7 +64,6 @@
     bool saveImageAsDownload,
     const std::string& storage_unit_id,
     const Operation::StartWriteCallback& callback) {
-
   OperationMap::iterator existing_operation = operations_.find(extension_id);
 
   if (existing_operation != operations_.end()) {
@@ -78,14 +78,10 @@
                                 hash,
                                 saveImageAsDownload,
                                 storage_unit_id));
-
   operations_[extension_id] = operation;
-
   BrowserThread::PostTask(BrowserThread::FILE,
                           FROM_HERE,
-                          base::Bind(&Operation::Start,
-                                     operation.get()));
-
+                          base::Bind(&Operation::Start, operation));
   callback.Run(true, "");
 }
 
@@ -105,14 +101,10 @@
                                  extension_id,
                                  path,
                                  storage_unit_id));
-
   operations_[extension_id] = operation;
-
   BrowserThread::PostTask(BrowserThread::FILE,
                           FROM_HERE,
-                          base::Bind(&Operation::Start,
-                                     operation.get()));
-
+                          base::Bind(&Operation::Start, operation));
   callback.Run(true, "");
 }
 
@@ -132,6 +124,27 @@
   }
 }
 
+void OperationManager::DestroyPartitions(
+    const ExtensionId& extension_id,
+    const std::string& storage_unit_id,
+    const Operation::StartWriteCallback& callback) {
+  OperationMap::iterator existing_operation = operations_.find(extension_id);
+
+  if (existing_operation != operations_.end()) {
+    return callback.Run(false, error::kOperationAlreadyInProgress);
+  }
+
+  scoped_refptr<Operation> operation(
+      new DestroyPartitionsOperation(weak_factory_.GetWeakPtr(),
+                                     extension_id,
+                                     storage_unit_id));
+  operations_[extension_id] = operation;
+  BrowserThread::PostTask(BrowserThread::FILE,
+                          FROM_HERE,
+                          base::Bind(&Operation::Start, operation));
+  callback.Run(true, "");
+}
+
 void OperationManager::OnProgress(const ExtensionId& extension_id,
                                   image_writer_api::Stage stage,
                                   int progress) {
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager.h b/chrome/browser/extensions/api/image_writer_private/operation_manager.h
index 56655fcb..00489bc 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager.h
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager.h
@@ -61,6 +61,11 @@
   void CancelWrite(const ExtensionId& extension_id,
                    const Operation::CancelWriteCallback& callback);
 
+  // Starts a write that removes the partition table.
+  void DestroyPartitions(const ExtensionId& extension_id,
+                         const std::string& storage_unit_id,
+                         const Operation::StartWriteCallback& callback);
+
   // Callback for progress events.
   virtual void OnProgress(const ExtensionId& extension_id,
                           image_writer_api::Stage stage,
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc b/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
index ed9c3f3..80ccfa6 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
@@ -5,8 +5,8 @@
 #include "base/command_line.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
+#include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
 #include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
-#include "chrome/browser/extensions/api/image_writer_private/write_from_file_operation.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/test/base/testing_profile.h"
@@ -25,6 +25,8 @@
 using testing::AnyNumber;
 using testing::AtLeast;
 
+namespace {
+
 class ImageWriterOperationManagerTest
     : public ImageWriterUnitTestBase {
  public:
@@ -68,8 +70,8 @@
 
   manager.StartWriteFromFile(
     kDummyExtensionId,
-    test_image_,
-    test_device_.AsUTF8Unsafe(),
+    test_image_path_,
+    test_device_path_.AsUTF8Unsafe(),
     base::Bind(&ImageWriterOperationManagerTest::StartCallback,
                base::Unretained(this)));
 
@@ -78,5 +80,20 @@
   EXPECT_EQ("", start_error_);
 }
 
-}  // namespace image_writer
-}  // namespace extensions
+TEST_F(ImageWriterOperationManagerTest, DestroyPartitions) {
+  OperationManager manager(&test_profile_);
+
+  manager.DestroyPartitions(
+      kDummyExtensionId,
+      test_device_path_.AsUTF8Unsafe(),
+      base::Bind(&ImageWriterOperationManagerTest::StartCallback,
+                 base::Unretained(this)));
+
+  EXPECT_TRUE(started_);
+  EXPECT_TRUE(start_success_);
+  EXPECT_EQ("", start_error_);
+}
+
+} // namespace
+} // namespace image_writer
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc
index 0df18fb..49ccd88 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc
@@ -8,6 +8,7 @@
 
 namespace extensions {
 namespace image_writer {
+namespace {
 
 class ImageWriterOperationTest : public ImageWriterUnitTestBase {
 };
@@ -25,13 +26,15 @@
 
 TEST_F(ImageWriterOperationTest, Create) {
   MockOperationManager manager;
-  scoped_refptr<Operation> op(new DummyOperation(manager.AsWeakPtr(),
-                                                 kDummyExtensionId,
-                                                 test_device_.AsUTF8Unsafe()));
+  scoped_refptr<Operation> op(
+      new DummyOperation(manager.AsWeakPtr(),
+                         kDummyExtensionId,
+                         test_device_path_.AsUTF8Unsafe()));
 
   EXPECT_EQ(0, op->GetProgress());
   EXPECT_EQ(image_writer_api::STAGE_UNKNOWN, op->GetStage());
 }
 
+}  // namespace
 }  // namespace image_writer
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/image_writer_private/test_utils.cc b/chrome/browser/extensions/api/image_writer_private/test_utils.cc
index 651ce0a..caef818c 100644
--- a/chrome/browser/extensions/api/image_writer_private/test_utils.cc
+++ b/chrome/browser/extensions/api/image_writer_private/test_utils.cc
@@ -4,26 +4,119 @@
 
 #include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
 
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_dbus_thread_manager.h"
+#include "chromeos/dbus/fake_image_burner_client.h"
+#endif
+
 namespace extensions {
 namespace image_writer {
 
-MockOperationManager::MockOperationManager()
-    : OperationManager(NULL) {
-}
+#if defined(OS_CHROMEOS)
+namespace {
 
-MockOperationManager::~MockOperationManager() {
-}
+class ImageWriterFakeImageBurnerClient
+    : public chromeos::FakeImageBurnerClient {
+ public:
+  ImageWriterFakeImageBurnerClient() {}
+  virtual ~ImageWriterFakeImageBurnerClient() {}
+
+  virtual void SetEventHandlers(
+      const BurnFinishedHandler& burn_finished_handler,
+      const BurnProgressUpdateHandler& burn_progress_update_handler) OVERRIDE {
+    burn_finished_handler_ = burn_finished_handler;
+    burn_progress_update_handler_ = burn_progress_update_handler;
+  }
+
+  virtual void BurnImage(const std::string& from_path,
+                         const std::string& to_path,
+                         const ErrorCallback& error_callback) OVERRIDE {
+    base::MessageLoop::current()->PostTask(FROM_HERE,
+        base::Bind(burn_progress_update_handler_, to_path, 0, 100));
+    base::MessageLoop::current()->PostTask(FROM_HERE,
+        base::Bind(burn_progress_update_handler_, to_path, 50, 100));
+    base::MessageLoop::current()->PostTask(FROM_HERE,
+        base::Bind(burn_progress_update_handler_, to_path, 100, 100));
+    base::MessageLoop::current()->PostTask(FROM_HERE,
+        base::Bind(burn_finished_handler_, to_path, true, ""));
+  }
+
+ private:
+  BurnFinishedHandler burn_finished_handler_;
+  BurnProgressUpdateHandler burn_progress_update_handler_;
+
+};
+
+} // namespace
+#endif
+
+MockOperationManager::MockOperationManager()
+    : OperationManager(NULL) {}
+
+MockOperationManager::~MockOperationManager() {}
+
+ImageWriterUnitTestBase::ImageWriterUnitTestBase() {}
+ImageWriterUnitTestBase::~ImageWriterUnitTestBase() {}
 
 void ImageWriterUnitTestBase::SetUp() {
-  ASSERT_TRUE(base::CreateTemporaryFile(&test_image_));
-  ASSERT_TRUE(base::CreateTemporaryFile(&test_device_));
+  testing::Test::SetUp();
+  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
 
-  ASSERT_EQ(32, file_util::WriteFile(test_image_, kTestData, 32));
+  ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
+                                             &test_image_path_));
+  ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
+                                             &test_device_path_));
+
+  ASSERT_TRUE(FillFile(test_image_path_, kImagePattern, kTestFileSize));
+  ASSERT_TRUE(FillFile(test_device_path_, kDevicePattern, kTestFileSize));
+
+#if defined(OS_CHROMEOS)
+  if (!chromeos::DBusThreadManager::IsInitialized()) {
+    chromeos::FakeDBusThreadManager* fake_dbus_thread_manager =
+        new chromeos::FakeDBusThreadManager;
+    scoped_ptr<chromeos::ImageBurnerClient>
+        image_burner_fake(new ImageWriterFakeImageBurnerClient());
+    fake_dbus_thread_manager->SetImageBurnerClient(image_burner_fake.Pass());
+    chromeos::DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager);
+  }
+#endif
 }
 
-void ImageWriterUnitTestBase::TearDown() {
-  base::DeleteFile(test_image_, false);
-  base::DeleteFile(test_device_, false);
+void ImageWriterUnitTestBase::TearDown() {}
+
+bool ImageWriterUnitTestBase::CompareImageAndDevice() {
+  scoped_ptr<char[]> image_buffer(new char[kTestFileSize]);
+  scoped_ptr<char[]> device_buffer(new char[kTestFileSize]);
+
+  while (true) {
+    int image_bytes_read = ReadFile(test_image_path_,
+                                    image_buffer.get(),
+                                    kTestFileSize);
+    int device_bytes_read = ReadFile(test_device_path_,
+                                     device_buffer.get(),
+                                     kTestFileSize);
+
+    if (image_bytes_read != device_bytes_read)
+      return false;
+
+    if (image_bytes_read == 0)
+      return true;
+
+    if (memcmp(image_buffer.get(), device_buffer.get(), image_bytes_read) != 0)
+      return false;
+  }
+
+  return false;
+}
+
+bool ImageWriterUnitTestBase::FillFile(const base::FilePath& file,
+                                       const int pattern,
+                                       const int length) {
+  scoped_ptr<char[]> buffer(new char[length]);
+  memset(buffer.get(), pattern, length);
+
+  return file_util::WriteFile(file, buffer.get(), length) == length;
 }
 
 }  // namespace image_writer
diff --git a/chrome/browser/extensions/api/image_writer_private/test_utils.h b/chrome/browser/extensions/api/image_writer_private/test_utils.h
index 88ccf492..b290e88 100644
--- a/chrome/browser/extensions/api/image_writer_private/test_utils.h
+++ b/chrome/browser/extensions/api/image_writer_private/test_utils.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_TEST_UTILS_H_
 
 #include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
@@ -17,8 +18,16 @@
 namespace image_writer {
 
 const char kDummyExtensionId[] = "DummyExtension";
-const char kTestData[] = "This is some test data, padded.  ";
 
+// Default file size to use in tests.  Currently 32kB.
+const int kTestFileSize = 32 * 1024;
+// Pattern to use in the image file.
+const int kImagePattern = 0x55555555; // 01010101
+// Pattern to use in the device file.
+const int kDevicePattern = 0xAAAAAAAA; // 10101010
+
+// A mock around the operation manager for tracking callbacks.  Note that there
+// are non-virtual methods on this class that should not be called in tests.
 class MockOperationManager : public OperationManager {
  public:
   MockOperationManager();
@@ -37,15 +46,32 @@
                              const std::string& error_message));
 };
 
+// Base class for unit tests that manages creating image and device files.
 class ImageWriterUnitTestBase : public testing::Test {
  public:
+  ImageWriterUnitTestBase();
+  virtual ~ImageWriterUnitTestBase();
+
+ protected:
   virtual void SetUp() OVERRIDE;
 
   virtual void TearDown() OVERRIDE;
 
-  base::FilePath test_image_;
-  base::FilePath test_device_;
+  // Compare the image and device files, returning true if they are the same,
+  // false if different.
+  bool CompareImageAndDevice();
+
+  base::ScopedTempDir temp_dir_;
+  base::FilePath test_image_path_;
+  base::FilePath test_device_path_;
+
  private:
+  // Fills |file| with |length| bytes of |pattern|, overwriting any existing
+  // data.
+  bool FillFile(const base::FilePath& file,
+                const int pattern,
+                const int length);
+
   content::TestBrowserThreadBundle thread_bundle_;
 };
 
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc
index c24484d..223d731 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc
@@ -23,10 +23,10 @@
   scoped_refptr<WriteFromFileOperation> op = new WriteFromFileOperation(
     manager.AsWeakPtr(),
     kDummyExtensionId,
-    test_image_,
-    test_device_.AsUTF8Unsafe());
+    test_image_path_,
+    test_device_path_.AsUTF8Unsafe());
 
-  base::DeleteFile(test_image_, false);
+  base::DeleteFile(test_image_path_, false);
 
   EXPECT_CALL(manager, OnProgress(kDummyExtensionId, _, _)).Times(0);
   EXPECT_CALL(manager, OnComplete(kDummyExtensionId)).Times(0);
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index d0e586d..0b3797fc 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -319,9 +319,11 @@
         'browser/extensions/api/idltest/idltest_api.h',
         'browser/extensions/api/image_writer_private/error_messages.cc',
         'browser/extensions/api/image_writer_private/error_messages.h',
+        'browser/extensions/api/image_writer_private/destroy_partitions_operation.cc',
+        'browser/extensions/api/image_writer_private/destroy_partitions_operation.h',
         'browser/extensions/api/image_writer_private/operation.cc',
-        'browser/extensions/api/image_writer_private/operation_chromeos.cc',
         'browser/extensions/api/image_writer_private/operation.h',
+        'browser/extensions/api/image_writer_private/operation_chromeos.cc',
         'browser/extensions/api/image_writer_private/operation_linux.cc',
         'browser/extensions/api/image_writer_private/operation_mac.cc',
         'browser/extensions/api/image_writer_private/operation_manager.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index a182db3..45ebdfb 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -848,6 +848,7 @@
         'browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc',
         'browser/extensions/api/identity/identity_mint_queue_unittest.cc',
         'browser/extensions/api/idle/idle_api_unittest.cc',
+        'browser/extensions/api/image_writer_private/destroy_partitions_operation_unittest.cc',
         'browser/extensions/api/image_writer_private/operation_manager_unittest.cc',
         'browser/extensions/api/image_writer_private/operation_unittest.cc',
         'browser/extensions/api/image_writer_private/test_utils.cc',
diff --git a/chrome/common/extensions/api/image_writer_private.idl b/chrome/common/extensions/api/image_writer_private.idl
index e46eefe..9fcd3d2 100644
--- a/chrome/common/extensions/api/image_writer_private.idl
+++ b/chrome/common/extensions/api/image_writer_private.idl
@@ -101,11 +101,11 @@
 
     // Destroys the partition table of a disk, effectively erasing it.  This is
     // a fairly quick operation and so it does not have complex stages or
-    // progress information.  However, it can fail and call the callback with
-    // an error.
+    // progress information, just a write phase.
     //
     // |storageUnitId|: The identifier of the storage unit to wipe
-    // |callback|: A callback which is called when the operation is complete.
+    // |callback|: A callback that triggers when the operation has been
+    // successfully started.
     static void destroyPartitions(DOMString storageUnitId,
                                   DestroyPartitionsCallback callback);