Revert "kill package_extract_dir"

This reverts commit 53c38b15381ace565227e49104a6fd64c4c28dcc.

Change-Id: I57db2e3581e05dcfc006328c5b50fbfea4ab3fe7
diff --git a/otautil/Android.bp b/otautil/Android.bp
index 75cf691..a109d39 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -20,6 +20,7 @@
     srcs: [
         "SysUtil.cpp",
         "DirUtil.cpp",
+        "ZipUtil.cpp",
         "ThermalUtil.cpp",
         "cache_location.cpp",
         "rangeset.cpp",
diff --git a/otautil/ZipUtil.cpp b/otautil/ZipUtil.cpp
new file mode 100644
index 0000000..7c2b9ba
--- /dev/null
+++ b/otautil/ZipUtil.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 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 "ZipUtil.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <utime.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+#include <ziparchive/zip_archive.h>
+
+#include "otautil/DirUtil.h"
+
+static constexpr mode_t UNZIP_DIRMODE = 0755;
+static constexpr mode_t UNZIP_FILEMODE = 0644;
+
+bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path,
+                             const std::string& dest_path, const struct utimbuf* timestamp,
+                             struct selabel_handle* sehnd) {
+    if (!zip_path.empty() && zip_path[0] == '/') {
+        LOG(ERROR) << "ExtractPackageRecursive(): zip_path must be a relative path " << zip_path;
+        return false;
+    }
+    if (dest_path.empty() || dest_path[0] != '/') {
+        LOG(ERROR) << "ExtractPackageRecursive(): dest_path must be an absolute path " << dest_path;
+        return false;
+    }
+
+    void* cookie;
+    std::string target_dir(dest_path);
+    if (dest_path.back() != '/') {
+        target_dir += '/';
+    }
+    std::string prefix_path(zip_path);
+    if (!zip_path.empty() && zip_path.back() != '/') {
+        prefix_path += '/';
+    }
+    const ZipString zip_prefix(prefix_path.c_str());
+
+    int ret = StartIteration(zip, &cookie, &zip_prefix, nullptr);
+    if (ret != 0) {
+        LOG(ERROR) << "failed to start iterating zip entries.";
+        return false;
+    }
+
+    std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
+    ZipEntry entry;
+    ZipString name;
+    int extractCount = 0;
+    while (Next(cookie, &entry, &name) == 0) {
+        std::string entry_name(name.name, name.name + name.name_length);
+        CHECK_LE(prefix_path.size(), entry_name.size());
+        std::string path = target_dir + entry_name.substr(prefix_path.size());
+        // Skip dir.
+        if (path.back() == '/') {
+            continue;
+        }
+
+        if (mkdir_recursively(path.c_str(), UNZIP_DIRMODE, true, sehnd) != 0) {
+            LOG(ERROR) << "failed to create dir for " << path;
+            return false;
+        }
+
+        char *secontext = NULL;
+        if (sehnd) {
+            selabel_lookup(sehnd, &secontext, path.c_str(), UNZIP_FILEMODE);
+            setfscreatecon(secontext);
+        }
+        android::base::unique_fd fd(open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, UNZIP_FILEMODE));
+        if (fd == -1) {
+            PLOG(ERROR) << "Can't create target file \"" << path << "\"";
+            return false;
+        }
+        if (secontext) {
+            freecon(secontext);
+            setfscreatecon(NULL);
+        }
+
+        int err = ExtractEntryToFile(zip, &entry, fd);
+        if (err != 0) {
+            LOG(ERROR) << "Error extracting \"" << path << "\" : " << ErrorCodeString(err);
+            return false;
+        }
+
+        if (fsync(fd) != 0) {
+            PLOG(ERROR) << "Error syncing file descriptor when extracting \"" << path << "\"";
+            return false;
+        }
+
+        if (timestamp != nullptr && utime(path.c_str(), timestamp)) {
+            PLOG(ERROR) << "Error touching \"" << path << "\"";
+            return false;
+        }
+
+        LOG(INFO) << "Extracted file \"" << path << "\"";
+        ++extractCount;
+    }
+
+    LOG(INFO) << "Extracted " << extractCount << " file(s)";
+    return true;
+}
diff --git a/otautil/ZipUtil.h b/otautil/ZipUtil.h
new file mode 100644
index 0000000..cda405c
--- /dev/null
+++ b/otautil/ZipUtil.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 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 _OTAUTIL_ZIPUTIL_H
+#define _OTAUTIL_ZIPUTIL_H
+
+#include <utime.h>
+
+#include <string>
+
+#include <selinux/label.h>
+#include <ziparchive/zip_archive.h>
+
+/*
+ * Inflate all files under zip_path to the directory specified by
+ * dest_path, which must exist and be a writable directory. The zip_path
+ * is allowed to be an empty string, in which case the whole package
+ * will be extracted.
+ *
+ * Directory entries are not extracted.
+ *
+ * The immediate children of zip_path will become the immediate
+ * children of dest_path; e.g., if the archive contains the entries
+ *
+ *     a/b/c/one
+ *     a/b/c/two
+ *     a/b/c/d/three
+ *
+ * and ExtractPackageRecursive(a, "a/b/c", "/tmp", ...) is called, the resulting
+ * files will be
+ *
+ *     /tmp/one
+ *     /tmp/two
+ *     /tmp/d/three
+ *
+ * If timestamp is non-NULL, file timestamps will be set accordingly.
+ *
+ * Returns true on success, false on failure.
+ */
+bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path,
+                             const std::string& dest_path, const struct utimbuf* timestamp,
+                             struct selabel_handle* sehnd);
+
+#endif // _OTAUTIL_ZIPUTIL_H
diff --git a/tests/Android.mk b/tests/Android.mk
index b3584fe..5e25d71 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -40,6 +40,7 @@
     unit/rangeset_test.cpp \
     unit/sysutil_test.cpp \
     unit/zip_test.cpp \
