| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "update_engine/test_utils.h" | 
 |  | 
 | #include <errno.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <sys/stat.h> | 
 | #include <sys/types.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include <set> | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | #include <base/logging.h> | 
 | #include <base/strings/string_util.h> | 
 | #include <base/strings/stringprintf.h> | 
 |  | 
 | #include "update_engine/file_writer.h" | 
 | #include "update_engine/payload_generator/filesystem_iterator.h" | 
 | #include "update_engine/utils.h" | 
 |  | 
 | using base::StringPrintf; | 
 | using std::set; | 
 | using std::string; | 
 | using std::vector; | 
 |  | 
 | namespace chromeos_update_engine { | 
 |  | 
 | const char* const kMountPathTemplate = "UpdateEngineTests_mnt-XXXXXX"; | 
 |  | 
 | bool WriteFileVector(const std::string& path, const std::vector<char>& data) { | 
 |   return utils::WriteFile(path.c_str(), &data[0], data.size()); | 
 | } | 
 |  | 
 | bool WriteFileString(const std::string& path, const std::string& data) { | 
 |   return utils::WriteFile(path.c_str(), data.data(), data.size()); | 
 | } | 
 |  | 
 | std::string Readlink(const std::string& path) { | 
 |   vector<char> buf(PATH_MAX + 1); | 
 |   ssize_t r = readlink(path.c_str(), &buf[0], buf.size()); | 
 |   if (r < 0) | 
 |     return ""; | 
 |   CHECK_LT(r, static_cast<ssize_t>(buf.size())); | 
 |   buf.resize(r); | 
 |   string ret; | 
 |   ret.insert(ret.begin(), buf.begin(), buf.end()); | 
 |   return ret; | 
 | } | 
 |  | 
 | std::vector<char> GzipCompressData(const std::vector<char>& data) { | 
 |   const char fname[] = "/tmp/GzipCompressDataTemp"; | 
 |   if (!WriteFileVector(fname, data)) { | 
 |     EXPECT_EQ(0, system((string("rm ") + fname).c_str())); | 
 |     return vector<char>(); | 
 |   } | 
 |   EXPECT_EQ(0, system((string("cat ") + fname + "|gzip>" + | 
 |                        fname + ".gz").c_str())); | 
 |   EXPECT_EQ(0, system((string("rm ") + fname).c_str())); | 
 |   vector<char> ret; | 
 |   EXPECT_TRUE(utils::ReadFile(string(fname) + ".gz", &ret)); | 
 |   EXPECT_EQ(0, system((string("rm ") + fname + ".gz").c_str())); | 
 |   return ret; | 
 | } | 
 |  | 
 | vector<char> GenerateSampleMbr() { | 
 |   // This is the actual MBR from my dev machine. Partition 1 (the first) | 
 |   // is currently marked bootable | 
 |   unsigned char mbr[512] = { | 
 |     0xeb, 0x48, 0x90, 0x10, 0x8e, 0xd0, 0xbc, 0x00, | 
 |     0xb0, 0xb8, 0x00, 0x00, 0x8e, 0xd8, 0x8e, 0xc0, | 
 |     0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, | 
 |     0x00, 0x02, 0xf3, 0xa4, 0xea, 0x21, 0x06, 0x00, | 
 |     0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, | 
 |     0x83, 0xc6, 0x10, 0x81, 0xfe, 0xfe, 0x07, 0x75, | 
 |     0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, | 
 |     0x00, 0x7c, 0xb2, 0x80, 0x8a, 0x74, 0x03, 0x02, | 
 |     0xff, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, | 
 |     0x00, 0x02, 0xfa, 0x90, 0x90, 0xf6, 0xc2, 0x80, | 
 |     0x75, 0x02, 0xb2, 0x80, 0xea, 0x59, 0x7c, 0x00, | 
 |     0x00, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xd0, 0xbc, | 
 |     0x00, 0x20, 0xfb, 0xa0, 0x40, 0x7c, 0x3c, 0xff, | 
 |     0x74, 0x02, 0x88, 0xc2, 0x52, 0xbe, 0x7f, 0x7d, | 
 |     0xe8, 0x34, 0x01, 0xf6, 0xc2, 0x80, 0x74, 0x54, | 
 |     0xb4, 0x41, 0xbb, 0xaa, 0x55, 0xcd, 0x13, 0x5a, | 
 |     0x52, 0x72, 0x49, 0x81, 0xfb, 0x55, 0xaa, 0x75, | 
 |     0x43, 0xa0, 0x41, 0x7c, 0x84, 0xc0, 0x75, 0x05, | 
 |     0x83, 0xe1, 0x01, 0x74, 0x37, 0x66, 0x8b, 0x4c, | 
 |     0x10, 0xbe, 0x05, 0x7c, 0xc6, 0x44, 0xff, 0x01, | 
 |     0x66, 0x8b, 0x1e, 0x44, 0x7c, 0xc7, 0x04, 0x10, | 
 |     0x00, 0xc7, 0x44, 0x02, 0x01, 0x00, 0x66, 0x89, | 
 |     0x5c, 0x08, 0xc7, 0x44, 0x06, 0x00, 0x70, 0x66, | 
 |     0x31, 0xc0, 0x89, 0x44, 0x04, 0x66, 0x89, 0x44, | 
 |     0x0c, 0xb4, 0x42, 0xcd, 0x13, 0x72, 0x05, 0xbb, | 
 |     0x00, 0x70, 0xeb, 0x7d, 0xb4, 0x08, 0xcd, 0x13, | 
 |     0x73, 0x0a, 0xf6, 0xc2, 0x80, 0x0f, 0x84, 0xea, | 
 |     0x00, 0xe9, 0x8d, 0x00, 0xbe, 0x05, 0x7c, 0xc6, | 
 |     0x44, 0xff, 0x00, 0x66, 0x31, 0xc0, 0x88, 0xf0, | 
 |     0x40, 0x66, 0x89, 0x44, 0x04, 0x31, 0xd2, 0x88, | 
 |     0xca, 0xc1, 0xe2, 0x02, 0x88, 0xe8, 0x88, 0xf4, | 
 |     0x40, 0x89, 0x44, 0x08, 0x31, 0xc0, 0x88, 0xd0, | 
 |     0xc0, 0xe8, 0x02, 0x66, 0x89, 0x04, 0x66, 0xa1, | 
 |     0x44, 0x7c, 0x66, 0x31, 0xd2, 0x66, 0xf7, 0x34, | 
 |     0x88, 0x54, 0x0a, 0x66, 0x31, 0xd2, 0x66, 0xf7, | 
 |     0x74, 0x04, 0x88, 0x54, 0x0b, 0x89, 0x44, 0x0c, | 
 |     0x3b, 0x44, 0x08, 0x7d, 0x3c, 0x8a, 0x54, 0x0d, | 
 |     0xc0, 0xe2, 0x06, 0x8a, 0x4c, 0x0a, 0xfe, 0xc1, | 
 |     0x08, 0xd1, 0x8a, 0x6c, 0x0c, 0x5a, 0x8a, 0x74, | 
 |     0x0b, 0xbb, 0x00, 0x70, 0x8e, 0xc3, 0x31, 0xdb, | 
 |     0xb8, 0x01, 0x02, 0xcd, 0x13, 0x72, 0x2a, 0x8c, | 
 |     0xc3, 0x8e, 0x06, 0x48, 0x7c, 0x60, 0x1e, 0xb9, | 
 |     0x00, 0x01, 0x8e, 0xdb, 0x31, 0xf6, 0x31, 0xff, | 
 |     0xfc, 0xf3, 0xa5, 0x1f, 0x61, 0xff, 0x26, 0x42, | 
 |     0x7c, 0xbe, 0x85, 0x7d, 0xe8, 0x40, 0x00, 0xeb, | 
 |     0x0e, 0xbe, 0x8a, 0x7d, 0xe8, 0x38, 0x00, 0xeb, | 
 |     0x06, 0xbe, 0x94, 0x7d, 0xe8, 0x30, 0x00, 0xbe, | 
 |     0x99, 0x7d, 0xe8, 0x2a, 0x00, 0xeb, 0xfe, 0x47, | 
 |     0x52, 0x55, 0x42, 0x20, 0x00, 0x47, 0x65, 0x6f, | 
 |     0x6d, 0x00, 0x48, 0x61, 0x72, 0x64, 0x20, 0x44, | 
 |     0x69, 0x73, 0x6b, 0x00, 0x52, 0x65, 0x61, 0x64, | 
 |     0x00, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x00, | 
 |     0xbb, 0x01, 0x00, 0xb4, 0x0e, 0xcd, 0x10, 0xac, | 
 |     0x3c, 0x00, 0x75, 0xf4, 0xc3, 0x00, 0x00, 0x00, | 
 |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
 |     0x50, 0xc1, 0x04, 0x00, 0x00, 0x00, 0x80, 0x01, | 
 |     0x01, 0x00, 0x83, 0xfe, 0xff, 0xff, 0x3f, 0x00, | 
 |     0x00, 0x00, 0x09, 0x7f, 0x32, 0x06, 0x00, 0xfe, | 
 |     0xff, 0xff, 0x05, 0xfe, 0xff, 0xff, 0x48, 0x7f, | 
 |     0x32, 0x06, 0x79, 0x59, 0x2d, 0x00, 0x00, 0x00, | 
 |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
 |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
 |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 
 |     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa | 
 |   }; | 
 |   vector<char> ret; | 
 |   ret.insert(ret.begin(), reinterpret_cast<char *>(mbr), | 
 |              reinterpret_cast<char *>(mbr + sizeof(mbr))); | 
 |   return ret; | 
 | } | 
 |  | 
 | // Binds provided |filename| to an unused loopback device, whose name is written | 
 | // to the string pointed to by |lo_dev_name_p|. Returns true on success, false | 
 | // otherwise (along with corresponding test failures), in which case the content | 
 | // of |lo_dev_name_p| is unknown. | 
 | bool BindToUnusedLoopDevice(const string& filename, string* lo_dev_name_p) { | 
 |   CHECK(lo_dev_name_p); | 
 |  | 
 |   // Bind to an unused loopback device, sanity check the device name. | 
 |   lo_dev_name_p->clear(); | 
 |   if (!(utils::ReadPipe("losetup --show -f " + filename, lo_dev_name_p) && | 
 |         StartsWithASCII(*lo_dev_name_p, "/dev/loop", true))) { | 
 |     ADD_FAILURE(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Strip anything from the first newline char. | 
 |   size_t newline_pos = lo_dev_name_p->find('\n'); | 
 |   if (newline_pos != string::npos) | 
 |     lo_dev_name_p->erase(newline_pos); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool ExpectVectorsEq(const vector<char>& expected, const vector<char>& actual) { | 
 |   EXPECT_EQ(expected.size(), actual.size()); | 
 |   if (expected.size() != actual.size()) | 
 |     return false; | 
 |   bool is_all_eq = true; | 
 |   for (unsigned int i = 0; i < expected.size(); i++) { | 
 |     EXPECT_EQ(expected[i], actual[i]) << "offset: " << i; | 
 |     is_all_eq = is_all_eq && (expected[i] == actual[i]); | 
 |   } | 
 |   return is_all_eq; | 
 | } | 
 |  | 
 | void FillWithData(vector<char>* buffer) { | 
 |   size_t input_counter = 0; | 
 |   for (vector<char>::iterator it = buffer->begin(); it != buffer->end(); ++it) { | 
 |     *it = kRandomString[input_counter]; | 
 |     input_counter++; | 
 |     input_counter %= sizeof(kRandomString); | 
 |   } | 
 | } | 
 |  | 
 | void CreateEmptyExtImageAtPath(const string& path, | 
 |                                size_t size, | 
 |                                int block_size) { | 
 |   EXPECT_EQ(0, System(StringPrintf("dd if=/dev/zero of=%s" | 
 |                                    " seek=%zu bs=1 count=1", | 
 |                                    path.c_str(), size))); | 
 |   EXPECT_EQ(0, System(StringPrintf("mkfs.ext3 -b %d -F %s", | 
 |                                    block_size, path.c_str()))); | 
 | } | 
 |  | 
 | void CreateExtImageAtPath(const string& path, vector<string>* out_paths) { | 
 |   // create 10MiB sparse file, mounted at a unique location. | 
 |   string mount_path; | 
 |   CHECK(utils::MakeTempDirectory(kMountPathTemplate, &mount_path)); | 
 |   ScopedDirRemover mount_path_unlinker(mount_path); | 
 |  | 
 |   EXPECT_EQ(0, System(StringPrintf("dd if=/dev/zero of=%s" | 
 |                                    " seek=10485759 bs=1 count=1", | 
 |                                    path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("mkfs.ext3 -b 4096 -F %s", path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("mount -o loop %s %s", path.c_str(), | 
 |                                    mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("echo hi > %s/hi", mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("echo hello > %s/hello", | 
 |                                    mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir", mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/empty_dir", | 
 |                                    mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/mnt", | 
 |                                    mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("echo T > %s/some_dir/test", | 
 |                                    mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("mkfifo %s/some_dir/fifo", | 
 |                                    mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("mknod %s/cdev c 2 3", mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("ln -s /some/target %s/sym", | 
 |                                    mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("ln %s/some_dir/test %s/testlink", | 
 |                                    mount_path.c_str(), mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("echo T > %s/srchardlink0", | 
 |                                    mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("ln %s/srchardlink0 %s/srchardlink1", | 
 |                                    mount_path.c_str(), mount_path.c_str()))); | 
 |   EXPECT_EQ(0, System(StringPrintf("ln -s bogus %s/boguslink", | 
 |                                    mount_path.c_str()))); | 
 |   EXPECT_TRUE(utils::UnmountFilesystem(mount_path.c_str())); | 
 |  | 
 |   if (out_paths) { | 
 |     out_paths->clear(); | 
 |     out_paths->push_back(""); | 
 |     out_paths->push_back("/hi"); | 
 |     out_paths->push_back("/boguslink"); | 
 |     out_paths->push_back("/hello"); | 
 |     out_paths->push_back("/some_dir"); | 
 |     out_paths->push_back("/some_dir/empty_dir"); | 
 |     out_paths->push_back("/some_dir/mnt"); | 
 |     out_paths->push_back("/some_dir/test"); | 
 |     out_paths->push_back("/some_dir/fifo"); | 
 |     out_paths->push_back("/cdev"); | 
 |     out_paths->push_back("/testlink"); | 
 |     out_paths->push_back("/sym"); | 
 |     out_paths->push_back("/srchardlink0"); | 
 |     out_paths->push_back("/srchardlink1"); | 
 |     out_paths->push_back("/lost+found"); | 
 |   } | 
 | } | 
 |  | 
 | void VerifyAllPaths(const string& parent, set<string> expected_paths) { | 
 |   FilesystemIterator iter(parent, set<string>()); | 
 |   ino_t test_ino = 0; | 
 |   ino_t testlink_ino = 0; | 
 |   while (!iter.IsEnd()) { | 
 |     string path = iter.GetFullPath(); | 
 |     EXPECT_TRUE(expected_paths.find(path) != expected_paths.end()) << path; | 
 |     EXPECT_EQ(1, expected_paths.erase(path)); | 
 |     if (utils::StringHasSuffix(path, "/hi") || | 
 |         utils::StringHasSuffix(path, "/hello") || | 
 |         utils::StringHasSuffix(path, "/test") || | 
 |         utils::StringHasSuffix(path, "/testlink")) { | 
 |       EXPECT_TRUE(S_ISREG(iter.GetStat().st_mode)); | 
 |       if (utils::StringHasSuffix(path, "/test")) | 
 |         test_ino = iter.GetStat().st_ino; | 
 |       else if (utils::StringHasSuffix(path, "/testlink")) | 
 |         testlink_ino = iter.GetStat().st_ino; | 
 |     } else if (utils::StringHasSuffix(path, "/some_dir") || | 
 |                utils::StringHasSuffix(path, "/empty_dir") || | 
 |                utils::StringHasSuffix(path, "/mnt") || | 
 |                utils::StringHasSuffix(path, "/lost+found") || | 
 |                parent == path) { | 
 |       EXPECT_TRUE(S_ISDIR(iter.GetStat().st_mode)); | 
 |     } else if (utils::StringHasSuffix(path, "/fifo")) { | 
 |       EXPECT_TRUE(S_ISFIFO(iter.GetStat().st_mode)); | 
 |     } else if (utils::StringHasSuffix(path, "/cdev")) { | 
 |       EXPECT_TRUE(S_ISCHR(iter.GetStat().st_mode)); | 
 |     } else if (utils::StringHasSuffix(path, "/sym")) { | 
 |       EXPECT_TRUE(S_ISLNK(iter.GetStat().st_mode)); | 
 |     } else { | 
 |       LOG(INFO) << "got non hardcoded path: " << path; | 
 |     } | 
 |     iter.Increment(); | 
 |   } | 
 |   EXPECT_EQ(testlink_ino, test_ino); | 
 |   EXPECT_NE(0, test_ino); | 
 |   EXPECT_FALSE(iter.IsErr()); | 
 |   EXPECT_TRUE(expected_paths.empty()); | 
 |   if (!expected_paths.empty()) { | 
 |     for (set<string>::const_iterator it = expected_paths.begin(); | 
 |          it != expected_paths.end(); ++it) { | 
 |       LOG(INFO) << "extra path: " << *it; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | ScopedLoopMounter::ScopedLoopMounter(const string& file_path, | 
 |                                      string* mnt_path, | 
 |                                      unsigned long flags) {  // NOLINT - long | 
 |   EXPECT_TRUE(utils::MakeTempDirectory("mnt.XXXXXX", mnt_path)); | 
 |   dir_remover_.reset(new ScopedDirRemover(*mnt_path)); | 
 |  | 
 |   string loop_dev; | 
 |   loop_binder_.reset(new ScopedLoopbackDeviceBinder(file_path, &loop_dev)); | 
 |  | 
 |   EXPECT_TRUE(utils::MountFilesystem(loop_dev, *mnt_path, flags)); | 
 |   unmounter_.reset(new ScopedFilesystemUnmounter(*mnt_path)); | 
 | } | 
 |  | 
 | static gboolean RunGMainLoopOnTimeout(gpointer user_data) { | 
 |   bool* timeout = static_cast<bool*>(user_data); | 
 |   *timeout = true; | 
 |   return FALSE;  // Remove timeout source | 
 | } | 
 |  | 
 | void RunGMainLoopUntil(int timeout_msec, base::Callback<bool()> terminate) { | 
 |   GMainLoop* loop = g_main_loop_new(NULL, FALSE); | 
 |   GMainContext* context = g_main_context_default(); | 
 |  | 
 |   bool timeout = false; | 
 |   guint source_id = g_timeout_add( | 
 |       timeout_msec, RunGMainLoopOnTimeout, &timeout); | 
 |  | 
 |   while (!timeout && (terminate.is_null() || !terminate.Run())) | 
 |     g_main_context_iteration(context, TRUE); | 
 |  | 
 |   g_source_remove(source_id); | 
 |   g_main_loop_unref(loop); | 
 | } | 
 |  | 
 | int RunGMainLoopMaxIterations(int iterations) { | 
 |   int result; | 
 |   GMainContext* context = g_main_context_default(); | 
 |   for (result = 0; | 
 |        result < iterations && g_main_context_iteration(context, FALSE); | 
 |        result++) {} | 
 |   return result; | 
 | } | 
 |  | 
 | GValue* GValueNewString(const char* str) { | 
 |   GValue* gval = g_new0(GValue, 1); | 
 |   g_value_init(gval, G_TYPE_STRING); | 
 |   g_value_set_string(gval, str); | 
 |   return gval; | 
 | } | 
 |  | 
 | void GValueFree(gpointer arg) { | 
 |   auto gval = reinterpret_cast<GValue*>(arg); | 
 |   g_value_unset(gval); | 
 |   g_free(gval); | 
 | } | 
 |  | 
 | }  // namespace chromeos_update_engine |