updater: Add more testcase for symlink().
Clean up SymlinkFn() a bit. Also clean up the temp files created when
running the tests; otherwise non-empty TemporaryDir won't be removed.
Test: recovery_component_test passes.
Change-Id: Id3844abebd168c40125c4dcec54e6ef680a83c3a
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index acc2b40..973c19d 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
#include <string>
#include <android-base/file.h>
@@ -212,10 +216,15 @@
// Parents create successfully.
TemporaryFile temp_file3;
TemporaryDir td;
- std::string temp_dir = std::string(td.path) + "/aaa/bbb/a.txt";
- std::string script3("rename(\"" + std::string(temp_file3.path) + "\", \"" +
- temp_dir + "\")");
- expect(temp_dir.c_str(), script3.c_str(), kNoCause);
+ 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) {
@@ -227,7 +236,31 @@
std::string script1("symlink(\"" + std::string(temp_file1.path) + "\", \"\")");
expect(nullptr, script1.c_str(), kSymlinkFailure);
- // symlink failed to remove old src.
- std::string script2("symlink(\"" + std::string(temp_file1.path) + "\", \"/proc\")");
+ 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()));
}
diff --git a/updater/install.cpp b/updater/install.cpp
index cba95f5..59c54dd 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -566,8 +566,9 @@
}
}
-// symlink target src1 src2 ...
-// unlinks any previously existing src1, src2, etc before creating symlinks.
+// symlink(target, [src1, src2, ...])
+// Creates all sources as symlinks to target. It unlinks any previously existing src1, src2, etc
+// before creating symlinks.
Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc == 0) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1+ args, got %d", name, argc);
@@ -579,33 +580,29 @@
std::vector<std::string> srcs;
if (!ReadArgs(state, argc-1, argv+1, &srcs)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)",
+ name);
}
- int bad = 0;
- for (int i = 0; i < argc-1; ++i) {
- if (unlink(srcs[i].c_str()) < 0) {
- if (errno != ENOENT) {
- printf("%s: failed to remove %s: %s\n",
- name, srcs[i].c_str(), strerror(errno));
- ++bad;
- }
- }
- if (!make_parents(srcs[i])) {
+ size_t bad = 0;
+ for (const auto& src : srcs) {
+ if (unlink(src.c_str()) == -1 && errno != ENOENT) {
+ printf("%s: failed to remove %s: %s\n", name, src.c_str(), strerror(errno));
+ ++bad;
+ } else if (!make_parents(src)) {
printf("%s: failed to symlink %s to %s: making parents failed\n",
- name, srcs[i].c_str(), target.c_str());
+ name, src.c_str(), target.c_str());
++bad;
- }
- if (symlink(target.c_str(), srcs[i].c_str()) < 0) {
+ } else if (symlink(target.c_str(), src.c_str()) == -1) {
printf("%s: failed to symlink %s to %s: %s\n",
- name, srcs[i].c_str(), target.c_str(), strerror(errno));
+ name, src.c_str(), target.c_str(), strerror(errno));
++bad;
}
}
- if (bad) {
- return ErrorAbort(state, kSymlinkFailure, "%s: some symlinks failed", name);
+ if (bad != 0) {
+ return ErrorAbort(state, kSymlinkFailure, "%s: Failed to create %zu symlink(s)", name, bad);
}
- return StringValue("");
+ return StringValue("t");
}
struct perm_parsed_args {