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);