1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <signal.h>
20 #include <strings.h>
21 #include <sys/mount.h>
22 #include <sys/socket.h>
23 #include <sys/stat.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26 
27 #include <string>
28 #include <vector>
29 
30 #include <android-base/file.h>
31 #include <android-base/unique_fd.h>
32 #include <gtest/gtest.h>
33 
34 #include "adb.h"
35 #include "adb_io.h"
36 #include "fuse_adb_provider.h"
37 #include "fuse_sideload.h"
38 #include "minadbd/types.h"
39 #include "minadbd_services.h"
40 #include "socket.h"
41 
42 class MinadbdServicesTest : public ::testing::Test {
43  protected:
44   static constexpr int EXIT_TIME_OUT = 10;
45 
SetUp()46   void SetUp() override {
47     ASSERT_TRUE(
48         android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &minadbd_socket_, &recovery_socket_));
49     SetMinadbdSocketFd(minadbd_socket_);
50     SetSideloadMountPoint(mount_point_.path);
51 
52     package_path_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME;
53     exit_flag_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG;
54 
55     signal(SIGPIPE, SIG_IGN);
56   }
57 
TearDown()58   void TearDown() override {
59     // Umount in case the test fails. Ignore the result.
60     umount(mount_point_.path);
61 
62     signal(SIGPIPE, SIG_DFL);
63   }
64 
ReadAndCheckCommandMessage(int fd,MinadbdCommand expected_command)65   void ReadAndCheckCommandMessage(int fd, MinadbdCommand expected_command) {
66     std::vector<uint8_t> received(kMinadbdMessageSize, '\0');
67     ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize));
68 
69     std::vector<uint8_t> expected(kMinadbdMessageSize, '\0');
70     memcpy(expected.data(), kMinadbdCommandPrefix, strlen(kMinadbdCommandPrefix));
71     memcpy(expected.data() + strlen(kMinadbdCommandPrefix), &expected_command,
72            sizeof(expected_command));
73     ASSERT_EQ(expected, received);
74   }
75 
WaitForFusePath()76   void WaitForFusePath() {
77     constexpr int TIME_OUT = 10;
78     for (int i = 0; i < TIME_OUT; ++i) {
79       struct stat sb;
80       if (stat(package_path_.c_str(), &sb) == 0) {
81         return;
82       }
83 
84       if (errno == ENOENT) {
85         sleep(1);
86         continue;
87       }
88       FAIL() << "Timed out waiting for the fuse-provided package " << strerror(errno);
89     }
90   }
91 
StatExitFlagAndExitProcess(int exit_code)92   void StatExitFlagAndExitProcess(int exit_code) {
93     struct stat sb;
94     if (stat(exit_flag_.c_str(), &sb) != 0) {
95       PLOG(ERROR) << "Failed to stat " << exit_flag_;
96     }
97 
98     exit(exit_code);
99   }
100 
WriteMinadbdCommandStatus(MinadbdCommandStatus status)101   void WriteMinadbdCommandStatus(MinadbdCommandStatus status) {
102     std::string status_message(kMinadbdMessageSize, '\0');
103     memcpy(status_message.data(), kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
104     memcpy(status_message.data() + strlen(kMinadbdStatusPrefix), &status, sizeof(status));
105     ASSERT_TRUE(
106         android::base::WriteFully(recovery_socket_, status_message.data(), kMinadbdMessageSize));
107   }
108 
ExecuteCommandAndWaitForExit(const std::string & command)109   void ExecuteCommandAndWaitForExit(const std::string& command) {
110     unique_fd fd = daemon_service_to_fd(command, nullptr);
111     ASSERT_NE(-1, fd);
112     sleep(EXIT_TIME_OUT);
113   }
114 
115   android::base::unique_fd minadbd_socket_;
116   android::base::unique_fd recovery_socket_;
117 
118   TemporaryDir mount_point_;
119   std::string package_path_;
120   std::string exit_flag_;
121 };
122 
TEST_F(MinadbdServicesTest,SideloadHostService_wrong_size_argument)123 TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) {
124   ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"),
125               ::testing::ExitedWithCode(kMinadbdHostCommandArgumentError), "");
126 }
127 
TEST_F(MinadbdServicesTest,SideloadHostService_wrong_block_size)128 TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) {
129   ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:10:20"),
130               ::testing::ExitedWithCode(kMinadbdFuseStartError), "");
131 }
132 
TEST_F(MinadbdServicesTest,SideloadHostService_broken_minadbd_socket)133 TEST_F(MinadbdServicesTest, SideloadHostService_broken_minadbd_socket) {
134   SetMinadbdSocketFd(-1);
135   ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"),
136               ::testing::ExitedWithCode(kMinadbdSocketIOError), "");
137 }
138 
TEST_F(MinadbdServicesTest,SideloadHostService_broken_recovery_socket)139 TEST_F(MinadbdServicesTest, SideloadHostService_broken_recovery_socket) {
140   recovery_socket_.reset();
141   ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"),
142               ::testing::ExitedWithCode(kMinadbdSocketIOError), "");
143 }
144 
TEST_F(MinadbdServicesTest,SideloadHostService_wrong_command_format)145 TEST_F(MinadbdServicesTest, SideloadHostService_wrong_command_format) {
146   auto test_body = [&](const std::string& command) {
147     unique_fd fd = daemon_service_to_fd(command, nullptr);
148     ASSERT_NE(-1, fd);
149     WaitForFusePath();
150     ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall);
151 
152     struct stat sb;
153     ASSERT_EQ(0, stat(exit_flag_.c_str(), &sb));
154     ASSERT_TRUE(android::base::WriteStringToFd("12345678", recovery_socket_));
155     sleep(EXIT_TIME_OUT);
156   };
157 
158   ASSERT_EXIT(test_body("sideload-host:4096:4096"),
159               ::testing::ExitedWithCode(kMinadbdMessageFormatError), "");
160 }
161 
TEST_F(MinadbdServicesTest,SideloadHostService_read_data_from_fuse)162 TEST_F(MinadbdServicesTest, SideloadHostService_read_data_from_fuse) {
163   auto test_body = [&]() {
164     std::vector<uint8_t> content(4096, 'a');
165     // Start a new process instead of a thread to read from the package mounted by FUSE. Because
166     // the test may not exit and report failures correctly when the thread blocks by a syscall.
167     pid_t pid = fork();
168     if (pid == 0) {
169       WaitForFusePath();
170       android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(package_path_.c_str(), O_RDONLY)));
171       // Do not use assertion here because we want to stat the exit flag and exit the process.
172       // Otherwise the test will wait for the time out instead of failing immediately.
173       if (fd == -1) {
174         PLOG(ERROR) << "Failed to open " << package_path_;
175         StatExitFlagAndExitProcess(1);
176       }
177       std::vector<uint8_t> content_from_fuse(4096);
178       if (!android::base::ReadFully(fd, content_from_fuse.data(), 4096)) {
179         PLOG(ERROR) << "Failed to read from " << package_path_;
180         StatExitFlagAndExitProcess(1);
181       }
182       if (content_from_fuse != content) {
183         LOG(ERROR) << "Content read from fuse doesn't match with the expected value";
184         StatExitFlagAndExitProcess(1);
185       }
186       StatExitFlagAndExitProcess(0);
187     }
188 
189     unique_fd fd = daemon_service_to_fd("sideload-host:4096:4096", nullptr);
190     ASSERT_NE(-1, fd);
191     ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall);
192 
193     // Mimic the response from adb host.
194     std::string adb_message(8, '\0');
195     ASSERT_TRUE(android::base::ReadFully(fd, adb_message.data(), 8));
196     ASSERT_EQ(android::base::StringPrintf("%08u", 0), adb_message);
197     ASSERT_TRUE(android::base::WriteFully(fd, content.data(), 4096));
198 
199     // Check that we read the correct data from fuse.
200     int child_status;
201     waitpid(pid, &child_status, 0);
202     ASSERT_TRUE(WIFEXITED(child_status));
203     ASSERT_EQ(0, WEXITSTATUS(child_status));
204 
205     WriteMinadbdCommandStatus(MinadbdCommandStatus::kSuccess);
206 
207     // TODO(xunchang) check if adb host-side receives "DONEDONE", there's a race condition between
208     // receiving the message and exit of test body (by detached thread in minadbd service).
209     exit(kMinadbdSuccess);
210   };
211 
212   ASSERT_EXIT(test_body(), ::testing::ExitedWithCode(kMinadbdSuccess), "");
213 }
214