Merge "Making the emulator respect user-specified data partition size for API 19+" into studio-1.4-dev
diff --git a/Makefile.common b/Makefile.common
index 791ac7f..becf3a5 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -492,6 +492,7 @@
     android/cbuffer.c \
     android/charpipe.c \
     android/core-init-utils.c   \
+    android/ext4_resize.cpp   \
     android/gps.c \
     android/hw-kmsg.c \
     android/hw-lcd.c \
diff --git a/android/ext4_resize.cpp b/android/ext4_resize.cpp
new file mode 100644
index 0000000..cbeb206
--- /dev/null
+++ b/android/ext4_resize.cpp
@@ -0,0 +1,164 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef _WIN32
+#include "android/base/system/Win32Utils.h"
+#include "android/utils/win32_cmdline_quote.h"
+#include <windows.h>
+
+using android::base::Win32Utils;
+#else
+#include <sys/wait.h>
+#endif
+
+#include "android/base/String.h"
+#include "android/ext4_resize.h"
+#include "android/utils/path.h"
+#include "base/system/System.h"
+#include "main-common.h"
+
+using android::base::String;
+using android::base::System;
+
+// Convenience function for formatting and printing system call/library
+// function errors that show up regardless of host platform. Equivalent
+// to printing the stringified error code from errno or GetLastError()
+// (for windows).
+void explainSystemErrors (const char * msg) {
+#ifdef _WIN32
+    char *pstr = NULL;
+    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+        FORMAT_MESSAGE_ALLOCATE_BUFFER,
+        NULL,
+        GetLastError(),
+        0,
+        (LPTSTR) &pstr,
+        2,
+        NULL);
+    fprintf(stderr, "ERROR: %s - %s\n", msg, pstr);
+    LocalFree(pstr);
+#else
+    fprintf(stderr, "ERROR: %s - %s\n", msg, strerror(errno));
+#endif
+}
+
+int resizeExt4Partition (const char * partitionPath, int64_t newByteSize) {
+    // sanity checks
+    if (partitionPath == NULL || !checkExt4PartitionSize(newByteSize)) {
+        return -1;
+    }
+
+    // format common arguments once
+    String executable = System::get()->findBundledExecutable("resize2fs");
+    if(executable.empty()) {
+        fprintf(stderr, "ERROR: couldn't get path to resize2fs binary\n");
+        return -1;
+    }
+
+    char size_in_MB[50];
+    int copied = snprintf(size_in_MB, sizeof(size_in_MB),
+        "%uM", convertBytesToMB(newByteSize));
+    size_in_MB[sizeof(size_in_MB) - 1] = '\0';
+    if (copied < 0 || copied >= sizeof(size_in_MB)) {
+        fprintf(stderr, "ERROR: failed to format size in resize2fs command\n");
+        return -1;
+    }
+
+
+#ifdef _WIN32
+    STARTUPINFO           startup;
+    PROCESS_INFORMATION   pinfo;
+    DWORD                 exitCode;
+
+    ZeroMemory(&startup, sizeof(startup));
+    ZeroMemory(&pinfo, sizeof(pinfo));
+    startup.cb = sizeof(startup);
+
+    char args[PATH_MAX * 2 + 1];
+    copied = snprintf(args, sizeof(args), "resize2fs.exe -f %s %s",
+                 Win32Utils::quoteCommandLine(partitionPath).c_str(),
+                 size_in_MB);
+    args[sizeof(args) - 1] = '\0';
+    if (copied < 0 || copied >= sizeof(args)) {
+        fprintf(stderr, "ERROR: failed to format resize2fs command\n");
+        return -1;
+    }
+
+    BOOL success = CreateProcess(
+        Win32Utils::quoteCommandLine(executable.c_str()).c_str(), /* program path */
+        args,                                                /* command line args */
+        NULL,                                /* process handle is not inheritable */
+        NULL,                                 /* thread handle is not inheritable */
+        FALSE,                                   /* no, don't inherit any handles */
+        CREATE_NO_WINDOW,               /* the new process doesn't have a console */
+        NULL,                                   /* use parent's environment block */
+        NULL,                                  /* use parent's starting directory */
+        &startup,                               /* startup info, i.e. std handles */
+        &pinfo);
+    if (!success) {
+        explainSystemErrors("failed to create process while resizing partition");
+        return -2;
+    }
+
+    WaitForSingleObject(pinfo.hProcess, INFINITE);
+    if (!GetExitCodeProcess(pinfo.hProcess, &exitCode)) {
+        explainSystemErrors("couldn't get exit code from resizing partition process");
+        CloseHandle(pinfo.hProcess);
+        CloseHandle(pinfo.hThread);
+        return -2;
+    }
+    CloseHandle(pinfo.hProcess);
+    CloseHandle(pinfo.hThread);
+
+#else
+    int32_t exitCode = 0;
+    pid_t pid;
+    pid_t child = fork();
+
+    if (child < 0) {
+        explainSystemErrors("couldn't create a child process to resize the partition");
+        return -2;
+    }else if (child == 0) {
+        execlp(executable.c_str(), executable.c_str(), "-f", partitionPath,
+            size_in_MB, NULL);
+        exit(-1);
+    }
+
+    while ((pid = waitpid(-1, &exitCode, 0)) != child) {
+        if (pid == -1) {
+            explainSystemErrors("resizing partition waitpid failed");
+            return -2;
+        }
+    }
+#endif
+    if(exitCode != 0) {
+        fprintf(stderr, "ERROR: resizing partition failed with exit code %d\n",
+            exitCode);
+        return exitCode;
+    }
+    return 0;
+}
+
+bool checkExt4PartitionSize (int64_t byteSize) {
+    uint64_t maxSizeMB = 16 * 1024 * 1024; // (16 TiB) * (1024 GiB / TiB) * (1024 MiB / GiB)
+    uint64_t minSizeMB = 128;
+    uint64_t sizeMB = convertBytesToMB(byteSize);
+
+    //compiler converts signed to unsigned
+    return (sizeMB >= minSizeMB) && (sizeMB <= maxSizeMB);
+}
diff --git a/android/ext4_resize.h b/android/ext4_resize.h
new file mode 100644
index 0000000..47ad5af
--- /dev/null
+++ b/android/ext4_resize.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ANDROID_EXT4_RESIZE_H
+#define ANDROID_EXT4_RESIZE_H
+
+#include <android/base/Limits.h>
+#include <android/utils/compiler.h>
+#include <stdint.h>
+
+ANDROID_BEGIN_HEADER
+
+// Resize the partition in |partitionPath| to be newSize.
+//
+// |partitionPath| should hold the full, null-terminated path to the
+// desired partition.
+// |newByteSize| should be the desired new size of the partition in bytes,
+// no smaller than 128 MiB and no greater than 16 TiB. If the |newByteSize|
+// is below the minimum allowed for the specified partition, resize2fs will
+// still fail and the error code will be returned to the user
+//
+// Returns:
+// 		 0 - indicating the resize was successful
+//		-1 - indicating that formatting the arguments failed
+//		-2 - indicating a system call went wrong
+//		Otherwise the exit code of the resize2fs process is returned.
+int resizeExt4Partition(const char * partitionPath, int64_t newByteSize);
+
+// Returns true if |byteSize| is a valid ext4 partition size; i.e. within the
+// range of 128 MiB and 16 TiB inclusive, false otherwise.
+//
+// ext4 partitions have a theoretical limit of 1 EiB, although on 32-bit
+// systems the partition limit is reduced to 16 TiB due to the data range
+// of 32-bits, so enforce 16 TiB to minimize differences between systems of
+// different bitness.  The minimum size of an ext4 partition is 128 MiB.
+bool checkExt4PartitionSize (int64_t byteSize);
+
+ANDROID_END_HEADER
+
+#endif /* _ANDROID_EXT4_RESIZE_H */
diff --git a/vl-android.c b/vl-android.c
index 260da49..72f683d 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -46,24 +46,29 @@
 #include "sysemu/blockdev.h"
 #include "audio/audio.h"
 
