diff --git a/src/aquarium.h b/src/aquarium.h index c55465e1..6ff9bd2e 100644 --- a/src/aquarium.h +++ b/src/aquarium.h @@ -18,6 +18,20 @@ typedef enum { AQUARIUM_OS_LINUX, } aquarium_os_info_t; +typedef enum { + AQUARIUM_TEMPLATE_KIND_BASE, + AQUARIUM_TEMPLATE_KIND_KERNEL, +} aquarium_template_kind_t; + +typedef enum { + AQUARIUM_DRIVE_KIND_MD, // memory disk + AQUARIUM_DRIVE_KIND_ADA, // ATA direct access + AQUARIUM_DRIVE_KIND_DA, // SCSI direct access + AQUARIUM_DRIVE_KIND_NVME, // NVMe + AQUARIUM_DRIVE_KIND_CD, // optical disk + AQUARIUM_DRIVE_KIND_OTHER, // could be 'mmcsd', 'mmc', 'at91_mci', or 'sdhci' +} aquarium_drive_kind_t; + // structs typedef struct { @@ -41,10 +55,20 @@ typedef struct { char* aquarium_path; } aquarium_db_ent_t; -typedef enum { - AQUARIUM_TEMPLATE_KIND_BASE, - AQUARIUM_TEMPLATE_KIND_KERNEL, -} aquarium_template_kind_t; +typedef struct { + aquarium_drive_kind_t kind; + char* provider; + int rank; + + uint64_t sectors; + uint64_t sector_size; + + uint64_t size; + + char* ident; + char* name; + char* label; +} aquarium_drive_t; // function prototypes @@ -57,3 +81,6 @@ int aquarium_download_template(aquarium_opts_t* opts, char const* path, char con int aquarium_extract_template(aquarium_opts_t* opts, char const* path, char const* name, aquarium_template_kind_t kind); int aquarium_create(aquarium_opts_t* opts, char const* path, char const* template, char const* kernel_template); + +int aquarium_read_drives(aquarium_drive_t** drives_ref, size_t* drives_len_ref); +void aquarium_free_drives(aquarium_drive_t* drives, size_t drives_len); diff --git a/src/lib/drive.c b/src/lib/drive.c new file mode 100644 index 00000000..a1ececfa --- /dev/null +++ b/src/lib/drive.c @@ -0,0 +1,186 @@ +// #include +#include "../aquarium.h" +#include +#include +#include +#include +#include +#include + +static aquarium_drive_kind_t drive_kind(char const* provider) { + // not super super robust but whatever + + if (!strncmp(provider, "md", 2)) return AQUARIUM_DRIVE_KIND_MD; + if (!strncmp(provider, "ad", 2)) return AQUARIUM_DRIVE_KIND_ADA; + if (!strncmp(provider, "da", 2)) return AQUARIUM_DRIVE_KIND_DA; + if (!strncmp(provider, "nv", 2)) return AQUARIUM_DRIVE_KIND_NVME; + if (!strncmp(provider, "cd", 2)) return AQUARIUM_DRIVE_KIND_CD; + + return AQUARIUM_DRIVE_KIND_OTHER; +} + +static int process_drive(aquarium_drive_t** drives_ref, size_t* drives_len_ref, struct ggeom* geom, struct gclass* class) { + aquarium_drive_t* drives = *drives_ref; + size_t drives_len = *drives_len_ref; + + if (LIST_EMPTY(&geom->lg_provider)) { + return 0; + } + + // find provider(s) + + struct gprovider* provider; + + LIST_FOREACH(provider, &geom->lg_provider, lg_provider) { + drives = realloc(drives, ++drives_len * sizeof *drives); + aquarium_drive_t* const drive = &drives[drives_len - 1]; + + memset(drive, 0, sizeof *drive); // 'realloc' doesn't zero out anything unfortunately + drive->kind = drive_kind(provider->lg_name); + + drive->provider = strdup(provider->lg_name); // TODO vs 'geom->lg_name'? + drive->rank = geom->lg_rank; + + drive->sector_size = provider->lg_sectorsize; + drive->size = provider->lg_mediasize; + + // find extra information on drive + + struct gconfig* config; + + LIST_FOREACH(config, &provider->lg_config, lg_config) { + if (!strcmp(config->lg_name, "ident") && config->lg_val) { + drive->ident = strdup(config->lg_val); + } + + else if (!strcmp(config->lg_name, "descr") && config->lg_val) { + drive->name = strdup(config->lg_val); + } + + else if (!strcmp(config->lg_name, "label") && config->lg_val) { + drive->label = strdup(config->lg_val); + } + } + } + + // find potential label(s) + + struct ggeom* label_geom; + + LIST_FOREACH(label_geom, &class->lg_geom, lg_geom) { + if (strcmp(label_geom->lg_name, geom->lg_name)) { + continue; + } + + // 'provider' already defined previously + + LIST_FOREACH(provider, &label_geom->lg_provider, lg_provider) { + for (size_t i = 0; i < drives_len; i++) { + aquarium_drive_t* drive = &drives[i]; + + if (strcmp(drive->provider, provider->lg_name)) { + continue; + } + + drive->label = strdup(provider->lg_name); + break; + } + } + + break; + } + + // set references + + *drives_ref = drives; + *drives_len_ref = drives_len; + + return 0; +} + +static void free_drive(aquarium_drive_t* drive) { + if (drive->provider) { + free(drive->provider); + } + + if (drive->ident) { + free(drive->ident); + } + + if (drive->name) { + free(drive->name); + } + + if (drive->label) { + free(drive->label); + } +} + +static int process_class(aquarium_drive_t** drives_ref, size_t* drives_len_ref, struct gmesh* mesh, char const* name) { + struct gclass* class; + + // find class we're interested in + + LIST_FOREACH(class, &mesh->lg_class, lg_class) { + if (!strcmp(class->lg_name, name)) { + break; + } + } + + if (!class) { + warnx("Failed to find '%s' class\n", name); + return -1; + } + + // traverse through first layer of disk geometry (rank 1) + + struct ggeom* geom; + + LIST_FOREACH(geom, &class->lg_geom, lg_geom) { + process_drive(drives_ref, drives_len_ref, geom, class); + } + + return 0; +} + +int aquarium_read_drives(aquarium_drive_t** drives_ref, size_t* drives_len_ref) { + int rv = -1; + + // get drive geometry mesh + + struct gmesh mesh; + + if (geom_gettree(&mesh) < 0) { + warnx("geom_gettree: %s\n", strerror(errno)); + goto err_gettree; + } + + if ( + process_class(drives_ref, drives_len_ref, &mesh, "DISK" ) < 0 || + process_class(drives_ref, drives_len_ref, &mesh, "MD" ) < 0 || + process_class(drives_ref, drives_len_ref, &mesh, "LABEL") < 0 + ) { + goto class_err; + } + + // success + + rv = 0; + +class_err: + + geom_deletetree(&mesh); + +err_gettree: + + return rv; +} + +void aquarium_free_drives(aquarium_drive_t* drives, size_t drives_len) { + for (size_t i = 0; i < drives_len; i++) { + aquarium_drive_t* const drive = &drives[i]; + free_drive(drive); + } + + free(drives); +}