tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

lib.rs (35982B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 pub mod adb;
      6 pub mod shell;
      7 
      8 #[cfg(test)]
      9 pub mod test;
     10 
     11 use log::{debug, info, trace, warn};
     12 use once_cell::sync::Lazy;
     13 use regex::Regex;
     14 use std::collections::BTreeMap;
     15 use std::fs::File;
     16 use std::io::{self, Read, Write};
     17 use std::net::TcpStream;
     18 use std::num::{ParseIntError, TryFromIntError};
     19 use std::path::{Component, Path};
     20 use std::str::{FromStr, Utf8Error};
     21 use std::time::{Duration, SystemTime};
     22 use thiserror::Error;
     23 pub use unix_path::{Path as UnixPath, PathBuf as UnixPathBuf};
     24 use uuid::Uuid;
     25 use walkdir::WalkDir;
     26 
     27 use crate::adb::{DeviceSerial, SyncCommand};
     28 
     29 pub type Result<T> = std::result::Result<T, DeviceError>;
     30 
     31 static SYNC_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"[^A-Za-z0-9_@%+=:,./-]").unwrap());
     32 
     33 #[derive(Debug, Default, Clone, Copy, PartialEq)]
     34 pub enum AndroidStorageInput {
     35    #[default]
     36    Auto,
     37    App,
     38    Internal,
     39    Sdcard,
     40 }
     41 
     42 impl FromStr for AndroidStorageInput {
     43    type Err = DeviceError;
     44 
     45    fn from_str(s: &str) -> Result<Self> {
     46        match s {
     47            "auto" => Ok(AndroidStorageInput::Auto),
     48            "app" => Ok(AndroidStorageInput::App),
     49            "internal" => Ok(AndroidStorageInput::Internal),
     50            "sdcard" => Ok(AndroidStorageInput::Sdcard),
     51            _ => Err(DeviceError::InvalidStorage),
     52        }
     53    }
     54 }
     55 
     56 #[derive(Debug, Clone, Copy, PartialEq)]
     57 pub enum AndroidStorage {
     58    App,
     59    Internal,
     60    Sdcard,
     61 }
     62 
     63 #[derive(Debug, Error)]
     64 pub enum DeviceError {
     65    #[error("{0}")]
     66    Adb(String),
     67    #[error(transparent)]
     68    FromInt(#[from] TryFromIntError),
     69    #[error("Invalid storage")]
     70    InvalidStorage,
     71    #[error(transparent)]
     72    Io(#[from] io::Error),
     73    #[error("Missing package")]
     74    MissingPackage,
     75    #[error("Multiple Android devices online")]
     76    MultipleDevices,
     77    #[error(transparent)]
     78    ParseInt(#[from] ParseIntError),
     79    #[error("Unknown Android device with serial '{0}'")]
     80    UnknownDevice(String),
     81    #[error(transparent)]
     82    Utf8(#[from] Utf8Error),
     83    #[error(transparent)]
     84    WalkDir(#[from] walkdir::Error),
     85 }
     86 
     87 fn encode_message(payload: &str) -> Result<String> {
     88    let hex_length = u16::try_from(payload.len()).map(|len| format!("{:0>4X}", len))?;
     89 
     90    Ok(format!("{}{}", hex_length, payload))
     91 }
     92 
     93 fn parse_device_info(line: &str) -> Option<DeviceInfo> {
     94    // Turn "serial\tdevice key1:value1 key2:value2 ..." into a `DeviceInfo`.
     95    let mut pairs = line.split_whitespace();
     96    let serial = pairs.next();
     97    let state = pairs.next();
     98    if let (Some(serial), Some("device")) = (serial, state) {
     99        let info: BTreeMap<String, String> = pairs
    100            .filter_map(|pair| {
    101                let mut kv = pair.split(':');
    102                if let (Some(k), Some(v), None) = (kv.next(), kv.next(), kv.next()) {
    103                    Some((k.to_owned(), v.to_owned()))
    104                } else {
    105                    None
    106                }
    107            })
    108            .collect();
    109 
    110        Some(DeviceInfo {
    111            serial: serial.to_owned(),
    112            info,
    113        })
    114    } else {
    115        None
    116    }
    117 }
    118 
    119 /// Reads the payload length of a host message from the stream.
    120 fn read_length<R: Read>(stream: &mut R) -> Result<usize> {
    121    let mut bytes: [u8; 4] = [0; 4];
    122    stream.read_exact(&mut bytes)?;
    123 
    124    let response = std::str::from_utf8(&bytes)?;
    125 
    126    Ok(usize::from_str_radix(response, 16)?)
    127 }
    128 
    129 /// Reads the payload length of a device message from the stream.
    130 fn read_length_little_endian(reader: &mut dyn Read) -> Result<usize> {
    131    let mut bytes: [u8; 4] = [0; 4];
    132    reader.read_exact(&mut bytes)?;
    133 
    134    let n: usize = (bytes[0] as usize)
    135        + ((bytes[1] as usize) << 8)
    136        + ((bytes[2] as usize) << 16)
    137        + ((bytes[3] as usize) << 24);
    138 
    139    Ok(n)
    140 }
    141 
    142 /// Writes the payload length of a device message to the stream.
    143 fn write_length_little_endian(writer: &mut dyn Write, n: usize) -> Result<usize> {
    144    let mut bytes = [0; 4];
    145    bytes[0] = (n & 0xFF) as u8;
    146    bytes[1] = ((n >> 8) & 0xFF) as u8;
    147    bytes[2] = ((n >> 16) & 0xFF) as u8;
    148    bytes[3] = ((n >> 24) & 0xFF) as u8;
    149 
    150    writer.write(&bytes[..]).map_err(DeviceError::Io)
    151 }
    152 
    153 fn read_response(stream: &mut TcpStream, has_output: bool, has_length: bool) -> Result<Vec<u8>> {
    154    let mut bytes: [u8; 1024] = [0; 1024];
    155 
    156    stream.read_exact(&mut bytes[0..4])?;
    157 
    158    if !bytes.starts_with(SyncCommand::Okay.code()) {
    159        let n = bytes.len().min(read_length(stream)?);
    160        stream.read_exact(&mut bytes[0..n])?;
    161 
    162        let message = std::str::from_utf8(&bytes[0..n]).map(|s| format!("adb error: {}", s))?;
    163 
    164        return Err(DeviceError::Adb(message));
    165    }
    166 
    167    let mut response = Vec::new();
    168 
    169    if has_output {
    170        stream.read_to_end(&mut response)?;
    171 
    172        if response.starts_with(SyncCommand::Okay.code()) {
    173            // Sometimes the server produces OKAYOKAY.  Sometimes there is a transport OKAY and
    174            // then the underlying command OKAY.  This is straight from `chromedriver`.
    175            response = response.split_off(4);
    176        }
    177 
    178        if response.starts_with(SyncCommand::Fail.code()) {
    179            // The server may even produce OKAYFAIL, which means the underlying
    180            // command failed. First split-off the `FAIL` and length of the message.
    181            response = response.split_off(8);
    182 
    183            let message = std::str::from_utf8(&response).map(|s| format!("adb error: {}", s))?;
    184 
    185            return Err(DeviceError::Adb(message));
    186        }
    187 
    188        if has_length {
    189            if response.len() >= 4 {
    190                let message = response.split_off(4);
    191                let slice: &mut &[u8] = &mut &*response;
    192 
    193                let n = read_length(slice)?;
    194                if n != message.len() {
    195                    warn!("adb server response contained hexstring len {} but remaining message length is {}", n, message.len());
    196                }
    197 
    198                trace!(
    199                    "adb server response was {:?}",
    200                    std::str::from_utf8(&message)?
    201                );
    202 
    203                return Ok(message);
    204            } else {
    205                return Err(DeviceError::Adb(format!(
    206                    "adb server response did not contain expected hexstring length: {:?}",
    207                    std::str::from_utf8(&response)?
    208                )));
    209            }
    210        }
    211    }
    212 
    213    Ok(response)
    214 }
    215 
    216 /// Detailed information about an ADB device.
    217 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
    218 pub struct DeviceInfo {
    219    pub serial: DeviceSerial,
    220    pub info: BTreeMap<String, String>,
    221 }
    222 
    223 /// Represents a connection to an ADB host, which multiplexes the connections to
    224 /// individual devices.
    225 #[derive(Debug)]
    226 pub struct Host {
    227    /// The TCP host to connect to.  Defaults to `"localhost"`.
    228    pub host: Option<String>,
    229    /// The TCP port to connect to.  Defaults to `5037`.
    230    pub port: Option<u16>,
    231    /// Optional TCP read timeout duration.  Defaults to 2s.
    232    pub read_timeout: Option<Duration>,
    233    /// Optional TCP write timeout duration.  Defaults to 2s.
    234    pub write_timeout: Option<Duration>,
    235 }
    236 
    237 impl Default for Host {
    238    fn default() -> Host {
    239        Host {
    240            host: Some("localhost".to_string()),
    241            port: Some(5037),
    242            read_timeout: Some(Duration::from_secs(2)),
    243            write_timeout: Some(Duration::from_secs(2)),
    244        }
    245    }
    246 }
    247 
    248 impl Host {
    249    /// Searches for available devices, and selects the one as specified by `device_serial`.
    250    ///
    251    /// If multiple devices are online, and no device has been specified,
    252    /// the `ANDROID_SERIAL` environment variable can be used to select one.
    253    pub fn device_or_default<T: AsRef<str>>(
    254        self,
    255        device_serial: Option<&T>,
    256        storage: AndroidStorageInput,
    257    ) -> Result<Device> {
    258        let serials: Vec<String> = self
    259            .devices::<Vec<_>>()?
    260            .into_iter()
    261            .map(|d| d.serial)
    262            .collect();
    263 
    264        if let Some(ref serial) = device_serial
    265            .map(|v| v.as_ref().to_owned())
    266            .or_else(|| std::env::var("ANDROID_SERIAL").ok())
    267        {
    268            if !serials.contains(serial) {
    269                return Err(DeviceError::UnknownDevice(serial.clone()));
    270            }
    271 
    272            return Device::new(self, serial.to_owned(), storage);
    273        }
    274 
    275        if serials.len() > 1 {
    276            return Err(DeviceError::MultipleDevices);
    277        }
    278 
    279        if let Some(ref serial) = serials.first() {
    280            return Device::new(self, serial.to_owned().to_string(), storage);
    281        }
    282 
    283        Err(DeviceError::Adb("No Android devices are online".to_owned()))
    284    }
    285 
    286    pub fn connect(&self) -> Result<TcpStream> {
    287        let stream = TcpStream::connect(format!(
    288            "{}:{}",
    289            self.host.clone().unwrap_or_else(|| "localhost".to_owned()),
    290            self.port.unwrap_or(5037)
    291        ))?;
    292        stream.set_read_timeout(self.read_timeout)?;
    293        stream.set_write_timeout(self.write_timeout)?;
    294        Ok(stream)
    295    }
    296 
    297    pub fn execute_command(
    298        &self,
    299        command: &str,
    300        has_output: bool,
    301        has_length: bool,
    302    ) -> Result<String> {
    303        let mut stream = self.connect()?;
    304 
    305        stream.write_all(encode_message(command)?.as_bytes())?;
    306        let bytes = read_response(&mut stream, has_output, has_length)?;
    307        // TODO: should we assert no bytes were read?
    308 
    309        let response = std::str::from_utf8(&bytes)?;
    310 
    311        Ok(response.to_owned())
    312    }
    313 
    314    pub fn execute_host_command(
    315        &self,
    316        host_command: &str,
    317        has_length: bool,
    318        has_output: bool,
    319    ) -> Result<String> {
    320        self.execute_command(&format!("host:{}", host_command), has_output, has_length)
    321    }
    322 
    323    pub fn features<B: FromIterator<String>>(&self) -> Result<B> {
    324        let features = self.execute_host_command("features", true, true)?;
    325        Ok(features.split(',').map(|x| x.to_owned()).collect())
    326    }
    327 
    328    pub fn devices<B: FromIterator<DeviceInfo>>(&self) -> Result<B> {
    329        let response = self.execute_host_command("devices-l", true, true)?;
    330 
    331        let infos: B = response.lines().filter_map(parse_device_info).collect();
    332 
    333        Ok(infos)
    334    }
    335 }
    336 
    337 /// Represents an ADB device.
    338 #[derive(Debug)]
    339 pub struct Device {
    340    /// ADB host that controls this device.
    341    pub host: Host,
    342 
    343    /// Serial number uniquely identifying this ADB device.
    344    pub serial: DeviceSerial,
    345 
    346    /// adb running as root
    347    pub adbd_root: bool,
    348 
    349    /// Flag for rooted device
    350    pub is_rooted: bool,
    351 
    352    /// "su 0" command available
    353    pub su_0_root: bool,
    354 
    355    /// "su -c" command available
    356    pub su_c_root: bool,
    357 
    358    pub run_as_package: Option<String>,
    359 
    360    pub storage: AndroidStorage,
    361 
    362    /// Cache intermediate tempfile name used in pushing via run_as.
    363    pub tempfile: UnixPathBuf,
    364 }
    365 
    366 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
    367 pub struct RemoteDirEntry {
    368    depth: usize,
    369    pub metadata: RemoteMetadata,
    370    pub name: String,
    371 }
    372 
    373 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
    374 pub enum RemoteMetadata {
    375    RemoteFile(RemoteFileMetadata),
    376    RemoteDir,
    377    RemoteSymlink,
    378 }
    379 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
    380 pub struct RemoteFileMetadata {
    381    mode: usize,
    382    size: usize,
    383 }
    384 
    385 impl Device {
    386    pub fn new(host: Host, serial: DeviceSerial, storage: AndroidStorageInput) -> Result<Device> {
    387        let mut device = Device {
    388            host,
    389            serial,
    390            adbd_root: false,
    391            is_rooted: false,
    392            run_as_package: None,
    393            storage: AndroidStorage::App,
    394            su_c_root: false,
    395            su_0_root: false,
    396            tempfile: UnixPathBuf::from("/data/local/tmp"),
    397        };
    398        device
    399            .tempfile
    400            .push(Uuid::new_v4().as_hyphenated().to_string());
    401 
    402        // check for rooted devices
    403        let uid_check = |id: String| id.contains("uid=0");
    404        device.adbd_root = device.execute_host_shell_command("id").is_ok_and(uid_check);
    405        device.su_0_root = device
    406            .execute_host_shell_command("su 0 id")
    407            .is_ok_and(uid_check);
    408        device.su_c_root = device
    409            .execute_host_shell_command("su -c id")
    410            .is_ok_and(uid_check);
    411        device.is_rooted = device.adbd_root || device.su_0_root || device.su_c_root;
    412 
    413        device.storage = match storage {
    414            AndroidStorageInput::App => AndroidStorage::App,
    415            AndroidStorageInput::Internal => AndroidStorage::Internal,
    416            AndroidStorageInput::Sdcard => AndroidStorage::Sdcard,
    417            AndroidStorageInput::Auto => AndroidStorage::Sdcard,
    418        };
    419 
    420        if device.is_rooted {
    421            info!("Device is rooted");
    422 
    423            // Set Permissive=1 if we have root.
    424            device.execute_host_shell_command("setenforce permissive")?;
    425        } else {
    426            info!("Device is unrooted");
    427        }
    428 
    429        Ok(device)
    430    }
    431 
    432    pub fn clear_app_data(&self, package: &str) -> Result<bool> {
    433        self.execute_host_shell_command(&format!("pm clear {}", package))
    434            .map(|v| v.contains("Success"))
    435    }
    436 
    437    pub fn create_dir(&self, path: &UnixPath) -> Result<()> {
    438        debug!("Creating {}", path.display());
    439 
    440        let enable_run_as = self.enable_run_as_for_path(path);
    441        self.execute_host_shell_command_as(&format!("mkdir -p {}", path.display()), enable_run_as)?;
    442 
    443        Ok(())
    444    }
    445 
    446    pub fn chmod(&self, path: &UnixPath, mask: &str, recursive: bool) -> Result<()> {
    447        let enable_run_as = self.enable_run_as_for_path(path);
    448 
    449        let recursive = match recursive {
    450            true => " -R",
    451            false => "",
    452        };
    453 
    454        self.execute_host_shell_command_as(
    455            &format!("chmod {} {} {}", recursive, mask, path.display()),
    456            enable_run_as,
    457        )?;
    458 
    459        Ok(())
    460    }
    461 
    462    pub fn execute_host_command(
    463        &self,
    464        command: &str,
    465        has_output: bool,
    466        has_length: bool,
    467    ) -> Result<String> {
    468        let mut stream = self.host.connect()?;
    469 
    470        let switch_command = format!("host:transport:{}", self.serial);
    471        trace!("execute_host_command: >> {:?}", &switch_command);
    472        stream.write_all(encode_message(&switch_command)?.as_bytes())?;
    473        let _bytes = read_response(&mut stream, false, false)?;
    474        trace!("execute_host_command: << {:?}", _bytes);
    475        // TODO: should we assert no bytes were read?
    476 
    477        trace!("execute_host_command: >> {:?}", &command);
    478        stream.write_all(encode_message(command)?.as_bytes())?;
    479        let bytes = read_response(&mut stream, has_output, has_length)?;
    480        let response = std::str::from_utf8(&bytes)?;
    481        trace!("execute_host_command: << {:?}", response);
    482 
    483        // Unify new lines by removing possible carriage returns
    484        Ok(response.replace("\r\n", "\n"))
    485    }
    486 
    487    pub fn enable_run_as_for_path(&self, path: &UnixPath) -> bool {
    488        match &self.run_as_package {
    489            Some(package) => {
    490                let mut p = UnixPathBuf::from("/data/data/");
    491                p.push(package);
    492                path.starts_with(p)
    493            }
    494            None => false,
    495        }
    496    }
    497 
    498    pub fn execute_host_shell_command(&self, shell_command: &str) -> Result<String> {
    499        self.execute_host_shell_command_as(shell_command, false)
    500    }
    501 
    502    pub fn execute_host_shell_command_as(
    503        &self,
    504        shell_command: &str,
    505        enable_run_as: bool,
    506    ) -> Result<String> {
    507        // We don't want to duplicate su invocations.
    508        if shell_command.starts_with("su") {
    509            return self.execute_host_command(&format!("shell:{}", shell_command), true, false);
    510        }
    511 
    512        let has_outer_quotes = shell_command.starts_with('"') && shell_command.ends_with('"')
    513            || shell_command.starts_with('\'') && shell_command.ends_with('\'');
    514 
    515        if self.adbd_root {
    516            return self.execute_host_command(&format!("shell:{}", shell_command), true, false);
    517        }
    518 
    519        if self.su_0_root {
    520            return self.execute_host_command(
    521                &format!("shell:su 0 {}", shell_command),
    522                true,
    523                false,
    524            );
    525        }
    526 
    527        if self.su_c_root {
    528            if has_outer_quotes {
    529                return self.execute_host_command(
    530                    &format!("shell:su -c {}", shell_command),
    531                    true,
    532                    false,
    533                );
    534            }
    535 
    536            if SYNC_REGEX.is_match(shell_command) {
    537                let arg: &str = &shell_command.replace('\'', "'\"'\"'")[..];
    538                return self.execute_host_command(&format!("shell:su -c '{}'", arg), true, false);
    539            }
    540 
    541            return self.execute_host_command(
    542                &format!("shell:su -c \"{}\"", shell_command),
    543                true,
    544                false,
    545            );
    546        }
    547 
    548        // Execute command as package
    549        if enable_run_as {
    550            let run_as_package = self
    551                .run_as_package
    552                .as_ref()
    553                .ok_or(DeviceError::MissingPackage)?;
    554 
    555            if has_outer_quotes {
    556                return self.execute_host_command(
    557                    &format!("shell:run-as {} {}", run_as_package, shell_command),
    558                    true,
    559                    false,
    560                );
    561            }
    562 
    563            if SYNC_REGEX.is_match(shell_command) {
    564                let arg: &str = &shell_command.replace('\'', "'\"'\"'")[..];
    565                return self.execute_host_command(
    566                    &format!("shell:run-as {} {}", run_as_package, arg),
    567                    true,
    568                    false,
    569                );
    570            }
    571 
    572            return self.execute_host_command(
    573                &format!("shell:run-as {} \"{}\"", run_as_package, shell_command),
    574                true,
    575                false,
    576            );
    577        }
    578 
    579        self.execute_host_command(&format!("shell:{}", shell_command), true, false)
    580    }
    581 
    582    pub fn is_app_installed(&self, package: &str) -> Result<bool> {
    583        self.execute_host_shell_command(&format!("pm path {}", package))
    584            .map(|v| v.contains("package:"))
    585    }
    586 
    587    pub fn launch<T: AsRef<str>>(
    588        &self,
    589        package: &str,
    590        activity: &str,
    591        am_start_args: &[T],
    592    ) -> Result<bool> {
    593        let mut am_start = format!("am start -S -W -n {}/{}", package, activity);
    594 
    595        for arg in am_start_args {
    596            am_start.push(' ');
    597            if SYNC_REGEX.is_match(arg.as_ref()) {
    598                am_start.push_str(&format!("\"{}\"", &shell::escape(arg.as_ref())));
    599            } else {
    600                am_start.push_str(&shell::escape(arg.as_ref()));
    601            };
    602        }
    603 
    604        self.execute_host_shell_command(&am_start)
    605            .map(|v| v.contains("Complete"))
    606    }
    607 
    608    pub fn force_stop(&self, package: &str) -> Result<()> {
    609        debug!("Force stopping Android package: {}", package);
    610        self.execute_host_shell_command(&format!("am force-stop {}", package))
    611            .and(Ok(()))
    612    }
    613 
    614    pub fn forward_port(&self, local: u16, remote: u16) -> Result<u16> {
    615        let command = format!(
    616            "host-serial:{}:forward:tcp:{};tcp:{}",
    617            self.serial, local, remote
    618        );
    619        let response = self.host.execute_command(&command, true, false)?;
    620 
    621        if local == 0 {
    622            Ok(response.parse::<u16>()?)
    623        } else {
    624            Ok(local)
    625        }
    626    }
    627 
    628    pub fn kill_forward_port(&self, local: u16) -> Result<()> {
    629        let command = format!("host-serial:{}:killforward:tcp:{}", self.serial, local);
    630        self.execute_host_command(&command, true, false).and(Ok(()))
    631    }
    632 
    633    pub fn kill_forward_all_ports(&self) -> Result<()> {
    634        let command = format!("host-serial:{}:killforward-all", self.serial);
    635        self.execute_host_command(&command, false, false)
    636            .and(Ok(()))
    637    }
    638 
    639    pub fn reverse_port(&self, remote: u16, local: u16) -> Result<u16> {
    640        let command = format!("reverse:forward:tcp:{};tcp:{}", remote, local);
    641        let response = self.execute_host_command(&command, true, false)?;
    642 
    643        if remote == 0 {
    644            Ok(response.parse::<u16>()?)
    645        } else {
    646            Ok(remote)
    647        }
    648    }
    649 
    650    pub fn kill_reverse_port(&self, remote: u16) -> Result<()> {
    651        let command = format!("reverse:killforward:tcp:{}", remote);
    652        self.execute_host_command(&command, true, true).and(Ok(()))
    653    }
    654 
    655    pub fn kill_reverse_all_ports(&self) -> Result<()> {
    656        let command = "reverse:killforward-all".to_owned();
    657        self.execute_host_command(&command, false, false)
    658            .and(Ok(()))
    659    }
    660 
    661    pub fn list_dir(&self, src: &UnixPath) -> Result<Vec<RemoteDirEntry>> {
    662        let src = src.to_path_buf();
    663        let mut queue = vec![(src.clone(), 0, "".to_string())];
    664 
    665        let mut listings = Vec::new();
    666 
    667        while let Some((next, depth, prefix)) = queue.pop() {
    668            for listing in self.list_dir_flat(&next, depth, prefix)? {
    669                if listing.metadata == RemoteMetadata::RemoteDir {
    670                    let mut child = src.clone();
    671                    child.push(listing.name.clone());
    672                    queue.push((child, depth + 1, listing.name.clone()));
    673                }
    674 
    675                listings.push(listing);
    676            }
    677        }
    678 
    679        Ok(listings)
    680    }
    681 
    682    fn list_dir_flat(
    683        &self,
    684        src: &UnixPath,
    685        depth: usize,
    686        prefix: String,
    687    ) -> Result<Vec<RemoteDirEntry>> {
    688        // Implement the ADB protocol to list a directory from the device.
    689        let mut stream = self.host.connect()?;
    690 
    691        // Send "host:transport" command with device serial
    692        let message = encode_message(&format!("host:transport:{}", self.serial))?;
    693        stream.write_all(message.as_bytes())?;
    694        let _bytes = read_response(&mut stream, false, true)?;
    695 
    696        // Send "sync:" command to initialize file transfer
    697        let message = encode_message("sync:")?;
    698        stream.write_all(message.as_bytes())?;
    699        let _bytes = read_response(&mut stream, false, true)?;
    700 
    701        // Send "LIST" command with name of the directory
    702        stream.write_all(SyncCommand::List.code())?;
    703        let args_ = format!("{}", src.display());
    704        let args = args_.as_bytes();
    705        write_length_little_endian(&mut stream, args.len())?;
    706        stream.write_all(args)?;
    707 
    708        // Use the maximum 64KB buffer to transfer the file contents.
    709        let mut buf = [0; 64 * 1024];
    710 
    711        let mut listings = Vec::new();
    712 
    713        // Read "DENT" command one or more times for the directory entries
    714        loop {
    715            stream.read_exact(&mut buf[0..4])?;
    716 
    717            if &buf[0..4] == SyncCommand::Dent.code() {
    718                // From https://github.com/cstyan/adbDocumentation/blob/6d025b3e4af41be6f93d37f516a8ac7913688623/README.md:
    719                //
    720                // A four-byte integer representing file mode - first 9 bits of this mode represent
    721                // the file permissions, as with chmod mode. Bits 14 to 16 seem to represent the
    722                // file type, one of 0b100 (file), 0b010 (directory), 0b101 (symlink)
    723                // A four-byte integer representing file size.
    724                // A four-byte integer representing last modified time in seconds since Unix Epoch.
    725                // A four-byte integer representing file name length.
    726                // A utf-8 string representing the file name.
    727                let mode = read_length_little_endian(&mut stream)?;
    728                let size = read_length_little_endian(&mut stream)?;
    729                let _time = read_length_little_endian(&mut stream)?;
    730                let name_length = read_length_little_endian(&mut stream)?;
    731                stream.read_exact(&mut buf[0..name_length])?;
    732 
    733                let mut name = std::str::from_utf8(&buf[0..name_length])?.to_owned();
    734 
    735                if name == "." || name == ".." {
    736                    continue;
    737                }
    738 
    739                if !prefix.is_empty() {
    740                    name = format!("{}/{}", prefix, &name);
    741                }
    742 
    743                let file_type = (mode >> 13) & 0b111;
    744                let metadata = match file_type {
    745                    0b010 => RemoteMetadata::RemoteDir,
    746                    0b100 => RemoteMetadata::RemoteFile(RemoteFileMetadata {
    747                        mode: mode & 0b111111111,
    748                        size,
    749                    }),
    750                    0b101 => RemoteMetadata::RemoteSymlink,
    751                    _ => return Err(DeviceError::Adb(format!("Invalid file mode {}", file_type))),
    752                };
    753 
    754                listings.push(RemoteDirEntry {
    755                    name,
    756                    depth,
    757                    metadata,
    758                });
    759            } else if &buf[0..4] == SyncCommand::Done.code() {
    760                // "DONE" command indicates end of file transfer
    761                break;
    762            } else if &buf[0..4] == SyncCommand::Fail.code() {
    763                let n = buf.len().min(read_length_little_endian(&mut stream)?);
    764 
    765                stream.read_exact(&mut buf[0..n])?;
    766 
    767                let message = std::str::from_utf8(&buf[0..n])
    768                    .map(|s| format!("adb error: {}", s))
    769                    .unwrap_or_else(|_| "adb error was not utf-8".into());
    770 
    771                return Err(DeviceError::Adb(message));
    772            } else {
    773                return Err(DeviceError::Adb("FAIL (unknown)".to_owned()));
    774            }
    775        }
    776 
    777        Ok(listings)
    778    }
    779 
    780    pub fn path_exists(&self, path: &UnixPath, enable_run_as: bool) -> Result<bool> {
    781        self.execute_host_shell_command_as(format!("ls {}", path.display()).as_str(), enable_run_as)
    782            .map(|path| !path.contains("No such file or directory"))
    783    }
    784 
    785    pub fn pull(&self, src: &UnixPath, buffer: &mut dyn Write) -> Result<()> {
    786        // Implement the ADB protocol to receive a file from the device.
    787        let mut stream = self.host.connect()?;
    788 
    789        // Send "host:transport" command with device serial
    790        let message = encode_message(&format!("host:transport:{}", self.serial))?;
    791        stream.write_all(message.as_bytes())?;
    792        let _bytes = read_response(&mut stream, false, true)?;
    793 
    794        // Send "sync:" command to initialize file transfer
    795        let message = encode_message("sync:")?;
    796        stream.write_all(message.as_bytes())?;
    797        let _bytes = read_response(&mut stream, false, true)?;
    798 
    799        // Send "RECV" command with name of the file
    800        stream.write_all(SyncCommand::Recv.code())?;
    801        let args_string = format!("{}", src.display());
    802        let args = args_string.as_bytes();
    803        write_length_little_endian(&mut stream, args.len())?;
    804        stream.write_all(args)?;
    805 
    806        // Use the maximum 64KB buffer to transfer the file contents.
    807        let mut buf = [0; 64 * 1024];
    808 
    809        // Read "DATA" command one or more times for the file content
    810        loop {
    811            stream.read_exact(&mut buf[0..4])?;
    812 
    813            if &buf[0..4] == SyncCommand::Data.code() {
    814                let len = read_length_little_endian(&mut stream)?;
    815                stream.read_exact(&mut buf[0..len])?;
    816                buffer.write_all(&buf[0..len])?;
    817            } else if &buf[0..4] == SyncCommand::Done.code() {
    818                // "DONE" command indicates end of file transfer
    819                break;
    820            } else if &buf[0..4] == SyncCommand::Fail.code() {
    821                let n = buf.len().min(read_length_little_endian(&mut stream)?);
    822 
    823                stream.read_exact(&mut buf[0..n])?;
    824 
    825                let message = std::str::from_utf8(&buf[0..n])
    826                    .map(|s| format!("adb error: {}", s))
    827                    .unwrap_or_else(|_| "adb error was not utf-8".into());
    828 
    829                return Err(DeviceError::Adb(message));
    830            } else {
    831                return Err(DeviceError::Adb("FAIL (unknown)".to_owned()));
    832            }
    833        }
    834 
    835        Ok(())
    836    }
    837 
    838    pub fn pull_dir(&self, src: &UnixPath, dest_dir: &Path) -> Result<()> {
    839        let src = src.to_path_buf();
    840        let dest_dir = dest_dir.to_path_buf();
    841 
    842        for entry in self.list_dir(&src)? {
    843            match entry.metadata {
    844                RemoteMetadata::RemoteSymlink => {} // Ignored.
    845                RemoteMetadata::RemoteDir => {
    846                    let mut d = dest_dir.clone();
    847                    d.push(&entry.name);
    848 
    849                    std::fs::create_dir_all(&d)?;
    850                }
    851                RemoteMetadata::RemoteFile(_) => {
    852                    let mut s = src.clone();
    853                    s.push(&entry.name);
    854                    let mut d = dest_dir.clone();
    855                    d.push(&entry.name);
    856 
    857                    self.pull(&s, &mut File::create(d)?)?;
    858                }
    859            }
    860        }
    861 
    862        Ok(())
    863    }
    864 
    865    pub fn push(&self, buffer: &mut dyn Read, dest: &UnixPath, mode: u32) -> Result<()> {
    866        // Implement the ADB protocol to send a file to the device.
    867        // The protocol consists of the following steps:
    868        // * Send "host:transport" command with device serial
    869        // * Send "sync:" command to initialize file transfer
    870        // * Send "SEND" command with name and mode of the file
    871        // * Send "DATA" command one or more times for the file content
    872        // * Send "DONE" command to indicate end of file transfer
    873 
    874        let enable_run_as = self.enable_run_as_for_path(&dest.to_path_buf());
    875        let dest1 = match enable_run_as {
    876            true => self.tempfile.as_path(),
    877            false => UnixPath::new(dest),
    878        };
    879 
    880        // If the destination directory does not exist, adb will
    881        // create it and any necessary ancestors however it will not
    882        // set the directory permissions to 0o777.  In addition,
    883        // Android 9 (P) has a bug in its push implementation which
    884        // will cause a push which creates directories to fail with
    885        // the error `secure_mkdirs failed: Operation not
    886        // permitted`. We can work around this by creating the
    887        // destination directories prior to the push.  Collect the
    888        // ancestors of the destination directory which do not yet
    889        // exist so we can create them and adjust their permissions
    890        // prior to performing the push.
    891        let mut current = dest.parent();
    892        let mut leaf: Option<&UnixPath> = None;
    893        let mut root: Option<&UnixPath> = None;
    894 
    895        while let Some(path) = current {
    896            if self.path_exists(path, enable_run_as)? {
    897                break;
    898            }
    899            if leaf.is_none() {
    900                leaf = Some(path);
    901            }
    902            root = Some(path);
    903            current = path.parent();
    904        }
    905 
    906        if let Some(path) = leaf {
    907            self.create_dir(path)?;
    908        }
    909 
    910        if let Some(path) = root {
    911            self.chmod(path, "777", true)?;
    912        }
    913 
    914        let mut stream = self.host.connect()?;
    915 
    916        let message = encode_message(&format!("host:transport:{}", self.serial))?;
    917        stream.write_all(message.as_bytes())?;
    918        let _bytes = read_response(&mut stream, false, true)?;
    919 
    920        let message = encode_message("sync:")?;
    921        stream.write_all(message.as_bytes())?;
    922        let _bytes = read_response(&mut stream, false, true)?;
    923 
    924        stream.write_all(SyncCommand::Send.code())?;
    925        let args_ = format!("{},{}", dest1.display(), mode);
    926        let args = args_.as_bytes();
    927        write_length_little_endian(&mut stream, args.len())?;
    928        stream.write_all(args)?;
    929 
    930        // Use a 32KB buffer to transfer the file contents
    931        // TODO: Maybe adjust to maxdata (256KB)
    932        let mut buf = [0; 32 * 1024];
    933 
    934        loop {
    935            let len = buffer.read(&mut buf)?;
    936 
    937            if len == 0 {
    938                break;
    939            }
    940 
    941            stream.write_all(SyncCommand::Data.code())?;
    942            write_length_little_endian(&mut stream, len)?;
    943            stream.write_all(&buf[0..len])?;
    944        }
    945 
    946        // https://android.googlesource.com/platform/system/core/+/master/adb/SYNC.TXT#66
    947        //
    948        // When the file is transferred a sync request "DONE" is sent, where length is set
    949        // to the last modified time for the file. The server responds to this last
    950        // request (but not to chunk requests) with an "OKAY" sync response (length can
    951        // be ignored).
    952        let time: u32 = ((SystemTime::now().duration_since(SystemTime::UNIX_EPOCH))
    953            .unwrap()
    954            .as_secs()
    955            & 0xFFFF_FFFF) as u32;
    956 
    957        stream.write_all(SyncCommand::Done.code())?;
    958        write_length_little_endian(&mut stream, time as usize)?;
    959 
    960        // Status.
    961        stream.read_exact(&mut buf[0..4])?;
    962 
    963        if buf.starts_with(SyncCommand::Okay.code()) {
    964            if enable_run_as {
    965                // Use cp -a to preserve the permissions set by push.
    966                let result = self.execute_host_shell_command_as(
    967                    format!("cp -aR {} {}", dest1.display(), dest.display()).as_str(),
    968                    enable_run_as,
    969                );
    970                if self.remove(dest1).is_err() {
    971                    warn!("Failed to remove {}", dest1.display());
    972                }
    973                result?;
    974            }
    975            Ok(())
    976        } else if buf.starts_with(SyncCommand::Fail.code()) {
    977            if enable_run_as && self.remove(dest1).is_err() {
    978                warn!("Failed to remove {}", dest1.display());
    979            }
    980            let n = buf.len().min(read_length_little_endian(&mut stream)?);
    981 
    982            stream.read_exact(&mut buf[0..n])?;
    983 
    984            let message = std::str::from_utf8(&buf[0..n])
    985                .map(|s| format!("adb error: {}", s))
    986                .unwrap_or_else(|_| "adb error was not utf-8".into());
    987 
    988            Err(DeviceError::Adb(message))
    989        } else {
    990            if self.remove(dest1).is_err() {
    991                warn!("Failed to remove {}", dest1.display());
    992            }
    993            Err(DeviceError::Adb("FAIL (unknown)".to_owned()))
    994        }
    995    }
    996 
    997    pub fn push_dir(&self, source: &Path, dest_dir: &UnixPath, mode: u32) -> Result<()> {
    998        debug!("Pushing {} to {}", source.display(), dest_dir.display());
    999 
   1000        let walker = WalkDir::new(source).follow_links(false).into_iter();
   1001 
   1002        for entry in walker {
   1003            let entry = entry?;
   1004            let path = entry.path();
   1005 
   1006            if !entry.metadata()?.is_file() {
   1007                continue;
   1008            }
   1009 
   1010            let mut file = File::open(path)?;
   1011 
   1012            let tail = path
   1013                .strip_prefix(source)
   1014                .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
   1015 
   1016            let dest = append_components(dest_dir, tail)?;
   1017            self.push(&mut file, &dest, mode)?;
   1018        }
   1019 
   1020        Ok(())
   1021    }
   1022 
   1023    pub fn remove(&self, path: &UnixPath) -> Result<()> {
   1024        debug!("Deleting {}", path.display());
   1025 
   1026        self.execute_host_shell_command_as(
   1027            &format!("rm -rf {}", path.display()),
   1028            self.enable_run_as_for_path(path),
   1029        )?;
   1030 
   1031        Ok(())
   1032    }
   1033 }
   1034 
   1035 pub(crate) fn append_components(
   1036    base: &UnixPath,
   1037    tail: &Path,
   1038 ) -> std::result::Result<UnixPathBuf, io::Error> {
   1039    let mut buf = base.to_path_buf();
   1040 
   1041    for component in tail.components() {
   1042        if let Component::Normal(segment) = component {
   1043            let utf8 = segment.to_str().ok_or_else(|| {
   1044                io::Error::new(
   1045                    io::ErrorKind::Other,
   1046                    "Could not represent path segment as UTF-8",
   1047                )
   1048            })?;
   1049            buf.push(utf8);
   1050        } else {
   1051            return Err(io::Error::new(
   1052                io::ErrorKind::Other,
   1053                "Unexpected path component".to_owned(),
   1054            ));
   1055        }
   1056    }
   1057 
   1058    Ok(buf)
   1059 }