/** * Gets a directory listing of the directory requested. * * @param client The client to get a directory listing from. * @param dir The directory to list. (must be a fully-qualified path) * @param list A char list of files in that directory, terminated by an empty * string or NULL if there was an error. * * @return AFC_E_SUCCESS on success or an AFC_E_* error value. */ afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***list) { uint32_t bytes = 0; char *data = NULL, **list_loc = NULL; afc_error_t ret = AFC_E_UNKNOWN_ERROR; if (!client || !dir || !list || (list && *list)) return AFC_E_INVALID_ARG; afc_lock(client); /* Send the command */ client->afc_packet->operation = AFC_OP_READ_DIR; client->afc_packet->entire_length = 0; client->afc_packet->this_length = 0; ret = afc_dispatch_packet(client, dir, strlen(dir)+1, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } /* Receive the data */ ret = afc_receive_data(client, &data, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return ret; } /* Parse the data */ list_loc = make_strings_list(data, bytes); if (data) free(data); afc_unlock(client); *list = list_loc; return ret; }
/** * Seeks to a given position of a pre-opened file on the device. * * @param client The client to use to seek to the position. * @param handle File handle of a previously opened. * @param offset Seek offset. * @param whence Seeking direction, one of SEEK_SET, SEEK_CUR, or SEEK_END. * * @return AFC_E_SUCCESS on success or an AFC_E_* error value. */ afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence) { char *buffer = (char *) malloc(sizeof(char) * 24); int64_t offset_loc = (int64_t)htole64(offset); uint64_t whence_loc = htole64(whence); uint32_t bytes = 0; afc_error_t ret = AFC_E_UNKNOWN_ERROR; if (!client || (handle == 0)) return AFC_E_INVALID_ARG; afc_lock(client); /* Send the command */ memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ memcpy(buffer + 8, &whence_loc, sizeof(uint64_t)); /* fromwhere */ memcpy(buffer + 16, &offset_loc, sizeof(uint64_t)); /* offset */ client->afc_packet->operation = AFC_OP_FILE_SEEK; client->afc_packet->this_length = client->afc_packet->entire_length = 0; ret = afc_dispatch_packet(client, buffer, 24, &bytes); free(buffer); buffer = NULL; if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } /* Receive response */ ret = afc_receive_data(client, &buffer, &bytes); if (buffer) free(buffer); afc_unlock(client); return ret; }
/** * Get device information for a connected client. The device information * returned is the device model as well as the free space, the total capacity * and blocksize on the accessed disk partition. * * @param client The client to get device info for. * @param infos A char ** list of parameters as given by AFC or NULL if there * was an error. * * @return AFC_E_SUCCESS on success or an AFC_E_* error value. */ afc_error_t afc_get_device_info(afc_client_t client, char ***infos) { uint32_t bytes = 0; char *data = NULL, **list = NULL; afc_error_t ret = AFC_E_UNKNOWN_ERROR; if (!client || !infos) return AFC_E_INVALID_ARG; afc_lock(client); /* Send the command */ client->afc_packet->operation = AFC_OP_GET_DEVINFO; client->afc_packet->entire_length = client->afc_packet->this_length = 0; ret = afc_dispatch_packet(client, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } /* Receive the data */ ret = afc_receive_data(client, &data, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return ret; } /* Parse the data */ list = make_strings_list(data, bytes); if (data) free(data); afc_unlock(client); *infos = list; return ret; }
/** Writes a given number of bytes to a file. * * @param client The client to use to write to the file. * @param handle File handle of previously opened file. * @param data The data to write to the file. * @param length How much data to write. * * @return The number of bytes written to the file, or a value less than 0 if * none were written... */ iphone_error_t afc_file_write(afc_client_t client, uint64_t handle, const char *data, int length, uint32_t * bytes) { char *acknowledgement = NULL; const int MAXIMUM_WRITE_SIZE = 1 << 15; uint32_t zero = 0, current_count = 0, i = 0; uint32_t segments = (length / MAXIMUM_WRITE_SIZE); int bytes_loc = 0; char *out_buffer = NULL; afc_error_t ret = AFC_E_SUCCESS; if (!client || !client->afc_packet || !client->connection || !bytes || (handle == 0)) return AFC_E_INVALID_ARGUMENT; afc_lock(client); log_debug_msg("%s: Write length: %i\n", __func__, length); // Divide the file into segments. for (i = 0; i < segments; i++) { // Send the segment client->afc_packet->this_length = sizeof(AFCPacket) + 8; client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE; client->afc_packet->operation = AFC_OP_WRITE; out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); memcpy(out_buffer, (char *)&handle, sizeof(uint64_t)); memcpy(out_buffer + 8, data + current_count, MAXIMUM_WRITE_SIZE); bytes_loc = afc_dispatch_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8); if (bytes_loc < 0) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } free(out_buffer); out_buffer = NULL; current_count += bytes_loc; ret = afc_receive_data(client, &acknowledgement, &bytes_loc); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return ret; } else { free(acknowledgement); } } // By this point, we should be at the end. i.e. the last segment that // didn't get sent in the for loop // this length is fine because it's always sizeof(AFCPacket) + 8, but // to be sure we do it again if (current_count == (uint32_t)length) { afc_unlock(client); *bytes = current_count; return ret; } client->afc_packet->this_length = sizeof(AFCPacket) + 8; client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count); client->afc_packet->operation = AFC_OP_WRITE; out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); memcpy(out_buffer, (char *) &handle, sizeof(uint64_t)); memcpy(out_buffer + 8, data + current_count, (length - current_count)); bytes_loc = afc_dispatch_packet(client, out_buffer, (length - current_count) + 8); free(out_buffer); out_buffer = NULL; current_count += bytes_loc; if (bytes_loc <= 0) { afc_unlock(client); *bytes = current_count; return AFC_E_SUCCESS; } zero = bytes_loc; ret = afc_receive_data(client, &acknowledgement, &bytes_loc); afc_unlock(client); if (ret != AFC_E_SUCCESS) { log_debug_msg("%s: uh oh?\n", __func__); } else { free(acknowledgement); } *bytes = current_count; return ret; }
/** Attempts to the read the given number of bytes from the given file. * * @param client The relevant AFC client * @param handle File handle of a previously opened file * @param data The pointer to the memory region to store the read data * @param length The number of bytes to read * * @return The number of bytes read if successful. If there was an error -1. */ iphone_error_t afc_file_read(afc_client_t client, uint64_t handle, char *data, int length, uint32_t * bytes) { char *input = NULL; int current_count = 0, bytes_loc = 0; const int MAXIMUM_READ_SIZE = 1 << 16; afc_error_t ret = AFC_E_SUCCESS; if (!client || !client->afc_packet || !client->connection || handle == 0) return AFC_E_INVALID_ARGUMENT; log_debug_msg("%s: called for length %i\n", __func__, length); afc_lock(client); // Looping here to get around the maximum amount of data that // afc_receive_data can handle while (current_count < length) { log_debug_msg("%s: current count is %i but length is %i\n", __func__, current_count, length); // Send the read command AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket)); packet->filehandle = handle; packet->size = ((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE; client->afc_packet->operation = AFC_OP_READ; client->afc_packet->entire_length = client->afc_packet->this_length = 0; bytes_loc = afc_dispatch_packet(client, (char *) packet, sizeof(AFCFilePacket)); free(packet); if (bytes_loc <= 0) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } // Receive the data ret = afc_receive_data(client, &input, &bytes_loc); log_debug_msg("%s: afc_receive_data returned error: %d\n", __func__, ret); log_debug_msg("%s: bytes returned: %i\n", __func__, bytes_loc); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return ret; } else if (bytes_loc == 0) { if (input) free(input); afc_unlock(client); *bytes = current_count; /* FIXME: check that's actually a success */ return ret; } else { if (input) { log_debug_msg("%s: %d\n", __func__, bytes_loc); memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); free(input); input = NULL; current_count += (bytes_loc > length) ? length : bytes_loc; } } } log_debug_msg("%s: returning current_count as %i\n", __func__, current_count); afc_unlock(client); *bytes = current_count; return ret; }
/** * Writes a given number of bytes to a file. * * @param client The client to use to write to the file. * @param handle File handle of previously opened file. * @param data The data to write to the file. * @param length How much data to write. * @param bytes_written The number of bytes actually written to the file. * * @return AFC_E_SUCCESS on success or an AFC_E_* error value. */ idevice_error_t afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t length, uint32_t *bytes_written) { char *acknowledgement = NULL; const uint32_t MAXIMUM_WRITE_SIZE = 1 << 15; uint32_t current_count = 0, i = 0; uint32_t segments = (length / MAXIMUM_WRITE_SIZE); uint32_t bytes_loc = 0; char *out_buffer = NULL; afc_error_t ret = AFC_E_SUCCESS; if (!client || !client->afc_packet || !client->parent || !bytes_written || (handle == 0)) return AFC_E_INVALID_ARG; afc_lock(client); debug_info("Write length: %i", length); /* Divide the file into segments. */ for (i = 0; i < segments; i++) { /* Send the segment */ client->afc_packet->this_length = sizeof(AFCPacket) + 8; client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE; client->afc_packet->operation = AFC_OP_WRITE; out_buffer = (char *) malloc(size_t(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket))); memcpy(out_buffer, (char *)&handle, sizeof(uint64_t)); memcpy(out_buffer + 8, data + current_count, MAXIMUM_WRITE_SIZE); ret = afc_dispatch_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8, &bytes_loc); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } free(out_buffer); out_buffer = NULL; current_count += bytes_loc; ret = afc_receive_data(client, &acknowledgement, &bytes_loc); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return ret; } else { free(acknowledgement); } } /* By this point, we should be at the end. i.e. the last segment that didn't get sent in the for loop. This length is fine because it's always sizeof(AFCPacket) + 8, but to be sure we do it again */ if (current_count == length) { afc_unlock(client); *bytes_written = current_count; return ret; } client->afc_packet->this_length = sizeof(AFCPacket) + 8; client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count); client->afc_packet->operation = AFC_OP_WRITE; out_buffer = (char *) malloc(size_t(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket))); memcpy(out_buffer, (char *) &handle, sizeof(uint64_t)); memcpy(out_buffer + 8, data + current_count, (length - current_count)); ret = afc_dispatch_packet(client, out_buffer, (length - current_count) + 8, &bytes_loc); free(out_buffer); out_buffer = NULL; current_count += bytes_loc; if (ret != AFC_E_SUCCESS) { afc_unlock(client); *bytes_written = current_count; return AFC_E_SUCCESS; } ret = afc_receive_data(client, &acknowledgement, &bytes_loc); afc_unlock(client); if (ret != AFC_E_SUCCESS) { debug_info("uh oh?"); } else { free(acknowledgement); } *bytes_written = current_count; return ret; }
/** * Attempts to the read the given number of bytes from the given file. * * @param client The relevant AFC client * @param handle File handle of a previously opened file * @param data The pointer to the memory region to store the read data * @param length The number of bytes to read * @param bytes_read The number of bytes actually read. * * @return AFC_E_SUCCESS on success or an AFC_E_* error value. */ idevice_error_t afc_file_read(afc_client_t client, uint64_t handle, char *data, uint32_t length, uint32_t *bytes_read) { char *input = NULL; uint32_t current_count = 0, bytes_loc = 0; const uint32_t MAXIMUM_READ_SIZE = 1 << 16; afc_error_t ret = AFC_E_SUCCESS; if (!client || !client->afc_packet || !client->parent || handle == 0) return AFC_E_INVALID_ARG; debug_info("called for length %i", length); afc_lock(client); /* Looping here to get around the maximum amount of data that afc_receive_data can handle */ while (current_count < length) { debug_info("current count is %i but length is %i", current_count, length); /* Send the read command */ AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket)); packet->filehandle = handle; packet->size = htole64(((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE); client->afc_packet->operation = AFC_OP_READ; client->afc_packet->entire_length = client->afc_packet->this_length = 0; ret = afc_dispatch_packet(client, (char *) packet, sizeof(AFCFilePacket), &bytes_loc); free(packet); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } /* Receive the data */ ret = afc_receive_data(client, &input, &bytes_loc); debug_info("afc_receive_data returned error: %d", ret); debug_info("bytes returned: %i", bytes_loc); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return ret; } else if (bytes_loc == 0) { if (input) free(input); afc_unlock(client); *bytes_read = current_count; /* FIXME: check that's actually a success */ return ret; } else { if (input) { debug_info("%d", bytes_loc); memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); free(input); input = NULL; current_count += (bytes_loc > length) ? length : bytes_loc; } } } debug_info("returning current_count as %i", current_count); afc_unlock(client); *bytes_read = current_count; return ret; }