/* * This is the implementation of routines to get and set disk * configuration. * * Copyright 2003-2004, Broadcom Corporation * All Rights Reserved. * * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; * the contents of this file may not be disclosed to third parties, copied * or duplicated in any form, in whole or in part, without the prior * written permission of Broadcom Corporation. */ /* * This file contains the implementation of the BroadNAS routines to * read the state of disk configuration and to make changes to that * configuration. */ /* * Defining _LARGEFILE64_SOURCE is necessary with the GNU libc to get support * for 64-bit offsets for files (fopen64() and fseeko64()). Since we read and * write pool headers at the start of partitions that can be anywhere on a raw * disk device, without 64-bit offsets we'd be crippled -- limited to less * than 5 GB drives. */ #define _LARGEFILE64_SOURCE #define PROC_PARTITIONS_HEADER "major minor #blocks name\n\n" #define SMART_GOOD_HEALTH_STRING "Check S.M.A.R.T. Passed." #define SMART_POOR_HEALTH_STRING \ "Please save all data and call drive manufacture immediately." #define PARTITION_TABLE_BYTES (512 * 2) #define PARTITION_TABLE_ENTRIES 63 #define DUMMY_DEVICE_NAME "/dev/null" #define BYTES_IN_RAID1_SUPERBLOCK 4 * 1024 #define RAID_MAGIC_VALUE 0xfc4e2ba9 #define RAID_SUPER_BLOCK_SIZE_OFFSET 32 #define RAID_SUPER_BLOCK_DISK_ENTRIES 27 #define RAID_SUPER_BLOCK_DISK_BYTES (32 * 4) #define RAID_SUPER_BLOCK_DISK_SPECIFIC_DATA_OFFSET 3968 #define RAID_SUPER_BLOCK_CHECK_SUM_BYTE_OFFSET 152 #define POOL_ENCRYPTION_RECOVERY_MAX_BYTES 2048 /* * Broadcom Disk Header Format * * Offset Meaning * 0- 33 Magic: ``Broadcom NAS Version 1.0 MBR Tag/0/0'' * 34- 39 NAS ID: 6 bytes of binary data to uniquely identify the NAS * that owns the disk. This can be the ethernet hardware * address, but need not be. * 40- 80 Disk Name: A zero-terminated ASCII string. After the * string-terminating zero byte the rest of the bytes are padded * out to zero. The zero terminator must be included -- i.e. * byte 80 must be zero. * 84- 99 Disk Unique ID: 16 bytes of binary data that uniquely identify * the disk. * 100- 100 A flag indicating which of the two partition table regions to * use: 0 means the first one and any other value means the * second one. * 104- 104 The current disk spin-down value. For IDE and SATA disks, * this represents the value programmed by the 0xe3 command of * the ATA spec, on disks that support this feature. The spin- * down value is an 8-bit write-only value that specifies how * much idle time should pass before the disk automatically spins * down. The ATA spec specifies how the 256 values map to times, * and Western Digital implements an alternate mapping in many of * its drives. * 512-1535 The first partition table region. * 1536-2559 The second partition table region. */ /* * Broadcom Partition Spec Block Format * * Offset Meaning * 0- 79 Name of disk. Each byte must be an ASCII upper- or lower-case * letter, ASCII digit, ASCII dash, ASCII underscore, ASCII space, * or zero. The first byte must not be zero. After a zero byte, * all subsequent bytes must be zero. * 80- 81 Zero. * 82- 87 NAS ID of the NAS that claimed the disk. * 88-103 Disk unique tag. * 104-107 Partition number. */ /* * Broadcom Pool Info Block Format * * Offset Meaning * 0- 79 Name of pool. Each byte must be an ASCII upper- or lower-case * letter, ASCII digit, ASCII dash, ASCII underscore, ASCII space, * or zero. The first byte must not be zero. After a zero byte, * all subsequent bytes must be zero. * 80- 83 Zero. * 84- 99 Unique tag of pool. * 100-105 Unique tag of NAS that created pool. * 106-107 Zero. * 108-116 Creation time/date stamp of pool. This stamp is created based * on the date and time (in universal time) that the pool was * created. The first 4 bytes are the year, the next byte the * month, the next byte the day, the next byte the hour, the next * byte the minute, and the final byte is the second. * 117-118 Zero. * 119-119 RAID 5 Tag. 0 => non RAID 5 partition, 5 => RAID 5 Partition * 120-123 Number of stripes in pool. * 124-127 Number of mirrors in pool. * 128-131 Number of spares in pool. * 132-135 Number of this pane. * 136-139 Number of chunks in this pane. * 140-143 Number of this chunk in this pane. * 144-147 Chunk size. * 148-255 Partition spec of the start of the next pane. * 256-363 Partition spec of the next chunk in this pane. * 364-367 Resize operation in progress. A zero means there is no resize * operation currently in progress and a one means there is * currently a resize operation in progress. Any other value is * illegal. * 368-375 Resize move backward bytes complete. If there is no resize * operation in progress, this should be zero. If there is a * resize operation in progress, this should hold a count of the * number of bytes completed in the backward movement pass of the * data movement for the resizing. * 376-383 Resize move forward bytes complete. If there is no resize * operation in progress, this should be zero. If there is a * resize operation in progress, this should hold a count of the * number of bytes completed in the forward movement pass of the * data movement for the resizing. * 384-391 Resize old kilobytes in this partition. * 392-399 Resize new kilobytes in this partition. * 400-495 Zero. * 496-511 Block validity marker: 0x5f9817820ea0bc335f9817820ea0bc33. */ /* Bits 0-119 collectively are the pool identifier. */ #define POOL_IDENTIFIER_BIT_COUNT 120 #define POOL_INFO_BLOCK_POOL_NAME_START 0 #define POOL_INFO_BLOCK_POOL_TAG_START 84 #define POOL_INFO_BLOCK_NAS_TAG_START 100 #define POOL_INFO_BLOCK_CREATION_START 108 #define POOL_INFO_BLOCK_SPARE_STRIPE_COUNT_START 118 #define POOL_INFO_BLOCK_RAID5_TAG_START 119 #define POOL_INFO_BLOCK_STRIPE_COUNT_START 120 #define POOL_INFO_BLOCK_MIRROR_COUNT_START 124 #define POOL_INFO_BLOCK_SPARE_COUNT_START 128 #define POOL_INFO_BLOCK_PANE_NUM_START 132 #define POOL_INFO_BLOCK_CHUNK_COUNT_START 136 #define POOL_INFO_BLOCK_CHUNK_NUM_START 140 #define POOL_INFO_BLOCK_CHUNK_SIZE_START 144 #define POOL_INFO_BLOCK_NEXT_PANE_START 148 #define POOL_INFO_BLOCK_NEXT_CHUNK_START 256 #define POOL_INFO_BLOCK_RESIZE_START 364 #define POOL_INFO_BLOCK_BACKWARD_START 368 #define POOL_INFO_BLOCK_FORWARD_START 376 #define POOL_INFO_BLOCK_OLD_SIZE_START 384 #define POOL_INFO_BLOCK_NEW_SIZE_START 392 #define POOL_INFO_BLOCK_VALIDITY_START 496 static void read_disk_header(char *device_name, disk_info_t *new_disk_info, u32 *blank_disk_num, u32 *foreign_disk_num, u32 *missing_disk_num, u32 *broken_disk_num, u64 block_count) { FILE *disk_header_fp; unsigned char header_data[DISK_HEADER_BYTES]; size_t read_result; push_formatted_activity_description("parsing partition table of %s", device_name); disk_header_fp = fopen(device_name, "rb"); if (disk_header_fp == NULL) { fprintf(stderr, "Failed trying to read the Disk Header for %s: %s\n", device_name, strerror(errno)); memset(new_disk_info->private_tag, 0, 16); memset(new_disk_info->claimer_unique_id, 0, 6); new_disk_info->classification = DC_BROKEN; sprintf(new_disk_info->tag, "Broken Disk %lu", (unsigned long)*broken_disk_num); ++(*broken_disk_num); new_disk_info->current_spin_down_setting = ~(u32)0; new_disk_info->partition_table = NULL; new_disk_info->free_partition_table_entries = 0; new_disk_info->free_regions = NULL; new_disk_info->size_ordered_free_regions = NULL; new_disk_info->size_ordered_free_region_last = NULL; pop_activity_description(); return; } read_result = fread(header_data, 1, DISK_HEADER_BYTES, disk_header_fp); if ((read_result < DISK_HEADER_BYTES) || ferror(disk_header_fp)) { fclose(disk_header_fp); fprintf(stderr, "Failed trying to read the Disk Header for %s: %s\n", device_name, strerror(errno)); memset(new_disk_info->private_tag, 0, 16); memset(new_disk_info->claimer_unique_id, 0, 6); new_disk_info->classification = DC_BROKEN; sprintf(new_disk_info->tag, "Broken Disk %lu", (unsigned long)*broken_disk_num); ++(*broken_disk_num); new_disk_info->current_spin_down_setting = ~(u32)0; new_disk_info->partition_table = NULL; new_disk_info->free_partition_table_entries = 0; new_disk_info->free_regions = NULL; new_disk_info->size_ordered_free_regions = NULL; new_disk_info->size_ordered_free_region_last = NULL; pop_activity_description(); return; } fclose(disk_header_fp); memcpy(new_disk_info->private_tag, &(header_data[84]), 16); memcpy(new_disk_info->claimer_unique_id, &(header_data[34]), 6); if (memcmp(header_data, BROADCOM_NAS_DISK_HEADER_MAGIC, strlen(BROADCOM_NAS_DISK_HEADER_MAGIC) + 1) != 0) { new_disk_info->classification = DC_FOREIGN; } else { disk_classification_t classification; unsigned long byte_num; classification = DC_RECOGNIZED; byte_num = 0; while (TRUE) { unsigned char new_byte; new_byte = header_data[40 + byte_num]; new_disk_info->tag[byte_num] = (char)new_byte; if (new_byte == 0) break; if (byte_num == 40) { classification = DC_FOREIGN; break; } ++byte_num; } new_disk_info->classification = classification; } if (new_disk_info->classification == DC_FOREIGN) { if ((header_data[0x1fe] != 0x55) || (header_data[0x1ff] != 0xaa)) { new_disk_info->classification = DC_BLANK; sprintf(new_disk_info->tag, "Blank Disk %lu", (unsigned long)(*blank_disk_num)); ++(*blank_disk_num); } else { sprintf(new_disk_info->tag, "Foreign Disk %lu", (unsigned long)*foreign_disk_num); ++(*foreign_disk_num); } new_disk_info->current_spin_down_setting = ~(u32)0; new_disk_info->partition_table = NULL; new_disk_info->free_partition_table_entries = 0; new_disk_info->free_regions = NULL; new_disk_info->size_ordered_free_regions = NULL; new_disk_info->size_ordered_free_region_last = NULL; } else { unsigned char *raw_table_data; new_disk_info->current_spin_down_setting = header_data[104]; if (header_data[100] == 0) { raw_table_data = &(header_data[DISK_HEADER_FIRST_PARTITION_TABLE_START]); } else { raw_table_data = &(header_data[DISK_HEADER_SECOND_PARTITION_TABLE_START]); } initialize_recognized_disk_space_info(new_disk_info, broken_disk_num, raw_table_data, block_count); } pop_activity_description(); } static void read_raid_and_pool_data(all_disk_info_t *all_disk_info, pool_reading_mode_t mode) { potential_pool_t *potential_pool_list; disk_info_t *follow_disks; pool_info_t **pool_list_tail; potential_pool_t *follow_potential_pools; disk_info_t *follow_disk_info; assert(all_disk_info != NULL); push_activity_description("reading disk pool information"); potential_pool_list = NULL; for (follow_disks = all_disk_info->disk_info; follow_disks != NULL; follow_disks = follow_disks->next) { partition_info_t *follow_partitions; if (follow_disks->classification != DC_RECOGNIZED) continue; if (follow_disks->partition_table == NULL) continue; for (follow_partitions = follow_disks->partitions; follow_partitions != NULL; follow_partitions = follow_partitions->next) { u64 block_num; FILE *fp; int seek_result; size_t read_result; unsigned char block1_data[POOL_INFO_BLOCK_BYTES]; unsigned char block2_data[POOL_INFO_BLOCK_BYTES]; potential_pool_chunk_t *new_chunk; if (follow_partitions->partition_number > PARTITION_TABLE_ENTRIES) continue; if (follow_partitions->partition_number < 1) continue; if (follow_partitions->partition_number == PARTITION_TABLE_ENTRIES) { if (follow_partitions->reference_count < ~(u32)0) ++(follow_partitions->reference_count); mark_partition_space_used(follow_disks, PARTITION_TABLE_ENTRIES); continue; } block_num = follow_disks->partition_table->entries[ follow_partitions->partition_number - 1].start_block_num; if (block_num < 2) { fprintf(stderr, "Failed trying to read the Pool Info Blocks for " "partition %llu of %s: the partition has an offset of " "%llu, which is less than 2.\n", (unsigned long long) (follow_partitions->partition_number), follow_disks->whole_disk_device, (unsigned long long)block_num); continue; } block_num -= 2; fp = fopen64(follow_disks->whole_disk_device, ((mode == POOL_MODE_READ_ONLY) ? "rb" : "rb+")); if (fp == NULL) { follow_disks->classification = DC_BROKEN; fprintf(stderr, "Failed trying to read the Pool Info Blocks for " "partition %llu of %s: %s\n", (unsigned long long) (follow_partitions->partition_number), follow_disks->whole_disk_device, strerror(errno)); break; } seek_result = fseeko64(fp, block_num * BYTES_PER_DISK_BLOCK, SEEK_SET); if (seek_result != 0) { fclose(fp); follow_disks->classification = DC_BROKEN; fprintf(stderr, "Failed trying to read the Pool Info Blocks for " "partition %llu of %s: %s\n", (unsigned long long) (follow_partitions->partition_number), follow_disks->whole_disk_device, strerror(errno)); break; } read_result = fread(block1_data, 1, POOL_INFO_BLOCK_BYTES, fp); if ((read_result < POOL_INFO_BLOCK_BYTES) || ferror(fp)) { fclose(fp); follow_disks->classification = DC_BROKEN; fprintf(stderr, "Failed trying to read the Pool Info Blocks for " "partition %llu of %s: %s\n", (unsigned long long) (follow_partitions->partition_number), follow_disks->whole_disk_device, strerror(errno)); break; } read_result = fread(block2_data, 1, POOL_INFO_BLOCK_BYTES, fp); if ((read_result < POOL_INFO_BLOCK_BYTES) || ferror(fp)) { fclose(fp); follow_disks->classification = DC_BROKEN; fprintf(stderr, "Failed trying to read the Pool Info Blocks for " "partition %llu of %s: %s\n", (unsigned long long) (follow_partitions->partition_number), follow_disks->whole_disk_device, strerror(errno)); break; } if ((memcmp(block1_data + POOL_INFO_BLOCK_VALIDITY_START, pool_info_block_validity_magic, POOL_INFO_BLOCK_VALIDITY_SIZE) != 0) && (memcmp(block2_data + POOL_INFO_BLOCK_VALIDITY_START, pool_info_block_validity_magic, POOL_INFO_BLOCK_VALIDITY_SIZE) != 0)) { fclose(fp); if (memcmp(block1_data, pool_info_block_reserved_magic, POOL_INFO_BLOCK_VALIDITY_SIZE) == 0) { partition_or_raid_t partition_or_raid; partition_or_raid.partition = follow_partitions; partition_or_raid.raid = NULL; add_partition_or_raid_reference(partition_or_raid); mark_partition_space_used(follow_disks, follow_partitions->partition_number); } continue; } new_chunk = CHECK_ALLOC_ONE(potential_pool_chunk_t); if (new_chunk == NULL) { fclose(fp); deallocate_potential_pool_list(potential_pool_list); all_disk_info->raid_info = NULL; all_disk_info->pool_info = NULL; pop_activity_description(); return; } new_chunk->partition = follow_partitions; u32 index; for (index = 0; index < 2; ++index) { unsigned char *block_data; u32 resize_in_progress; potential_pool_t **follow_potential_pools; if (index == 0) block_data = block1_data; else block_data = block2_data; if (memcmp(block_data + POOL_INFO_BLOCK_VALIDITY_START, pool_info_block_validity_magic, POOL_INFO_BLOCK_VALIDITY_SIZE) != 0) { new_chunk->info[index].is_valid = FALSE; if (index == 0) new_chunk->next_primary = NULL; else new_chunk->next_secondary = NULL; continue; } new_chunk->info[index].is_valid = TRUE; new_chunk->info[index].stripe_count = u32_from_u8_array_big_endian( block_data + POOL_INFO_BLOCK_STRIPE_COUNT_START); /*minimum 3 disks for RAID5 array*/ /*doing this special condition because the raid5_tag was added later and all the older systems don't have this tag. Although the value of POOL_INFO_BLOCK_RAID5_TAG_START byte should be zero in all the older system, yet this done to be "safer". Doing so will enable raid5 flag only if this condition met */ if(new_chunk->info[index].stripe_count >= 3) { /* read RAID5 tag only if stripe count is grater or equal to 3*/ new_chunk->info[index].raid5_tag = block_data[POOL_INFO_BLOCK_RAID5_TAG_START]; new_chunk->info[index].spare_stripe_count = block_data[POOL_INFO_BLOCK_SPARE_STRIPE_COUNT_START]; } else { new_chunk->info[index].raid5_tag = 0; new_chunk->info[index].spare_stripe_count = 0; } new_chunk->info[index].mirrors_count = u32_from_u8_array_big_endian( block_data + POOL_INFO_BLOCK_MIRROR_COUNT_START); new_chunk->info[index].spare_count = u32_from_u8_array_big_endian( block_data + POOL_INFO_BLOCK_SPARE_COUNT_START); new_chunk->info[index].number_of_this_pane = u32_from_u8_array_big_endian( block_data + POOL_INFO_BLOCK_PANE_NUM_START); new_chunk->info[index].chunk_count = u32_from_u8_array_big_endian( block_data + POOL_INFO_BLOCK_CHUNK_COUNT_START); new_chunk->info[index].number_of_this_chunk = u32_from_u8_array_big_endian( block_data + POOL_INFO_BLOCK_CHUNK_NUM_START); new_chunk->info[index].chunk_size = u32_from_u8_array_big_endian( block_data + POOL_INFO_BLOCK_CHUNK_SIZE_START); new_chunk->info[index].start_of_next_pane = partition_from_partition_spec_block(block_data, POOL_INFO_BLOCK_NEXT_PANE_START, all_disk_info, ((block_num + index) * BYTES_PER_DISK_BLOCK), ((mode == POOL_MODE_READ_ONLY) ? NULL : fp)); if (new_chunk->info[index].start_of_next_pane == NULL) { fclose(fp); free(new_chunk); deallocate_potential_pool_list(potential_pool_list); all_disk_info->raid_info = NULL; all_disk_info->pool_info = NULL; pop_activity_description(); return; } new_chunk->info[index].next_chunk_in_pane = partition_from_partition_spec_block(block_data, POOL_INFO_BLOCK_NEXT_CHUNK_START, all_disk_info, ((block_num + index) * BYTES_PER_DISK_BLOCK), ((mode == POOL_MODE_READ_ONLY) ? NULL : fp)); if (new_chunk->info[index].next_chunk_in_pane == NULL) { fclose(fp); free(new_chunk); deallocate_potential_pool_list(potential_pool_list); all_disk_info->raid_info = NULL; all_disk_info->pool_info = NULL; pop_activity_description(); return; } resize_in_progress = u32_from_u8_array_big_endian( block_data + POOL_INFO_BLOCK_RESIZE_START); if (resize_in_progress == 0) { new_chunk->info[index].resize_in_progress = FALSE; } else if (resize_in_progress == 1) { new_chunk->info[index].resize_in_progress = TRUE; } else { fclose(fp); free(new_chunk); deallocate_potential_pool_list(potential_pool_list); all_disk_info->raid_info = NULL; all_disk_info->pool_info = NULL; pop_activity_description(); return; } new_chunk->info[index].move_backward_bytes_complete = u64_from_raw_disk( block_data + POOL_INFO_BLOCK_BACKWARD_START); new_chunk->info[index].move_forward_bytes_complete = u64_from_raw_disk( block_data + POOL_INFO_BLOCK_FORWARD_START); new_chunk->info[index].old_size_in_kilobytes = u64_from_raw_disk( block_data + POOL_INFO_BLOCK_OLD_SIZE_START); new_chunk->info[index].new_size_in_kilobytes = u64_from_raw_disk( block_data + POOL_INFO_BLOCK_NEW_SIZE_START); follow_potential_pools = &potential_pool_list; while (TRUE) { if (*follow_potential_pools == NULL) { potential_pool_t *new_potential; new_potential = CHECK_ALLOC_ONE(potential_pool_t); if (new_potential == NULL) { fclose(fp); deallocate_potential_pool_list( potential_pool_list); all_disk_info->raid_info = NULL; all_disk_info->pool_info = NULL; pop_activity_description(); return; } memcpy(new_potential->pool_identifier_bits, block_data, POOL_IDENTIFIER_BIT_COUNT); new_potential->primary_chunks = NULL; new_potential->secondary_chunks = NULL; new_potential->next = NULL; *follow_potential_pools = new_potential; break; } if (memcmp((*follow_potential_pools)->pool_identifier_bits, block_data, POOL_IDENTIFIER_BIT_COUNT) == 0) { break; } follow_potential_pools = &((*follow_potential_pools)->next); } new_chunk->info[index].potential_pool = *follow_potential_pools; if (index == 0) { new_chunk->next_primary = (*follow_potential_pools)->primary_chunks; (*follow_potential_pools)->primary_chunks = new_chunk; } else { new_chunk->next_secondary = (*follow_potential_pools)->secondary_chunks; (*follow_potential_pools)->secondary_chunks = new_chunk; } } fclose(fp); } } all_disk_info->raid_info = NULL; all_disk_info->pool_info = NULL; pool_list_tail = &(all_disk_info->pool_info); for (follow_potential_pools = potential_pool_list; follow_potential_pools != NULL; follow_potential_pools = follow_potential_pools->next) { bool error; bool found_a_pool; potential_pool_chunk_t *follow_chunks; error = FALSE; found_a_pool = handle_potential_pool(follow_potential_pools, TRUE, all_disk_info, &pool_list_tail, &error); if (error) { deallocate_potential_pool_list(potential_pool_list); find_raid_devices(all_disk_info); find_encryption_info(all_disk_info); find_pool_mount_names(all_disk_info); pop_activity_description(); return; } if (!found_a_pool) continue; for (follow_chunks = follow_potential_pools->primary_chunks; follow_chunks != NULL; follow_chunks = follow_chunks->next_primary) { if (follow_chunks->info[1].is_valid) { potential_pool_t *blasted_secondary; potential_pool_chunk_t *follow_blasted_chunks; blasted_secondary = follow_chunks->info[1].potential_pool; follow_blasted_chunks = blasted_secondary->secondary_chunks; while (follow_blasted_chunks != NULL) { assert(follow_blasted_chunks->info[1].is_valid); follow_blasted_chunks->info[1].is_valid = FALSE; follow_blasted_chunks = follow_blasted_chunks->next_secondary; } blasted_secondary->secondary_chunks = NULL; assert(!(follow_chunks->info[1].is_valid)); } } } for (follow_potential_pools = potential_pool_list; follow_potential_pools != NULL; follow_potential_pools = follow_potential_pools->next) { bool error; error = FALSE; handle_potential_pool(follow_potential_pools, FALSE, all_disk_info, &pool_list_tail, &error); if (error) { deallocate_potential_pool_list(potential_pool_list); find_raid_devices(all_disk_info); find_encryption_info(all_disk_info); find_pool_mount_names(all_disk_info); pop_activity_description(); return; } } find_raid_devices(all_disk_info); find_encryption_info(all_disk_info); find_pool_mount_names(all_disk_info); for (follow_disks = all_disk_info->disk_info; follow_disks != NULL; follow_disks = follow_disks->next) { partition_info_t *follow_partitions; if (follow_disks->classification != DC_RECOGNIZED) continue; for (follow_partitions = follow_disks->partitions; follow_partitions != NULL;) { partition_info_t *old_partition; old_partition = follow_partitions; follow_partitions = follow_partitions->next; remove_partition_reference(old_partition); } } if (mode != POOL_MODE_READ_ONLY) { pool_info_t *follow_pools; follow_pools = all_disk_info->pool_info; while (follow_pools != NULL) { if (follow_pools->resize_in_progress) { fprintf(stderr, "Pool Resizing: Recovering from an interupted resize " "operation on pool `%s'.\n", follow_pools->name); complete_pool_resizing(follow_pools, all_disk_info); fprintf(stderr, "Pool Resizing: Recovery from an interupted resize " "operation on pool `%s' is complete.\n", follow_pools->name); } follow_pools = follow_pools->next; } } for (follow_disk_info = all_disk_info->disk_info; follow_disk_info != NULL; follow_disk_info = follow_disk_info->next) { if (follow_disk_info->classification == DC_MISSING) { char *summary; char *details; summary = ((char *)(check_alloc( strlen(follow_disk_info->tag) + 100))); if (summary == NULL) continue; sprintf(summary, TRANSLATE("Error: Disk `%s' is Missing."), follow_disk_info->tag); details = ((char *)(check_alloc( strlen(follow_disk_info->tag) + 5000))); if (details == NULL) { free(summary); continue; } sprintf(details, TRANSLATE("One or more pools reference a disk named `%s' that cannot currently" " be\n" "found on the system.\n" "\n" "To resolve this problem, you should either replace the disk or remove\n" "the references to it. If the disk still exists and is undamaged, it\n" "is best to reconnect it to the BroadNAS. If the disk has been damaged\n" "or destroyed, however, reconnecting it may not be an option. In that\n" "case, you will need to remove all the references to the disk from\n" "pools on disks that are still connected to the BroadNAS. If the pools\n" "are mirrored, you can remove just the mirrors or spares that reference\n" "the missing disk. If, however, the pool is not mirrored, or no\n" "complete mirror exists on the remaining disks, the data on that pool\n" "is lost and you must remove the pool.\n"), follow_disk_info->tag); create_user_note(summary, "missing_disk", NULL, details); free(summary); free(details); } } pop_activity_description(); }