Initial commit
[nyangpt.git] / nyangpt.c
blob7cfb258d89e3ca4762b113faddbe285d334beae5
1 #ifndef NYANGPT_C
2 #define NYANGPT_C
3 /*
4 * Copyright 2021 Sylvain BERTRAND <sylvain.bertrand@legeek.net>
5 * LICENSE: GNU AFFERO GPLV3
6 * (NO WARANTY OF ANY KIND AND BLAHBLAHBLAH)
7 */
9 /* quick and dirty, minimal, line input oriented uefi gpt partition creator */
12 * TODO:
13 * - implement ANSI terminal pagination (listing of entries and types)
14 * - utf8 to utf16 converter for names (no iconv)
16 #include <stdarg.h>
17 #include <stdbool.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <stdint.h>
25 #include <inttypes.h>
26 #include <endian.h>
27 #include <errno.h>
29 * how to get pertinent device information from sysfs for gpt partitioning
31 * for a /dev/sdX block device:
32 * /sys/block/sdX/size = size of the device in blocks of 512 bytes (hardcoded)
33 * /sys/block/sdX/queue/logical_block_size = size in bytes of a logical block
34 * which is the size used by lba (Logical Block Access) offsets used
35 * in gpt partitions
36 * /sys/block/sdX/queue/physical_block_size = size in bytes of a physical block
38 /*{{{ global preprocessor definitions */
39 #define STATIC static
40 #define utf8 uint8_t
41 #define utf16 uint16_t
42 #define X64_UTF8_BYTES_MAX (sizeof("18446744073709551615") - 1)
43 /* meh... */
44 #define strtou64 strtoul
45 #define u8 uint8_t
46 #define u16 uint16_t
47 #define u32 uint32_t
48 #define s32 int32_t
49 #define u64 uint64_t
50 #define loop for(;;)
51 #define ARRAY_N(x) (sizeof(x) / sizeof(x[0]))
52 /*}}} global preprocessor definitions -- end */
53 /*{{{ nyangpt namespace */
54 #define blk_dev nyangpt_blk_dev
55 #define build_data_from_previous_gpt nyangpt_data_from_previous_gpt
56 #define change_disk_guid_prompt nyangpt_change_disk_guid_prompt
57 #define colors_on nyangpt_colors_on
58 #define delete_entry_prompt nyangpt_delete_entry_prompt
59 #define edit_entry nyangpt_edit_entry
60 #define edit_entry_attrs_prompt nyangpt_edit_entry_attrs_prompt
61 #define edit_entry_first_lba_prompt nyangpt_edit_entry_first_lba_prompt
62 #define edit_entry_last_lba_prompt nyangpt_edit_entry_last_lba_prompt
63 #define edit_entry_prompt nyangpt_edit_entry_prompt
64 #define edit_entry_to_entries nyangpt_edit_entry_to_entries
65 #define edit_entry_type_prompt nyangpt_edit_entry_type_prompt
66 #define edit_entry_uniq_guid_prompt nyangpt_edit_entry_uniq_guid_prompt
67 #define entries nyangpt_entries
68 #define entries_lbas_n nyangpt_entries_lbas_n
69 #define entries_load nyangpt_entries_load
70 #define entries_n nyangpt_entries_n
71 #define entries_reset nyangpt_entries_reset
72 #define entries_serialized_array nyangpt_entries_serialized_array
73 #define entries_serialized_array_crc32 nyangpt_entries_serialized_array_crc32
74 #define entries_serialized_array_gen nyangpt_entries_serialized_array_gen
75 #define entries_serialized_array_gen_entry nyangpt_entries_serialized_array_gen_entry
76 #define entries_serialized_array_primary_write nyangpt_entries_serialized_array_primary_write
77 #define entries_serialized_array_secondary_write nyangpt_entries_serialized_array_secondary_write
78 #define entries_show nyangpt_entries_show
79 #define entry_bytes_n nyangpt_entry_bytes_n
80 #define entry_delete nyangpt_entry_delete
81 #define entry_is_used nyangpt_entry_is_used
82 #define entry_show nyangpt_entry_show
83 #define entry_t nyangpt_entry_t
84 #define error_pf nyangpt_error_pf
85 #define first_lba_default_select nyangpt_first_lba_default_select
86 #define gpt_write nyangpt_gpt_write
87 #define guid_is_zero nyangpt_guid_is_zero
88 #define guid_t nyangpt_guid_t
89 #define guid_randomize nyangpt_guid_randomize
90 #define guid_read nyangpt_guid_read
91 #define guid_read_from_input nyangph_guid_read_from_input
92 #define guid_write nyangpt_guid_write
93 #define hdr_load nyangpt_hdr_load
94 #define hdr_show nyangpt_hdr_show
95 #define hdr_primary_serialized nyangpt_hdr_primary_serialized
96 #define hdr_primary_serialized_gen nyangpt_hdr_primary_serialized_gen
97 #define hdr_primary_serialized_write nyangpt_hdr_primary_serialized_write
98 #define hdr_secondary_serialized nyangpt_hdr_secondary_serialized
99 #define hdr_secondary_serialized_gen nyangpt_hdr_secondary_serialized_gen
100 #define hdr_secondary_serialized_write nyangpt_hdr_secondary_serialized_write
101 #define hdr_t nyangpt_hdr_t
102 #define hdr_validate nyangpt_hdr_validate
103 #define hdrs nyangpt_hdrs
104 #define hdrs_usable_lbas_compute nyangpt_hdrs_usable_lbas_compute
105 #define init_once nyangpt_init_once
106 #define input_line nyangpt_input_line
107 #define input_line_consume nyangpt_input_line_consume
108 #define input_line_loop nyangpt_input_line_loop
109 #define input_state nyangpt_input_state
110 #define last_lba_default_select nyangpt_last_lba_default_select
111 #define le_crc32_for_byte nyangpt_le_crc32_for_byte
112 #define le_crc32_tbl nyangpt_le_crc32_tbl
113 #define le_crc32_tbl_gen nyangpt_le_crc32_tbl_gen
114 #define le_crc32_update nyangpt_le_crc32_update
115 #define load_previous_gpt nyangpt_load_previous_gpt
116 #define main_menu_prompt nyangpt_main_menu_prompt
117 #define main_menu_show nyangpt_main_menu_show
118 #define options_parse nyangpt_options_parse
119 #define out_bold_pf nyangpt_out_bold_pf
120 #define out_guid nyangpt_out_guid
121 #define out_pf nyangpt_out_pf
122 #define pf nyangpt_pf
123 #define previous_gpt_load nyangpt_previous_gpt_load
124 #define protective_mbr nyangpt_protective_mbr
125 #define protective_mbr_gen nyangpt_protective_mbr_gen
126 #define protective_mbr_write nyangpt_protective_mbr_write
127 #define read_full nyangpt_read_full
128 #define state_change_disk_guid nyangpt_state_change_disk_guid
129 #define state_delete_entry nyangpt_state_delete_entry
130 #define state_edit_entry nyangpt_state_edit_entry
131 #define state_edit_entry_substate_attrs nyangpt_state_edit_entry_substate_attrs
132 #define state_edit_entry_substate_first_lba nyangpt_state_edit_entry_substate_first_lba
133 #define state_edit_entry_substate_last_lba nyangpt_state_edit_entry_substate_last_lba
134 #define state_edit_entry_substate_type nyangpt_state_edit_entry_substate_type
135 #define state_edit_entry_substate_uniq_quid nyangpt_state_edit_entry_substate_uniq_quid
136 #define state_main_menu nyangpt_state_main_menu
137 #define sysfs_infos_get nyangpt_sysfs_infos_get
138 #define type_guids_lookup_name nyangpt_type_guids_lookup_name
139 #define types nyangpt_types
140 #define types_show nyangpt_types_show
141 #define usage nyangpt_usage
142 #define utf16_strdup nyangpt_utf16_strdup
143 #define warning_pf nyangpt_warning_pf
144 #define write_full nyangpt_write_full
145 /*----------------------------------------------------------------------------*/
146 #define nyangpt_main main
147 /*}}} nyangpt namespace -- end -----------------------------------------------*/
148 /*{{{ crc32b little endian */
149 /* stolen and cosmetized, not validated on big endian */
150 /* http://home.thep.lu.se/~bjorn/crc/ */
151 STATIC u32 le_crc32_for_byte(u32 r)
153 u8 j;
155 j = 0;
156 loop {
157 if (j == 8)
158 break;
159 r = (r & 1 ? 0 : (u32)0xedb88320) ^ r >> 1;
160 ++j;
162 return r ^ (u32)0xff000000;
164 STATIC u32 le_crc32_tbl[0x100];
165 STATIC void le_crc32_tbl_gen(void)
167 u32 i;
169 i = 0;
170 loop {
171 if (i == 0x100)
172 break;
173 le_crc32_tbl[i] = le_crc32_for_byte(i);
174 ++i;
177 STATIC void le_crc32_update(void *data, u64 bytes_n, u32* crc)
179 u64 i;
181 i = 0;
182 loop {
183 if (i == bytes_n)
184 break;
185 *crc = le_crc32_tbl[(u8)*crc ^ ((u8*)data)[i]] ^ *crc >> 8;
186 ++i;
189 /*}}} crc32b little endian -- end *********************************************/
190 /*{{{ types */
191 struct guid_t {
192 u32 blk_0;
193 u16 blk_1;
194 u16 blk_2;
195 u16 blk_3;
196 u8 blk_4[6];
198 /* serialized offsets */
199 #define BLK_0 0x0
200 #define BLK_1 0x4
201 #define BLK_2 0x6
202 #define BLK_3 0x8
203 #define BLK_4 0xa
204 /*----------------------------------------------------------------------------*/
205 struct hdr_t {
206 bool valid;
207 utf8 *str;
208 u64 read_from_lba; /* _should_ be the same as the lba field */
210 u8 *data;
211 /* fields -- start */
212 /* signature */
213 /* revision */
214 u32 bytes_n;
215 u32 le_crc32;
216 u64 lba;
217 u64 alternate_hdr_lba;
218 /* first usable lba */
219 /* last usable lba */
220 struct guid_t disk_guid;
221 u64 entries_lba;
222 u32 entries_n;
223 u32 entry_bytes_n;
224 u32 entries_le_crc32;
225 /* fields -- end */
227 bool entries_valid;
228 u8 *entries;
230 /*----------------------------------------------------------------------------*/
231 struct entry_t {
232 struct guid_t type;
233 struct guid_t uniq;
234 u64 first;
235 u64 last;
236 u64 attrs;
237 utf16 *name;
239 #define ENTRIES_ARRAY_MIN_BYTES_N 16384 /* specs: minimum to book on the disk */
240 #define ENTRY_BYTES_N 0x80 /* specs */
241 /*}}} types -- end */
242 STATIC bool colors_on;
243 STATIC bool load_previous_gpt;
244 /*----------------------------------------------------------------------------*/
245 STATIC struct {
246 utf8 *path;
247 u64 sz_512_n;
248 u64 logical_blk_bytes_n;
249 u64 physical_blk_bytes_n;
250 int fd;
251 u64 last_lba;
252 } blk_dev;
253 /*----------------------------------------------------------------------------*/
254 STATIC u8 *protective_mbr;
255 /* serialized offsets */
256 #define BOOT_SIGNATURE_0X55 0x1fe
257 #define BOOT_SIGNATURE_0XAA 0x1ff
258 #define PART_0 0x1be
259 /*----------------------------------------------------------------------------*/
260 STATIC struct { /* there are 2 headers, each must fit in a logical block */
261 u64 first_usable_lba;
262 u64 last_usable_lba;
263 struct guid_t disk_guid;
264 u32 bytes_n; /* the mitigated hdr size */
266 struct {
267 struct hdr_t primary;
268 struct hdr_t secondary;
269 } previous;
270 } hdrs;
271 #define HDR_SIGNATURE "EFI PART"
272 #define HDR_SIGNATURE_BYTES_N 8
273 #define HDR_REVISION 0x00010000 /* 1.0 */
274 #define HDR_BYTES_N 0x5c /* specs: 92 bytes */
275 STATIC u8 *hdr_primary_serialized;
276 STATIC u8 *hdr_secondary_serialized;
277 /*----------------------------------------------------------------------------*/
278 STATIC struct entry_t *entries;
279 STATIC u32 entries_n;
280 STATIC u32 entry_bytes_n;
281 STATIC u32 entries_lbas_n;
282 STATIC u8 *entries_serialized_array;
283 STATIC u32 entries_serialized_array_crc32;
284 /*----------------------------------------------------------------------------*/
285 STATIC utf8 *pf(utf8 *fmt,...)
287 va_list ap;
288 int r;
289 utf8 *r_str;
291 va_start(ap, fmt);
292 r = vsnprintf(0, 0, fmt, ap);
293 va_end(ap);
295 r_str = malloc(r + 1); /* we want a terminating 0 */
297 va_start(ap, fmt);
298 vsnprintf(r_str, r + 1, fmt, ap); /* has room for the terminating 0 */
299 va_end(ap);
300 return r_str;
302 #define BOLD_RED if (colors_on) dprintf(1,"\x1b[38;2;255;0;0m\x1b[1m")
303 #define BOLD_ORANGE if (colors_on) dprintf(1,"\x1b[38;2;255;140;0m\x1b[1m")
304 #define BOLD if (colors_on) dprintf(1,"\x1b[1m");
305 #define RESTORE if (colors_on) dprintf(1, "\x1b[m")
306 STATIC void error_pf(utf8 *fmt,...)
308 va_list ap;
310 BOLD_RED;
311 dprintf(1, "ERROR:");
312 va_start(ap, fmt);
313 vdprintf(1, fmt, ap);
314 va_end(ap);
315 RESTORE;
316 exit(1);
318 STATIC void warning_pf(utf8 *fmt,...)
320 va_list ap;
322 BOLD_ORANGE;
323 dprintf(1, "WARNING:");
324 va_start(ap, fmt);
325 vdprintf(1, fmt, ap);
326 va_end(ap);
327 RESTORE;
329 STATIC void out_pf(utf8 *fmt,...)
331 va_list ap;
333 va_start(ap, fmt);
334 vdprintf(1, fmt, ap);
335 va_end(ap);
337 STATIC void out_bold_pf(utf8 *fmt,...)
339 va_list ap;
341 BOLD;
342 va_start(ap, fmt);
343 vdprintf(1, fmt, ap);
344 va_end(ap);
345 RESTORE;
347 STATIC bool read_full(int fd, void *dest, u64 bytes_n)
349 u64 read_bytes_n;
351 read_bytes_n = 0;
352 loop {
353 ssize_t r;
355 errno = 0;
356 r = read(fd, dest + read_bytes_n,
357 (size_t)(bytes_n - read_bytes_n));
358 if (r == -1) {
359 if (errno == EINTR)
360 continue;
361 return false;
363 read_bytes_n += (u64)r;
364 if (read_bytes_n == bytes_n)
365 return true;
367 /* unreachable */
369 STATIC bool write_full(int fd, void *src, u64 bytes_n)
371 u64 written_bytes_n;
373 /* short writes */
374 written_bytes_n = 0;
375 loop {
376 ssize_t r;
378 errno = 0;
379 r = write(blk_dev.fd, src + written_bytes_n,
380 (size_t)(bytes_n - written_bytes_n));
381 if (r == -1) {
382 if (errno == EINTR)
383 continue;
384 return false;
386 written_bytes_n += (u64)r;
387 if (written_bytes_n == bytes_n)
388 return true;
390 /* unreachable */
392 STATIC utf16 *utf16_strdup(utf16 *start)
394 utf16 *end;
395 utf16 *dst;
396 size_t bytes_n;
398 end = start;
399 loop {
400 if (*end == 0)
401 break;
402 ++end;
404 ++end;
405 bytes_n = (size_t)((end - start) * sizeof(utf16));
406 dst = calloc(1,bytes_n);
407 memcpy(dst, start, bytes_n);
409 /* brain damaged mixed-endian guid */
410 STATIC void guid_write(void *dest, struct guid_t *src)
412 u8 *d;
413 u32 *p32;
414 u16 *p16;
416 d = (u8*)dest;
418 p32 = (u32*)d;
419 *p32 = htole32(src->blk_0); /* little endian */
420 d += 4;
421 p16 = (u16*)d;
422 *p16 = htole16(src->blk_1); /* little endian */
423 d += 2;
424 p16 = (u16*)d;
425 *p16 = htole16(src->blk_2); /* little endian */
426 d += 2;
427 p16 = (u16*)d;
428 *p16 = htobe16(src->blk_3); /* big endian */
429 d += 2;
430 d[0] = src->blk_4[0];
431 d[1] = src->blk_4[1];
432 d[2] = src->blk_4[2];
433 d[3] = src->blk_4[3];
434 d[4] = src->blk_4[4];
435 d[5] = src->blk_4[5];
437 /* brain damaged mixed-endian guid */
438 STATIC void guid_read(struct guid_t *dest, void *src)
440 u32 *p32;
441 u16 *p16;
442 u8 *p8;
444 p32 = src;
445 dest->blk_0 = le32toh(*p32);
446 src += 4;
447 p16 = src;
448 dest->blk_1 = le16toh(*p16);
449 src += 2;
450 p16 = src;
451 dest->blk_2 = le16toh(*p16);
452 src += 2;
453 p16 = src;
454 dest->blk_3 = be16toh(*p16);
455 src += 2;
456 memcpy(dest->blk_4, src, 6);
458 STATIC void sysfs_infos_get(void)
460 int fd;
461 int r;
462 utf8 val_str[X64_UTF8_BYTES_MAX + 1]; /* 0 terminating char */
463 utf8 *blk_dev_name;
464 utf8 *sz_512_n_path;
465 utf8 *logical_blk_bytes_n_path;
466 utf8 *physical_blk_bytes_n_path;
468 blk_dev_name = strrchr(blk_dev.path, '/');
469 ++blk_dev_name;
470 sz_512_n_path = pf("/sys/block/%s/size", blk_dev_name);
471 out_pf("%s:reading %s\n", blk_dev.path, sz_512_n_path);
472 fd = open(sz_512_n_path, O_RDONLY);
473 if (fd == -1)
474 error_pf("%s:unable to open %s\n", blk_dev.path, sz_512_n_path);
475 free(sz_512_n_path);
476 /* reads are supposed to be atomic from sysfs... I guess */
477 r = read(fd, val_str, sizeof(val_str));
478 val_str[r - 1] = 0; /* remove the terminating '\n' */
479 blk_dev.sz_512_n = strtou64(val_str, 0, 10);
480 out_bold_pf("%s:size is %"PRIu64" blocks of 512 bytes\n", blk_dev.path, blk_dev.sz_512_n);
481 close(fd);
483 logical_blk_bytes_n_path = pf("/sys/block/%s/queue/logical_block_size", blk_dev_name);
484 out_pf("%s:reading %s\n", blk_dev.path, logical_blk_bytes_n_path);
485 fd = open(logical_blk_bytes_n_path, O_RDONLY);
486 if (fd == -1)
487 error_pf("%s:unable to open %s\n", blk_dev.path, logical_blk_bytes_n_path);
488 free(logical_blk_bytes_n_path);
489 /* reads are supposed to be atomic from sysfs... I guess */
490 r = read(fd, val_str, sizeof(val_str));
491 val_str[r - 1] = 0; /* remove the terminating '\n' */
492 blk_dev.logical_blk_bytes_n = strtou64(val_str, 0, 10);
493 out_bold_pf("%s:logical block size is %"PRIu64" bytes\n", blk_dev.path, blk_dev.logical_blk_bytes_n);
494 close(fd);
496 physical_blk_bytes_n_path = pf("/sys/block/%s/queue/physical_block_size", blk_dev_name);
497 out_pf("%s:reading %s\n", blk_dev.path, physical_blk_bytes_n_path);
498 fd = open(physical_blk_bytes_n_path, O_RDONLY);
499 if (fd == -1)
500 error_pf("%s:unable to open %s\n", blk_dev.path, physical_blk_bytes_n_path);
501 free(physical_blk_bytes_n_path);
502 /* reads are supposed to be atomic from sysfs... I guess */
503 r = read(fd, val_str, sizeof(val_str));
504 val_str[r - 1] = 0; /* remove the terminating '\n' */
505 blk_dev.physical_blk_bytes_n = strtou64(val_str, 0, 10);
506 out_bold_pf("%s:physical block size is %"PRIu64" bytes\n", blk_dev.path, blk_dev.physical_blk_bytes_n);
507 close(fd);
509 STATIC void protective_mbr_gen(void)
511 u32 *le32_whole_dev_logical_blks_n;
512 u64 whole_dev_bytes_n;
513 u64 whole_dev_logical_blks_n;
515 if (blk_dev.logical_blk_bytes_n < 512)
516 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);
517 protective_mbr = calloc(1, blk_dev.logical_blk_bytes_n);
519 protective_mbr[PART_0 + 0x02] = 0x02; /* first CHS */
520 protective_mbr[PART_0 + 0x04] = 0xee; /* partition type */
521 protective_mbr[PART_0 + 0x05] = 0xff; /* last head */
522 protective_mbr[PART_0 + 0x06] = 0xff; /* last cylinder MSBs + last sector */
523 protective_mbr[PART_0 + 0x07] = 0xff; /* last cylinder LSBs */
524 protective_mbr[PART_0 + 0x08] = 0x1; /* little endian */
526 whole_dev_bytes_n = blk_dev.sz_512_n * 512;
527 whole_dev_logical_blks_n = whole_dev_bytes_n / blk_dev.logical_blk_bytes_n;
528 /* cap to max, remove the MBR in LBA 0 */
529 if (whole_dev_logical_blks_n > 0x100000000)
530 whole_dev_logical_blks_n = 0xffffffff;
531 le32_whole_dev_logical_blks_n = (u32*)&protective_mbr[PART_0 + 0x0c];
532 *le32_whole_dev_logical_blks_n = htole32( /* remove mbr */
533 (u32)whole_dev_logical_blks_n - 1);
535 protective_mbr[BOOT_SIGNATURE_0X55] = 0x55;
536 protective_mbr[BOOT_SIGNATURE_0XAA] = 0xaa;
538 STATIC void protective_mbr_write(void)
540 off_t r0;
542 r0 = lseek(blk_dev.fd, 0, SEEK_SET);
543 if (r0 != 0)
544 error_pf("%s:unable to reach the start of the device\n", blk_dev.path);
545 if (!write_full(blk_dev.fd, protective_mbr,
546 blk_dev.logical_blk_bytes_n))
547 error_pf("%s:unable to write the protective master boot record (mbr), device mbr is now probably corrupted\n", blk_dev.path);
548 out_bold_pf("%s:protective master boot record (mbr) written, %"PRIu64" bytes\n", blk_dev.path, blk_dev.logical_blk_bytes_n);
550 STATIC void entries_serialized_array_gen_entry(u32 entry_idx)
552 u8 *entry;
553 u64 *p64;
555 entry = entries_serialized_array + entry_bytes_n * entry_idx;
556 guid_write(&entry[0x00], &entries[entry_idx].type);
557 guid_write(&entry[0x10], &entries[entry_idx].uniq);
558 p64 = (u64*)&entry[0x20];
559 *p64 = htole64(entries[entry_idx].first);
560 p64 = (u64*)&entry[0x28];
561 *p64 = htole64(entries[entry_idx].last);
563 STATIC void entries_serialized_array_gen(void)
565 u32 entry_idx;
567 entries_serialized_array = calloc(1, entries_lbas_n
568 * blk_dev.logical_blk_bytes_n);
569 entry_idx = 0;
570 loop {
571 if (entry_idx == entries_n)
572 break;
573 entries_serialized_array_gen_entry(entry_idx);
574 ++entry_idx;
577 STATIC void hdr_primary_serialized_gen(void)
579 u64 *p64;
580 u32 *p32;
581 u16 *p16;
582 u32 le_crc32;
584 hdr_primary_serialized = calloc(1, blk_dev.logical_blk_bytes_n);
586 memcpy(hdr_primary_serialized, HDR_SIGNATURE, 8);
588 p32 = (u32*)&hdr_primary_serialized[0x08];
589 *p32 = htole32(HDR_REVISION);
591 p32 =(u32*)&hdr_primary_serialized[0x0c];
592 *p32 = htole32(hdrs.bytes_n);
594 /* the CRC32 will go there, 0 for its calculation */
596 p64 = (u64*)&hdr_primary_serialized[0x18];
597 *p64 = htole64(0x00000001); /* lba of this hdr */
599 p64 = (u64*)&hdr_primary_serialized[0x20];
600 *p64 = htole64(blk_dev.last_lba); /* the hdr at the end */
602 p64 = (u64*)&hdr_primary_serialized[0x28];
603 *p64 = htole64(hdrs.first_usable_lba);
605 p64 = (u64*)&hdr_primary_serialized[0x30];
606 *p64 = htole64(hdrs.last_usable_lba);
608 guid_write(&hdr_primary_serialized[0x38], &hdrs.disk_guid);
610 p64 = (u64*)&hdr_primary_serialized[0x48];
611 *p64 = htole64(2); /* skip mbr and hdr */
613 p32 = (u32*)&hdr_primary_serialized[0x50];
614 *p32 = htole32(entries_n);
616 p32 = (u32*)&hdr_primary_serialized[0x54];
617 *p32 = htole32(entry_bytes_n);
619 p32 = (u32*)&hdr_primary_serialized[0x58];
620 *p32 = htole32(entries_serialized_array_crc32);
622 /* crc32 on exactly the header size */
623 le_crc32 = 0;
624 le_crc32_update(hdr_primary_serialized, hdrs.bytes_n, &le_crc32);
625 p32 = (u32*)&hdr_primary_serialized[0x10];
626 *p32 = le_crc32;
628 STATIC void hdr_secondary_serialized_gen(void)
630 u64 *p64;
631 u32 *p32;
632 u16 *p16;
633 u32 le_crc32;
635 hdr_secondary_serialized = calloc(1, blk_dev.logical_blk_bytes_n);
637 memcpy(hdr_secondary_serialized, HDR_SIGNATURE, 8);
639 p32 = (u32*)&hdr_secondary_serialized[0x08];
640 *p32 = htole32(HDR_REVISION);
642 p32 =(u32*)&hdr_secondary_serialized[0x0c];
643 *p32 = htole32(hdrs.bytes_n);
645 /* the CRC32 will go there, 0 for its calculation */
647 p64 = (u64*)&hdr_secondary_serialized[0x18];
648 *p64 = htole64(blk_dev.last_lba); /* this hdr */
650 p64 = (u64*)&hdr_secondary_serialized[0x20];
651 *p64 = htole64(1); /* the hdr at the beginning */
653 p64 = (u64*)&hdr_secondary_serialized[0x28];
654 *p64 = htole64(hdrs.first_usable_lba);
656 p64 = (u64*)&hdr_secondary_serialized[0x30];
657 *p64 = htole64(hdrs.last_usable_lba);
659 guid_write(&hdr_secondary_serialized[0x38], &hdrs.disk_guid);
661 p64 = (u64*)&hdr_secondary_serialized[0x48];
662 *p64 = htole64(blk_dev.last_lba - entries_lbas_n);
664 p32 = (u32*)&hdr_secondary_serialized[0x50];
665 *p32 = htole32(entries_n);
667 p32 = (u32*)&hdr_secondary_serialized[0x54];
668 *p32 = htole32(entry_bytes_n);
670 p32 = (u32*)&hdr_secondary_serialized[0x58];
671 *p32 = htole32(entries_serialized_array_crc32);
673 /* crc32 on exactly the header size */
674 le_crc32 = 0;
675 le_crc32_update(hdr_secondary_serialized, hdrs.bytes_n, &le_crc32);
676 p32 = (u32*)&hdr_secondary_serialized[0x10];
677 *p32 = le_crc32;
679 STATIC void hdr_primary_serialized_write(void)
681 off_t r;
682 off_t start;
683 u64 bytes_to_write_n;
685 /* skip the mbr */
686 start = (off_t)blk_dev.logical_blk_bytes_n;
687 r = lseek(blk_dev.fd, start, SEEK_SET);
688 if (r != start)
689 error_pf("%s:unable to reach the first lba of the device\n", blk_dev.path);
690 bytes_to_write_n = (size_t)(blk_dev.logical_blk_bytes_n);
691 if (!write_full(blk_dev.fd, hdr_primary_serialized, bytes_to_write_n))
692 error_pf("%s:unable to write the primary GPT header, block device is now probably corrupted\n", blk_dev.path);
693 out_bold_pf("%s:primary GPT header written, %"PRIu64" bytes\n", blk_dev.path, bytes_to_write_n);
695 STATIC void hdr_secondary_serialized_write(void)
697 off_t r;
698 off_t start;
699 u64 bytes_to_write_n;
701 start = (off_t)(blk_dev.last_lba * blk_dev.logical_blk_bytes_n);
702 r = lseek(blk_dev.fd, start, SEEK_SET);
703 if (r != start)
704 error_pf("%s:unable to reach the lba of the secondary header\n", blk_dev.path);
705 bytes_to_write_n = blk_dev.logical_blk_bytes_n;
706 if (!write_full(blk_dev.fd, hdr_secondary_serialized, bytes_to_write_n))
707 error_pf("%s:unable to write the secondary GPT header, block device is now probably corrupted\n", blk_dev.path);
708 out_bold_pf("%s:secondary GPT header written, %"PRIu64" bytes\n", blk_dev.path, bytes_to_write_n);
710 STATIC void entries_serialized_array_primary_write(void)
712 off_t r;
713 off_t start;
714 u64 bytes_to_write_n;
716 /* skip the mbr and the primary hdr */
717 start = (off_t)(blk_dev.logical_blk_bytes_n * 2);
718 r = lseek(blk_dev.fd, start, SEEK_SET);
719 if (r != start)
720 error_pf("%s:unable to reach the first lba for the primary entries\n", blk_dev.path);
721 bytes_to_write_n = blk_dev.logical_blk_bytes_n * entries_lbas_n;
722 if (!write_full(blk_dev.fd, entries_serialized_array, bytes_to_write_n))
723 error_pf("%s:unable to write the primary entries, block device is now probably corrupted\n", blk_dev.path);
724 out_bold_pf("%s:primary entries written, %"PRIu64" bytes\n", blk_dev.path, bytes_to_write_n);
726 STATIC void entries_serialized_array_secondary_write(void)
728 off_t r;
729 off_t start;
730 u64 bytes_to_write_n;
732 /* skip the secondary hdr, offset arithmetic */
733 start = (off_t)(blk_dev.logical_blk_bytes_n * (blk_dev.last_lba
734 - entries_lbas_n));
735 r = lseek(blk_dev.fd, start, SEEK_SET);
736 if (r != start)
737 error_pf("%s:unable to reach the first lba for the secondary entries\n", blk_dev.path);
738 bytes_to_write_n = blk_dev.logical_blk_bytes_n * entries_lbas_n;
739 if (!write_full(blk_dev.fd, entries_serialized_array, bytes_to_write_n))
740 error_pf("%s:unable to write the secondary entries, block device is now probably corrupted\n", blk_dev.path);
741 out_bold_pf("%s:secondary entries written, %"PRIu64" bytes\n", blk_dev.path, bytes_to_write_n);
743 STATIC void hdr_load(struct hdr_t *hdr)
745 off_t r;
746 off_t lba_off;
748 lba_off = (off_t)(blk_dev.logical_blk_bytes_n * hdr->read_from_lba);
750 hdr->data = calloc(1, blk_dev.logical_blk_bytes_n);
751 r = lseek(blk_dev.fd, lba_off, SEEK_SET);
752 if (r != lba_off) {
753 warning_pf("%s:unable to seek the device to the %s GPT header\n", blk_dev.path, hdr->str);
754 return;
756 if (!read_full(blk_dev.fd, hdr->data, blk_dev.logical_blk_bytes_n)) {
757 warning_pf("%s:unable to load the %s GPT header logical block\n", blk_dev.path, hdr->str);
758 free(hdr->data);
759 hdr->data = 0;
762 STATIC void hdr_validate(struct hdr_t *hdr)
764 u32 *p32;
765 u32 le_crc32;
766 u64 *p64;
768 if (hdr->data == 0)
769 return;
770 if (memcmp(hdr->data, HDR_SIGNATURE, HDR_SIGNATURE_BYTES_N) != 0) {
771 warning_pf("%s:the %s GPT header has the wrong signature\n", blk_dev.path, hdr->str);
772 goto free_data;
774 out_bold_pf("%s:found, %s, GPT header\n", blk_dev.path, hdr->str);
775 p32 = (u32*)&hdr->data[0x10];
776 hdr->le_crc32 = *p32;
777 /* must 0 the crc32 to perform our crc32 */
778 *p32 = 0;
779 p32 = (u32*)&hdr->data[0x0c];
780 hdr->bytes_n = le32toh(*p32);
781 le_crc32 = 0;
782 le_crc32_update(hdr->data, hdr->bytes_n, &le_crc32);
783 if (le_crc32 != hdr->le_crc32) {
784 warning_pf("%s:the %s GPT header has a bad crc32\n", blk_dev.path, hdr->str);
785 goto free_data;
787 p64 = (u64*)&hdr->data[0x18];
788 hdr->lba = le64toh(*p64);
789 p64 = (u64*)&hdr->data[0x20];
790 hdr->alternate_hdr_lba = le64toh(*p64);
791 guid_read(&hdr->disk_guid, &hdr->data[0x38]);
792 p64 = (u64*)&hdr->data[0x48];
793 hdr->entries_lba = le64toh(*p64);
794 p32 = (u32*)&hdr->data[0x50];
795 hdr->entries_n = le32toh(*p32);
796 p32 = (u32*)&hdr->data[0x54];
797 hdr->entry_bytes_n = le32toh(*p32);
798 p32 = (u32*)&hdr->data[0x58];
799 hdr->entries_le_crc32 = *p32;
801 hdr->valid = true;
802 return;
803 free_data:
804 free(hdr->data);
805 hdr->data = 0;
807 STATIC void entries_load(struct hdr_t *hdr)
809 off_t r;
810 off_t entries_off;
811 u32 hdr_entries_bytes_n;
812 u32 le_crc32;
814 entries_off = hdr->entries_lba * blk_dev.logical_blk_bytes_n;
815 r = lseek(blk_dev.fd, entries_off, SEEK_SET);
816 if (r != entries_off) {
817 warning_pf("%s:unable to seek the device to the, %s, GPT header\n", blk_dev.path, hdr->str);
818 return;
820 hdr_entries_bytes_n = hdr->entry_bytes_n * hdr->entries_n;
821 hdr->entries = calloc(1, (size_t)(hdr_entries_bytes_n));
822 if(!read_full(blk_dev.fd, hdr->entries, hdr_entries_bytes_n)) {
823 warning_pf("%s:unable to read the, %s, entries\n", blk_dev.path, hdr->str);
824 goto free_entries;
826 le_crc32 = 0;
827 le_crc32_update(hdr->entries, hdr_entries_bytes_n, &le_crc32);
828 if (le_crc32 != hdr->entries_le_crc32) {
829 warning_pf("%s:%s entries have a bad crc32\n", blk_dev.path, hdr->str);
830 goto free_entries;
832 hdr->entries_valid = true;
833 return;
834 free_entries:
835 free(hdr->entries);
837 STATIC void build_data_from_previous_gpt(void)
839 struct hdr_t *hdr;
840 u32 entry_idx;
841 u32 entries_bytes_n;
843 /* first, lookup for valid data */
844 if (hdrs.previous.primary.valid && hdrs.previous.primary.entries_valid)
845 hdr = &hdrs.previous.primary;
846 else if (hdrs.previous.secondary.valid
847 && hdrs.previous.secondary.entries_valid)
848 hdr = &hdrs.previous.secondary;
849 else
850 return; /* no valid data to build from */
851 /* header */
852 memcpy(&hdrs.disk_guid, &hdr->disk_guid, sizeof(hdrs.disk_guid));
853 hdrs.bytes_n = hdr->bytes_n;
854 /* entries */
855 entries = calloc(hdr->entries_n, hdr->entry_bytes_n);
856 entries_n = hdr->entries_n;
857 entry_bytes_n = hdr->entry_bytes_n;
858 entries_bytes_n = hdr->entries_n * hdr->entry_bytes_n;
859 entries_lbas_n = entries_bytes_n / blk_dev.logical_blk_bytes_n
860 + ((entries_bytes_n % blk_dev.logical_blk_bytes_n) == 0
861 ? 0 : 1);
862 entry_idx = 0;
863 loop {
864 u8 *src_entry;
865 struct entry_t *dst_entry;
866 u64 *p64;
868 if (entry_idx == entries_n)
869 break;
870 src_entry = hdr->entries + entry_idx * hdr->entry_bytes_n;
871 dst_entry = &entries[entry_idx];
873 guid_read(&dst_entry->type, src_entry);
874 guid_read(&dst_entry->uniq, &src_entry[0x10]);
875 p64 = (u64*)&src_entry[0x20];
876 dst_entry->first = le64toh(*p64);
877 p64 = (u64*)&src_entry[0x28];
878 dst_entry->last = le64toh(*p64);
879 p64 = (u64*)&src_entry[0x30];
880 dst_entry->attrs = le64toh(*p64);
881 dst_entry->name = utf16_strdup((utf16*)&src_entry[0x38]);
882 ++entry_idx;
885 STATIC void previous_gpt_load(void)
887 hdrs.previous.primary.read_from_lba = 1;
888 hdrs.previous.primary.str = "primary";
889 hdr_load(&hdrs.previous.primary);
890 hdr_validate(&hdrs.previous.primary);
891 if (hdrs.previous.primary.valid) {
892 entries_load(&hdrs.previous.primary);
893 if (hdrs.previous.primary.entries_valid)
894 return;
895 /* damn! we have a valid header but with invalid entries */
896 hdrs.previous.secondary.read_from_lba =
897 hdrs.previous.primary.alternate_hdr_lba;
898 hdrs.previous.secondary.str = "alternate from primary header";
899 } else { /* arg! try the device last lba */
900 hdrs.previous.secondary.read_from_lba = blk_dev.last_lba;
901 hdrs.previous.secondary.str = "from device last lba";
904 * here, something went wrong with the primary header. 2 cases:
905 * - the primary header is ok but the entries are not, then we are
906 * trying the alternate header from the primary header data.
907 * - the primary header is nok, we are try to look for a secondary
908 * header from the last lba of the device.
910 hdr_load(&hdrs.previous.secondary);
911 hdr_validate(&hdrs.previous.secondary);
912 if (hdrs.previous.secondary.valid)
913 entries_load(&hdrs.previous.secondary);
915 STATIC void hdrs_usable_lbas_compute(void)
917 /* protective mbr, hdr, entries */
918 hdrs.first_usable_lba = 0 + 2 + entries_lbas_n;
919 /* hdr, entries */
920 hdrs.last_usable_lba = blk_dev.last_lba - 1 - entries_lbas_n;
922 STATIC void entries_reset(void)
924 u32 i;
925 utf16 zero;
926 u32 entries_bytes_n;
928 if (entries_n != 0) {
929 i = 0;
930 loop { /* the utf16 names */
931 if (i == entries_n)
932 break;
933 free(entries[i].name);
934 ++i;
936 free(entries);
938 /* actually 128 entries */
939 entries_n = ENTRIES_ARRAY_MIN_BYTES_N / ENTRY_BYTES_N;
940 entries = calloc(entries_n, ENTRY_BYTES_N);
941 entry_bytes_n = ENTRY_BYTES_N;
943 zero = 0;
944 i = 0;
945 loop {
946 if (i == entries_n)
947 break;
948 entries[i].name = utf16_strdup(&zero);
949 ++i;
951 entries_bytes_n = entries_n * entry_bytes_n;
952 entries_lbas_n = entries_bytes_n / blk_dev.logical_blk_bytes_n
953 + (entries_bytes_n % entry_bytes_n == 0 ? 0 : 1);
955 /******************************************************************************/
956 STATIC void out_guid(struct guid_t *guid)
958 u8 i;
959 out_pf("%08x-%04x-%04x-%04x-", guid->blk_0, guid->blk_1, guid->blk_2,
960 guid->blk_3);
961 i = 0;
962 loop {
963 if (i == 6)
964 break;
965 out_pf("%02x", guid->blk_4[i]);
966 ++i;
969 struct {
970 struct guid_t guid;
971 utf8 *name;
972 } types[] = {
973 [0].guid.blk_0 = 0xc12a7328,
974 [0].guid.blk_1 = 0xf81f,
975 [0].guid.blk_2 = 0x11d2,
976 [0].guid.blk_3 = 0xba4b,
977 [0].guid.blk_4 = {0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b},
978 [0].name = "EFI system",
980 [1].guid.blk_0 = 0x4f68bce3,
981 [1].guid.blk_1 = 0xe8cd,
982 [1].guid.blk_2 = 0x4db1,
983 [1].guid.blk_3 = 0x96e7,
984 [1].guid.blk_4 = {0xfb, 0xca, 0xf9, 0x84, 0xb7, 0x09},
985 [1].name = "linux root x86_64",
987 STATIC void types_show(void)
989 u32 i;
990 u32 n;
992 n = ARRAY_N(types);
993 i = 0;
994 loop {
995 if (i == n)
996 break;
997 out_pf("%"PRIu32": ", i + 1);
998 out_guid(&types[i].guid);
999 out_pf(" %s\n", types[i].name);
1000 ++i;
1003 STATIC utf8 *type_guids_lookup_name(struct guid_t *guid)
1005 u32 i;
1006 u32 n;
1008 n = ARRAY_N(types);
1009 i = 0;
1010 loop {
1011 if (i == n)
1012 break;
1013 if (memcmp(guid, &types[i].guid, sizeof(*guid)) == 0)
1014 return types[i].name;
1015 ++i;
1017 return "unknown";
1019 STATIC void entry_show(u32 idx, struct entry_t *entry)
1021 utf16 *pc;
1022 utf8 *type_str;
1024 out_pf("%"PRIu32": %"PRIu64" %"PRIu64" ", idx + 1, entry->first, entry->last);
1025 out_guid(&entry->type);
1026 type_str = type_guids_lookup_name(&entry->type);
1027 out_pf("(%s)", type_str);
1028 out_pf("\n ");
1029 out_guid(&entry->uniq);
1030 out_pf(" ");
1031 out_pf("0x%"PRIx64" \"", entry->attrs);
1032 pc = entry->name;
1033 loop {
1034 u16 c;
1036 c = le16toh(*pc);
1037 if (c == 0)
1038 break;
1039 /* keep it simple */
1040 if ((u16)'0' <= c && c <= (u16)'9'
1041 && (u16)'A' <= c && c <= (u16)'Z'
1042 && (u16)'a' <= c && c <= (u16)'z')
1043 out_pf("%c", c);
1044 else
1045 out_pf("�");
1046 ++pc;
1048 out_pf("\"\n");
1050 STATIC bool guid_is_zero(struct guid_t *guid)
1052 u8 *b;
1053 u8 *last;
1055 b = (u8*)guid;
1056 last = b + sizeof(*guid) - 1;
1057 loop {
1058 if (*b != 0)
1059 return false;
1060 if (b == last)
1061 return true;
1062 ++b;
1065 STATIC bool entry_is_used(u32 idx)
1067 struct entry_t *entry;
1069 entry = &entries[idx];
1070 if ( guid_is_zero(&entry->type)
1071 && guid_is_zero(&entry->uniq)
1072 && entry->first == 0
1073 && entry->last == 0
1074 && entry->attrs == 0
1075 && entry->name[0] == 0) {
1076 return false;
1078 return true;
1080 STATIC void entry_delete(u32 idx)
1082 struct entry_t *entry;
1083 utf16 empty_str;
1085 empty_str = 0;
1086 entry = &entries[idx];
1087 free(entry->name);
1088 memset(entry, 0, sizeof(*entry));
1089 entry->name = utf16_strdup(&empty_str);
1091 STATIC void entries_show(void)
1093 u32 entry_idx;
1095 if (entries_n == 0) {
1096 out_pf("no partition entries\n");
1097 return;
1098 } else {
1099 out_bold_pf("array of partition entries has %"PRIu32" slots:\n", entries_n);
1101 entry_idx = 0;
1102 out_bold_pf("slot / first / last / type(guid) \n");
1103 out_bold_pf(" uniq_guid / attributes / name\n");
1104 loop {
1105 if (entry_idx == entries_n)
1106 break;
1107 if (entry_is_used(entry_idx))
1108 entry_show(entry_idx, &entries[entry_idx]);
1109 ++entry_idx;
1112 STATIC void main_menu_show(void)
1114 out_pf("\
1115 Command / Action\n\
1116 p print the partition entries\n\
1117 n new partition entry\n\
1118 d delete a partition entry\n\
1119 h print the header\n\
1120 u change disk guid\n\
1121 w write and quit\n\
1122 q quit without saving\n");
1124 STATIC u8 input_line[BUFSIZ];
1125 STATIC void *(*input_state)(size_t lf);
1126 /* list of states -- start */
1127 STATIC void *state_main_menu(size_t lf);
1128 STATIC void *state_delete_entry(size_t lf);
1129 STATIC void *state_change_disk_guid(size_t lf);
1130 STATIC void *state_edit_entry(size_t lf);
1131 STATIC void *state_edit_entry_substate_first_lba(size_t lf);
1132 STATIC void *state_edit_entry_substate_last_lba(size_t lf);
1133 STATIC void *state_edit_entry_substate_type(size_t lf);
1134 STATIC void *state_edit_entry_substate_uniq_quid(size_t lf);
1135 STATIC void *state_edit_entry_substate_attrs(size_t lf);
1136 /* list of states -- end */
1137 STATIC struct {
1138 u32 num;
1139 u64 first_lba_default;
1140 u64 first_lba;
1141 u64 last_lba_default;
1142 u64 last_lba;
1143 u32 type;
1144 struct guid_t uniq;
1145 u64 attrs;
1146 } edit_entry;
1147 /*{{{ prompts */
1148 STATIC void edit_entry_attrs_prompt(void)
1150 out_pf("attributes as an hexadecimal number (default to 0x0): ");
1152 STATIC void edit_entry_uniq_guid_prompt(void)
1154 out_pf("uniq guid (r to randomize): ");
1156 STATIC void edit_entry_type_prompt(void)
1158 out_pf("number of the type of partition (l for the list): ");
1160 STATIC void main_menu_prompt(void)
1162 out_pf("command (m for help): ");
1164 STATIC void delete_entry_prompt(void)
1166 out_pf("number of the partition entry to delete: ");
1168 STATIC void edit_entry_last_lba_prompt(void)
1170 out_pf("last logical block lba (default to %"PRIu64"): ",
1171 edit_entry.last_lba_default);
1173 STATIC void edit_entry_prompt(void)
1175 out_pf("partition entry number (1-%"PRIu32"): ", entries_n);
1177 STATIC void edit_entry_first_lba_prompt(void)
1179 out_pf("first logical block lba (default to %"PRIu64"): ",
1180 edit_entry.first_lba_default);
1182 STATIC void change_disk_guid_prompt(void)
1184 out_pf("current disk guid is ");
1185 out_guid(&hdrs.disk_guid);
1186 out_pf("\nnew one (r to randomize): ");
1188 /*}}} prompts -- end */
1189 STATIC void first_lba_default_select(void)
1191 u32 idx;
1193 /* mbr, header, entries. Should not need to cap this */
1194 edit_entry.first_lba_default = hdrs.first_usable_lba;
1195 if (edit_entry.num == 1)
1196 return;
1197 /* num >= 2, idx >= 1 */
1198 idx = edit_entry.num - 2;
1200 * we are going "backward" to locate the right before used entry, then
1201 * we will use the last lba from this entry to compute the first
1202 * lba for this one
1204 loop {
1205 if (entry_is_used(idx)) {
1206 edit_entry.first_lba_default = entries[idx].last + 1;
1207 /* hard capping */
1208 if (edit_entry.first_lba_default < hdrs.first_usable_lba)
1209 edit_entry.first_lba_default = hdrs.first_usable_lba;
1210 if (edit_entry.first_lba_default > hdrs.last_usable_lba)
1211 edit_entry.first_lba_default = hdrs.last_usable_lba;
1212 break;
1214 if (idx == 0)
1215 break;
1216 idx--;
1219 STATIC void last_lba_default_select(void)
1221 u32 idx;
1223 edit_entry.last_lba_default = hdrs.last_usable_lba;
1224 if (edit_entry.num == entries_n)
1225 return;
1226 /* num <= entries_n - 1, idx <= entries_n - 2 */
1227 idx = edit_entry.num;
1229 * we are going forward to reach the first used entry and then use
1230 * the lba right before its first lba.
1232 loop {
1233 if (idx == entries_n)
1234 break;
1235 if (entry_is_used(idx)) {
1236 edit_entry.last_lba_default = entries[idx].first - 1;
1237 /* hard capping */
1238 if (edit_entry.last_lba_default < hdrs.first_usable_lba)
1239 edit_entry.last_lba_default = hdrs.first_usable_lba;
1240 if (edit_entry.last_lba_default > hdrs.last_usable_lba)
1241 edit_entry.last_lba_default = hdrs.last_usable_lba;
1242 break;
1244 ++idx;
1247 STATIC void *hdr_show(void)
1249 out_pf("header:\ndisk guid ");
1250 out_guid(&hdrs.disk_guid);
1251 out_pf("\nfirst usable lba %"PRIu64"\n", hdrs.first_usable_lba);
1252 out_pf("last usable lba %"PRIu64"\n", hdrs.last_usable_lba);
1253 out_pf("%s last lba is %"PRIu64"\n", blk_dev.path, blk_dev.last_lba);
1255 STATIC void guid_randomize(struct guid_t *guid)
1257 int fd;
1259 fd = open("/dev/urandom", O_RDONLY);
1260 if (fd == -1) {
1261 warning_pf("unable to open /dev/urandom for guid randomization\n");
1262 return;
1264 if (!read_full(fd, guid, sizeof(*guid)))
1265 warning_pf("something went wrong while reading from /dev/urandom, the guid is probably corrupted\n");
1266 close(fd);
1268 /* best effort */
1269 STATIC void guid_read_from_input(struct guid_t *guid, size_t lf)
1271 u8 *blk_start;
1272 u8 *blk_end;
1273 u8 save;
1274 u8 *end;
1276 input_line[lf] = 0;
1277 end = input_line + lf;
1279 blk_start = input_line;
1280 blk_end = strchr(blk_start, '-');
1281 if (blk_end == 0)
1282 return;
1283 *blk_end = 0;
1284 guid->blk_0 = (u32)strtou64(blk_start, 0, 16);
1285 blk_start = blk_end + 1;
1286 if (blk_start >= end)
1287 return;
1288 blk_end = strchr(blk_start, '-');
1289 if (blk_end == 0)
1290 return;
1291 *blk_end = 0;
1292 guid->blk_1 = (u16)strtou64(blk_start, 0, 16);
1293 blk_start = blk_end + 1;
1294 if (blk_start >= end)
1295 return;
1296 blk_end = strchr(blk_start, '-');
1297 if (blk_end == 0)
1298 return;
1299 *blk_end = 0;
1300 guid->blk_2 = (u16)strtou64(blk_start, 0, 16);
1301 blk_start = blk_end + 1;
1302 if (blk_start >= end)
1303 return;
1304 blk_end = strchr(blk_start, '-');
1305 if (blk_end == 0)
1306 return;
1307 *blk_end = 0;
1308 guid->blk_3 = (u16)strtou64(blk_start, 0, 16);
1309 /* blk_4 */
1310 blk_start = blk_end + 1;
1311 if (blk_start >= end)
1312 return;
1313 blk_end = blk_start + 12; /* 6 bytes */
1314 if (blk_end > end)
1315 return;
1316 save = blk_start[2];
1317 blk_start[2] = 0;
1318 guid->blk_4[0] = (u8)strtou64(&blk_start[0], 0, 16);
1319 blk_start[2] = save;
1321 save = blk_start[4];
1322 blk_start[4] = 0;
1323 guid->blk_4[1] = (u8)strtou64(&blk_start[2], 0, 16);
1324 blk_start[4] = save;
1326 save = blk_start[6];
1327 blk_start[6] = 0;
1328 guid->blk_4[2] = (u8)strtou64(&blk_start[4], 0, 16);
1329 blk_start[6] = save;
1331 save = blk_start[8];
1332 blk_start[8] = 0;
1333 guid->blk_4[3] = (u8)strtou64(&blk_start[6], 0, 16);
1334 blk_start[8] = save;
1336 save = blk_start[10];
1337 blk_start[10] = 0;
1338 guid->blk_4[4] = (u8)strtou64(&blk_start[8], 0, 16);
1339 blk_start[10] = save;
1341 guid->blk_4[5] = (u8)strtou64(&blk_start[10], 0, 16);
1343 STATIC void *edit_entry_to_entries(void)
1345 struct entry_t *dst_entry;
1347 dst_entry = &entries[edit_entry.num - 1];
1348 memcpy(&dst_entry->type, &types[edit_entry.type - 1].guid,
1349 sizeof(dst_entry->type));
1350 memcpy(&dst_entry->uniq, &edit_entry.uniq, sizeof(dst_entry->uniq));
1351 dst_entry->first = edit_entry.first_lba;
1352 dst_entry->last = edit_entry.last_lba;
1353 dst_entry->attrs = edit_entry.attrs;
1354 /* don't support name input yet */
1356 STATIC void gpt_write(void)
1358 entries_serialized_array_gen();
1359 /* crc32 on exactly the entries array size, not lba bounded */
1360 entries_serialized_array_crc32 = 0;
1361 le_crc32_update(entries_serialized_array, entry_bytes_n * entries_n,
1362 &entries_serialized_array_crc32);
1363 hdr_primary_serialized_gen();
1364 hdr_secondary_serialized_gen();
1365 protective_mbr_gen();
1366 protective_mbr_write();
1367 hdr_primary_serialized_write();
1368 hdr_secondary_serialized_write();
1369 entries_serialized_array_primary_write();
1370 entries_serialized_array_secondary_write();
1372 /*{{{ states */
1373 STATIC void *state_edit_entry_substate_attrs(size_t lf)
1375 if (lf == 0) {
1376 edit_entry.attrs = 0;
1377 goto exit;
1379 input_line[lf] = 0;
1380 edit_entry.attrs = strtou64(input_line, 0, 16);
1381 exit:
1383 * TODO: don't want to use iconv, will implement an utf8 to utf16
1384 * converter another time.
1386 edit_entry_to_entries();
1387 main_menu_prompt();
1388 return state_main_menu;
1390 STATIC void *state_edit_entry_substate_uniq_quid(size_t lf)
1392 if (lf == 1 && input_line[0] == 'r') {
1393 guid_randomize(&edit_entry.uniq);
1394 out_pf("generated guid is : ");
1395 goto exit;
1397 guid_read_from_input(&edit_entry.uniq, lf);
1398 out_pf("read guid is : ");
1399 exit:
1400 out_guid(&edit_entry.uniq);
1401 out_pf("\n");
1402 edit_entry_attrs_prompt();
1403 return state_edit_entry_substate_attrs;
1405 STATIC void *state_edit_entry_substate_type(size_t lf)
1407 if (lf == 1 && input_line[0] == 'l') {
1408 types_show();
1409 edit_entry_type_prompt();
1410 return state_edit_entry_substate_type;
1412 input_line[lf] = 0;
1413 edit_entry.type = (u32)strtou64(input_line, 0, 10);
1414 if (edit_entry.type == 0 || edit_entry.type > ARRAY_N(types)) {
1415 warning_pf("invalid type number\n");
1416 edit_entry_type_prompt();
1417 return state_edit_entry_substate_type;
1419 edit_entry_uniq_guid_prompt();
1420 return state_edit_entry_substate_uniq_quid;
1422 STATIC void *state_edit_entry_substate_last_lba(size_t lf)
1424 if (lf == 0)
1425 edit_entry.last_lba = edit_entry.last_lba_default;
1426 else {
1427 input_line[lf] = 0;
1428 edit_entry.last_lba = strtou64(input_line, 0, 10);
1430 edit_entry_type_prompt();
1431 return state_edit_entry_substate_type;
1433 STATIC void *state_edit_entry_substate_first_lba(size_t lf)
1435 if (lf == 0) {
1436 edit_entry.first_lba = edit_entry.first_lba_default;
1437 } else {
1438 input_line[lf] = 0;
1439 edit_entry.first_lba = strtou64(input_line, 0, 10);
1441 last_lba_default_select();
1442 edit_entry_last_lba_prompt();
1443 return state_edit_entry_substate_last_lba;
1445 STATIC void *state_edit_entry(size_t lf)
1447 input_line[lf] = 0;
1448 edit_entry.num = (u32)strtou64(input_line, 0, 10);
1449 if (1 <= edit_entry.num && edit_entry.num <= entries_n) {
1450 first_lba_default_select();
1451 edit_entry_first_lba_prompt();
1452 return state_edit_entry_substate_first_lba;
1453 } else
1454 warning_pf("%"PRIu32" is an invalid partition entry number\n", edit_entry.num);
1455 main_menu_prompt();
1456 return state_main_menu;
1458 STATIC void *state_delete_entry(size_t lf)
1460 u8 *end;
1461 u32 entry_num;
1463 input_line[lf] = 0;
1464 entry_num = (u32)strtou64(input_line, 0, 10);
1465 if (1 <= entry_num && entry_num <= entries_n) {
1466 out_bold_pf("deleting partition entry %"PRIu64"\n", entry_num);
1467 entry_delete((u32)(entry_num - 1));
1468 } else
1469 out_bold_pf("partition number out of range %"PRIu64"\n", entry_num);
1470 main_menu_prompt();
1471 return state_main_menu;
1473 STATIC void *state_change_disk_guid(size_t lf)
1475 if (lf == 1) {
1476 if (input_line[0] == 'r') {
1477 guid_randomize(&hdrs.disk_guid);
1478 out_pf("randomized disk guid is ");
1480 } else {
1481 guid_read_from_input(&hdrs.disk_guid, lf);
1482 out_pf("read disk guid is ");
1484 out_guid(&hdrs.disk_guid);
1485 out_pf("\n");
1486 main_menu_prompt();
1487 return state_main_menu;
1489 STATIC void *state_main_menu(size_t lf)
1491 if (lf == 1) {
1492 switch(input_line[0]) {
1493 case 'q':
1494 out_pf("quit\n");
1495 exit(0);
1496 case 'm':
1497 main_menu_show();
1498 break;
1499 case 'p':
1500 entries_show();
1501 break;
1502 case 'h':
1503 hdr_show();
1504 break;
1505 case 'd':
1506 delete_entry_prompt();
1507 return state_delete_entry;
1508 case 'n':
1509 edit_entry_prompt();
1510 return state_edit_entry;
1511 case 'u':
1512 change_disk_guid_prompt();
1513 return state_change_disk_guid;
1514 case 'w':
1515 gpt_write();
1516 exit(0);
1517 default:
1518 break;
1521 main_menu_prompt();
1522 return state_main_menu;
1524 /*}}} states -- end */
1525 /* end is the index of the byte past the last read byte */
1526 STATIC size_t input_line_consume(size_t lf, size_t end)
1528 input_state = input_state(lf);
1530 /* update the input line buffer and offsets */
1531 if (lf == (BUFSIZ - 1)) { /* lf is the last char in our line buffer */
1532 memset(input_line, 0, BUFSIZ);
1533 return 0;
1535 memmove(input_line, input_line + lf + 1, end - (lf + 1));
1536 return end - (lf + 1);
1538 STATIC void input_line_loop(void)
1540 bool discarding;
1541 size_t end;
1543 end = 0;
1544 memset(input_line, 0, BUFSIZ);
1545 discarding = false;
1546 input_state = state_main_menu;
1547 main_menu_prompt();
1548 loop {
1549 int r;
1550 size_t lf;
1552 errno = 0;
1553 r = read(0, &input_line[end], BUFSIZ - end);
1554 if (r == -1) {
1555 if (errno == EINTR)
1556 continue;
1557 error_pf("error reading input line from standard input\n");
1559 lf = end;
1560 end += (size_t)r;
1561 loop { /* scan what was read for lf */
1562 if (lf == end)
1563 break;
1564 if (input_line[lf] == '\n') {
1565 if (discarding) {
1566 discarding = false;
1567 end = 0;
1568 break;
1570 end = input_line_consume(lf, end);
1571 break;
1573 ++lf;
1575 if (end == BUFSIZ) {
1576 if (!discarding) {
1577 out_pf("your input line is too long, discarding...\n");
1578 discarding = true;
1580 end = 0;
1584 STATIC void init_once(void)
1586 le_crc32_tbl_gen();
1587 colors_on = true;
1588 load_previous_gpt = true;
1589 entries = 0;
1590 entries_n = 0;
1591 memset(&hdrs, 0, sizeof(hdrs));
1593 STATIC void usage(void)
1595 BOLD;
1596 out_pf("\
1597 nyangpt: line input oriented minimal GPT partition creator\n\
1599 usage:\n\
1600 nyangpt [-nc] [-nl] [-h|--help][ [--] device_path\n\
1602 -nc: disable ANSI terminal colors\n\
1603 -nl: don't load previous GPT partitions\n\
1604 -h|--help: this summary\n");
1605 RESTORE;
1606 exit(0);
1608 STATIC void options_parse(u32 argc, utf8 **argv)
1610 u32 arg;
1611 bool no_more_options;
1612 bool print_usage;
1614 if (argc == 1)
1615 usage();
1616 arg = 1;
1617 no_more_options = false;
1618 print_usage = false;
1619 blk_dev.path = 0;
1620 loop {
1621 if (arg == argc)
1622 break;
1623 if (no_more_options) {
1624 if (blk_dev.path == 0)
1625 blk_dev.path = argv[arg];
1626 } else if (strcmp(argv[arg], "--") == 0) {
1627 no_more_options = true;
1628 } else if (strcmp(argv[arg], "-nc") == 0) {
1629 colors_on = false;
1630 } else if (strcmp(argv[arg], "-nl") == 0) {
1631 load_previous_gpt = false;
1632 } else if (strcmp(argv[arg], "-h") == 0
1633 || strcmp(argv[arg], "--help") == 0) {
1634 print_usage = true;
1635 } else {
1636 if (blk_dev.path == 0)
1637 blk_dev.path = argv[arg];
1639 ++arg;
1641 if (print_usage || blk_dev.path == 0)
1642 usage();
1644 int nyangpt_main(int argc, utf8 **argv)
1646 u64 whole_dev_bytes_n;
1647 u64 entries_bytes_n;
1649 init_once();
1650 options_parse(argc, argv);
1651 out_pf("block device path is %s, opening...\n", blk_dev.path);
1652 blk_dev.fd = open(blk_dev.path, O_RDWR | O_SYNC);
1653 if (blk_dev.fd == -1)
1654 error_pf("%s:unable to open\n", blk_dev.path);
1655 out_pf("%s:opened\n", blk_dev.path);
1657 sysfs_infos_get();
1659 whole_dev_bytes_n = blk_dev.sz_512_n * 512;
1660 if ((whole_dev_bytes_n % blk_dev.logical_blk_bytes_n) != 0)
1661 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);
1662 /* the total number of lba, -1 to get the offset of the last one */
1663 blk_dev.last_lba = blk_dev.sz_512_n * 512 / blk_dev.logical_blk_bytes_n - 1;
1664 out_pf("%s:last lba is %"PRIu64"\n", blk_dev.path, blk_dev.last_lba);
1666 if (load_previous_gpt) {
1667 previous_gpt_load();
1668 build_data_from_previous_gpt();
1671 * Resizing the array of entries is expensive and dangerous, try to
1672 * keep it constant as much as possible.
1673 * If it was not initialized based from the previous gpt, init one
1674 * with sane defaults.
1676 if (entries_n == 0)
1677 entries_reset();
1678 if (hdrs.bytes_n == 0)
1679 hdrs.bytes_n = HDR_BYTES_N;
1680 if (guid_is_zero(&hdrs.disk_guid))
1681 guid_randomize(&hdrs.disk_guid);
1683 * Once we have "an array of entries", the first and last usable
1684 * lbas must be recomputed.
1685 * We ignore the values from the previous gpt headers, we prefer
1686 * to recompute them.
1688 hdrs_usable_lbas_compute();
1689 input_line_loop();
1690 return 0;
1692 /*{{{ preprocessor space cleanup */
1693 #undef ARRAY_N
1694 #undef BLK_0
1695 #undef BLK_1
1696 #undef BLK_2
1697 #undef BLK_3
1698 #undef BLK_4
1699 #undef BOLD_RED
1700 #undef BOLD_ORANGE
1701 #undef BOLD
1702 #undef RESTORE
1703 #undef BOOT_SIGNATURE_0X55
1704 #undef BOOT_SIGNATURE_0XAA
1705 #undef ENTRIES_ARRAY_MIN_BYTES_N
1706 #undef ENTRY_BYTES_N
1707 #undef HDR_BYTES_N
1708 #undef HDR_REVISION
1709 #undef HDR_SIGNATURE
1710 #undef HDR_SIGNATURE_BYTES_N
1711 #undef loop
1712 #undef PART_0
1713 #undef s32
1714 #undef STATIC
1715 #undef strtou64
1716 #undef u8
1717 #undef u16
1718 #undef u32
1719 #undef u64
1720 #undef utf16
1721 #undef utf8
1722 #undef X64_UTF8_BYTES_MAX
1723 /*}}} preprocessor space cleanup -- end */
1724 /*{{{ namespace cleanup ------------------------------------------------------*/
1725 #undef nyangpt_blk_dev
1726 #undef nyangpt_build_data_from_previous_gpt
1727 #undef nyangpt_change_disk_guid_prompt
1728 #undef nyangpt_colors_on
1729 #undef nyangpt_delete_entry_prompt
1730 #undef nyangpt_edit_entry
1731 #undef nyangpt_edit_entry_attrs_prompt
1732 #undef nyangpt_edit_entry_first_lba_prompt
1733 #undef nyangpt_edit_entry_last_lba_prompt
1734 #undef nyangpt_edit_entry_prompt
1735 #undef nyangpt_edit_entry_to_entries
1736 #undef nyangpt_edit_entry_type_prompt
1737 #undef nyangpt_edit_entry_uniq_guid_prompt
1738 #undef nyangpt_entries
1739 #undef nyangpt_entries_lbas_n
1740 #undef nyangpt_entries_load
1741 #undef nyangpt_entries_n
1742 #undef nyangpt_entries_reset
1743 #undef nyangpt_entries_serialized_array
1744 #undef nyangpt_entries_serialized_array_crc32
1745 #undef nyangpt_entries_serialized_array_gen
1746 #undef nyangpt_entries_serialized_array_gen_entry
1747 #undef nyangpt_entries_serialized_array_primary_write
1748 #undef nyangpt_entries_serialized_array_secondary_write
1749 #undef nyangpt_entries_show
1750 #undef nyangpt_entry_bytes_n
1751 #undef nyangpt_entry_delete
1752 #undef nyangpt_entry_is_used
1753 #undef nyangpt_entry_show
1754 #undef nyangpt_entry_t
1755 #undef nyangpt_error_pf
1756 #undef nyangpt_first_lba_default_select
1757 #undef nyangpt_gpt_write
1758 #undef nyangpt_guid_is_zero
1759 #undef nyangpt_guid_t
1760 #undef nyangpt_guid_randomize
1761 #undef nyangpt_guid_read
1762 #undef nyangpt_guid_read_from_input
1763 #undef nyangpt_guid_write
1764 #undef nyangpt_hdr_load
1765 #undef nyangpt_hdr_show
1766 #undef nyangpt_hdr_primary_serialized
1767 #undef nyangpt_hdr_primary_serialized_gen
1768 #undef nyangpt_hdr_primary_serialized_write
1769 #undef nyangpt_hdr_secondary_serialized
1770 #undef nyangpt_hdr_secondary_serialized_gen
1771 #undef nyangpt_hdr_secondary_serialized_write
1772 #undef nyangpt_hdr_t
1773 #undef nyangpt_hdr_validate
1774 #undef nyangpt_hdrs
1775 #undef nyangpt_hdrs_usable_lbas_compute
1776 #undef nyangpt_init_once
1777 #undef nyangpt_input_line
1778 #undef nyangpt_input_line_consume
1779 #undef nyangpt_input_line_loop
1780 #undef nyangpt_input_state
1781 #undef nyangpt_last_lba_default_select
1782 #undef nyangpt_le_crc32_tbl_gen
1783 #undef nyangpt_le_crc32_update
1784 #undef nyangpt_load_previous_gpt
1785 #undef nyangpt_main_menu_prompt
1786 #undef nyangpt_main_menu_show
1787 #undef nyangpt_options_parse
1788 #undef nyangpt_out_bold_pf
1789 #undef nyangpt_out_guid
1790 #undef nyangpt_out_pf
1791 #undef nyangpt_gpt_load
1792 #undef nyangpt_protective_mbr
1793 #undef nyangpt_protective_mbr_gen
1794 #undef nyangpt_protective_mbr_write
1795 #undef nyangpt_read_full
1796 #undef nyangpt_state_change_disk_guid
1797 #undef nyangpt_state_delete_entry
1798 #undef nyangpt_state_edit_entry
1799 #undef nyangpt_state_edit_entry_substate_attrs
1800 #undef nyangpt_state_edit_entry_substate_first_lba
1801 #undef nyangpt_state_edit_entry_substate_last_lba
1802 #undef nyangpt_state_edit_entry_substate_type
1803 #undef nyangpt_state_edit_entry_substate_uniq_quid
1804 #undef nyangpt_state_main_menu
1805 #undef nyangpt_sysfs_infos_get
1806 #undef nyangpt_type_guids_lookup_name
1807 #undef nyangpt_types
1808 #undef nyangpt_types_show
1809 #undef nyangpt_usage
1810 #undef nyangpt_utf16_strdup
1811 #undef nyangpt_warning_pf
1812 #undef nyangpt_write_full
1813 /*----------------------------------------------------------------------------*/
1814 #undef nyangpt_main
1815 /*}}} namespace cleanup -- end -----------------------------------------------*/
1816 #endif