Bring back file-based OTA edify functions

Author: Tom Marshall <tdm.code@gmail.com>
Date:   Wed Oct 25 20:27:08 2017 +0200

    Revert "kill package_extract_dir"

    changes for P:
     - bring back the mkdir_recursively variant which takes a timestamp.
     - add libziparchive dependency
     - fix otautil header paths

    changes for Q:
     - change ziputil naming convention to lowercase

    This reverts commit 53c38b15381ace565227e49104a6fd64c4c28dcc.

    Change-Id: I71c488e96a1f23aace3c38fc283aae0165129a12

Author: Tom Marshall <tdm.code@gmail.com>
Date:   Thu Dec 14 22:37:17 2017 +0100

    Revert "Remove the obsolete package_extract_dir() test"

    This reverts commit bb7e005a7906b02857ba328c5dfb11f1f3cb938e.

    Change-Id: I643235d6605d7da2a189eca10ec999b25c23e1f9

Author: Tom Marshall <tdm.code@gmail.com>
Date:   Wed Aug 23 18:14:00 2017 +0000

    Revert "updater: Remove some obsoleted functions for file-based OTA."

    This reverts commit 63d786cf22cb44fe32e8b9c1f18b32da3c9d2e1b.

    These functions will be used for third party OTA zips, so keep them.

    Change-Id: I24b67ba4c86f8f86d0a41429a395fece1a383efd

Author: Stricted <info@stricted.net>
Date:   Mon Mar 12 18:11:56 2018 +0100

    recovery: updater: Fix SymlinkFn args

    Change-Id: If2ba1b7a8b5ac471a2db84f352273fd0ea7c81a2

Author: Simon Shields <simon@lineageos.org>
Date:   Thu Aug 9 01:17:21 2018 +1000

    Revert "updater: Remove dead make_parents()."

    This reverts commit 5902691764e041bfed8edbc66a72e0854d18dfda.

    Change-Id: I69eadf1a091f6ecd45531789dedf72a178a055ba

Author: Simon Shields <simon@lineageos.org>
Date:   Thu Aug 9 01:20:40 2018 +1000

    Revert "otautil: Delete dirUnlinkHierarchy()."

    changes for P:
     - Fix missing PATH_MAX macro from limits.h

    This reverts commit 7934985e0cac4a3849418af3b8c9671f4d61078a.

    Change-Id: I67ce71a1644b58a393dce45a6c3dee97830b9ee4

[mikeioannina]: Squash for Q and run through clang-format

Change-Id: I91973bc9e9f8d100688c0112fda9043fd45eb86a
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index a0a7b66..af2deae 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -306,6 +306,215 @@
     expect("", script6, kNoCause);
 }
 