-#include "migration/qemu-file.h"
 #include "android/android.h"
+#include "android/camera/camera-service.h"
 #include "android/charpipe.h"
-#include "android/log-rotate.h"
-#include "modem_driver.h"
+#include "android/display-core.h"
+#include "android/ext4_resize.h"
 #include "android/filesystems/ext4_utils.h"
 #include "android/filesystems/fstab_parser.h"
 #include "android/filesystems/partition_types.h"
 #include "android/filesystems/ramdisk_extractor.h"
+#include "android/globals.h"
 #include "android/gps.h"
 #include "android/hw-kmsg.h"
 #include "android/hw-pipe-net.h"
 #include "android/hw-qemud.h"
 #include "android/hw-sensors.h"
-#include "android/camera/camera-service.h"
+#include "android/log-rotate.h"
 #include "android/multitouch-port.h"
+#include "android/multitouch-screen.h"
+#include "android/opengles.h"
+#include "android/opengl/emugl_config.h"
 #include "android/skin/charmap.h"
-#include "android/globals.h"
+#include "android/snapshot.h"
+#include "android/tcpdump.h"
 #include "android/utils/bufprint.h"
 #include "android/utils/debug.h"
 #include "android/utils/filelock.h"
@@ -71,22 +76,17 @@
 #include "android/utils/socket_drainer.h"
 #include "android/utils/stralloc.h"
 #include "android/utils/tempfile.h"
