Merge changes I636784f7,I6bbc2e31,Ia9d986d1,I4e8178ce

* changes:
  Parse fstab.goldfish file from ramdisk.img
  androidPartitionType_fromString(): new function.
  android/filesystems/fstab_parser.h: New file.
  android/filesystems/ramdisk_extractor.h: new file.
diff --git a/Makefile.common b/Makefile.common
index 17f886e..2d8e0b1 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -123,7 +123,9 @@
 	android/base/StringView.cpp \
 	android/emulation/CpuAccelerator.cpp \
 	android/filesystems/ext4_utils.cpp \
+	android/filesystems/fstab_parser.cpp \
 	android/filesystems/partition_types.cpp \
+	android/filesystems/ramdisk_extractor.cpp \
 	android/kernel/kernel_utils.cpp \
 	android/utils/assert.c \
 	android/utils/bufprint.c \
diff --git a/Makefile.tests b/Makefile.tests
index b9a5a28..bcaca81 100644
--- a/Makefile.tests
+++ b/Makefile.tests
@@ -23,7 +23,9 @@
   android/base/StringView_unittest.cpp \
   android/emulation/CpuAccelerator_unittest.cpp \
   android/filesystems/ext4_utils_unittest.cpp \
+  android/filesystems/fstab_parser_unittest.cpp \
   android/filesystems/partition_types_unittest.cpp \
+  android/filesystems/ramdisk_extractor_unittest.cpp \
   android/filesystems/testing/TestSupport.cpp \
   android/kernel/kernel_utils_unittest.cpp \
 
