#ifndef NYANGPT_C
#define NYANGPT_C
/*
* Copyright 2021 Sylvain BERTRAND <sylvain.bertrand@legeek.net>
* LICENSE: GNU AFFERO GPLV3
* (NO WARANTY OF ANY KIND AND BLAHBLAHBLAH)
*/
/* quick and dirty, minimal, line input oriented uefi gpt partition creator */
/*
* TODO:
* - implement ANSI terminal pagination (listing of entries and types)
* - utf8 to utf16 converter for names (no iconv)
*/
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdint.h>
#include <inttypes.h>
#include <endian.h>
#include <errno.h>
/*
* how to get pertinent device information from sysfs for gpt partitioning
*
* for a /dev/sdX block device:
* /sys/block/sdX/size = size of the device in blocks of 512 bytes (hardcoded)
* /sys/block/sdX/queue/logical_block_size = size in bytes of a logical block
* which is the size used by lba (Logical Block Access) offsets used
* in gpt partitions
* /sys/block/sdX/queue/physical_block_size = size in bytes of a physical block
*/
/*{{{ global preprocessor definitions */
#define STATIC static
#define utf8 uint8_t
#define utf16 uint16_t
#define X64_UTF8_BYTES_MAX (sizeof("18446744073709551615") - 1)
/* meh... */
#define strtou64 strtoul
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define s32 int32_t
#define u64 uint64_t
#define loop for(;;)
#define ARRAY_N(x) (sizeof(x) / sizeof(x[0]))
/*}}} global preprocessor definitions -- end */
/*{{{ nyangpt namespace */
#define blk_dev nyangpt_blk_dev
#define build_data_from_previous_gpt nyangpt_data_from_previous_gpt
#define change_disk_guid_prompt nyangpt_change_disk_guid_prompt
#define colors_on nyangpt_colors_on
#define delete_entry_prompt nyangpt_delete_entry_prompt
#define edit_entry nyangpt_edit_entry
#define edit_entry_attrs_prompt nyangpt_edit_entry_attrs_prompt
#define edit_entry_first_lba_prompt nyangpt_edit_entry_first_lba_prompt
#define edit_entry_last_lba_prompt nyangpt_edit_entry_last_lba_prompt
#define edit_entry_prompt nyangpt_edit_entry_prompt
#define edit_entry_to_entries nyangpt_edit_entry_to_entries
#define edit_entry_type_prompt nyangpt_edit_entry_type_prompt
#define edit_entry_uniq_guid_prompt nyangpt_edit_entry_uniq_guid_prompt
#define entries nyangpt_entries
#define entries_lbas_n nyangpt_entries_lbas_n
#define entries_load nyangpt_entries_load
#define entries_n nyangpt_entries_n
#define entries_reset nyangpt_entries_reset
#define entries_serialized_array nyangpt_entries_serialized_array
#define entries_serialized_array_crc32 nyangpt_entries_serialized_array_crc32
#define entries_serialized_array_gen nyangpt_entries_serialized_array_gen
#define entries_serialized_array_gen_entry nyangpt_entries_serialized_array_gen_entry
#define entries_serialized_array_primary_write nyangpt_entries_serialized_array_primary_write
#define entries_serialized_array_secondary_write nyangpt_entries_serialized_array_secondary_write
#define entries_show nyangpt_entries_show
#define entry_bytes_n nyangpt_entry_bytes_n
#define entry_delete nyangpt_entry_delete
#define entry_is_used nyangpt_entry_is_used
#define entry_show nyangpt_entry_show
#define entry_t nyangpt_entry_t
#define error_pf nyangpt_error_pf
#define first_lba_default_select nyangpt_first_lba_default_select
#define gpt_write nyangpt_gpt_write
#define guid_is_zero nyangpt_guid_is_zero
#define guid_t nyangpt_guid_t
#define guid_randomize nyangpt_guid_randomize
#define guid_read nyangpt_guid_read
#define guid_read_from_input nyangph_guid_read_from_input
#define guid_write nyangpt_guid_write
#define hdr_load nyangpt_hdr_load
#define hdr_show nyangpt_hdr_show
#define hdr_primary_serialized nyangpt_hdr_primary_serialized
#define hdr_primary_serialized_gen nyangpt_hdr_primary_serialized_gen
#define hdr_primary_serialized_write nyangpt_hdr_primary_serialized_write
#define hdr_secondary_serialized nyangpt_hdr_secondary_serialized
#define hdr_secondary_serialized_gen nyangpt_hdr_secondary_serialized_gen
#define hdr_secondary_serialized_write nyangpt_hdr_secondary_serialized_write
#define hdr_t nyangpt_hdr_t
#define hdr_validate nyangpt_hdr_validate
#define hdrs nyangpt_hdrs
#define hdrs_usable_lbas_compute nyangpt_hdrs_usable_lbas_compute
#define init_once nyangpt_init_once
#define input_line nyangpt_input_line
#define input_line_consume nyangpt_input_line_consume
#define input_line_loop nyangpt_input_line_loop
#define input_state nyangpt_input_state
#define last_lba_default_select nyangpt_last_lba_default_select
#define le_crc32_for_byte nyangpt_le_crc32_for_byte
#define le_crc32_tbl nyangpt_le_crc32_tbl
#define le_crc32_tbl_gen nyangpt_le_crc32_tbl_gen
#define le_crc32_update nyangpt_le_crc32_update
#define load_previous_gpt nyangpt_load_previous_gpt
#define main_menu_prompt nyangpt_main_menu_prompt
#define main_menu_show nyangpt_main_menu_show
#define options_parse nyangpt_options_parse
#define out_bold_pf nyangpt_out_bold_pf
#define out_guid nyangpt_out_guid
#define out_pf nyangpt_out_pf
#define pf nyangpt_pf
#define previous_gpt_load nyangpt_previous_gpt_load
#define protective_mbr nyangpt_protective_mbr
#define protective_mbr_gen nyangpt_protective_mbr_gen
#define protective_mbr_write nyangpt_protective_mbr_write
#define read_full nyangpt_read_full
#define state_change_disk_guid nyangpt_state_change_disk_guid
#define state_delete_entry nyangpt_state_delete_entry
#define state_edit_entry nyangpt_state_edit_entry
#define state_edit_entry_substate_attrs nyangpt_state_edit_entry_substate_attrs
#define state_edit_entry_substate_first_lba nyangpt_state_edit_entry_substate_first_lba
#define state_edit_entry_substate_last_lba nyangpt_state_edit_entry_substate_last_lba
#define state_edit_entry_substate_type nyangpt_state_edit_entry_substate_type
#define state_edit_entry_substate_uniq_quid nyangpt_state_edit_entry_substate_uniq_quid
#define state_main_menu nyangpt_state_main_menu
#define sysfs_infos_get nyangpt_sysfs_infos_get
#define type_guids_lookup_name nyangpt_type_guids_lookup_name
#define types nyangpt_types
#define types_show nyangpt_types_show
#define usage nyangpt_usage
#define utf16_strdup nyangpt_utf16_strdup
#define warning_pf nyangpt_warning_pf
#define write_full nyangpt_write_full
/*----------------------------------------------------------------------------*/
#define nyangpt_main main
/*}}} nyangpt namespace -- end -----------------------------------------------*/
/*{{{ crc32b little endian */
/* stolen and cosmetized, not validated on big endian */
/* http://home.thep.lu.se/~bjorn/crc/ */
STATIC u32 le_crc32_for_byte(u32 r)
{
u8 j;
j = 0;
loop {
if (j == 8)
break;
r = (r & 1 ? 0 : (u32)0xedb88320) ^ r >> 1;
++j;
}
return r ^ (u32)0xff000000;
}
STATIC u32 le_crc32_tbl[0x100];
STATIC void le_crc32_tbl_gen(void)
{
u32 i;
i = 0;
loop {
if (i == 0x100)
break;
le_crc32_tbl[i] = le_crc32_for_byte(i);
++i;
}
}
STATIC void le_crc32_update(void *data, u64 bytes_n, u32* crc)
{
u64 i;
i = 0;
loop {
if (i == bytes_n)
break;
*crc = le_crc32_tbl[(u8)*crc ^ ((u8*)data)[i]] ^ *crc >> 8;
++i;
}
}
/*}}} crc32b little endian -- end *********************************************/
/*{{{ types */
struct guid_t {
u32 blk_0;
u16 blk_1;
u16 blk_2;
u16 blk_3;
u8 blk_4[6];
};
/* serialized offsets */
#define BLK_0 0x0
#define BLK_1 0x4
#define BLK_2 0x6
#define BLK_3 0x8
#define BLK_4 0xa
/*----------------------------------------------------------------------------*/
struct hdr_t {
bool valid;
utf8 *str;
u64 read_from_lba; /* _should_ be the same as the lba field */
u8 *data;
/* fields -- start */
/* signature */
/* revision */
u32 bytes_n;
u32 le_crc32;
u64 lba;
u64 alternate_hdr_lba;
/* first usable lba */
/* last usable lba */
struct guid_t disk_guid;
u64 entries_lba;
u32 entries_n;
u32 entry_bytes_n;
u32 entries_le_crc32;
/* fields -- end */
bool entries_valid;
u8 *entries;
};
/*----------------------------------------------------------------------------*/
struct entry_t {
struct guid_t type;
struct guid_t uniq;
u64 first;
u64 last;
u64 attrs;
utf16 *name;
};
#define ENTRIES_ARRAY_MIN_BYTES_N 16384 /* specs: minimum to book on the disk */
#define ENTRY_BYTES_N 0x80 /* specs */
/*}}} types -- end */
STATIC bool colors_on;
STATIC bool load_previous_gpt;
/*----------------------------------------------------------------------------*/
STATIC struct {
utf8 *path;
u64 sz_512_n;
u64 logical_blk_bytes_n;
u64 physical_blk_bytes_n;
int fd;
u64 last_lba;
} blk_dev;
/*----------------------------------------------------------------------------*/
STATIC u8 *protective_mbr;
/* serialized offsets */
#define BOOT_SIGNATURE_0X55 0x1fe
#define BOOT_SIGNATURE_0XAA 0x1ff
#define PART_0 0x1be
/*----------------------------------------------------------------------------*/
STATIC struct { /* there are 2 headers, each must fit in a logical block */
u64 first_usable_lba;
u64 last_usable_lba;
struct guid_t disk_guid;
u32 bytes_n; /* the mitigated hdr size */
struct {
struct hdr_t primary;
struct hdr_t secondary;
} previous;
} hdrs;
#define HDR_SIGNATURE "EFI PART"
#define HDR_SIGNATURE_BYTES_N 8
#define HDR_REVISION 0x00010000 /* 1.0 */
#define HDR_BYTES_N 0x5c /* specs: 92 bytes */
STATIC u8 *hdr_primary_serialized;
STATIC u8 *hdr_secondary_serialized;
/*----------------------------------------------------------------------------*/
STATIC struct entry_t *entries;
STATIC u32 entries_n;
STATIC u32 entry_bytes_n;
STATIC u32 entries_lbas_n;
STATIC u8 *entries_serialized_array;
STATIC u32 entries_serialized_array_crc32;
/*----------------------------------------------------------------------------*/
STATIC utf8 *pf(utf8 *fmt,...)
{
va_list ap;
int r;
utf8 *r_str;
va_start(ap, fmt);
r = vsnprintf(0, 0, fmt, ap);
va_end(ap);
r_str = malloc(r + 1); /* we want a terminating 0 */
va_start(ap, fmt);
vsnprintf(r_str, r + 1, fmt, ap); /* has room for the terminating 0 */
va_end(ap);
return r_str;
}
#define BOLD_RED if (colors_on) dprintf(1,"\x1b[38;2;255;0;0m\x1b[1m")
#define BOLD_ORANGE if (colors_on) dprintf(1,"\x1b[38;2;255;140;0m\x1b[1m")
#define BOLD if (colors_on) dprintf(1,"\x1b[1m");
#define RESTORE if (colors_on) dprintf(1, "\x1b[m")
STATIC void error_pf(utf8 *fmt,...)
{
va_list ap;
BOLD_RED;
dprintf(1, "ERROR:");
va_start(ap, fmt);
vdprintf(1, fmt, ap);
va_end(ap);
RESTORE;
exit(1);
}
STATIC void warning_pf(utf8 *fmt,...)
{
va_list ap;
BOLD_ORANGE;
dprintf(1, "WARNING:");
va_start(ap, fmt);
vdprintf(1, fmt, ap);
va_end(ap);
RESTORE;
}
STATIC void out_pf(utf8 *fmt,...)
{
va_list ap;
va_start(ap, fmt);
vdprintf(1, fmt, ap);
va_end(ap);
}
STATIC void out_bold_pf(utf8 *fmt,...)
{
va_list ap;
BOLD;
va_start(ap, fmt);
vdprintf(1, fmt, ap);
va_end(ap);
RESTORE;
}
STATIC bool read_full(int fd, void *dest, u64 bytes_n)
{
u64 read_bytes_n;
read_bytes_n = 0;
loop {
ssize_t r;
errno = 0;
r = read(fd, dest + read_bytes_n,
(size_t)(bytes_n - read_bytes_n));
if (r == -1) {
if (errno == EINTR)
continue;
return false;
}
read_bytes_n += (u64)r;
if (read_bytes_n == bytes_n)
return true;
}
/* unreachable */
}
STATIC bool write_full(int fd, void *src, u64 bytes_n)
{
u64 written_bytes_n;
/* short writes */
written_bytes_n = 0;
loop {
ssize_t r;
errno = 0;
r = write(blk_dev.fd, src + written_bytes_n,
(size_t)(bytes_n - written_bytes_n));
if (r == -1) {
if (errno == EINTR)
continue;
return false;
}
written_bytes_n += (u64)r;
if (written_bytes_n == bytes_n)
return true;
}
/* unreachable */
}
STATIC utf16 *utf16_strdup(utf16 *start)
{
utf16 *end;
utf16 *dst;
size_t bytes_n;
end = start;
loop {
if (*end == 0)
break;
++end;
}
++end;
bytes_n = (size_t)((end - start) * sizeof(utf16));
dst = calloc(1,bytes_n);
memcpy(dst, start, bytes_n);
}
/* brain damaged mixed-endian guid */
STATIC void guid_write(void *dest, struct guid_t *src)
{
u8 *d;
u32 *p32;
u16 *p16;
d = (u8*)dest;
p32 = (u32*)d;
*p32 = htole32(src->blk_0); /* little endian */
d += 4;
p16 = (u16*)d;
*p16 = htole16(src->blk_1); /* little endian */
d += 2;
p16 = (u16*)d;
*p16 = htole16(src->blk_2); /* little endian */
d += 2;
p16 = (u16*)d;
*p16 = htobe16(src->blk_3); /* big endian */
d += 2;
d[0] = src->blk_4[0];
d[1] = src->blk_4[1];
d[2] = src->blk_4[2];
d[3] = src->blk_4[3];
d[4] = src->blk_4[4];
d[5] = src->blk_4[5];
}
/* brain damaged mixed-endian guid */
STATIC void guid_read(struct guid_t *dest, void *src)
{
u32 *p32;
u16 *p16;
u8 *p8;
p32 = src;
dest->blk_0 = le32toh(*p32);
src += 4;
p16 = src;
dest->blk_1 = le16toh(*p16);
src += 2;
p16 = src;
dest->blk_2 = le16toh(*p16);
src += 2;
p16 = src;
dest->blk_3 = be16toh(*p16);
src += 2;
memcpy(dest->blk_4, src, 6);
}
STATIC void sysfs_infos_get(void)
{
int fd;
int r;
utf8 val_str[X64_UTF8_BYTES_MAX + 1]; /* 0 terminating char */
utf8 *blk_dev_name;
utf8 *sz_512_n_path;
utf8 *logical_blk_bytes_n_path;
utf8 *physical_blk_bytes_n_path;
blk_dev_name = strrchr(blk_dev.path, '/');
++blk_dev_name;
sz_512_n_path = pf("/sys/block/%s/size", blk_dev_name);
out_pf("%s:reading %s\n", blk_dev.path, sz_512_n_path);
fd = open(sz_512_n_path, O_RDONLY);
if (fd == -1)
error_pf("%s:unable to open %s\n", blk_dev.path, sz_512_n_path);
free(sz_512_n_path);
/* reads are supposed to be atomic from sysfs... I guess */
r = read(fd, val_str, sizeof(val_str));
val_str[r - 1] = 0; /* remove the terminating '\n' */
blk_dev.sz_512_n = strtou64(val_str, 0, 10);
out_bold_pf("%s:size is %"PRIu64" blocks of 512 bytes\n", blk_dev.path, blk_dev.sz_512_n);
close(fd);
logical_blk_bytes_n_path = pf("/sys/block/%s/queue/logical_block_size", blk_dev_name);
out_pf("%s:reading %s\n", blk_dev.path, logical_blk_bytes_n_path);
fd = open(logical_blk_bytes_n_path, O_RDONLY);
if (fd == -1)
error_pf("%s:unable to open %s\n", blk_dev.path, logical_blk_bytes_n_path);
free(logical_blk_bytes_n_path);
/* reads are supposed to be atomic from sysfs... I guess */
r = read(fd, val_str, sizeof(val_str));
val_str[r - 1] = 0; /* remove the terminating '\n' */
blk_dev.logical_blk_bytes_n = strtou64(val_str, 0, 10);
out_bold_pf("%s:logical block size is %"PRIu64" bytes\n", blk_dev.path, blk_dev.logical_blk_bytes_n);
close(fd);
physical_blk_bytes_n_path = pf("/sys/block/%s/queue/physical_block_size", blk_dev_name);
out_pf("%s:reading %s\n", blk_dev.path, physical_blk_bytes_n_path);
fd = open(physical_blk_bytes_n_path, O_RDONLY);
if (fd == -1)
error_pf("%s:unable to open %s\n", blk_dev.path, physical_blk_bytes_n_path);
free(physical_blk_bytes_n_path);
/* reads are supposed to be atomic from sysfs... I guess */
r = read(fd, val_str, sizeof(val_str));
val_str[r - 1] = 0; /* remove the terminating '\n' */
blk_dev.physical_blk_bytes_n = strtou64(val_str, 0, 10);
out_bold_pf("%s:physical block size is %"PRIu64" bytes\n", blk_dev.path, blk_dev.physical_blk_bytes_n);
close(fd);
}
STATIC void protective_mbr_gen(void)
{
u32 *le32_whole_dev_logical_blks_n;
u64 whole_dev_bytes_n;
u64 whole_dev_logical_blks_n;
if (blk_dev.logical_blk_bytes_n < 512)
error_pf("%s: something is wrong, the logical block size is %"PRIu64", below 512/0x200 bytes", blk_dev.path, blk_dev.logical_blk_bytes_n);
protective_mbr = calloc(1, blk_dev.logical_blk_bytes_n);
protective_mbr[PART_0 + 0x02] = 0x02; /* first CHS */
protective_mbr[PART_0 + 0x04] = 0xee; /* partition type */
protective_mbr[PART_0 + 0x05] = 0xff; /* last head */
protective_mbr[PART_0 + 0x06] = 0xff; /* last cylinder MSBs + last sector */
protective_mbr[PART_0 + 0x07] = 0xff; /* last cylinder LSBs */
protective_mbr[PART_0 + 0x08] = 0x1; /* little endian */
whole_dev_bytes_n = blk_dev.sz_512_n * 512;
whole_dev_logical_blks_n = whole_dev_bytes_n / blk_dev.logical_blk_bytes_n;
/* cap to max, remove the MBR in LBA 0 */
if (whole_dev_logical_blks_n > 0x100000000)
whole_dev_logical_blks_n = 0xffffffff;
le32_whole_dev_logical_blks_n = (u32*)&protective_mbr[PART_0 + 0x0c];
*le32_whole_dev_logical_blks_n = htole32( /* remove mbr */
(u32)whole_dev_logical_blks_n - 1);
protective_mbr[BOOT_SIGNATURE_0X55] = 0x55;
protective_mbr[BOOT_SIGNATURE_0XAA] = 0xaa;
}
STATIC void protective_mbr_write(void)
{
off_t r0;
r0 = lseek(blk_dev.fd, 0, SEEK_SET);
if (r0 != 0)
error_pf("%s:unable to reach the start of the device\n", blk_dev.path);
if (!write_full(blk_dev.fd, protective_mbr,
blk_dev.logical_blk_bytes_n))
error_pf("%s:unable to write the protective master boot record (mbr), device mbr is now probably corrupted\n", blk_dev.path);
out_bold_pf("%s:protective master boot record (mbr) written, %"PRIu64" bytes\n", blk_dev.path, blk_dev.logical_blk_bytes_n);
}
STATIC void entries_serialized_array_gen_entry(u32 entry_idx)
{
u8 *entry;
u64 *p64;
entry = entries_serialized_array + entry_bytes_n * entry_idx;
guid_write(&entry[0x00], &entries[entry_idx].type);
guid_write(&entry[0x10], &entries[entry_idx].uniq);
p64 = (u64*)&entry[0x20];
*p64 = htole64(entries[entry_idx].first);
p64 = (u64*)&entry[0x28];
*p64 = htole64(entries[entry_idx].last);
}
STATIC void entries_serialized_array_gen(void)
{
u32 entry_idx;
entries_serialized_array = calloc(1, entries_lbas_n
* blk_dev.logical_blk_bytes_n);
entry_idx = 0;
loop {
if (entry_idx == entries_n)
break;
entries_serialized_array_gen_entry(entry_idx);
++entry_idx;
}
}
STATIC void hdr_primary_serialized_gen(void)
{
u64 *p64;
u32 *p32;
u16 *p16;
u32 le_crc32;
hdr_primary_serialized = calloc(1, blk_dev.logical_blk_bytes_n);
memcpy(hdr_primary_serialized, HDR_SIGNATURE, 8);
p32 = (u32*)&hdr_primary_serialized[0x08];
*p32 = htole32(HDR_REVISION);
p32 =(u32*)&hdr_primary_serialized[0x0c];
*p32 = htole32(hdrs.bytes_n);
/* the CRC32 will go there, 0 for its calculation */
p64 = (u64*)&hdr_primary_serialized[0x18];
*p64 = htole64(0x00000001); /* lba of this hdr */
p64 = (u64*)&hdr_primary_serialized[0x20];
*p64 = htole64(blk_dev.last_lba); /* the hdr at the end */
p64 = (u64*)&hdr_primary_serialized[0x28];
*p64 = htole64(hdrs.first_usable_lba);
p64 = (u64*)&hdr_primary_serialized[0x30];
*p64 = htole64(hdrs.last_usable_lba);
guid_write(&hdr_primary_serialized[0x38], &hdrs.disk_guid);
p64 = (u64*)&hdr_primary_serialized[0x48];
*p64 = htole64(2); /* skip mbr and hdr */
p32 = (u32*)&hdr_primary_serialized[0x50];
*p32 = htole32(entries_n);
p32 = (u32*)&hdr_primary_serialized[0x54];
*p32 = htole32(entry_bytes_n);
p32 = (u32*)&hdr_primary_serialized[0x58];
*p32 = htole32(entries_serialized_array_crc32);
/* crc32 on exactly the header size */
le_crc32 = 0;
le_crc32_update(hdr_primary_serialized, hdrs.bytes_n, &le_crc32);
p32 = (u32*)&hdr_primary_serialized[0x10];
*p32 = le_crc32;
}
STATIC void hdr_secondary_serialized_gen(void)
{
u64 *p64;
u32 *p32;
u16 *p16;
u32 le_crc32;
hdr_secondary_serialized = calloc(1, blk_dev.logical_blk_bytes_n);
memcpy(hdr_secondary_serialized, HDR_SIGNATURE, 8);
p32 = (u32*)&hdr_secondary_serialized[0x08];
*p32 = htole32(HDR_REVISION);
p32 =(u32*)&hdr_secondary_serialized[0x0c];
*p32 = htole32(hdrs.bytes_n);
/* the CRC32 will go there, 0 for its calculation */
p64 = (u64*)&hdr_secondary_serialized[0x18];
*p64 = htole64(blk_dev.last_lba); /* this hdr */
p64 = (u64*)&hdr_secondary_serialized[0x20];
*p64 = htole64(1); /* the hdr at the beginning */
p64 = (u64*)&hdr_secondary_serialized[0x28];
*p64 = htole64(hdrs.first_usable_lba);
p64 = (u64*)&hdr_secondary_serialized[0x30];
*p64 = htole64(hdrs.last_usable_lba);
guid_write(&hdr_secondary_serialized[0x38], &hdrs.disk_guid);
p64 = (u64*)&hdr_secondary_serialized[0x48];
*p64 = htole64(blk_dev.last_lba - entries_lbas_n);
p32 = (u32*)&hdr_secondary_serialized[0x50];
*p32 = htole32(entries_n);
p32 = (u32*)&hdr_secondary_serialized[0x54];
*p32 = htole32(entry_bytes_n);
p32 = (u32*)&hdr_secondary_serialized[0x58];
*p32 = htole32(entries_serialized_array_crc32);
/* crc32 on exactly the header size */
le_crc32 = 0;
le_crc32_update(hdr_secondary_serialized, hdrs.bytes_n, &le_crc32);
p32 = (u32*)&hdr_secondary_serialized[0x10];
*p32 = le_crc32;
}
STATIC void hdr_primary_serialized_write(void)
{
off_t r;
off_t start;
u64 bytes_to_write_n;
/* skip the mbr */
start = (off_t)blk_dev.logical_blk_bytes_n;
r = lseek(blk_dev.fd, start, SEEK_SET);
if (r != start)
error_pf("%s:unable to reach the first lba of the device\n", blk_dev.path);
bytes_to_write_n = (size_t)(blk_dev.logical_blk_bytes_n);
if (!write_full(blk_dev.fd, hdr_primary_serialized, bytes_to_write_n))
error_pf("%s:unable to write the primary GPT header, block device is now probably corrupted\n", blk_dev.path);
out_bold_pf("%s:primary GPT header written, %"PRIu64" bytes\n", blk_dev.path, bytes_to_write_n);
}
STATIC void hdr_secondary_serialized_write(void)
{
off_t r;
off_t start;
u64 bytes_to_write_n;
start = (off_t)(blk_dev.last_lba * blk_dev.logical_blk_bytes_n);
r = lseek(blk_dev.fd, start, SEEK_SET);
if (r != start)
error_pf("%s:unable to reach the lba of the secondary header\n", blk_dev.path);
bytes_to_write_n = blk_dev.logical_blk_bytes_n;
if (!write_full(blk_dev.fd, hdr_secondary_serialized, bytes_to_write_n))
error_pf("%s:unable to write the secondary GPT header, block device is now probably corrupted\n", blk_dev.path);
out_bold_pf("%s:secondary GPT header written, %"PRIu64" bytes\n", blk_dev.path, bytes_to_write_n);
}
STATIC void entries_serialized_array_primary_write(void)
{
off_t r;
off_t start;
u64 bytes_to_write_n;
/* skip the mbr and the primary hdr */
start = (off_t)(blk_dev.logical_blk_bytes_n * 2);
r = lseek(blk_dev.fd, start, SEEK_SET);
if (r != start)
error_pf("%s:unable to reach the first lba for the primary entries\n", blk_dev.path);
bytes_to_write_n = blk_dev.logical_blk_bytes_n * entries_lbas_n;
if (!write_full(blk_dev.fd, entries_serialized_array, bytes_to_write_n))
error_pf("%s:unable to write the primary entries, block device is now probably corrupted\n", blk_dev.path);
out_bold_pf("%s:primary entries written, %"PRIu64" bytes\n", blk_dev.path, bytes_to_write_n);
}
STATIC void entries_serialized_array_secondary_write(void)
{
off_t r;
off_t start;
u64 bytes_to_write_n;
/* skip the secondary hdr, offset arithmetic */
start = (off_t)(blk_dev.logical_blk_bytes_n * (blk_dev.last_lba
- entries_lbas_n));
r = lseek(blk_dev.fd, start, SEEK_SET);
if (r != start)
error_pf("%s:unable to reach the first lba for the secondary entries\n", blk_dev.path);
bytes_to_write_n = blk_dev.logical_blk_bytes_n * entries_lbas_n;
if (!write_full(blk_dev.fd, entries_serialized_array, bytes_to_write_n))
error_pf("%s:unable to write the secondary entries, block device is now probably corrupted\n", blk_dev.path);
out_bold_pf("%s:secondary entries written, %"PRIu64" bytes\n", blk_dev.path, bytes_to_write_n);
}
STATIC void hdr_load(struct hdr_t *hdr)
{
off_t r;
off_t lba_off;
lba_off = (off_t)(blk_dev.logical_blk_bytes_n * hdr->read_from_lba);
hdr->data = calloc(1, blk_dev.logical_blk_bytes_n);
r = lseek(blk_dev.fd, lba_off, SEEK_SET);
if (r != lba_off) {
warning_pf("%s:unable to seek the device to the %s GPT header\n", blk_dev.path, hdr->str);
return;
}
if (!read_full(blk_dev.fd, hdr->data, blk_dev.logical_blk_bytes_n)) {
warning_pf("%s:unable to load the %s GPT header logical block\n", blk_dev.path, hdr->str);
free(hdr->data);
hdr->data = 0;
}
}
STATIC void hdr_validate(struct hdr_t *hdr)
{
u32 *p32;
u32 le_crc32;
u64 *p64;
if (hdr->data == 0)
return;
if (memcmp(hdr->data, HDR_SIGNATURE, HDR_SIGNATURE_BYTES_N) != 0) {
warning_pf("%s:the %s GPT header has the wrong signature\n", blk_dev.path, hdr->str);
goto free_data;
}
out_bold_pf("%s:found, %s, GPT header\n", blk_dev.path, hdr->str);
p32 = (u32*)&hdr->data[0x10];
hdr->le_crc32 = *p32;
/* must 0 the crc32 to perform our crc32 */
*p32 = 0;
p32 = (u32*)&hdr->data[0x0c];
hdr->bytes_n = le32toh(*p32);
le_crc32 = 0;
le_crc32_update(hdr->data, hdr->bytes_n, &le_crc32);
if (le_crc32 != hdr->le_crc32) {
warning_pf("%s:the %s GPT header has a bad crc32\n", blk_dev.path, hdr->str);
goto free_data;
}
p64 = (u64*)&hdr->data[0x18];
hdr->lba = le64toh(*p64);
p64 = (u64*)&hdr->data[0x20];
hdr->alternate_hdr_lba = le64toh(*p64);
guid_read(&hdr->disk_guid, &hdr->data[0x38]);
p64 = (u64*)&hdr->data[0x48];
hdr->entries_lba = le64toh(*p64);
p32 = (u32*)&hdr->data[0x50];
hdr->entries_n = le32toh(*p32);
p32 = (u32*)&hdr->data[0x54];
hdr->entry_bytes_n = le32toh(*p32);
p32 = (u32*)&hdr->data[0x58];
hdr->entries_le_crc32 = *p32;
hdr->valid = true;
return;
free_data:
free(hdr->data);
hdr->data = 0;
}
STATIC void entries_load(struct hdr_t *hdr)
{
off_t r;
off_t entries_off;
u32 hdr_entries_bytes_n;
u32 le_crc32;
entries_off = hdr->entries_lba * blk_dev.logical_blk_bytes_n;
r = lseek(blk_dev.fd, entries_off, SEEK_SET);
if (r != entries_off) {
warning_pf("%s:unable to seek the device to the, %s, GPT header\n", blk_dev.path, hdr->str);
return;
}
hdr_entries_bytes_n = hdr->entry_bytes_n * hdr->entries_n;
hdr->entries = calloc(1, (size_t)(hdr_entries_bytes_n));
if(!read_full(blk_dev.fd, hdr->entries, hdr_entries_bytes_n)) {
warning_pf("%s:unable to read the, %s, entries\n", blk_dev.path, hdr->str);
goto free_entries;
}
le_crc32 = 0;
le_crc32_update(hdr->entries, hdr_entries_bytes_n, &le_crc32);
if (le_crc32 != hdr->entries_le_crc32) {
warning_pf("%s:%s entries have a bad crc32\n", blk_dev.path, hdr->str);
goto free_entries;
}
hdr->entries_valid = true;
return;
free_entries:
free(hdr->entries);
}
STATIC void build_data_from_previous_gpt(void)
{
struct hdr_t *hdr;
u32 entry_idx;
u32 entries_bytes_n;
/* first, lookup for valid data */
if (hdrs.previous.primary.valid && hdrs.previous.primary.entries_valid)
hdr = &hdrs.previous.primary;
else if (hdrs.previous.secondary.valid
&& hdrs.previous.secondary.entries_valid)
hdr = &hdrs.previous.secondary;
else
return; /* no valid data to build from */
/* header */
memcpy(&hdrs.disk_guid, &hdr->disk_guid, sizeof(hdrs.disk_guid));
hdrs.bytes_n = hdr->bytes_n;
/* entries */
entries = calloc(hdr->entries_n, hdr->entry_bytes_n);
entries_n = hdr->entries_n;
entry_bytes_n = hdr->entry_bytes_n;
entries_bytes_n = hdr->entries_n * hdr->entry_bytes_n;
entries_lbas_n = entries_bytes_n / blk_dev.logical_blk_bytes_n
+ ((entries_bytes_n % blk_dev.logical_blk_bytes_n) == 0
? 0 : 1);
entry_idx = 0;
loop {
u8 *src_entry;
struct entry_t *dst_entry;
u64 *p64;
if (entry_idx == entries_n)
break;
src_entry = hdr->entries + entry_idx * hdr->entry_bytes_n;
dst_entry = &entries[entry_idx];
guid_read(&dst_entry->type, src_entry);
guid_read(&dst_entry->uniq, &src_entry[0x10]);
p64 = (u64*)&src_entry[0x20];
dst_entry->first = le64toh(*p64);
p64 = (u64*)&src_entry[0x28];
dst_entry->last = le64toh(*p64);
p64 = (u64*)&src_entry[0x30];
dst_entry->attrs = le64toh(*p64);
dst_entry->name = utf16_strdup((utf16*)&src_entry[0x38]);
++entry_idx;
}
}
STATIC void previous_gpt_load(void)
{
hdrs.previous.primary.read_from_lba = 1;
hdrs.previous.primary.str = "primary";
hdr_load(&hdrs.previous.primary);
hdr_validate(&hdrs.previous.primary);
if (hdrs.previous.primary.valid) {
entries_load(&hdrs.previous.primary);
if (hdrs.previous.primary.entries_valid)
return;
/* damn! we have a valid header but with invalid entries */
hdrs.previous.secondary.read_from_lba =
hdrs.previous.primary.alternate_hdr_lba;
hdrs.previous.secondary.str = "alternate from primary header";
} else { /* arg! try the device last lba */
hdrs.previous.secondary.read_from_lba = blk_dev.last_lba;
hdrs.previous.secondary.str = "from device last lba";
}
/*
* here, something went wrong with the primary header. 2 cases:
* - the primary header is ok but the entries are not, then we are
* trying the alternate header from the primary header data.
* - the primary header is nok, we are try to look for a secondary
* header from the last lba of the device.
*/
hdr_load(&hdrs.previous.secondary);
hdr_validate(&hdrs.previous.secondary);
if (hdrs.previous.secondary.valid)
entries_load(&hdrs.previous.secondary);
}
STATIC void hdrs_usable_lbas_compute(void)
{
/* protective mbr, hdr, entries */
hdrs.first_usable_lba = 0 + 2 + entries_lbas_n;
/* hdr, entries */
hdrs.last_usable_lba = blk_dev.last_lba - 1 - entries_lbas_n;
}
STATIC void entries_reset(void)
{
u32 i;
utf16 zero;
u32 entries_bytes_n;
if (entries_n != 0) {
i = 0;
loop { /* the utf16 names */
if (i == entries_n)
break;
free(entries[i].name);
++i;
}
free(entries);
}
/* actually 128 entries */
entries_n = ENTRIES_ARRAY_MIN_BYTES_N / ENTRY_BYTES_N;
entries = calloc(entries_n, ENTRY_BYTES_N);
entry_bytes_n = ENTRY_BYTES_N;
zero = 0;
i = 0;
loop {
if (i == entries_n)
break;
entries[i].name = utf16_strdup(&zero);
++i;
}
entries_bytes_n = entries_n * entry_bytes_n;
entries_lbas_n = entries_bytes_n / blk_dev.logical_blk_bytes_n
+ (entries_bytes_n % entry_bytes_n == 0 ? 0 : 1);
}
/******************************************************************************/
STATIC void out_guid(struct guid_t *guid)
{
u8 i;
out_pf("%08x-%04x-%04x-%04x-", guid->blk_0, guid->blk_1, guid->blk_2,
guid->blk_3);
i = 0;
loop {
if (i == 6)
break;
out_pf("%02x", guid->blk_4[i]);
++i;
}
}
struct {
struct guid_t guid;
utf8 *name;
} types[] = {
[0].guid.blk_0 = 0xc12a7328,
[0].guid.blk_1 = 0xf81f,
[0].guid.blk_2 = 0x11d2,
[0].guid.blk_3 = 0xba4b,
[0].guid.blk_4 = {0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b},
[0].name = "EFI system",
[1].guid.blk_0 = 0x4f68bce3,
[1].guid.blk_1 = 0xe8cd,
[1].guid.blk_2 = 0x4db1,
[1].guid.blk_3 = 0x96e7,
[1].guid.blk_4 = {0xfb, 0xca, 0xf9, 0x84, 0xb7, 0x09},
[1].name = "linux root x86_64",
[2].guid.blk_0 = 0x0657fd6d,
[2].guid.blk_1 = 0xa4ab,
[2].guid.blk_2 = 0x43c4,
[2].guid.blk_3 = 0x84e5,
[2].guid.blk_4 = {0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f},
[2].name = "linux swap",
};
STATIC void types_show(void)
{
u32 i;
u32 n;
n = ARRAY_N(types);
i = 0;
loop {
if (i == n)
break;
out_pf("%"PRIu32": ", i + 1);
out_guid(&types[i].guid);
out_pf(" %s\n", types[i].name);
++i;
}
}
STATIC utf8 *type_guids_lookup_name(struct guid_t *guid)
{
u32 i;
u32 n;
n = ARRAY_N(types);
i = 0;
loop {
if (i == n)
break;
if (memcmp(guid, &types[i].guid, sizeof(*guid)) == 0)
return types[i].name;
++i;
}
return "unknown";
}
STATIC void entry_show(u32 idx, struct entry_t *entry)
{
utf16 *pc;
utf8 *type_str;
out_pf("%"PRIu32": %"PRIu64" %"PRIu64" ", idx + 1, entry->first, entry->last);
out_guid(&entry->type);
type_str = type_guids_lookup_name(&entry->type);
out_pf("(%s)", type_str);
out_pf("\n ");
out_guid(&entry->uniq);
out_pf(" ");
out_pf("0x%"PRIx64" \"", entry->attrs);
pc = entry->name;
loop {
u16 c;
c = le16toh(*pc);
if (c == 0)
break;
/* keep it simple */
if ((u16)'0' <= c && c <= (u16)'9'
&& (u16)'A' <= c && c <= (u16)'Z'
&& (u16)'a' <= c && c <= (u16)'z')
out_pf("%c", c);
else
out_pf("�");
++pc;
}
out_pf("\"\n");
}
STATIC bool guid_is_zero(struct guid_t *guid)
{
u8 *b;
u8 *last;
b = (u8*)guid;
last = b + sizeof(*guid) - 1;
loop {
if (*b != 0)
return false;
if (b == last)
return true;
++b;
}
}
STATIC bool entry_is_used(u32 idx)
{
struct entry_t *entry;
entry = &entries[idx];
if ( guid_is_zero(&entry->type)
&& guid_is_zero(&entry->uniq)
&& entry->first == 0
&& entry->last == 0
&& entry->attrs == 0
&& entry->name[0] == 0) {
return false;
}
return true;
}
STATIC void entry_delete(u32 idx)
{
struct entry_t *entry;
utf16 empty_str;
empty_str = 0;
entry = &entries[idx];
free(entry->name);
memset(entry, 0, sizeof(*entry));
entry->name = utf16_strdup(&empty_str);
}
STATIC void entries_show(void)
{
u32 entry_idx;
if (entries_n == 0) {
out_pf("no partition entries\n");
return;
} else {
out_bold_pf("array of partition entries has %"PRIu32" slots:\n", entries_n);
}
entry_idx = 0;
out_bold_pf("slot / first / last / type(guid) \n");
out_bold_pf(" uniq_guid / attributes / name\n");
loop {
if (entry_idx == entries_n)
break;
if (entry_is_used(entry_idx))
entry_show(entry_idx, &entries[entry_idx]);
++entry_idx;
}
}
STATIC void main_menu_show(void)
{
out_pf("\
Command / Action\n\
p print the partition entries\n\
n new partition entry\n\
d delete a partition entry\n\
h print the header\n\
u change disk guid\n\
w write and quit\n\
q quit without saving\n");
}
STATIC u8 input_line[BUFSIZ];
STATIC void *(*input_state)(size_t lf);
/* list of states -- start */
STATIC void *state_main_menu(size_t lf);
STATIC void *state_delete_entry(size_t lf);
STATIC void *state_change_disk_guid(size_t lf);
STATIC void *state_edit_entry(size_t lf);
STATIC void *state_edit_entry_substate_first_lba(size_t lf);
STATIC void *state_edit_entry_substate_last_lba(size_t lf);
STATIC void *state_edit_entry_substate_type(size_t lf);
STATIC void *state_edit_entry_substate_uniq_quid(size_t lf);
STATIC void *state_edit_entry_substate_attrs(size_t lf);
/* list of states -- end */
STATIC struct {
u32 num;
u64 first_lba_default;
u64 first_lba;
u64 last_lba_default;
u64 last_lba;
u32 type;
struct guid_t uniq;
u64 attrs;
} edit_entry;
/*{{{ prompts */
STATIC void edit_entry_attrs_prompt(void)
{
out_pf("attributes as an hexadecimal number (default to 0x0): ");
}
STATIC void edit_entry_uniq_guid_prompt(void)
{
out_pf("uniq guid (r to randomize): ");
}
STATIC void edit_entry_type_prompt(void)
{
out_pf("number of the type of partition (l for the list): ");
}
STATIC void main_menu_prompt(void)
{
out_pf("command (m for help): ");
}
STATIC void delete_entry_prompt(void)
{
out_pf("number of the partition entry to delete: ");
}
STATIC void edit_entry_last_lba_prompt(void)
{
out_pf("last logical block lba (default to %"PRIu64"): ",
edit_entry.last_lba_default);
}
STATIC void edit_entry_prompt(void)
{
out_pf("partition entry number (1-%"PRIu32"): ", entries_n);
}
STATIC void edit_entry_first_lba_prompt(void)
{
out_pf("first logical block lba (default to %"PRIu64"): ",
edit_entry.first_lba_default);
}
STATIC void change_disk_guid_prompt(void)
{
out_pf("current disk guid is ");
out_guid(&hdrs.disk_guid);
out_pf("\nnew one (r to randomize): ");
}
/*}}} prompts -- end */
STATIC void first_lba_default_select(void)
{
u32 idx;
/* mbr, header, entries. Should not need to cap this */
edit_entry.first_lba_default = hdrs.first_usable_lba;
if (edit_entry.num == 1)
return;
/* num >= 2, idx >= 1 */
idx = edit_entry.num - 2;
/*
* we are going "backward" to locate the right before used entry, then
* we will use the last lba from this entry to compute the first
* lba for this one
*/
loop {
if (entry_is_used(idx)) {
edit_entry.first_lba_default = entries[idx].last + 1;
/* hard capping */
if (edit_entry.first_lba_default < hdrs.first_usable_lba)
edit_entry.first_lba_default = hdrs.first_usable_lba;
if (edit_entry.first_lba_default > hdrs.last_usable_lba)
edit_entry.first_lba_default = hdrs.last_usable_lba;
break;
}
if (idx == 0)
break;
idx--;
}
}
STATIC void last_lba_default_select(void)
{
u32 idx;
edit_entry.last_lba_default = hdrs.last_usable_lba;
if (edit_entry.num == entries_n)
return;
/* num <= entries_n - 1, idx <= entries_n - 2 */
idx = edit_entry.num;
/*
* we are going forward to reach the first used entry and then use
* the lba right before its first lba.
*/
loop {
if (idx == entries_n)
break;
if (entry_is_used(idx)) {
edit_entry.last_lba_default = entries[idx].first - 1;
/* hard capping */
if (edit_entry.last_lba_default < hdrs.first_usable_lba)
edit_entry.last_lba_default = hdrs.first_usable_lba;
if (edit_entry.last_lba_default > hdrs.last_usable_lba)
edit_entry.last_lba_default = hdrs.last_usable_lba;
break;
}
++idx;
}
}
STATIC void *hdr_show(void)
{
out_pf("header:\ndisk guid ");
out_guid(&hdrs.disk_guid);
out_pf("\nfirst usable lba %"PRIu64"\n", hdrs.first_usable_lba);
out_pf("last usable lba %"PRIu64"\n", hdrs.last_usable_lba);
out_pf("%s last lba is %"PRIu64"\n", blk_dev.path, blk_dev.last_lba);
}
STATIC void guid_randomize(struct guid_t *guid)
{
int fd;
fd = open("/dev/urandom", O_RDONLY);
if (fd == -1) {
warning_pf("unable to open /dev/urandom for guid randomization\n");
return;
}
if (!read_full(fd, guid, sizeof(*guid)))
warning_pf("something went wrong while reading from /dev/urandom, the guid is probably corrupted\n");
close(fd);
}
/* best effort */
STATIC void guid_read_from_input(struct guid_t *guid, size_t lf)
{
u8 *blk_start;
u8 *blk_end;
u8 save;
u8 *end;
input_line[lf] = 0;
end = input_line + lf;
blk_start = input_line;
blk_end = strchr(blk_start, '-');
if (blk_end == 0)
return;
*blk_end = 0;
guid->blk_0 = (u32)strtou64(blk_start, 0, 16);
blk_start = blk_end + 1;
if (blk_start >= end)
return;
blk_end = strchr(blk_start, '-');
if (blk_end == 0)
return;
*blk_end = 0;
guid->blk_1 = (u16)strtou64(blk_start, 0, 16);
blk_start = blk_end + 1;
if (blk_start >= end)
return;
blk_end = strchr(blk_start, '-');
if (blk_end == 0)
return;
*blk_end = 0;
guid->blk_2 = (u16)strtou64(blk_start, 0, 16);
blk_start = blk_end + 1;
if (blk_start >= end)
return;
blk_end = strchr(blk_start, '-');
if (blk_end == 0)
return;
*blk_end = 0;
guid->blk_3 = (u16)strtou64(blk_start, 0, 16);
/* blk_4 */
blk_start = blk_end + 1;
if (blk_start >= end)
return;
blk_end = blk_start + 12; /* 6 bytes */
if (blk_end > end)
return;
save = blk_start[2];
blk_start[2] = 0;
guid->blk_4[0] = (u8)strtou64(&blk_start[0], 0, 16);
blk_start[2] = save;
save = blk_start[4];
blk_start[4] = 0;
guid->blk_4[1] = (u8)strtou64(&blk_start[2], 0, 16);
blk_start[4] = save;
save = blk_start[6];
blk_start[6] = 0;
guid->blk_4[2] = (u8)strtou64(&blk_start[4], 0, 16);
blk_start[6] = save;
save = blk_start[8];
blk_start[8] = 0;
guid->blk_4[3] = (u8)strtou64(&blk_start[6], 0, 16);
blk_start[8] = save;
save = blk_start[10];
blk_start[10] = 0;
guid->blk_4[4] = (u8)strtou64(&blk_start[8], 0, 16);
blk_start[10] = save;
guid->blk_4[5] = (u8)strtou64(&blk_start[10], 0, 16);
}
STATIC void *edit_entry_to_entries(void)
{
struct entry_t *dst_entry;
dst_entry = &entries[edit_entry.num - 1];
memcpy(&dst_entry->type, &types[edit_entry.type - 1].guid,
sizeof(dst_entry->type));
memcpy(&dst_entry->uniq, &edit_entry.uniq, sizeof(dst_entry->uniq));
dst_entry->first = edit_entry.first_lba;
dst_entry->last = edit_entry.last_lba;
dst_entry->attrs = edit_entry.attrs;
/* don't support name input yet */
}
STATIC void gpt_write(void)
{
entries_serialized_array_gen();
/* crc32 on exactly the entries array size, not lba bounded */
entries_serialized_array_crc32 = 0;
le_crc32_update(entries_serialized_array, entry_bytes_n * entries_n,
&entries_serialized_array_crc32);
hdr_primary_serialized_gen();
hdr_secondary_serialized_gen();
protective_mbr_gen();
protective_mbr_write();
hdr_primary_serialized_write();
hdr_secondary_serialized_write();
entries_serialized_array_primary_write();
entries_serialized_array_secondary_write();
}
/*{{{ states */
STATIC void *state_edit_entry_substate_attrs(size_t lf)
{
if (lf == 0) {
edit_entry.attrs = 0;
goto exit;
}
input_line[lf] = 0;
edit_entry.attrs = strtou64(input_line, 0, 16);
exit:
/*
* TODO: don't want to use iconv, will implement an utf8 to utf16
* converter another time.
*/
edit_entry_to_entries();
main_menu_prompt();
return state_main_menu;
}
STATIC void *state_edit_entry_substate_uniq_quid(size_t lf)
{
if (lf == 1 && input_line[0] == 'r') {
guid_randomize(&edit_entry.uniq);
out_pf("generated guid is : ");
goto exit;
}
guid_read_from_input(&edit_entry.uniq, lf);
out_pf("read guid is : ");
exit:
out_guid(&edit_entry.uniq);
out_pf("\n");
edit_entry_attrs_prompt();
return state_edit_entry_substate_attrs;
}
STATIC void *state_edit_entry_substate_type(size_t lf)
{
if (lf == 1 && input_line[0] == 'l') {
types_show();
edit_entry_type_prompt();
return state_edit_entry_substate_type;
}
input_line[lf] = 0;
edit_entry.type = (u32)strtou64(input_line, 0, 10);
if (edit_entry.type == 0 || edit_entry.type > ARRAY_N(types)) {
warning_pf("invalid type number\n");
edit_entry_type_prompt();
return state_edit_entry_substate_type;
}
edit_entry_uniq_guid_prompt();
return state_edit_entry_substate_uniq_quid;
}
STATIC void *state_edit_entry_substate_last_lba(size_t lf)
{
if (lf == 0)
edit_entry.last_lba = edit_entry.last_lba_default;
else {
input_line[lf] = 0;
edit_entry.last_lba = strtou64(input_line, 0, 10);
}
edit_entry_type_prompt();
return state_edit_entry_substate_type;
}
STATIC void *state_edit_entry_substate_first_lba(size_t lf)
{
if (lf == 0) {
edit_entry.first_lba = edit_entry.first_lba_default;
} else {
input_line[lf] = 0;
edit_entry.first_lba = strtou64(input_line, 0, 10);
}
last_lba_default_select();
edit_entry_last_lba_prompt();
return state_edit_entry_substate_last_lba;
}
STATIC void *state_edit_entry(size_t lf)
{
input_line[lf] = 0;
edit_entry.num = (u32)strtou64(input_line, 0, 10);
if (1 <= edit_entry.num && edit_entry.num <= entries_n) {
first_lba_default_select();
edit_entry_first_lba_prompt();
return state_edit_entry_substate_first_lba;
} else
warning_pf("%"PRIu32" is an invalid partition entry number\n", edit_entry.num);
main_menu_prompt();
return state_main_menu;
}
STATIC void *state_delete_entry(size_t lf)
{
u8 *end;
u32 entry_num;
input_line[lf] = 0;
entry_num = (u32)strtou64(input_line, 0, 10);
if (1 <= entry_num && entry_num <= entries_n) {
out_bold_pf("deleting partition entry %"PRIu64"\n", entry_num);
entry_delete((u32)(entry_num - 1));
} else
out_bold_pf("partition number out of range %"PRIu64"\n", entry_num);
main_menu_prompt();
return state_main_menu;
}
STATIC void *state_change_disk_guid(size_t lf)
{
if (lf == 1) {
if (input_line[0] == 'r') {
guid_randomize(&hdrs.disk_guid);
out_pf("randomized disk guid is ");
}
} else {
guid_read_from_input(&hdrs.disk_guid, lf);
out_pf("read disk guid is ");
}
out_guid(&hdrs.disk_guid);
out_pf("\n");
main_menu_prompt();
return state_main_menu;
}
STATIC void *state_main_menu(size_t lf)
{
if (lf == 1) {
switch(input_line[0]) {
case 'q':
out_pf("quit\n");
exit(0);
case 'm':
main_menu_show();
break;
case 'p':
entries_show();
break;
case 'h':
hdr_show();
break;
case 'd':
delete_entry_prompt();
return state_delete_entry;
case 'n':
edit_entry_prompt();
return state_edit_entry;
case 'u':
change_disk_guid_prompt();
return state_change_disk_guid;
case 'w':
gpt_write();
exit(0);
default:
break;
}
}
main_menu_prompt();
return state_main_menu;
}
/*}}} states -- end */
/* end is the index of the byte past the last read byte */
STATIC size_t input_line_consume(size_t lf, size_t end)
{
input_state = input_state(lf);
/* update the input line buffer and offsets */
if (lf == (BUFSIZ - 1)) { /* lf is the last char in our line buffer */
memset(input_line, 0, BUFSIZ);
return 0;
}
memmove(input_line, input_line + lf + 1, end - (lf + 1));
return end - (lf + 1);
}
STATIC void input_line_loop(void)
{
bool discarding;
size_t end;
end = 0;
memset(input_line, 0, BUFSIZ);
discarding = false;
input_state = state_main_menu;
main_menu_prompt();
loop {
int r;
size_t lf;
errno = 0;
r = read(0, &input_line[end], BUFSIZ - end);
if (r == -1) {
if (errno == EINTR)
continue;
error_pf("error reading input line from standard input\n");
}
lf = end;
end += (size_t)r;
loop { /* scan what was read for lf */
if (lf == end)
break;
if (input_line[lf] == '\n') {
if (discarding) {
discarding = false;
end = 0;
break;
}
end = input_line_consume(lf, end);
break;
}
++lf;
}
if (end == BUFSIZ) {
if (!discarding) {
out_pf("your input line is too long, discarding...\n");
discarding = true;
}
end = 0;
}
}
}
STATIC void init_once(void)
{
le_crc32_tbl_gen();
colors_on = true;
load_previous_gpt = true;
entries = 0;
entries_n = 0;
memset(&hdrs, 0, sizeof(hdrs));
}
STATIC void usage(void)
{
BOLD;
out_pf("\
nyangpt: line input oriented minimal GPT partition creator\n\
\n\
usage:\n\
nyangpt [-nc] [-nl] [-h|--help][ [--] device_path\n\
\n\
-nc: disable ANSI terminal colors\n\
-nl: don't load previous GPT partitions\n\
-h|--help: this summary\n");
RESTORE;
exit(0);
}
STATIC void options_parse(u32 argc, utf8 **argv)
{
u32 arg;
bool no_more_options;
bool print_usage;
if (argc == 1)
usage();
arg = 1;
no_more_options = false;
print_usage = false;
blk_dev.path = 0;
loop {
if (arg == argc)
break;
if (no_more_options) {
if (blk_dev.path == 0)
blk_dev.path = argv[arg];
} else if (strcmp(argv[arg], "--") == 0) {
no_more_options = true;
} else if (strcmp(argv[arg], "-nc") == 0) {
colors_on = false;
} else if (strcmp(argv[arg], "-nl") == 0) {
load_previous_gpt = false;
} else if (strcmp(argv[arg], "-h") == 0
|| strcmp(argv[arg], "--help") == 0) {
print_usage = true;
} else {
if (blk_dev.path == 0)
blk_dev.path = argv[arg];
}
++arg;
}
if (print_usage || blk_dev.path == 0)
usage();
}
int nyangpt_main(int argc, utf8 **argv)
{
u64 whole_dev_bytes_n;
u64 entries_bytes_n;
init_once();
options_parse(argc, argv);
out_pf("block device path is %s, opening...\n", blk_dev.path);
blk_dev.fd = open(blk_dev.path, O_RDWR | O_SYNC);
if (blk_dev.fd == -1)
error_pf("%s:unable to open\n", blk_dev.path);
out_pf("%s:opened\n", blk_dev.path);
sysfs_infos_get();
whole_dev_bytes_n = blk_dev.sz_512_n * 512;
if ((whole_dev_bytes_n % blk_dev.logical_blk_bytes_n) != 0)
error_pf("%s: the whole device size %"PRIu64" is not a multiple of the logical block size %"PRIu64" bytes\n", whole_dev_bytes_n, blk_dev.logical_blk_bytes_n);
/* the total number of lba, -1 to get the offset of the last one */
blk_dev.last_lba = blk_dev.sz_512_n * 512 / blk_dev.logical_blk_bytes_n - 1;
out_pf("%s:last lba is %"PRIu64"\n", blk_dev.path, blk_dev.last_lba);
if (load_previous_gpt) {
previous_gpt_load();
build_data_from_previous_gpt();
}
/*
* Resizing the array of entries is expensive and dangerous, try to
* keep it constant as much as possible.
* If it was not initialized based from the previous gpt, init one
* with sane defaults.
*/
if (entries_n == 0)
entries_reset();
if (hdrs.bytes_n == 0)
hdrs.bytes_n = HDR_BYTES_N;
if (guid_is_zero(&hdrs.disk_guid))
guid_randomize(&hdrs.disk_guid);
/*
* Once we have "an array of entries", the first and last usable
* lbas must be recomputed.
* We ignore the values from the previous gpt headers, we prefer
* to recompute them.
*/
hdrs_usable_lbas_compute();
input_line_loop();
return 0;
}
/*{{{ preprocessor space cleanup */
#undef ARRAY_N
#undef BLK_0
#undef BLK_1
#undef BLK_2
#undef BLK_3
#undef BLK_4
#undef BOLD_RED
#undef BOLD_ORANGE
#undef BOLD
#undef RESTORE
#undef BOOT_SIGNATURE_0X55
#undef BOOT_SIGNATURE_0XAA
#undef ENTRIES_ARRAY_MIN_BYTES_N
#undef ENTRY_BYTES_N
#undef HDR_BYTES_N
#undef HDR_REVISION
#undef HDR_SIGNATURE
#undef HDR_SIGNATURE_BYTES_N
#undef loop
#undef PART_0
#undef s32
#undef STATIC
#undef strtou64
#undef u8
#undef u16
#undef u32
#undef u64
#undef utf16
#undef utf8
#undef X64_UTF8_BYTES_MAX
/*}}} preprocessor space cleanup -- end */
/*{{{ namespace cleanup ------------------------------------------------------*/
#undef blk_dev
#undef build_data_from_previous_gpt
#undef change_disk_guid_prompt
#undef colors_on
#undef delete_entry_prompt
#undef edit_entry
#undef edit_entry_attrs_prompt
#undef edit_entry_first_lba_prompt
#undef edit_entry_last_lba_prompt
#undef edit_entry_prompt
#undef edit_entry_to_entries
#undef edit_entry_type_prompt
#undef edit_entry_uniq_guid_prompt
#undef entries
#undef entries_lbas_n
#undef entries_load
#undef entries_n
#undef entries_reset
#undef entries_serialized_array
#undef entries_serialized_array_crc32
#undef entries_serialized_array_gen
#undef entries_serialized_array_gen_entry
#undef entries_serialized_array_primary_write
#undef entries_serialized_array_secondary_write
#undef entries_show
#undef entry_bytes_n
#undef entry_delete
#undef entry_is_used
#undef entry_show
#undef entry_t
#undef error_pf
#undef first_lba_default_select
#undef gpt_write
#undef guid_is_zero
#undef guid_t
#undef guid_randomize
#undef guid_read
#undef guid_read_from_input
#undef guid_write
#undef hdr_load
#undef hdr_show
#undef hdr_primary_serialized
#undef hdr_primary_serialized_gen
#undef hdr_primary_serialized_write
#undef hdr_secondary_serialized
#undef hdr_secondary_serialized_gen
#undef hdr_secondary_serialized_write
#undef hdr_t
#undef hdr_validate
#undef hdrs
#undef hdrs_usable_lbas_compute
#undef init_once
#undef input_line
#undef input_line_consume
#undef input_line_loop
#undef input_state
#undef last_lba_default_select
#undef le_crc32_tbl_gen
#undef le_crc32_update
#undef load_previous_gpt
#undef main_menu_prompt
#undef main_menu_show
#undef options_parse
#undef out_bold_pf
#undef out_guid
#undef out_pf
#undef gpt_load
#undef protective_mbr
#undef protective_mbr_gen
#undef protective_mbr_write
#undef read_full
#undef state_change_disk_guid
#undef state_delete_entry
#undef state_edit_entry
#undef state_edit_entry_substate_attrs
#undef state_edit_entry_substate_first_lba
#undef state_edit_entry_substate_last_lba
#undef state_edit_entry_substate_type
#undef state_edit_entry_substate_uniq_quid
#undef state_main_menu
#undef sysfs_infos_get
#undef type_guids_lookup_name
#undef types
#undef types_show
#undef usage
#undef utf16_strdup
#undef warning_pf
#undef write_full
/*----------------------------------------------------------------------------*/
#undef nyangpt_main
/*}}} namespace cleanup -- end -----------------------------------------------*/
#endif