+TEST_F(UpdaterTest, delete) {
+  // Delete none.
+  expect("0", "delete()", kNoCause);
+  expect("0", "delete(\"/doesntexist\")", kNoCause);
+  expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\")", kNoCause);
+  expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\", \"/doesntexist3\")", kNoCause);
+
+  // Delete one file.
+  TemporaryFile temp_file1;
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file1.path));
+  std::string script1("delete(\"" + std::string(temp_file1.path) + "\")");
+  expect("1", script1.c_str(), kNoCause);
+
+  // Delete two files.
+  TemporaryFile temp_file2;
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file2.path));
+  TemporaryFile temp_file3;
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file3.path));
+  std::string script2("delete(\"" + std::string(temp_file2.path) + "\", \"" +
+                      std::string(temp_file3.path) + "\")");
+  expect("2", script2.c_str(), kNoCause);
+
+  // Delete already deleted files.
+  expect("0", script2.c_str(), kNoCause);
+
+  // Delete one out of three.
+  TemporaryFile temp_file4;
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file4.path));
+  std::string script3("delete(\"/doesntexist1\", \"" + std::string(temp_file4.path) +
+                      "\", \"/doesntexist2\")");
+  expect("1", script3.c_str(), kNoCause);
+}
+
+TEST_F(UpdaterTest, rename) {
+  // rename() expects two arguments.
+  expect(nullptr, "rename()", kArgsParsingFailure);
+  expect(nullptr, "rename(\"arg1\")", kArgsParsingFailure);
+  expect(nullptr, "rename(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+  // src_name or dst_name cannot be empty.
+  expect(nullptr, "rename(\"\", \"arg2\")", kArgsParsingFailure);
+  expect(nullptr, "rename(\"arg1\", \"\")", kArgsParsingFailure);
+
+  // File doesn't exist (both of src and dst).
+  expect(nullptr, "rename(\"/doesntexist\", \"/doesntexisteither\")", kFileRenameFailure);
+
+  // Can't create parent directory.
+  TemporaryFile temp_file1;
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file1.path));
+  std::string script1("rename(\"" + std::string(temp_file1.path) + "\", \"/proc/0/file1\")");
+  expect(nullptr, script1.c_str(), kFileRenameFailure);
+
+  // Rename.
+  TemporaryFile temp_file2;
+  std::string script2("rename(\"" + std::string(temp_file1.path) + "\", \"" +
+                      std::string(temp_file2.path) + "\")");
+  expect(temp_file2.path, script2.c_str(), kNoCause);
+
+  // Already renamed.
+  expect(temp_file2.path, script2.c_str(), kNoCause);
+
+  // Parents create successfully.
+  TemporaryFile temp_file3;
+  TemporaryDir td;
+  std::string temp_dir(td.path);
+  std::string dst_file = temp_dir + "/aaa/bbb/a.txt";
+  std::string script3("rename(\"" + std::string(temp_file3.path) + "\", \"" + dst_file + "\")");
+  expect(dst_file.c_str(), script3.c_str(), kNoCause);
+
+  // Clean up the temp files under td.
+  ASSERT_EQ(0, unlink(dst_file.c_str()));
+  ASSERT_EQ(0, rmdir((temp_dir + "/aaa/bbb").c_str()));
+  ASSERT_EQ(0, rmdir((temp_dir + "/aaa").c_str()));
+}
+
+TEST_F(UpdaterTest, symlink) {
+  // symlink expects 1+ argument.
+  expect(nullptr, "symlink()", kArgsParsingFailure);
+
+  // symlink should fail if src is an empty string.
+  TemporaryFile temp_file1;
+  std::string script1("symlink(\"" + std::string(temp_file1.path) + "\", \"\")");
+  expect(nullptr, script1.c_str(), kSymlinkFailure);
+
+  std::string script2("symlink(\"" + std::string(temp_file1.path) + "\", \"src1\", \"\")");
+  expect(nullptr, script2.c_str(), kSymlinkFailure);
+
+  // symlink failed to remove old src.
+  std::string script3("symlink(\"" + std::string(temp_file1.path) + "\", \"/proc\")");
+  expect(nullptr, script3.c_str(), kSymlinkFailure);
+
+  // symlink can create symlinks.
+  TemporaryFile temp_file;
+  std::string content = "magicvalue";
+  ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
+
+  TemporaryDir td;
+  std::string src1 = std::string(td.path) + "/symlink1";
+  std::string src2 = std::string(td.path) + "/symlink2";
+  std::string script4("symlink(\"" + std::string(temp_file.path) + "\", \"" + src1 + "\", \"" +
+                      src2 + "\")");
+  expect("t", script4.c_str(), kNoCause);
+
+  // Verify the created symlinks.
+  struct stat sb;
+  ASSERT_TRUE(lstat(src1.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode));
+  ASSERT_TRUE(lstat(src2.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode));
+
+  // Clean up the leftovers.
+  ASSERT_EQ(0, unlink(src1.c_str()));
+  ASSERT_EQ(0, unlink(src2.c_str()));
+}
+
+TEST_F(UpdaterTest, package_extract_dir) {
+  // package_extract_dir expects 2 arguments.
+  expect(nullptr, "package_extract_dir()", kArgsParsingFailure);
+  expect(nullptr, "package_extract_dir(\"arg1\")", kArgsParsingFailure);
+  expect(nullptr, "package_extract_dir(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+  std::string zip_path = from_testdata_base("ziptest_valid.zip");
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+  // Need to set up the ziphandle.
+  UpdaterInfo updater_info;
+  updater_info.package_zip = handle;
+
+  // Extract "b/c.txt" and "b/d.txt" with package_extract_dir("b", "<dir>").
+  TemporaryDir td;
+  std::string temp_dir(td.path);
+  std::string script("package_extract_dir(\"b\", \"" + temp_dir + "\")");
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  // Verify.
+  std::string data;
+  std::string file_c = temp_dir + "/c.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+  ASSERT_EQ(kCTxtContents, data);
+
+  std::string file_d = temp_dir + "/d.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+  ASSERT_EQ(kDTxtContents, data);
+
+  // Modify the contents in order to retry. It's expected to be overwritten.
+  ASSERT_TRUE(android::base::WriteStringToFile("random", file_c));
+  ASSERT_TRUE(android::base::WriteStringToFile("random", file_d));
+
+  // Extract again and verify.
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+  ASSERT_EQ(kCTxtContents, data);
+  ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+  ASSERT_EQ(kDTxtContents, data);
+
+  // Clean up the temp files under td.
+  ASSERT_EQ(0, unlink(file_c.c_str()));
+  ASSERT_EQ(0, unlink(file_d.c_str()));
+
+  // Extracting "b/" (with slash) should give the same result.
+  script = "package_extract_dir(\"b/\", \"" + temp_dir + "\")";
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+  ASSERT_EQ(kCTxtContents, data);
+  ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+  ASSERT_EQ(kDTxtContents, data);
+
+  ASSERT_EQ(0, unlink(file_c.c_str()));
+  ASSERT_EQ(0, unlink(file_d.c_str()));
+
+  // Extracting "" is allowed. The entries will carry the path name.
+  script = "package_extract_dir(\"\", \"" + temp_dir + "\")";
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  std::string file_a = temp_dir + "/a.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_a, &data));
+  ASSERT_EQ(kATxtContents, data);
+  std::string file_b = temp_dir + "/b.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_b, &data));
+  ASSERT_EQ(kBTxtContents, data);
+  std::string file_b_c = temp_dir + "/b/c.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_b_c, &data));
+  ASSERT_EQ(kCTxtContents, data);
+  std::string file_b_d = temp_dir + "/b/d.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_b_d, &data));
+  ASSERT_EQ(kDTxtContents, data);
+
+  ASSERT_EQ(0, unlink(file_a.c_str()));
+  ASSERT_EQ(0, unlink(file_b.c_str()));
+  ASSERT_EQ(0, unlink(file_b_c.c_str()));
+  ASSERT_EQ(0, unlink(file_b_d.c_str()));
+  ASSERT_EQ(0, rmdir((temp_dir + "/b").c_str()));
+
+  // Extracting non-existent entry should still give "t".
+  script = "package_extract_dir(\"doesntexist\", \"" + temp_dir + "\")";
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  // Only relative zip_path is allowed.
+  script = "package_extract_dir(\"/b\", \"" + temp_dir + "\")";
+  expect("", script.c_str(), kNoCause, &updater_info);
+
+  // Only absolute dest_path is allowed.
+  script = "package_extract_dir(\"b\", \"path\")";
+  expect("", script.c_str(), kNoCause, &updater_info);
+
+  CloseArchive(handle);
+}
+
 // TODO: Test extracting to block device.
 TEST_F(UpdaterTest, package_extract_file) {
   // package_extract_file expects 1 or 2 arguments.