Reduce the size of the stateful update by only copying dirs and some clean up.

Besides speeding up this step this also reduces the size necessray for a
stateful update since we only need the size of dev_image + var as scracth
space rather than a whole partition size (1 or 2GB before ... now ~ 128MB)

Change-Id: Ic566f89d44a29955707513b5cb2e4b7bf781fed6

BUG=8885
TEST=Ran it on a vm.

Review URL: http://codereview.chromium.org/4516004
diff --git a/autoupdate.py b/autoupdate.py
index b1d5ae7..a8e02fe 100644
--- a/autoupdate.py
+++ b/autoupdate.py
@@ -4,13 +4,14 @@
 
 from buildutil import BuildObject
 from xml.dom import minidom
-
 import cherrypy
 import os
 import shutil
-import socket
+import subprocess
+import tempfile
 import time
 
+
 def _LogMessage(message):
   cherrypy.log(message, 'UPDATE')
 
@@ -48,6 +49,7 @@
     self.src_image = src_image
     self.vm = vm
     self.board = board
+    self.crosutils = os.path.join(os.path.dirname(__file__), '../../scripts')
 
   def _GetSecondsSinceMidnight(self):
     """Returns the seconds since midnight as a decimal value."""
@@ -84,21 +86,6 @@
       return int(latest_tokens[i]) > int(client_tokens[i])
     return False
 
-  def _UnpackStatefulPartition(self, image_path, stateful_file):
-    """Given an image, unpacks its stateful partition to stateful_file."""
-    image_dir = os.path.dirname(image_path)
-    image_file = os.path.basename(image_path)
-
-    get_offset = '$(cgpt show -b -i 1 %s)' % image_file
-    get_size = '$(cgpt show -s -i 1 %s)' % image_file
-    unpack_command = (
-        'cd %s && '
-        'dd if=%s of=%s bs=512 skip=%s count=%s' % (image_dir, image_file,
-                                                    stateful_file, get_offset,
-                                                    get_size))
-    _LogMessage(unpack_command)
-    return os.system(unpack_command) == 0
-
   def _UnpackZip(self, image_dir):
     """Unpacks an image.zip into a given directory."""
     image = os.path.join(image_dir, self._GetImageName())
@@ -248,17 +235,51 @@
     Returns:
       Path to created stateful update_payload or None on error.
     """
-    stateful_partition_path = '%s/stateful.image' % os.path.dirname(image_path)
+    _LogMessage('Generating stateful update file.')
+    from_dir = os.path.dirname(image_path)
+    image = os.path.basename(image_path)
+    output_gz = os.path.join(from_dir, 'stateful.tgz')
 
-    # Unpack to get stateful partition.
-    if self._UnpackStatefulPartition(image_path, stateful_partition_path):
-      mkstatefulupdate_command = 'gzip -f %s' % stateful_partition_path
-      if os.system(mkstatefulupdate_command) == 0:
-        _LogMessage('Successfully generated %s.gz' % stateful_partition_path)
-        return '%s.gz' % stateful_partition_path
+    # Temporary directories for this function.
+    rootfs_dir = tempfile.mkdtemp(suffix='rootfs', prefix='tmp')
+    stateful_dir = tempfile.mkdtemp(suffix='stateful', prefix='tmp')
 
-    _LogMessage('Failed to create stateful update file')
-    return None
+    # Mount the image to pull out the important directories.
+    try:
+      # Only need stateful partition, but this saves us having to manage our
+      # own loopback device.
+      subprocess.check_call(['%s/mount_gpt_image.sh' % self.crosutils,
+                             '--from=%s' % from_dir,
+                             '--image=%s' % image,
+                             '--read_only',
+                             '--rootfs_mountpt=%s' % rootfs_dir,
+                             '--stateful_mountpt=%s' % stateful_dir,
+                            ])
+      _LogMessage('Tarring up /usr/local and /var!')
+      subprocess.check_call(['sudo',
+                             'tar',
+                             '-czf',
+                             output_gz,
+                             '--directory=%s' % stateful_dir,
+                             'dev_image',
+                             'var',
+                            ])
+    except:
+      _LogMessage('Failed to create stateful update file')
+      raise
+    finally:
+      # Unmount best effort regardless.
+      subprocess.call(['%s/mount_gpt_image.sh' % self.crosutils,
+                       '--unmount',
+                       '--rootfs_mountpt=%s' % rootfs_dir,
+                       '--stateful_mountpt=%s' % stateful_dir,
+                      ])
+      # Clean up our directories.
+      os.rmdir(rootfs_dir)
+      os.rmdir(stateful_dir)
+
+    _LogMessage('Successfully generated %s' % output_gz)
+    return output_gz
 
   def MoveImagesToStaticDir(self, update_path, stateful_update_path,
                             static_image_dir):