+    unit/ziputil_test.cpp
 
 LOCAL_C_INCLUDES := bootable/recovery
 LOCAL_SHARED_LIBRARIES := liblog
diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp
index 8276685..df4e38c 100644
--- a/tests/unit/zip_test.cpp
+++ b/tests/unit/zip_test.cpp
@@ -24,10 +24,47 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 #include <otautil/SysUtil.h>
+#include <otautil/ZipUtil.h>
 #include <ziparchive/zip_archive.h>
 
 #include "common/test_constants.h"
 
+TEST(ZipTest, ExtractPackageRecursive) {
+  std::string zip_path = from_testdata_base("ziptest_valid.zip");
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+  // Extract the whole package into a temp directory.
+  TemporaryDir td;
+  ASSERT_NE(nullptr, td.path);
+  ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr);
+
+  // Make sure all the files are extracted correctly.
+  std::string path(td.path);
+  ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK));
+  ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK));
+  ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK));
+  ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK));
+
+  // The content of the file is the same as expected.
+  std::string content1;
+  ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1));
+  ASSERT_EQ(kATxtContents, content1);
+
+  std::string content2;
+  ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2));
+  ASSERT_EQ(kDTxtContents, content2);
+
+  CloseArchive(handle);
+
+  // Clean up.
+  ASSERT_EQ(0, unlink((path + "/a.txt").c_str()));
+  ASSERT_EQ(0, unlink((path + "/b.txt").c_str()));
+  ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str()));
+  ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str()));
+  ASSERT_EQ(0, rmdir((path + "/b").c_str()));
+}
+
 TEST(ZipTest, OpenFromMemory) {
   std::string zip_path = from_testdata_base("ziptest_dummy-update.zip");
   MemMapping map;
diff --git a/tests/unit/ziputil_test.cpp b/tests/unit/ziputil_test.cpp
new file mode 100644
index 0000000..14e5416
--- /dev/null
+++ b/tests/unit/ziputil_test.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2016 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 <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <otautil/ZipUtil.h>
+#include <ziparchive/zip_archive.h>
+
+#include "common/test_constants.h"
+
+TEST(ZipUtilTest, invalid_args) {
+  std::string zip_path = from_testdata_base("ziptest_valid.zip");
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+  // zip_path must be a relative path.
+  ASSERT_FALSE(ExtractPackageRecursive(handle, "/a/b", "/tmp", nullptr, nullptr));
+
+  // dest_path must be an absolute path.
+  ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "tmp", nullptr, nullptr));
+  ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "", nullptr, nullptr));
+
+  CloseArchive(handle);
+}
+
+TEST(ZipUtilTest, extract_all) {
+  std::string zip_path = from_testdata_base("ziptest_valid.zip");
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+  // Extract the whole package into a temp directory.
+  TemporaryDir td;
+  ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr);
+
+  // Make sure all the files are extracted correctly.
+  std::string path(td.path);
+  ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK));
+  ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK));
+  ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK));
+  ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK));
+
+  // The content of the file is the same as expected.
+  std::string content1;
+  ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1));
+  ASSERT_EQ(kATxtContents, content1);
+
+  std::string content2;
+  ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2));
+  ASSERT_EQ(kDTxtContents, content2);
+
+  // Clean up the temp files under td.
+  ASSERT_EQ(0, unlink((path + "/a.txt").c_str()));
+  ASSERT_EQ(0, unlink((path + "/b.txt").c_str()));
+  ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str()));
+  ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str()));
+  ASSERT_EQ(0, rmdir((path + "/b").c_str()));
+
+  CloseArchive(handle);
+}
+
+TEST(ZipUtilTest, extract_prefix_with_slash) {
+  std::string zip_path = from_testdata_base("ziptest_valid.zip");
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+  // Extract all the entries starting with "b/".
+  TemporaryDir td;
+  ExtractPackageRecursive(handle, "b/", td.path, nullptr, nullptr);
+
+  // Make sure all the files with "b/" prefix are extracted correctly.
+  std::string path(td.path);
+  ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK));
+  ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK));
+
+  // And the rest are not extracted.
+  ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK));
+  ASSERT_EQ(ENOENT, errno);
+  ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK));
+  ASSERT_EQ(ENOENT, errno);
+
+  // The content of the file is the same as expected.
+  std::string content1;
+  ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1));
+  ASSERT_EQ(kCTxtContents, content1);
+
+  std::string content2;
+  ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2));
+  ASSERT_EQ(kDTxtContents, content2);
+
+  // Clean up the temp files under td.
+  ASSERT_EQ(0, unlink((path + "/c.txt").c_str()));
+  ASSERT_EQ(0, unlink((path + "/d.txt").c_str()));
+
+  CloseArchive(handle);
+}
+
+TEST(ZipUtilTest, extract_prefix_without_slash) {
+  std::string zip_path = from_testdata_base("ziptest_valid.zip");
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+  // Extract all the file entries starting with "b/".
+  TemporaryDir td;
+  ExtractPackageRecursive(handle, "b", td.path, nullptr, nullptr);
+
+  // Make sure all the files with "b/" prefix are extracted correctly.
+  std::string path(td.path);
+  ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK));
+  ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK));
+
+  // And the rest are not extracted.
+  ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK));
+  ASSERT_EQ(ENOENT, errno);
+  ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK));
+  ASSERT_EQ(ENOENT, errno);
+
+  // The content of the file is the same as expected.
+  std::string content1;
+  ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1));
+  ASSERT_EQ(kCTxtContents, content1);
+
+  std::string content2;
+  ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2));
+  ASSERT_EQ(kDTxtContents, content2);
+
+  // Clean up the temp files under td.
+  ASSERT_EQ(0, unlink((path + "/c.txt").c_str()));
+  ASSERT_EQ(0, unlink((path + "/d.txt").c_str()));
+
+  CloseArchive(handle);
+}
+
+TEST(ZipUtilTest, set_timestamp) {
+  std::string zip_path = from_testdata_base("ziptest_valid.zip");
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+  // Set the timestamp to 8/1/2008.
+  constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };
+
+  // Extract all the entries starting with "b/".
+  TemporaryDir td;
+  ExtractPackageRecursive(handle, "b", td.path, &timestamp, nullptr);
+
+  // Make sure all the files with "b/" prefix are extracted correctly.
+  std::string path(td.path);
+  std::string file_c = path + "/c.txt";
+  std::string file_d = path + "/d.txt";
+  ASSERT_EQ(0, access(file_c.c_str(), F_OK));
+  ASSERT_EQ(0, access(file_d.c_str(), F_OK));
+
+  // Verify the timestamp.
+  timespec time;
+  time.tv_sec = 1217592000;
+  time.tv_nsec = 0;
+
+  struct stat sb;
+  ASSERT_EQ(0, stat(file_c.c_str(), &sb)) << strerror(errno);
+  ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
+  ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
+
+  ASSERT_EQ(0, stat(file_d.c_str(), &sb)) << strerror(errno);
+  ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
+  ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
+
+  // Clean up the temp files under td.
+  ASSERT_EQ(0, unlink(file_c.c_str()));
+  ASSERT_EQ(0, unlink(file_d.c_str()));
+
+  CloseArchive(handle);
+}
diff --git a/updater/install.cpp b/updater/install.cpp
index 7e3aac9..ff9e8aa 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -62,6 +62,7 @@
 #include "otautil/DirUtil.h"
 #include "otautil/error_code.h"
 #include "otautil/print_sha1.h"
