mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
Write afc inline docs
This commit is contained in:
@@ -7,8 +7,11 @@ use super::{
|
|||||||
packet::{AfcPacket, AfcPacketHeader},
|
packet::{AfcPacket, AfcPacketHeader},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Maximum transfer size for file operations (64KB)
|
||||||
const MAX_TRANSFER: u64 = 64 * 1024; // this is what go-ios uses
|
const MAX_TRANSFER: u64 = 64 * 1024; // this is what go-ios uses
|
||||||
|
|
||||||
|
/// Handle for an open file on the device.
|
||||||
|
/// Call close before dropping
|
||||||
pub struct FileDescriptor<'a> {
|
pub struct FileDescriptor<'a> {
|
||||||
pub(crate) client: &'a mut super::AfcClient,
|
pub(crate) client: &'a mut super::AfcClient,
|
||||||
pub(crate) fd: u64,
|
pub(crate) fd: u64,
|
||||||
@@ -16,6 +19,7 @@ pub struct FileDescriptor<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FileDescriptor<'_> {
|
impl FileDescriptor<'_> {
|
||||||
|
/// Closes the file descriptor
|
||||||
pub async fn close(self) -> Result<(), IdeviceError> {
|
pub async fn close(self) -> Result<(), IdeviceError> {
|
||||||
let header_payload = self.fd.to_le_bytes().to_vec();
|
let header_payload = self.fd.to_le_bytes().to_vec();
|
||||||
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
|
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
|
||||||
@@ -40,6 +44,10 @@ impl FileDescriptor<'_> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads the entire contents of the file
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A vector containing the file's data
|
||||||
pub async fn read(&mut self) -> Result<Vec<u8>, IdeviceError> {
|
pub async fn read(&mut self) -> Result<Vec<u8>, IdeviceError> {
|
||||||
// Get the file size first
|
// Get the file size first
|
||||||
let mut bytes_left = self.client.get_file_info(&self.path).await?.size;
|
let mut bytes_left = self.client.get_file_info(&self.path).await?.size;
|
||||||
@@ -74,6 +82,10 @@ impl FileDescriptor<'_> {
|
|||||||
Ok(collected_bytes)
|
Ok(collected_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes data to the file
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `bytes` - Data to write to the file
|
||||||
pub async fn write(&mut self, bytes: &[u8]) -> Result<(), IdeviceError> {
|
pub async fn write(&mut self, bytes: &[u8]) -> Result<(), IdeviceError> {
|
||||||
let chunks = bytes.chunks(MAX_TRANSFER as usize);
|
let chunks = bytes.chunks(MAX_TRANSFER as usize);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
// Jackson Coxson
|
//! AFC (Apple File Conduit) client implementation for interacting with iOS devices.
|
||||||
|
//!
|
||||||
|
//! This module provides functionality to interact with the file system of iOS devices
|
||||||
|
//! through the AFC protocol.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@@ -15,29 +18,45 @@ pub mod file;
|
|||||||
pub mod opcode;
|
pub mod opcode;
|
||||||
pub mod packet;
|
pub mod packet;
|
||||||
|
|
||||||
|
/// The magic number used in AFC protocol communications
|
||||||
pub const MAGIC: u64 = 0x4141504c36414643;
|
pub const MAGIC: u64 = 0x4141504c36414643;
|
||||||
|
|
||||||
|
/// Client for interacting with the AFC service on iOS devices
|
||||||
pub struct AfcClient {
|
pub struct AfcClient {
|
||||||
|
/// The underlying iDevice connection
|
||||||
pub idevice: Idevice,
|
pub idevice: Idevice,
|
||||||
package_number: u64,
|
package_number: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about a file on the device
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FileInfo {
|
pub struct FileInfo {
|
||||||
|
/// Size of the file in bytes
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
|
/// Number of blocks allocated for the file
|
||||||
pub blocks: usize,
|
pub blocks: usize,
|
||||||
|
/// Creation timestamp of the file
|
||||||
pub creation: chrono::NaiveDateTime,
|
pub creation: chrono::NaiveDateTime,
|
||||||
|
/// Last modification timestamp of the file
|
||||||
pub modified: chrono::NaiveDateTime,
|
pub modified: chrono::NaiveDateTime,
|
||||||
|
/// Number of hard links to the file
|
||||||
pub st_nlink: String,
|
pub st_nlink: String,
|
||||||
|
/// File type (e.g., "S_IFREG" for regular file)
|
||||||
pub st_ifmt: String,
|
pub st_ifmt: String,
|
||||||
|
/// Target path if this is a symbolic link
|
||||||
pub st_link_target: Option<String>,
|
pub st_link_target: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about the device's filesystem
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DeviceInfo {
|
pub struct DeviceInfo {
|
||||||
|
/// Device model identifier
|
||||||
pub model: String,
|
pub model: String,
|
||||||
|
/// Total storage capacity in bytes
|
||||||
pub total_bytes: usize,
|
pub total_bytes: usize,
|
||||||
|
/// Free storage space in bytes
|
||||||
pub free_bytes: usize,
|
pub free_bytes: usize,
|
||||||
|
/// Filesystem block size in bytes
|
||||||
pub block_size: usize,
|
pub block_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +65,13 @@ impl IdeviceService for AfcClient {
|
|||||||
"com.apple.afc"
|
"com.apple.afc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Connects to the AFC service on the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `provider` - The iDevice provider to use for the connection
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A new `AfcClient` instance on success
|
||||||
async fn connect(
|
async fn connect(
|
||||||
provider: &dyn crate::provider::IdeviceProvider,
|
provider: &dyn crate::provider::IdeviceProvider,
|
||||||
) -> Result<Self, IdeviceError> {
|
) -> Result<Self, IdeviceError> {
|
||||||
@@ -71,6 +97,10 @@ impl IdeviceService for AfcClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AfcClient {
|
impl AfcClient {
|
||||||
|
/// Creates a new AFC client from an existing iDevice connection
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `idevice` - An established iDevice connection
|
||||||
pub fn new(idevice: Idevice) -> Self {
|
pub fn new(idevice: Idevice) -> Self {
|
||||||
Self {
|
Self {
|
||||||
idevice,
|
idevice,
|
||||||
@@ -78,6 +108,13 @@ impl AfcClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lists the contents of a directory on the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `path` - Path to the directory to list
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A vector of file/directory names in the specified directory
|
||||||
pub async fn list_dir(&mut self, path: impl Into<String>) -> Result<Vec<String>, IdeviceError> {
|
pub async fn list_dir(&mut self, path: impl Into<String>) -> Result<Vec<String>, IdeviceError> {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let header_payload = path.as_bytes().to_vec();
|
let header_payload = path.as_bytes().to_vec();
|
||||||
@@ -110,6 +147,10 @@ impl AfcClient {
|
|||||||
Ok(strings)
|
Ok(strings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new directory on the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `path` - Path of the directory to create
|
||||||
pub async fn mk_dir(&mut self, path: impl Into<String>) -> Result<(), IdeviceError> {
|
pub async fn mk_dir(&mut self, path: impl Into<String>) -> Result<(), IdeviceError> {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let header_payload = path.as_bytes().to_vec();
|
let header_payload = path.as_bytes().to_vec();
|
||||||
@@ -136,6 +177,13 @@ impl AfcClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves information about a file or directory
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `path` - Path to the file or directory
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A `FileInfo` struct containing information about the file
|
||||||
pub async fn get_file_info(
|
pub async fn get_file_info(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: impl Into<String>,
|
path: impl Into<String>,
|
||||||
@@ -218,6 +266,10 @@ impl AfcClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves information about the device's filesystem
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A `DeviceInfo` struct containing device filesystem information
|
||||||
pub async fn get_device_info(&mut self) -> Result<DeviceInfo, IdeviceError> {
|
pub async fn get_device_info(&mut self) -> Result<DeviceInfo, IdeviceError> {
|
||||||
let header_len = AfcPacketHeader::LEN;
|
let header_len = AfcPacketHeader::LEN;
|
||||||
|
|
||||||
@@ -279,6 +331,10 @@ impl AfcClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes a file or directory
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `path` - Path to the file or directory to remove
|
||||||
pub async fn remove(&mut self, path: impl Into<String>) -> Result<(), IdeviceError> {
|
pub async fn remove(&mut self, path: impl Into<String>) -> Result<(), IdeviceError> {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let header_payload = path.as_bytes().to_vec();
|
let header_payload = path.as_bytes().to_vec();
|
||||||
@@ -305,6 +361,10 @@ impl AfcClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursively removes a directory and all its contents
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `path` - Path to the directory to remove
|
||||||
pub async fn remove_all(&mut self, path: impl Into<String>) -> Result<(), IdeviceError> {
|
pub async fn remove_all(&mut self, path: impl Into<String>) -> Result<(), IdeviceError> {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let header_payload = path.as_bytes().to_vec();
|
let header_payload = path.as_bytes().to_vec();
|
||||||
@@ -331,6 +391,14 @@ impl AfcClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens a file on the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `path` - Path to the file to open
|
||||||
|
/// * `mode` - Opening mode (read, write, etc.)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A `FileDescriptor` struct for the opened file
|
||||||
pub async fn open(
|
pub async fn open(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: impl Into<String>,
|
path: impl Into<String>,
|
||||||
@@ -370,6 +438,12 @@ impl AfcClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a hard or symbolic link
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `target` - Target path of the link
|
||||||
|
/// * `source` - Path where the link should be created
|
||||||
|
/// * `kind` - Type of link to create (hard or symbolic)
|
||||||
pub async fn link(
|
pub async fn link(
|
||||||
&mut self,
|
&mut self,
|
||||||
target: impl Into<String>,
|
target: impl Into<String>,
|
||||||
@@ -408,6 +482,11 @@ impl AfcClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Renames a file or directory
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `source` - Current path of the file/directory
|
||||||
|
/// * `target` - New path for the file/directory
|
||||||
pub async fn rename(
|
pub async fn rename(
|
||||||
&mut self,
|
&mut self,
|
||||||
source: impl Into<String>,
|
source: impl Into<String>,
|
||||||
@@ -444,6 +523,10 @@ impl AfcClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads a response packet from the device
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The received `AfcPacket`
|
||||||
pub async fn read(&mut self) -> Result<AfcPacket, IdeviceError> {
|
pub async fn read(&mut self) -> Result<AfcPacket, IdeviceError> {
|
||||||
let res = AfcPacket::read(&mut self.idevice).await?;
|
let res = AfcPacket::read(&mut self.idevice).await?;
|
||||||
if res.header.operation == AfcOpcode::Status {
|
if res.header.operation == AfcOpcode::Status {
|
||||||
@@ -462,6 +545,10 @@ impl AfcClient {
|
|||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends a packet to the device
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `packet` - The packet to send
|
||||||
pub async fn send(&mut self, packet: AfcPacket) -> Result<(), IdeviceError> {
|
pub async fn send(&mut self, packet: AfcPacket) -> Result<(), IdeviceError> {
|
||||||
let packet = packet.serialize();
|
let packet = packet.serialize();
|
||||||
self.idevice.send_raw(&packet).await?;
|
self.idevice.send_raw(&packet).await?;
|
||||||
|
|||||||
Reference in New Issue
Block a user