From 25059e44c34fb630065b9c4c6004bf201a6f1cb6 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Sun, 6 Apr 2025 14:22:57 -0600 Subject: [PATCH] Write afc inline docs --- idevice/src/afc/file.rs | 12 ++++++ idevice/src/afc/mod.rs | 89 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/idevice/src/afc/file.rs b/idevice/src/afc/file.rs index 11897ee..5727d97 100644 --- a/idevice/src/afc/file.rs +++ b/idevice/src/afc/file.rs @@ -7,8 +7,11 @@ use super::{ packet::{AfcPacket, AfcPacketHeader}, }; +/// Maximum transfer size for file operations (64KB) 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(crate) client: &'a mut super::AfcClient, pub(crate) fd: u64, @@ -16,6 +19,7 @@ pub struct FileDescriptor<'a> { } impl FileDescriptor<'_> { + /// Closes the file descriptor 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; @@ -40,6 +44,10 @@ impl FileDescriptor<'_> { Ok(()) } + /// Reads the entire contents of the file + /// + /// # Returns + /// A vector containing the file's data pub async fn read(&mut self) -> Result, IdeviceError> { // Get the file size first let mut bytes_left = self.client.get_file_info(&self.path).await?.size; @@ -74,6 +82,10 @@ impl FileDescriptor<'_> { 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> { let chunks = bytes.chunks(MAX_TRANSFER as usize); diff --git a/idevice/src/afc/mod.rs b/idevice/src/afc/mod.rs index 69babdb..3882598 100644 --- a/idevice/src/afc/mod.rs +++ b/idevice/src/afc/mod.rs @@ -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; @@ -15,29 +18,45 @@ pub mod file; pub mod opcode; pub mod packet; +/// The magic number used in AFC protocol communications pub const MAGIC: u64 = 0x4141504c36414643; +/// Client for interacting with the AFC service on iOS devices pub struct AfcClient { + /// The underlying iDevice connection pub idevice: Idevice, package_number: u64, } +/// Information about a file on the device #[derive(Clone, Debug)] pub struct FileInfo { + /// Size of the file in bytes pub size: usize, + /// Number of blocks allocated for the file pub blocks: usize, + /// Creation timestamp of the file pub creation: chrono::NaiveDateTime, + /// Last modification timestamp of the file pub modified: chrono::NaiveDateTime, + /// Number of hard links to the file pub st_nlink: String, + /// File type (e.g., "S_IFREG" for regular file) pub st_ifmt: String, + /// Target path if this is a symbolic link pub st_link_target: Option, } +/// Information about the device's filesystem #[derive(Clone, Debug)] pub struct DeviceInfo { + /// Device model identifier pub model: String, + /// Total storage capacity in bytes pub total_bytes: usize, + /// Free storage space in bytes pub free_bytes: usize, + /// Filesystem block size in bytes pub block_size: usize, } @@ -46,6 +65,13 @@ impl IdeviceService for AfcClient { "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( provider: &dyn crate::provider::IdeviceProvider, ) -> Result { @@ -71,6 +97,10 @@ impl IdeviceService for 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 { Self { 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) -> Result, IdeviceError> { let path = path.into(); let header_payload = path.as_bytes().to_vec(); @@ -110,6 +147,10 @@ impl AfcClient { 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) -> Result<(), IdeviceError> { let path = path.into(); let header_payload = path.as_bytes().to_vec(); @@ -136,6 +177,13 @@ impl AfcClient { 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( &mut self, path: impl Into, @@ -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 { 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) -> Result<(), IdeviceError> { let path = path.into(); let header_payload = path.as_bytes().to_vec(); @@ -305,6 +361,10 @@ impl AfcClient { 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) -> Result<(), IdeviceError> { let path = path.into(); let header_payload = path.as_bytes().to_vec(); @@ -331,6 +391,14 @@ impl AfcClient { 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( &mut self, path: impl Into, @@ -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( &mut self, target: impl Into, @@ -408,6 +482,11 @@ impl AfcClient { 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( &mut self, source: impl Into, @@ -444,6 +523,10 @@ impl AfcClient { Ok(()) } + /// Reads a response packet from the device + /// + /// # Returns + /// The received `AfcPacket` pub async fn read(&mut self) -> Result { let res = AfcPacket::read(&mut self.idevice).await?; if res.header.operation == AfcOpcode::Status { @@ -462,6 +545,10 @@ impl AfcClient { Ok(res) } + /// Sends a packet to the device + /// + /// # Arguments + /// * `packet` - The packet to send pub async fn send(&mut self, packet: AfcPacket) -> Result<(), IdeviceError> { let packet = packet.serialize(); self.idevice.send_raw(&packet).await?;