diff --git a/android/filesystems/fstab_parser.cpp b/android/filesystems/fstab_parser.cpp
new file mode 100644
index 0000000..2566fe2
--- /dev/null
+++ b/android/filesystems/fstab_parser.cpp
@@ -0,0 +1,104 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#include "android/filesystems/fstab_parser.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+namespace {
+
+const char* skipWhitespace(const char* p, const char* end) {
+    while (p < end && isspace(*p)) {
+        p++;
+    }
+    return p;
+}
+
+const char* skipNonWhitespace(const char* p, const char* end) {
+    while (p < end && !isspace(*p)) {
+        p++;
+    }
+    return p;
+}
+
+const char* skipExpectedToken(const char* p, const char* end) {
+    p = skipWhitespace(p, end);
+    if (p == end) {
+        return NULL;
+    }
+    return skipNonWhitespace(p, end);
+}
+
+size_t getTokenLen(const char* p, const char* end) {
+    const char* q = skipNonWhitespace(p, end);
+    return (size_t)(q - p);
+}
+
+}  // namespace
+
+bool android_parseFstabPartitionFormat(const char* fstabData,
+                                       size_t fstabSize,
+                                       const char* partitionName,
+                                       char** out) {
+    const char* p = fstabData;
+    const char* end = p + fstabSize;
+
+    size_t partitionNameLen = strlen(partitionName);
+
+    while (p < end) {
+        // Find end of current line, and start of next one.
+        const char* line = p;
+        const char* line_end = ::strchr(p, '\n');
+        if (!line_end) {
+            line_end = end;
+            p = end;
+        } else {
+            p = line_end + 1;
+        }
+
+        // Skip empty or comment lines.
+        line = skipWhitespace(line, line_end);
+        if (line == line_end || line[0] == '#') {
+            continue;
+        }
+
+        // expected format: <device><ws><partition><ws><format><ws><options>
+
+        // skip over device name.
+        line = skipExpectedToken(line, line_end);
+        if (!line) {
+            continue;
+        }
+
+        line = skipWhitespace(line, line_end);
+        size_t tokenLen = getTokenLen(line, line_end);
+        if (tokenLen != partitionNameLen ||
+            memcmp(line, partitionName, tokenLen) != 0) {
+            // Not the right partition.
+            continue;
+        }
+
+        line = skipWhitespace(line + tokenLen, line_end);
+        size_t formatLen = getTokenLen(line, line_end);
+        if (formatLen == 0) {
+            // Malformed data.
+            return false;
+        }
+
+        *out = static_cast<char*>(malloc(formatLen + 1U));
+        memcpy(*out, line, formatLen);
+        (*out)[formatLen] = '\0';
+        return true;
+    }
+    return false;
+}
diff --git a/android/filesystems/fstab_parser.h b/android/filesystems/fstab_parser.h
new file mode 100644
index 0000000..274764b
--- /dev/null
+++ b/android/filesystems/fstab_parser.h
@@ -0,0 +1,32 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#ifndef ANDROID_FILESYSTEMS_FSTAB_PARSER_H
+#define ANDROID_FILESYSTEMS_FSTAB_PARSER_H
+
+#include "android/utils/compiler.h"
+
+#include <stddef.h>
+
+ANDROID_BEGIN_HEADER
+
+// Parse the Linux fstab file at |fstabData| of |fstabSize| bytes
+// and extract the format of the partition named |partitionName|.
+// On success, return true and sets |*outFormat| to a heap-allocated
+// string that must be freed by the caller. On failure, return false.
+bool android_parseFstabPartitionFormat(const char* fstabData,
+                                       size_t fstabDize,
+                                       const char* partitionName,
+                                       char** outFormat);
+
+ANDROID_END_HEADER
+
+#endif  // ANDROID_FILESYSTEMS_FSTAB_PARSER_H
diff --git a/android/filesystems/fstab_parser_unittest.cpp b/android/filesystems/fstab_parser_unittest.cpp
new file mode 100644
index 0000000..286abbe
--- /dev/null
+++ b/android/filesystems/fstab_parser_unittest.cpp
@@ -0,0 +1,47 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#include "android/filesystems/fstab_parser.h"
+
+#include <gtest/gtest.h>
+
+TEST(FstabParser, ParsePartitionFormat) {
+    static const char kFstab[] =
+        "# Android fstab file.\n"
+        "#<src>                                                  <mnt_point>         <type>    <mnt_flags and options>                              <fs_mgr_flags>\n"
+        "# The filesystem that contains the filesystem checker binary (typically /system) cannot\n"
+        "# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK\n"
+        "/dev/block/mtdblock0                                    /system             ext4      ro,barrier=1                                         wait\n"
+        "/dev/block/mtdblock1\t      \t                          /data\t             yaffs2    noatime,nosuid,nodev,barrier=1,nomblk_io_submit      wait,check\n"
+        "/dev/block/mtdblock2\t/cache\tntfs\tnoatime,nosuid,nodev  wait,check\n"
+        "/devices/platform/goldfish_mmc.0\t\t\tauto\tvfat      defaults                                             voldmanaged=sdcard:auto\n"
+        ;
+    static const size_t kFstabSize = sizeof(kFstab);
+
+    char* out = NULL;
+    EXPECT_TRUE(android_parseFstabPartitionFormat(kFstab, kFstabSize,
+                                                  "/system", &out));
+    EXPECT_STREQ("ext4", out);
+    free(out);
+
+    EXPECT_TRUE(android_parseFstabPartitionFormat(kFstab, kFstabSize,
+                                                  "/data", &out));
+    EXPECT_STREQ("yaffs2", out);
+    free(out);
+
+    EXPECT_TRUE(android_parseFstabPartitionFormat(kFstab, kFstabSize,
+                                                  "/cache", &out));
+    EXPECT_STREQ("ntfs", out);
+    free(out);
+
+    EXPECT_FALSE(android_parseFstabPartitionFormat(kFstab, kFstabSize,
+                                                   "/unknown", &out));
+}
diff --git a/android/filesystems/partition_types.cpp b/android/filesystems/partition_types.cpp
index f0dfba7..40d59ce 100644
--- a/android/filesystems/partition_types.cpp
+++ b/android/filesystems/partition_types.cpp
@@ -17,19 +17,40 @@
 
 #include <errno.h>
 
