mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
afc pull files
This commit is contained in:
76
idevice/src/afc/file.rs
Normal file
76
idevice/src/afc/file.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use crate::IdeviceError;
|
||||
|
||||
use super::{
|
||||
opcode::AfcOpcode,
|
||||
packet::{AfcPacket, AfcPacketHeader},
|
||||
};
|
||||
|
||||
const MAX_TRANSFER: u64 = 64 * 1024; // this is what go-ios uses
|
||||
|
||||
pub struct FileDescriptor<'a> {
|
||||
pub(crate) client: &'a mut super::AfcClient,
|
||||
pub(crate) fd: u64,
|
||||
pub(crate) path: String,
|
||||
}
|
||||
|
||||
impl FileDescriptor<'_> {
|
||||
pub async fn close(self) -> Result<(), IdeviceError> {
|
||||
let header_payload = self.fd.to_le_bytes().to_vec();
|
||||
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
|
||||
|
||||
let header = AfcPacketHeader {
|
||||
magic: super::MAGIC,
|
||||
entire_len: header_len, // it's the same since the payload is empty for this
|
||||
header_payload_len: header_len,
|
||||
packet_num: self.client.package_number,
|
||||
operation: AfcOpcode::FileClose,
|
||||
};
|
||||
self.client.package_number += 1;
|
||||
|
||||
let packet = AfcPacket {
|
||||
header,
|
||||
header_payload,
|
||||
payload: Vec::new(),
|
||||
};
|
||||
|
||||
self.client.send(packet).await?;
|
||||
self.client.read().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read(&mut self) -> Result<Vec<u8>, IdeviceError> {
|
||||
// Get the file size first
|
||||
let mut bytes_left = self.client.get_file_info(&self.path).await?.size;
|
||||
let mut collected_bytes = Vec::with_capacity(bytes_left);
|
||||
|
||||
while bytes_left > 0 {
|
||||
let mut header_payload = self.fd.to_le_bytes().to_vec();
|
||||
header_payload.extend_from_slice(&MAX_TRANSFER.to_le_bytes());
|
||||
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
|
||||
|
||||
let header = AfcPacketHeader {
|
||||
magic: super::MAGIC,
|
||||
entire_len: header_len, // it's the same since the payload is empty for this
|
||||
header_payload_len: header_len,
|
||||
packet_num: self.client.package_number,
|
||||
operation: AfcOpcode::Read,
|
||||
};
|
||||
self.client.package_number += 1;
|
||||
|
||||
let packet = AfcPacket {
|
||||
header,
|
||||
header_payload,
|
||||
payload: Vec::new(),
|
||||
};
|
||||
|
||||
self.client.send(packet).await?;
|
||||
let res = self.client.read().await?;
|
||||
bytes_left -= res.payload.len();
|
||||
collected_bytes.extend(res.payload);
|
||||
}
|
||||
|
||||
Ok(collected_bytes)
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,15 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use errors::AfcError;
|
||||
use file::FileDescriptor;
|
||||
use log::warn;
|
||||
use opcode::AfcOpcode;
|
||||
use opcode::{AfcFopenMode, AfcOpcode};
|
||||
use packet::{AfcPacket, AfcPacketHeader};
|
||||
|
||||
use crate::{lockdown::LockdownClient, Idevice, IdeviceError, IdeviceService};
|
||||
|
||||
pub mod errors;
|
||||
pub mod file;
|
||||
pub mod opcode;
|
||||
pub mod packet;
|
||||
|
||||
@@ -329,6 +331,45 @@ impl AfcClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn open(
|
||||
&mut self,
|
||||
path: impl Into<String>,
|
||||
mode: AfcFopenMode,
|
||||
) -> Result<FileDescriptor, IdeviceError> {
|
||||
let path = path.into();
|
||||
let mut header_payload = (mode as u64).to_le_bytes().to_vec();
|
||||
header_payload.extend(path.as_bytes());
|
||||
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
|
||||
|
||||
let header = AfcPacketHeader {
|
||||
magic: MAGIC,
|
||||
entire_len: header_len, // it's the same since the payload is empty for this
|
||||
header_payload_len: header_len,
|
||||
packet_num: self.package_number,
|
||||
operation: AfcOpcode::FileOpen,
|
||||
};
|
||||
self.package_number += 1;
|
||||
|
||||
let packet = AfcPacket {
|
||||
header,
|
||||
header_payload,
|
||||
payload: Vec::new(),
|
||||
};
|
||||
|
||||
self.send(packet).await?;
|
||||
let res = self.read().await?;
|
||||
if res.header_payload.len() < 8 {
|
||||
warn!("Header payload fd is less than 8 bytes");
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
let fd = u64::from_le_bytes(res.header_payload[..8].try_into().unwrap());
|
||||
Ok(FileDescriptor {
|
||||
client: self,
|
||||
fd,
|
||||
path,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn read(&mut self) -> Result<AfcPacket, IdeviceError> {
|
||||
let res = AfcPacket::read(&mut self.idevice).await?;
|
||||
if res.header.operation == AfcOpcode::Status {
|
||||
|
||||
@@ -35,6 +35,7 @@ pub enum AfcOpcode {
|
||||
RemovePathAndContents = 0x00000022,
|
||||
}
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum AfcFopenMode {
|
||||
RdOnly = 0x00000001, // r O_RDONLY
|
||||
Rw = 0x00000002, // r+ O_RDWR | O_CREAT
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use idevice::{afc::AfcClient, IdeviceService};
|
||||
use idevice::{
|
||||
afc::{opcode::AfcFopenMode, AfcClient},
|
||||
IdeviceService,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
@@ -39,6 +42,12 @@ async fn main() {
|
||||
.about("Lists the items in the directory")
|
||||
.arg(Arg::new("path").required(true).index(1)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("download")
|
||||
.about("Creates a directory")
|
||||
.arg(Arg::new("path").required(true).index(1))
|
||||
.arg(Arg::new("save").required(true).index(2)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("mkdir")
|
||||
.about("Creates a directory")
|
||||
@@ -90,6 +99,19 @@ async fn main() {
|
||||
} else if let Some(matches) = matches.subcommand_matches("mkdir") {
|
||||
let path = matches.get_one::<String>("path").expect("No path passed");
|
||||
afc_client.mk_dir(path).await.expect("Failed to mkdir");
|
||||
} else if let Some(matches) = matches.subcommand_matches("download") {
|
||||
let path = matches.get_one::<String>("path").expect("No path passed");
|
||||
let save = matches.get_one::<String>("save").expect("No path passed");
|
||||
|
||||
let mut file = afc_client
|
||||
.open(path, AfcFopenMode::RdOnly)
|
||||
.await
|
||||
.expect("Failed to open");
|
||||
|
||||
let res = file.read().await.expect("Failed to read");
|
||||
tokio::fs::write(save, res)
|
||||
.await
|
||||
.expect("Failed to write to file");
|
||||
} else if let Some(matches) = matches.subcommand_matches("remove") {
|
||||
let path = matches.get_one::<String>("path").expect("No path passed");
|
||||
afc_client.remove(path).await.expect("Failed to remove");
|
||||
|
||||
Reference in New Issue
Block a user