-#include "android/wear-agent/android_wear_agent.h"
-#include "android/display-core.h"
 #include "android/utils/timezone.h"
-#include "android/snapshot.h"
-#include "android/opengles.h"
-#include "android/opengl/emugl_config.h"
-#include "android/multitouch-screen.h"
+#include "android/wear-agent/android_wear_agent.h"
 #include "exec/hwaddr.h"
-#include "android/tcpdump.h"
-
-#include <unistd.h>
+#include "migration/qemu-file.h"
+#include "modem_driver.h"
+#include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
-#include <time.h>
-#include <errno.h>
 #include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
 #include <zlib.h>
 
 /* Needed early for CONFIG_BSD etc. */
@@ -3127,6 +3127,18 @@
                            android_hw->disk_systemPartition_path,
                            android_hw->disk_systemPartition_initPath);
 
+    /* For ext4, to extend an internal partition to more than the default size
+     * you need to initialize userdata-qemu.img to the desired size and restore
+     * it after moving the data in - yaffs is resilient enough for this not to
+     * matter
+    */
+    if(android_op_wipe_data &&
+            userdata_partition_type == ANDROID_PARTITION_TYPE_EXT4) {
+        androidPartitionType_makeEmptyFile(userdata_partition_type,
+                                           android_hw->disk_dataPartition_size,
+                                           android_hw->disk_dataPartition_path);
+    }
+
     /* Initialize data partition image */
     android_nand_add_image("userdata",
                            userdata_partition_type,
@@ -3135,6 +3147,15 @@
                            android_hw->disk_dataPartition_path,
                            android_hw->disk_dataPartition_initPath);
 
+    /* Extend the userdata-qemu.img to the desired size - resize2fs can only
+     * extend partitions to fill available space
+    */
+    if(android_op_wipe_data &&
+            userdata_partition_type == ANDROID_PARTITION_TYPE_EXT4) {
+        resizeExt4Partition(android_hw->disk_dataPartition_path,
+                            android_hw->disk_dataPartition_size);
+    }
+
     /* Initialize cache partition image, if any. Its type depends on the
      * kernel version. For anything >= 3.10, it must be EXT4, or
      * YAFFS2 otherwise.