#include #include #include /** * Prints an error message with the given string and error code. * * @param message The message to print. * @param error The error code to print. * @return error as int */ static int print_error_code(const char* message, icsneoc2_error_t error) { char error_str[64]; size_t error_str_len = sizeof(error_str); icsneoc2_error_code_get(error, error_str, &error_str_len); printf("%s: \"%s\" (%u)\n", message, error_str, error); return (int)error; } /** * Progress callback invoked periodically during disk formatting. * * @param sectors_formatted Number of sectors formatted so far. * @param total_sectors Total number of sectors to format. * @param user_data Unused opaque pointer. * @return icsneoc2_disk_format_directive_continue to keep formatting. */ static icsneoc2_disk_format_directive_t format_progress(uint64_t sectors_formatted, uint64_t total_sectors, void* user_data) { (void)user_data; double pct = total_sectors > 0 ? (100.0 * (double)sectors_formatted / (double)total_sectors) : 0.0; printf("\r Progress: %" PRIu64 " / %" PRIu64 " sectors (%d%%)", sectors_formatted, total_sectors, (int)pct); fflush(stdout); return icsneoc2_disk_format_directive_continue; } int main() { /* Open the first available device (no online needed for formatting) */ printf("Opening first available device...\n"); icsneoc2_device_t* device = NULL; icsneoc2_error_t res = icsneoc2_device_open_first(0, ICSNEOC2_OPEN_OPTIONS_NONE, &device); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to open first device", res); } /* Get a description of the opened device */ char description[255] = {0}; size_t description_length = sizeof(description); res = icsneoc2_device_description_get(device, description, &description_length); if(res != icsneoc2_error_success) { icsneoc2_device_free(device); return print_error_code("\tFailed to get device description", res); } printf("\tOpened device: %s\n", description); /* Check disk formatting support */ bool supported = false; res = icsneoc2_device_supports_disk_formatting(device, &supported); if(res != icsneoc2_error_success) { print_error_code("\tFailed to check disk formatting support", res); icsneoc2_device_close(device); icsneoc2_device_free(device); return -1; } if(!supported) { printf("\terror: %s does not support disk formatting\n", description); icsneoc2_device_close(device); icsneoc2_device_free(device); return -1; } size_t disk_count = 0; icsneoc2_device_disk_count_get(device, &disk_count); printf("\tDisk count: %zu\n", disk_count); /* Query disk details */ printf("\tQuerying disk details... "); fflush(stdout); icsneoc2_disk_details_t* details = NULL; res = icsneoc2_device_disk_details_get(device, &details); if(res != icsneoc2_error_success) { printf("FAIL\n"); print_error_code("\tFailed to get disk details", res); icsneoc2_device_close(device); icsneoc2_device_free(device); return -1; } printf("OK\n"); /* Display current state */ icsneoc2_disk_layout_t layout = 0; icsneoc2_disk_details_layout_get(details, &layout); printf("\t Layout : %s\n", layout == icsneoc2_disk_layout_raid0 ? "RAID0" : "Spanned"); size_t detail_count = 0; icsneoc2_disk_details_count_get(details, &detail_count); for(size_t i = 0; i < detail_count; i++) { icsneoc2_disk_format_flags_t flags = 0; icsneoc2_disk_details_flags_get(details, i, &flags); printf("\t Disk [%zu]:\n", i); printf("\t Present : %s\n", (flags & ICSNEOC2_DISK_FORMAT_FLAGS_PRESENT) ? "yes" : "no"); printf("\t Initialized : %s\n", (flags & ICSNEOC2_DISK_FORMAT_FLAGS_INITIALIZED) ? "yes" : "no"); printf("\t Formatted : %s\n", (flags & ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED) ? "yes" : "no"); if(flags & ICSNEOC2_DISK_FORMAT_FLAGS_PRESENT) { uint64_t sectors = 0, bps = 0; icsneoc2_disk_details_size_get(details, i, §ors, &bps); printf("\t Size : %" PRIu64 " MB (%" PRIu64 " sectors x %" PRIu64 " bytes)\n", (sectors * bps) / (1024 * 1024), sectors, bps); } } /* Choose operation */ printf("\n\tChoose operation:\n"); printf("\t [f] Format the disk(s)\n"); printf("\t [u] Update the disk configuration without formatting\n"); printf("\t [q] Quit\n"); printf("\tSelection [f/u/q]: "); char choice[8] = {0}; if(scanf("%7s", choice) != 1) { choice[0] = 'q'; } if(choice[0] == 'u' || choice[0] == 'U') { /* Force a disk config update (e.g. changing the disk layout) without formatting. * Unlike a format, this preserves the existing data on the disk(s). */ printf("\n\tChoose disk layout:\n"); printf("\t [s] Spanned\n"); printf("\t [r] RAID0\n"); printf("\tSelection [s/r] (current: %s): ", layout == icsneoc2_disk_layout_raid0 ? "RAID0" : "Spanned"); char layout_choice[8] = {0}; if(scanf("%7s", layout_choice) != 1) { layout_choice[0] = '\0'; } if(layout_choice[0] == 'r' || layout_choice[0] == 'R') { icsneoc2_disk_details_layout_set(details, icsneoc2_disk_layout_raid0); } else if(layout_choice[0] == 's' || layout_choice[0] == 'S') { icsneoc2_disk_details_layout_set(details, icsneoc2_disk_layout_spanned); } else { printf("\tKeeping current layout.\n"); } /* Enable/disable individual disks in the configuration. A disk's enabled * state is carried by the FORMATTED flag; only present disks can be enabled. */ for(size_t i = 0; i < detail_count; i++) { icsneoc2_disk_format_flags_t flags = 0; icsneoc2_disk_details_flags_get(details, i, &flags); if(!(flags & ICSNEOC2_DISK_FORMAT_FLAGS_PRESENT)) { continue; /* Can't enable a disk that isn't present */ } printf("\tEnable disk [%zu]? [y/N] (currently %s): ", i, (flags & ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED) ? "enabled" : "disabled"); char disk_choice[8] = {0}; if(scanf("%7s", disk_choice) != 1) { disk_choice[0] = '\0'; } if(disk_choice[0] == 'y' || disk_choice[0] == 'Y') { flags |= ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED; } else { flags &= ~(icsneoc2_disk_format_flags_t)ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED; } icsneoc2_disk_details_flags_set(details, i, flags); } icsneoc2_disk_layout_t new_layout = 0; icsneoc2_disk_details_layout_get(details, &new_layout); printf("\n\tForcing disk config update on %s to %s layout (no data will be erased)...\n", description, new_layout == icsneoc2_disk_layout_raid0 ? "RAID0" : "Spanned"); res = icsneoc2_device_force_disk_config_update(device, details); if(res != icsneoc2_error_success) { print_error_code("\tForce disk config update failed", res); icsneoc2_disk_details_free(details); icsneoc2_device_close(device); icsneoc2_device_free(device); return -1; } printf("\tDisk config update complete!\n"); icsneoc2_disk_details_free(details); icsneoc2_device_close(device); icsneoc2_device_free(device); return 0; } if(choice[0] != 'f' && choice[0] != 'F') { printf("\tAborted.\n"); icsneoc2_disk_details_free(details); icsneoc2_device_close(device); icsneoc2_device_free(device); return 0; } /* Build format config: mark present disks for formatting */ bool any_present = false; for(size_t i = 0; i < detail_count; i++) { icsneoc2_disk_format_flags_t flags = 0; icsneoc2_disk_details_flags_get(details, i, &flags); if(flags & ICSNEOC2_DISK_FORMAT_FLAGS_PRESENT) { flags |= ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED; icsneoc2_disk_details_flags_set(details, i, flags); any_present = true; } } icsneoc2_disk_details_full_format_set(details, false); /* Quick format */ if(!any_present) { printf("\n\terror: no disks are present in the device\n"); icsneoc2_disk_details_free(details); icsneoc2_device_close(device); icsneoc2_device_free(device); return -1; } /* Confirm */ printf("\n\tThis will format the disk(s) in %s.\n", description); printf("\tAll existing data will be lost. Continue? [y/N]: "); char confirm[8] = {0}; if(scanf("%7s", confirm) != 1 || (confirm[0] != 'y' && confirm[0] != 'Y')) { printf("\tAborted.\n"); icsneoc2_disk_details_free(details); icsneoc2_device_close(device); icsneoc2_device_free(device); return 0; } /* Format */ printf("\n\tStarting format...\n"); res = icsneoc2_device_format_disk(device, details, format_progress, NULL); printf("\n"); /* newline after progress line */ if(res != icsneoc2_error_success) { print_error_code("\tFormat failed", res); icsneoc2_disk_details_free(details); icsneoc2_device_close(device); icsneoc2_device_free(device); return -1; } printf("\tFormat complete!\n"); icsneoc2_disk_details_free(details); /* Verify */ printf("\n\tVerifying disk state after format... "); fflush(stdout); icsneoc2_disk_details_t* post_details = NULL; res = icsneoc2_device_disk_details_get(device, &post_details); if(res != icsneoc2_error_success) { printf("FAIL (could not re-query disk details)\n"); } else { printf("OK\n"); size_t post_count = 0; icsneoc2_disk_details_count_get(post_details, &post_count); for(size_t i = 0; i < post_count; i++) { icsneoc2_disk_format_flags_t flags = 0; icsneoc2_disk_details_flags_get(post_details, i, &flags); printf("\t Disk [%zu] formatted: %s\n", i, (flags & ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED) ? "yes" : "no"); } icsneoc2_disk_details_free(post_details); } printf("\tClosing device: %s...\n", description); icsneoc2_device_close(device); icsneoc2_device_free(device); return 0; }