+namespace {
+
+const struct {
+    const char* name;
+    AndroidPartitionType value;
+} kPartitionTypeMap[] = {
+    { "unknown", ANDROID_PARTITION_TYPE_UNKNOWN },
+    { "yaffs2", ANDROID_PARTITION_TYPE_YAFFS2 },
+    { "ext4", ANDROID_PARTITION_TYPE_EXT4 },
+};
+
+const size_t kPartitionTypeMapSize =
+        sizeof(kPartitionTypeMap) / sizeof(kPartitionTypeMap[0]);
+
+}  // namespace
+
 const char* androidPartitionType_toString(AndroidPartitionType part_type) {
-    switch (part_type) {
-        case ANDROID_PARTITION_TYPE_UNKNOWN:
-            return "unknown";
-        case ANDROID_PARTITION_TYPE_YAFFS2:
-            return "yaffs2";
-        case ANDROID_PARTITION_TYPE_EXT4:
-            return "ext4";
-        default:
-            APANIC("Invalid partition type value %d", part_type);
+    for (size_t n = 0; n < kPartitionTypeMapSize; ++n) {
+        if (kPartitionTypeMap[n].value == part_type) {
+            return kPartitionTypeMap[n].name;
+        }
     }
+    APANIC("Invalid partition type value %d", part_type);
+    return "unknown";
 }
 
+AndroidPartitionType androidPartitionType_fromString(const char* part_type) {
+    for (size_t n = 0; n < kPartitionTypeMapSize; ++n) {
+        if (!strcmp(kPartitionTypeMap[n].name, part_type)) {
+            return kPartitionTypeMap[n].value;
+        }
+    }
+    return ANDROID_PARTITION_TYPE_UNKNOWN;
+}
 
 AndroidPartitionType androidPartitionType_probeFile(const char* image_file) {
     if (!path_exists(image_file)) {
diff --git a/android/filesystems/partition_types.h b/android/filesystems/partition_types.h
index 9bbdcf4..853b389 100644
--- a/android/filesystems/partition_types.h
+++ b/android/filesystems/partition_types.h
@@ -29,6 +29,9 @@
 // Note: this will panic if |part_type| is an invalid value.
 const char* androidPartitionType_toString(AndroidPartitionType part_type);
 
+// Return an AndroidPartitionType from a string description.
+AndroidPartitionType androidPartitionType_fromString(const char* part_type);
+
 // Probe a given image file and return its partition image type.
 // Note: this returns ANDROID_PARTITION_TYPE_UNKNOWN if the file does
 // not exist or cannot be read.
diff --git a/android/filesystems/partition_types_unittest.cpp b/android/filesystems/partition_types_unittest.cpp
index 5bdddd9..a51a3dd 100644
--- a/android/filesystems/partition_types_unittest.cpp
+++ b/android/filesystems/partition_types_unittest.cpp
@@ -52,9 +52,26 @@
 }  // namespace
 
 TEST(AndroidPartitionType, ToString) {
-    EXPECT_STREQ("unknown", androidPartitionType_toString(ANDROID_PARTITION_TYPE_UNKNOWN));
-    EXPECT_STREQ("yaffs2", androidPartitionType_toString(ANDROID_PARTITION_TYPE_YAFFS2));
-    EXPECT_STREQ("ext4", androidPartitionType_toString(ANDROID_PARTITION_TYPE_EXT4));
+    EXPECT_STREQ(
+            "unknown",
+            androidPartitionType_toString(ANDROID_PARTITION_TYPE_UNKNOWN));
+    EXPECT_STREQ(
+            "yaffs2",
+            androidPartitionType_toString(ANDROID_PARTITION_TYPE_YAFFS2));
+    EXPECT_STREQ(
+            "ext4",
+            androidPartitionType_toString(ANDROID_PARTITION_TYPE_EXT4));
+}
+
+TEST(AndroidPartitionType, FromString) {
+    EXPECT_EQ(ANDROID_PARTITION_TYPE_YAFFS2,
+              androidPartitionType_fromString("yaffs2"));
+    EXPECT_EQ(ANDROID_PARTITION_TYPE_EXT4,
+              androidPartitionType_fromString("ext4"));
+    EXPECT_EQ(ANDROID_PARTITION_TYPE_UNKNOWN,
+              androidPartitionType_fromString("unknown"));
+    EXPECT_EQ(ANDROID_PARTITION_TYPE_UNKNOWN,
+              androidPartitionType_fromString("foobar"));
 }
 
 TEST(AndroidPartitionType, ProbeFileYaffs2) {
diff --git a/android/filesystems/ramdisk_extractor.cpp b/android/filesystems/ramdisk_extractor.cpp
new file mode 100644
index 0000000..62e4445
--- /dev/null
+++ b/android/filesystems/ramdisk_extractor.cpp
@@ -0,0 +1,315 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#include "android/filesystems/ramdisk_extractor.h"
+
+#include "android/base/Compiler.h"
+#include "android/base/Log.h"
+#include "android/base/String.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#define DEBUG 0
+
+#if DEBUG
+#  define D(...)   printf(__VA_ARGS__), fflush(stdout)
+#else
+#  define D(...)   ((void)0)
+#endif
+
+// Ramdisk images are gzipped cpio archives using the new ASCII
+// format as described at [1]. Hence this source file first implements
+// a gzip-based input stream class, then 
+//
+// [1] http://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt
+
+namespace {
+
+// Helper class used to implement a gzip-based input stream.
+// Usage is as follows:
+//
+//     GZipInputStream input(filePath);
+//     if (input.error()) {
+//        fprintf(stderr, "Could not open file: %s\n",
+//                filePath, strerror(input.error()));
+//     } else {
+//        uint8_t header[32];
+//        if (!doRead(&input, header, sizeof header)) {
+//           ... error, could not read header.
+//        }
+//     }
+//      .. stream is closed automatically on scope exit.
+class GZipInputStream {
+public:
+    // Open a new input stream to read from file |filePath|.
+    // The constructor can never fail, so call error() after
+    // this to see if an error occured.
+    explicit GZipInputStream(const char* filePath) {
+        mFile = gzopen(filePath, "rb");
+        if (mFile == Z_NULL) {
+            mFile = NULL;
+            mError = errno;
+        } else {
+            mError = 0;
+        }
+    }
+
+    // Return the last error that occured on this stream,
+    // or 0 if everything's well. Note that as soon as an
+    // error occurs, the stream cannot be used anymore.
+    int error() const { return mError; }
+
+    // Close the stream, note that this is called automatically
+    // from the destructor, but clients might want to do this
+    // before.
+    void close() {
+        if (mFile) {
+            gzclose(mFile);
+            mFile = NULL;
+        }
+    }
+
+    ~GZipInputStream() {
+        close();
+    }
+
+    // Try to read up to |len| bytes of data from the input
+    // stream into |buffer|. On success, return true and sets
+    // |*readBytes| to the number of bytes that were read.
+    // On failure, return false and set error().
+    bool tryRead(void* buffer, size_t len, size_t* readBytes) {
+        *readBytes = 0;
+
+        if (mError) {
+            return false;
+        }
+
+        if (len == 0) {
+            return true;
+        }
+
+        // Warning, gzread() takes an unsigned int parameter for
+        // the length, but will return an error if its value
+        // exceeds INT_MAX anyway.
+        char* buff = reinterpret_cast<char*>(buffer);
+
+        while (len > 0) {
+            size_t avail = len;
+            if (avail > INT_MAX)
+                avail = INT_MAX;
+
+            int ret = gzread(mFile, buff, static_cast<unsigned int>(avail));
+            if (ret < 0) {
+                gzerror(mFile, &mError);
+                break;
+            }
+            if (ret == 0) {
+                if (gzeof(mFile)) {
+                    break;
+                }
+                gzerror(mFile, &mError);
+                break;
+            }
+            len -= ret;
+            buff += ret;
+            *readBytes += ret;
+        }
+
+        return (!mError && *readBytes > 0);
+    }
+
+    // Read exactly |len| bytes of data into |buffer|. On success,
+    // return true, on failure, return false and set error().
+    bool doRead(void* buffer, size_t len) {
+        size_t readCount = 0;
+        if (!tryRead(buffer, len, &readCount)) {
+            return false;
+        }
+        if (readCount < len) {
+            mError = EIO;
+            return false;
+        }
+        return true;
+    }
+
+    bool doSkip(size_t len) {
+        if (mError) {
+            return false;
+        }
+        if (gzseek(mFile, len, SEEK_CUR) < 0) {
+            gzerror(mFile, &mError);
+            return false;
+        }
+        return true;
+    }
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(GZipInputStream);
+
+    gzFile mFile;
+    int mError;
+};
+
+// Parse an hexadecimal string of 8 characters. On success,
+// return true and sets |*value| to its value. On failure,
+// return false.
+bool parse_hex8(const char* input, uint32_t* value) {
+    uint32_t result = 0;
+    for (int n = 0; n < 8; ++n) {
+        int c = input[n];
+        unsigned d = static_cast<unsigned>(c - '0');
+        if (d >= 10) {
+            d = static_cast<unsigned>(c - 'a');
+            if (d >= 6) {
+                d = static_cast<unsigned>(c - 'A');
+                if (d >= 6) {
+                    return false;
+                }
+            }
+            d += 10;
+        }
+        result = (result << 4) | d;
+    }
+    *value = result;
+    return true;
+}
+
+}  // namespace
+
+bool android_extractRamdiskFile(const char* ramdiskPath,
+                                const char* fileName,
+                                char** out,
+                                size_t* outSize) {
+    *out = NULL;
+    *outSize = 0;
+
+    GZipInputStream input(ramdiskPath);
+    if (input.error()) {
+        errno = input.error();
+        return false;
+    }
+
+    // Type of cpio new ASCII header.
+    struct cpio_newc_header {
+        char c_magic[6];
+        char c_ino[8];
+        char c_mode[8];
+        char c_uid[8];
+        char c_gid[8];
+        char c_nlink[8];
+        char c_mtime[8];
+        char c_filesize[8];
+        char c_devmajor[8];
+        char c_devminor[8];
+        char c_rdevmajor[8];
+        char c_rdevminor[8];
+        char c_namesize[8];
+        char c_check[8];
+    };
+
+    size_t fileNameLen = strlen(fileName);
+
+    for (;;) {
+        // Read the header then check it.
+        cpio_newc_header header;
+        if (!input.doRead(&header, sizeof header)) {
+            // Assume end of input here.
+            D("Could not find %s in ramdisk image at %s\n",
+              fileName, ramdiskPath);
+            return false;
+        }
+
+        D("HEADER %.6s\n", header.c_magic);
+        if (memcmp(header.c_magic, "070701", 6) != 0) {
+            D("Not a valid ramdisk image file: %s\n", ramdiskPath);
+            errno = EINVAL;
+            return false;
+        }
+
+        // Compare file names, note that files with a size of 0 are
+        // hard links and should be ignored.
+        uint32_t nameSize;
+        uint32_t entrySize;
+        if (!parse_hex8(header.c_namesize, &nameSize) ||
+            !parse_hex8(header.c_filesize, &entrySize)) {
+            D("Could not parse ramdisk file entry header!");
+            break;
+        }
+
+        D("---- %d nameSize=%d entrySize=%d\n", __LINE__, nameSize, entrySize);
+
+        // The header is followed by the name, followed by 4-byte padding
+        // with NUL bytes. Compute the number of bytes to skip over the
+        // name.
+        size_t skipName =
+                ((sizeof header + nameSize + 3) & ~3) - sizeof header;
+
+        // The file data is 4-byte padded with NUL bytes too.
+        size_t skipFile = (entrySize + 3) & ~3;
+        size_t skipCount = 0;
+
+        // Last record is named 'TRAILER!!!' and indicates end of archive.
+        static const char kTrailer[] = "TRAILER!!!";
+        static const size_t kTrailerSize = sizeof(kTrailer) - 1U;
+
+        if ((entrySize == 0 || nameSize != fileNameLen + 1U) &&
+            nameSize != kTrailerSize + 1U) {
+            D("---- %d Skipping\n", __LINE__);
+            skipCount = skipName + skipFile;
+        } else {
+            // Read the name and compare it.
+            nameSize -= 1U;
+            android::base::String entryName;
+            entryName.resize(nameSize);
+            if (!input.doRead(&entryName[0], nameSize)) {
+                D("Could not read ramdisk file entry name!");
+                break;
+            }
+            D("---- %d Name=[%s]\n", __LINE__, entryName.c_str());
+            skipCount -= nameSize;
+
+            // Check for last entry.
+            if (nameSize == kTrailerSize &&
+                !strcmp(entryName.c_str(), kTrailer)) {
+                D("End of archive reached. Could not find %s in ramdisk image at %s",
+                  fileName, ramdiskPath);
+                return false;
+            }
+
+            // Check for the search file name.
+            if (nameSize == entryName.size() &&
+                !strcmp(entryName.c_str(), fileName)) {
+                // Found it !! Skip over padding.
+                if (!input.doSkip(skipName - nameSize)) {
+                    D("Could not skip ramdisk name entry!");
+                    break;
+                }
+
+                // Then read data into head-allocated buffer.
+                *out = reinterpret_cast<char*>(malloc(entrySize));
+                *outSize = entrySize;
+
+                return input.doRead(*out, entrySize);
+            }
+        }
+        if (!input.doSkip(skipCount)) {
+            D("Could not skip ramdisk entry!");
+            break;
+        }
+    }
+
+    errno = input.error();
+    return false;
+}
diff --git a/android/filesystems/ramdisk_extractor.h b/android/filesystems/ramdisk_extractor.h
new file mode 100644
index 0000000..840d4de
--- /dev/null
+++ b/android/filesystems/ramdisk_extractor.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#ifndef ANDROID_FILESYSTEMS_RAMDISK_EXTRACTOR_H
+#define ANDROID_FILESYSTEMS_RAMDISK_EXTRACTOR_H
+
+#include "android/utils/compiler.h"
+
+#include <stddef.h>
+
+
+ANDROID_BEGIN_HEADER
+
+// Extract the content of a given file from a ramdisk image.
+// |ramdisk_path| is the path to the ramdisk.img file.
+// |file_path| is the path of the file within the ramdisk.
+// On success, returns true and sets |*out| to point to a heap allocated
+// block containing the extracted content, of size |*out_size| bytes.
+// On failure, return false.
+bool android_extractRamdiskFile(const char* ramdisk_path,
+                                const char* file_path,
+                                char** out,
+                                size_t* out_size);
+
+ANDROID_END_HEADER
+
+#endif  // ANDROID_FILESYSTEMS_RAMDISK_EXTRACTOR_H
diff --git a/android/filesystems/ramdisk_extractor_unittest.cpp b/android/filesystems/ramdisk_extractor_unittest.cpp
new file mode 100644
index 0000000..2462b5d
--- /dev/null
+++ b/android/filesystems/ramdisk_extractor_unittest.cpp
@@ -0,0 +1,88 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#include "android/filesystems/ramdisk_extractor.h"
+
+#include "android/base/EintrWrapper.h"
+#include "android/filesystems/testing/TestSupport.h"
+
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+
+namespace {
+
+#include "android/filesystems/testing/TestRamdiskImage.h"
+
+class RamdiskExtractorTest : public ::testing::Test {
+public:
+    RamdiskExtractorTest() :
+        mTempFilePath(android::testing::CreateTempFilePath()) {}
+
+    bool fillData(const void* data, size_t dataSize) {
+        FILE* file = ::fopen(mTempFilePath.c_str(), "wb");
+        if (!file) {
+            return false;
+        }
+
+        bool result = (fwrite(data, dataSize, 1, file) == 1);
+        fclose(file);
+        return result;
+    }
+
+    ~RamdiskExtractorTest() {
+        if (!mTempFilePath.empty()) {
+            HANDLE_EINTR(unlink(mTempFilePath.c_str()));
+        }
+    }
+
+    const char* path() const { return mTempFilePath.c_str(); }
+
+private:
+    std::string mTempFilePath;
+};
+
+}  // namespace
+
+TEST_F(RamdiskExtractorTest, FindFoo) {
+    static const char kExpected[] = "Hello World!\n";
+    static const size_t kExpectedSize = sizeof(kExpected) - 1U;
+    char* out = NULL;
+    size_t outSize = 0;
+
+    EXPECT_TRUE(fillData(kTestRamdiskImage, kTestRamdiskImageSize));
+    EXPECT_TRUE(android_extractRamdiskFile(path(), "foo", &out, &outSize));
+    EXPECT_EQ(kExpectedSize, outSize);
+    EXPECT_TRUE(out);
+    EXPECT_TRUE(!memcmp(out, kExpected, outSize));
+    free(out);
+}
+
+TEST_F(RamdiskExtractorTest, FindBar2) {
+    static const char kExpected[] = "La vie est un long fleuve tranquille\n";
+    static const size_t kExpectedSize = sizeof(kExpected) - 1U;
+    char* out = NULL;
+    size_t outSize = 0;
+
+    EXPECT_TRUE(fillData(kTestRamdiskImage, kTestRamdiskImageSize));
+    EXPECT_TRUE(android_extractRamdiskFile(path(), "bar2", &out, &outSize));
+    EXPECT_EQ(kExpectedSize, outSize);
+    EXPECT_TRUE(out);
+    EXPECT_TRUE(!memcmp(out, kExpected, outSize));
+    free(out);
+}
+
+TEST_F(RamdiskExtractorTest, MissingFile) {
+    char* out = NULL;
+    size_t outSize = 0;
+    EXPECT_TRUE(fillData(kTestRamdiskImage, kTestRamdiskImageSize));
+    EXPECT_FALSE(android_extractRamdiskFile(path(), "zoolander", &out, &outSize));
+}
diff --git a/android/filesystems/testing/TestRamdiskImage.h b/android/filesystems/testing/TestRamdiskImage.h
new file mode 100644
index 0000000..baec302
--- /dev/null
+++ b/android/filesystems/testing/TestRamdiskImage.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#ifndef ANDROID_FILESYSTEMS_TESTING_TEST_RAMDISK_IMAGE_H
+#define ANDROID_FILESYSTEMS_TESTING_TEST_RAMDISK_IMAGE_H
+
+/* Auto-generated by create_ramdisk_test_data.sh on 2014-06-23 - DO NOT EDIT!! */
+
+static const unsigned char kTestRamdiskImage[] = {
+  0x1f, 0x8b, 0x08, 0x00, 0x6d, 0x1a, 0xa8, 0x53, 0x02, 0x03, 0xbd, 0x8e,
+  0x41, 0x0a, 0x83, 0x30, 0x10, 0x45, 0xb3, 0xee, 0x29, 0x26, 0x37, 0x98,
+  0xc4, 0x46, 0xd3, 0x65, 0xac, 0x86, 0x16, 0x5c, 0x49, 0xa1, 0x6b, 0x4b,
+  0x63, 0x11, 0x06, 0x43, 0xad, 0x7a, 0xfe, 0x2a, 0x41, 0x90, 0x2e, 0x8a,
+  0xdd, 0xf4, 0x6d, 0xfe, 0x1f, 0x66, 0x60, 0x1e, 0x26, 0x98, 0xa0, 0x40,
+  0x14, 0xb9, 0xd4, 0x07, 0x8b, 0x13, 0x5a, 0x98, 0x39, 0x30, 0xb6, 0x26,
+  0x9d, 0x53, 0x44, 0x5a, 0x63, 0x40, 0xa8, 0xc8, 0x4c, 0xeb, 0x38, 0x0b,
+  0xa3, 0x54, 0x21, 0xed, 0x11, 0xbf, 0xa3, 0x96, 0x72, 0xab, 0x3a, 0xc9,
+  0x58, 0x51, 0xc1, 0xd8, 0x38, 0x70, 0xaf, 0x1e, 0x86, 0x16, 0xc8, 0xb7,
+  0x0f, 0xa8, 0xc9, 0x0d, 0xa3, 0x83, 0xbe, 0xab, 0xda, 0xe7, 0xd0, 0x10,
+  0xb9, 0x1d, 0x63, 0x0c, 0xd7, 0x6e, 0xd9, 0x6f, 0x6e, 0x98, 0x6d, 0x74,
+  0xdb, 0x2f, 0xa5, 0xf6, 0x7e, 0xfa, 0x79, 0x72, 0x44, 0x1e, 0xae, 0xbe,
+  0xa3, 0x3b, 0x5f, 0x3b, 0x6c, 0x62, 0xeb, 0xdd, 0x27, 0xe9, 0x52, 0x2e,
+  0xa5, 0x39, 0x17, 0x79, 0xc9, 0x39, 0x67, 0x7f, 0xe2, 0x0d, 0xb9, 0x32,
+  0x87, 0xaa, 0x00, 0x02, 0x00, 0x00
+};
+
+static const size_t kTestRamdiskImageSize = sizeof(kTestRamdiskImage);
+
+#endif  // ANDROID_FILESYSTEMS_TESTING_TEST_RAMDISK_IMAGE_H
diff --git a/android/filesystems/testing/create_ramdisk_test_data.sh b/android/filesystems/testing/create_ramdisk_test_data.sh
new file mode 100755
index 0000000..1bd3e42
--- /dev/null
+++ b/android/filesystems/testing/create_ramdisk_test_data.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# This script is used to generate souce files containing test
+# data for the ramdisk_extractor unit tests.
+
+set -e
+
+export LANG=C
+export LC_ALL=C
+
+PROGNAME=$(basename "$0")
+DATE=$(date +%Y-%m-%d)
+
+# $1: Root directory
+create_ramdisk_header_from () {
+    local FILE_LIST
+    FILE_LIST=$(cd $2 && find . -type f 2>/dev/null | sed -e 's|^./||g')
+    echo "$FILE_LIST" | cpio --create --format=newc --quiet | gzip -9c | xxd -i -
+}
+
+TMPDIR=/tmp/$USER-ramdisk-test-data
+rm -rf $TMPDIR/dir1 && mkdir -p $TMPDIR/dir1
+cd $TMPDIR/dir1
+cat > foo <<EOF
+Hello World!
+EOF
+cat > bar2 <<EOF
+La vie est un long fleuve tranquille
+EOF
+
+echo "/* Auto-generated by $PROGNAME on $DATE - DO NOT EDIT!! */"
+echo ""
+echo "static const unsigned char kTestRamdiskImage[] = {"
+create_ramdisk_header_from $TMPDIR/ramdisk1.img.h $TMPDIR/dir1
+echo "};"
+echo ""
+echo "static const size_t kTestRamdiskImageSize = sizeof(kTestRamdiskImage);"
+echo ""
+
diff --git a/vl-android.c b/vl-android.c
index 2d4751a..7023b44 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -54,7 +54,9 @@
 #include "android/log-rotate.h"
 #include "modem_driver.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/gps.h"
 #include "android/hw-kmsg.h"
 #include "android/hw-pipe-net.h"
@@ -1870,6 +1872,36 @@
 }
 
 
+// Extract the partition type/format of a given partition image
+// from the content of fstab.goldfish.
+// |fstab| is the address of the fstab.goldfish data in memory.
+// |fstabSize| is its size in bytes.
+// |partitionName| is the name of the partition for debugging
+// purposes (e.g. 'userdata').
+// |partitionPath| is the partition path as it appears in the
+// fstab file (e.g. '/data').
+// On success, sets |*partitionType| to an appropriate value,
+// on failure (i.e. |partitionPath| does not appear in the fstab
+// file), leave the value untouched.
+void android_extractPartitionFormat(const char* fstab,
+                                    size_t fstabSize,
+                                    const char* partitionName,
+                                    const char* partitionPath,
+                                    AndroidPartitionType* partitionType) {
+    char* partFormat = NULL;
+    if (!android_parseFstabPartitionFormat(fstab, fstabSize, partitionPath,
+                                           &partFormat)) {
+        VERBOSE_PRINT(init, "Could not extract format of %s partition!",
+                      partitionName);
+        return;
+    }
+    VERBOSE_PRINT(init, "Found format of %s partition: '%s'",
+                  partitionName, partFormat);
+    *partitionType = androidPartitionType_fromString(partFormat);
+    free(partFormat);
+}
+
+
 // List of value describing how to handle partition images in
 // android_nand_add_image() below, when no initiali partition image
 // file is provided.
@@ -3070,9 +3102,55 @@
         }
     }
 
