Index: source/Plugins/Platform/Android/AdbClient.h =================================================================== --- source/Plugins/Platform/Android/AdbClient.h +++ source/Plugins/Platform/Android/AdbClient.h @@ -119,6 +119,9 @@ Error Shell (const char* command, uint32_t timeout_ms, std::string* output); + Error ShellToFile(const char *command, uint32_t timeout_ms, + const FileSpec &output_file_spec); + std::unique_ptr GetSyncService (Error &error); @@ -156,8 +159,10 @@ Error StartSync (); - Error - ReadAllBytes (void *buffer, size_t size); + Error internalShell(const char *command, uint32_t timeout_ms, + std::vector &output_buf); + + Error ReadAllBytes(void *buffer, size_t size); std::string m_device_id; std::unique_ptr m_conn; Index: source/Plugins/Platform/Android/AdbClient.cpp =================================================================== --- source/Plugins/Platform/Android/AdbClient.cpp +++ source/Plugins/Platform/Android/AdbClient.cpp @@ -383,31 +383,62 @@ return ::ReadAllBytes (*m_conn, buffer, size); } -Error -AdbClient::Shell (const char* command, uint32_t timeout_ms, std::string* output) -{ - auto error = SwitchDeviceTransport (); - if (error.Fail ()) - return Error ("Failed to switch to device transport: %s", error.AsCString ()); - - StreamString adb_command; - adb_command.Printf("shell:%s", command); - error = SendMessage (adb_command.GetData(), false); - if (error.Fail ()) - return error; - - error = ReadResponseStatus (); - if (error.Fail ()) - return error; - - std::vector in_buffer; - error = ReadMessageStream (in_buffer, timeout_ms); - if (error.Fail()) - return error; - - if (output) - output->assign(in_buffer.begin(), in_buffer.end()); - return error; +Error AdbClient::internalShell(const char *command, uint32_t timeout_ms, + std::vector &output_buf) { + output_buf.clear(); + + auto error = SwitchDeviceTransport(); + if (error.Fail()) + return Error("Failed to switch to device transport: %s", error.AsCString()); + + StreamString adb_command; + adb_command.Printf("shell:%s", command); + error = SendMessage(adb_command.GetData(), false); + if (error.Fail()) return error; + + error = ReadResponseStatus(); + if (error.Fail()) return error; + + error = ReadMessageStream(output_buf, timeout_ms); + if (error.Fail()) return error; + + // ADB doesn't propagate return code of shell execution - if + // output starts with /system/bin/sh: most likely command failed. + static const char *kShellPrefix = "/system/bin/sh:"; + if (output_buf.size() > strlen(kShellPrefix)) { + if (!memcmp(&output_buf[0], kShellPrefix, strlen(kShellPrefix))) + return Error("Shell command %s failed: %s", command, + std::string(output_buf.begin(), output_buf.end()).c_str()); + } + + return Error(); +} + +Error AdbClient::Shell(const char *command, uint32_t timeout_ms, + std::string *output) { + std::vector output_buffer; + auto error = internalShell(command, timeout_ms, output_buffer); + if (error.Fail()) return error; + + if (output) output->assign(output_buffer.begin(), output_buffer.end()); + return error; +} + +Error AdbClient::ShellToFile(const char *command, uint32_t timeout_ms, + const FileSpec &output_file_spec) { + std::vector output_buffer; + auto error = internalShell(command, timeout_ms, output_buffer); + if (error.Fail()) return error; + + const auto output_filename = output_file_spec.GetPath(); + std::ofstream dst(output_filename, std::ios::out | std::ios::binary); + if (!dst.is_open()) + return Error("Unable to open local file %s", output_filename.c_str()); + + dst.write(&output_buffer[0], output_buffer.size()); + dst.close(); + if (!dst) return Error("Failed to write file %s", output_filename.c_str()); + return Error(); } std::unique_ptr Index: source/Plugins/Platform/Android/PlatformAndroid.cpp =================================================================== --- source/Plugins/Platform/Android/PlatformAndroid.cpp +++ source/Plugins/Platform/Android/PlatformAndroid.cpp @@ -230,7 +230,30 @@ if (error.Fail ()) return error; - return sync_service->PullFile (source_spec, destination); + uint32_t mode = 0, size = 0, mtime = 0; + error = sync_service->Stat(source_spec, mode, size, mtime); + if (error.Fail()) return error; + + if (mode != 0) return sync_service->PullFile(source_spec, destination); + + auto source_file = source_spec.GetCString(false); + + Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("Got mode == 0 on '%s': try to get file via 'shell cat'", + source_file); + + if (strchr(source_file, '\'') != nullptr) + return Error("Doesn't support single-quotes in filenames"); + + // mode == 0 can signify that adbd cannot access the file + // due security constraints - try "cat ..." as a fallback. + AdbClient adb(m_device_id); + + char cmd[PATH_MAX]; + snprintf(cmd, sizeof(cmd), "cat '%s'", source_file); + + return adb.ShellToFile(cmd, 60000 /* ms */, destination); } Error