+#include "otautil/ZipUtil.h"
 #include "updater/updater.h"
 
 // Send over the buffer to recovery though the command pipe.
@@ -689,6 +690,36 @@
   return StringValue(frac_str);
 }
 
+// package_extract_dir(package_dir, dest_dir)
+//   Extracts all files from the package underneath package_dir and writes them to the
+//   corresponding tree beneath dest_dir. Any existing files are overwritten.
+//   Example: package_extract_dir("system", "/system")
+//
+//   Note: package_dir needs to be a relative path; dest_dir needs to be an absolute path.
+Value* PackageExtractDirFn(const char* name, State* state,
+                           const std::vector<std::unique_ptr<Expr>>&argv) {
+  if (argv.size() != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
+                      argv.size());
+  }
+
+  std::vector<std::string> args;
+  if (!ReadArgs(state, argv, &args)) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+  }
+  const std::string& zip_path = args[0];
+  const std::string& dest_path = args[1];
+
+  ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
+
+  // To create a consistent system image, never use the clock for timestamps.
+  constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };  // 8/1/2008 default
+
+  bool success = ExtractPackageRecursive(za, zip_path, dest_path, &timestamp, sehandle);
+
+  return StringValue(success ? "t" : "");
+}
+
 Value* GetPropFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
   if (argv.size() != 1) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
@@ -1362,6 +1393,7 @@
   RegisterFunction("set_progress", SetProgressFn);
   RegisterFunction("delete", DeleteFn);
   RegisterFunction("delete_recursive", DeleteFn);
+  RegisterFunction("package_extract_dir", PackageExtractDirFn);
   RegisterFunction("package_extract_file", PackageExtractFileFn);
   RegisterFunction("symlink", SymlinkFn);