+    // Determine format of all partition images, if possible.
+    // Note that _UNKNOWN means the file, if it exists, will be probed.
+    AndroidPartitionType system_partition_type =
+            ANDROID_PARTITION_TYPE_UNKNOWN;
+    AndroidPartitionType userdata_partition_type =
+            ANDROID_PARTITION_TYPE_UNKNOWN;
+    AndroidPartitionType cache_partition_type =
+            ANDROID_PARTITION_TYPE_UNKNOWN;
+
+    {
+        // Starting with Android 4.4.x, the ramdisk.img contains
+        // an fstab.goldfish file that lists the format of each partition.
+        // If the file exists, parse it to get the appropriate values.
+        char* fstab = NULL;
+        size_t fstabSize = 0;
+
+        if (android_extractRamdiskFile(android_hw->disk_ramdisk_path,
+                                       "fstab.goldfish",
+                                       &fstab,
+                                       &fstabSize)) {
+            VERBOSE_PRINT(init, "Ramdisk image contains fstab.goldfish file");
+
+            android_extractPartitionFormat(fstab,
+                                           fstabSize,
+                                           "system",
+                                           "/system",
+                                           &system_partition_type);
+
+            android_extractPartitionFormat(fstab,
+                                           fstabSize,
+                                           "userdata",
+                                           "/data",
+                                           &userdata_partition_type);
+
+            android_extractPartitionFormat(fstab,
+                                           fstabSize,
+                                           "cache",
+                                           "/cache",
+                                           &cache_partition_type);
+
+            free(fstab);
+        } else {
+            VERBOSE_PRINT(init, "No fstab.goldfish file in ramdisk image");
+        }
+    }
+
     /* Initialize system partition image */
     android_nand_add_image("system",
-                           ANDROID_PARTITION_TYPE_UNKNOWN,
+                           system_partition_type,
                            ANDROID_PARTITION_OPEN_MODE_MUST_EXIST,
                            android_hw->disk_systemPartition_size,
                            android_hw->disk_systemPartition_path,
@@ -3080,7 +3158,7 @@
 
     /* Initialize data partition image */
     android_nand_add_image("userdata",
-                           ANDROID_PARTITION_TYPE_UNKNOWN,
+                           userdata_partition_type,
                            ANDROID_PARTITION_OPEN_MODE_CREATE_IF_NEEDED,
                            android_hw->disk_dataPartition_size,
                            android_hw->disk_dataPartition_path,
@@ -3091,19 +3169,21 @@
      * YAFFS2 otherwise.
      */
     if (android_hw->disk_cachePartition != 0) {
-        AndroidPartitionType cache_part_type =
+        if (cache_partition_type == ANDROID_PARTITION_TYPE_UNKNOWN) {
+            cache_partition_type =
                 (androidHwConfig_getKernelYaffs2Support(android_hw) >= 1) ?
                         ANDROID_PARTITION_TYPE_YAFFS2 :
                         ANDROID_PARTITION_TYPE_EXT4;
+        }
 
-        AndroidPartitionOpenMode cache_part_mode =
+        AndroidPartitionOpenMode cache_partition_mode =
                 (android_op_wipe_data ?
                         ANDROID_PARTITION_OPEN_MODE_MUST_WIPE :
                         ANDROID_PARTITION_OPEN_MODE_CREATE_IF_NEEDED);
 
         android_nand_add_image("cache",
-                               cache_part_type,
-                               cache_part_mode,
+                               cache_partition_type,
+                               cache_partition_mode,
                                android_hw->disk_cachePartition_size,
                                android_hw->disk_cachePartition_path,
                                NULL);