add linux swap guid
[nyangpt.git] / nyangpt.c
blob740386be2905ab4c88367e3dccb318718afca674
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 [2].guid.blk_0 = 0x0657fd6d,
988 [2].guid.blk_1 = 0xa4ab,
989 [2].guid.blk_2 = 0x43c4,
990 [2].guid.blk_3 = 0x84e5,
991 [2].guid.blk_4 = {0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f},
992 [2].name = "linux swap",
994 STATIC void types_show(void)
996 u32 i;
997 u32 n;
999 n = ARRAY_N(types);
1000 i = 0;
1001 loop {
1002 if (i == n)
1003 break;
1004 out_pf("%"PRIu32": ", i + 1);
1005 out_guid(&types[i].guid);
1006 out_pf(" %s\n", types[i].name);
1007 ++i;
1010 STATIC utf8 *type_guids_lookup_name(struct guid_t *guid)
1012 u32 i;
1013 u32 n;
1015 n = ARRAY_N(types);
1016 i = 0;
1017 loop {
1018 if (i == n)
1019 break;
1020 if (memcmp(guid, &types[i].guid, sizeof(*guid)) == 0)
1021 return types[i].name;
1022 ++i;
1024 return "unknown";
1026 STATIC void entry_show(u32 idx, struct entry_t *entry)
1028 utf16 *pc;
1029 utf8 *type_str;
1031 out_pf("%"PRIu32": %"PRIu64" %"PRIu64" ", idx + 1, entry->first, entry->last);
1032 out_guid(&entry->type);
1033 type_str = type_guids_lookup_name(&entry->type);
1034 out_pf("(%s)", type_str);
1035 out_pf("\n ");
1036 out_guid(&entry->uniq);
1037 out_pf(" ");
1038 out_pf("0x%"PRIx64" \"", entry->attrs);
1039 pc = entry->name;
1040 loop {
1041 u16 c;
1043 c = le16toh(*pc);
1044 if (c == 0)
1045 break;
1046 /* keep it simple */
1047 if ((u16)'0' <= c && c <= (u16)'9'
1048 && (u16)'A' <= c && c <= (u16)'Z'
1049 && (u16)'a' <= c && c <= (u16)'z')
1050 out_pf("%c", c);
1051 else
1052 out_pf("�");
1053 ++pc;
1055 out_pf("\"\n");
1057 STATIC bool guid_is_zero(struct guid_t *guid)
1059 u8 *b;
1060 u8 *last;
1062 b = (u8*)guid;
1063 last = b + sizeof(*guid) - 1;
1064 loop {
1065 if (*b != 0)
1066 return false;
1067 if (b == last)
1068 return true;
1069 ++b;
1072 STATIC bool entry_is_used(u32 idx)
1074 struct entry_t *entry;
1076 entry = &entries[idx];
1077 if ( guid_is_zero(&entry->type)
1078 && guid_is_zero(&entry->uniq)
1079 && entry->first == 0
1080 && entry->last == 0
1081 && entry->attrs == 0
1082 && entry->name[0] == 0) {
1083 return false;
1085 return true;
1087 STATIC void entry_delete(u32 idx)
1089 struct entry_t *entry;
1090 utf16 empty_str;
1092 empty_str = 0;
1093 entry = &entries[idx];
1094 free(entry->name);
1095 memset(entry, 0, sizeof(*entry));
1096 entry->name = utf16_strdup(&empty_str);
1098 STATIC void entries_show(void)
1100 u32 entry_idx;
1102 if (entries_n == 0) {
1103 out_pf("no partition entries\n");
1104 return;
1105 } else {
1106 out_bold_pf("array of partition entries has %"PRIu32" slots:\n", entries_n);
1108 entry_idx = 0;
1109 out_bold_pf("slot / first / last / type(guid) \n");
1110 out_bold_pf(" uniq_guid / attributes / name\n");
1111 loop {
1112 if (entry_idx == entries_n)
1113 break;
1114 if (entry_is_used(entry_idx))
1115 entry_show(entry_idx, &entries[entry_idx]);
1116 ++entry_idx;
1119 STATIC void main_menu_show(void)
1121 out_pf("\
1122 Command / Action\n\
1123 p print the partition entries\n\
1124 n new partition entry\n\
1125 d delete a partition entry\n\
1126 h print the header\n\
1127 u change disk guid\n\
1128 w write and quit\n\
1129 q quit without saving\n");
1131 STATIC u8 input_line[BUFSIZ];
1132 STATIC void *(*input_state)(size_t lf);
1133 /* list of states -- start */
1134 STATIC void *state_main_menu(size_t lf);
1135 STATIC void *state_delete_entry(size_t lf);
1136 STATIC void *state_change_disk_guid(size_t lf);
1137 STATIC void *state_edit_entry(size_t lf);
1138 STATIC void *state_edit_entry_substate_first_lba(size_t lf);
1139 STATIC void *state_edit_entry_substate_last_lba(size_t lf);
1140 STATIC void *state_edit_entry_substate_type(size_t lf);
1141 STATIC void *state_edit_entry_substate_uniq_quid(size_t lf);
1142 STATIC void *state_edit_entry_substate_attrs(size_t lf);
1143 /* list of states -- end */
1144 STATIC struct {
1145 u32 num;
1146 u64 first_lba_default;
1147 u64 first_lba;
1148 u64 last_lba_default;
1149 u64 last_lba;
1150 u32 type;
1151 struct guid_t uniq;
1152 u64 attrs;
1153 } edit_entry;
1154 /*{{{ prompts */
1155 STATIC void edit_entry_attrs_prompt(void)
1157 out_pf("attributes as an hexadecimal number (default to 0x0): ");
1159 STATIC void edit_entry_uniq_guid_prompt(void)
1161 out_pf("uniq guid (r to randomize): ");
1163 STATIC void edit_entry_type_prompt(void)
1165 out_pf("number of the type of partition (l for the list): ");
1167 STATIC void main_menu_prompt(void)
1169 out_pf("command (m for help): ");
1171 STATIC void delete_entry_prompt(void)
1173 out_pf("number of the partition entry to delete: ");
1175 STATIC void edit_entry_last_lba_prompt(void)
1177 out_pf("last logical block lba (default to %"PRIu64"): ",
1178 edit_entry.last_lba_default);
1180 STATIC void edit_entry_prompt(void)
1182 out_pf("partition entry number (1-%"PRIu32"): ", entries_n);
1184 STATIC void edit_entry_first_lba_prompt(void)
1186 out_pf("first logical block lba (default to %"PRIu64"): ",
1187 edit_entry.first_lba_default);
1189 STATIC void change_disk_guid_prompt(void)
1191 out_pf("current disk guid is ");
1192 out_guid(&hdrs.disk_guid);
1193 out_pf("\nnew one (r to randomize): ");
1195 /*}}} prompts -- end */
1196 STATIC void first_lba_default_select(void)
1198 u32 idx;
1200 /* mbr, header, entries. Should not need to cap this */
1201 edit_entry.first_lba_default = hdrs.first_usable_lba;
1202 if (edit_entry.num == 1)
1203 return;
1204 /* num >= 2, idx >= 1 */
1205 idx = edit_entry.num - 2;
1207 * we are going "backward" to locate the right before used entry, then
1208 * we will use the last lba from this entry to compute the first
1209 * lba for this one
1211 loop {
1212 if (entry_is_used(idx)) {
1213 edit_entry.first_lba_default = entries[idx].last + 1;
1214 /* hard capping */
1215 if (edit_entry.first_lba_default < hdrs.first_usable_lba)
1216 edit_entry.first_lba_default = hdrs.first_usable_lba;
1217 if (edit_entry.first_lba_default > hdrs.last_usable_lba)
1218 edit_entry.first_lba_default = hdrs.last_usable_lba;
1219 break;
1221 if (idx == 0)
1222 break;
1223 idx--;
1226 STATIC void last_lba_default_select(void)
1228 u32 idx;
1230 edit_entry.last_lba_default = hdrs.last_usable_lba;
1231 if (edit_entry.num == entries_n)
1232 return;
1233 /* num <= entries_n - 1, idx <= entries_n - 2 */
1234 idx = edit_entry.num;
1236 * we are going forward to reach the first used entry and then use
1237 * the lba right before its first lba.
1239 loop {
1240 if (idx == entries_n)
1241 break;
1242 if (entry_is_used(idx)) {
1243 edit_entry.last_lba_default = entries[idx].first - 1;
1244 /* hard capping */
1245 if (edit_entry.last_lba_default < hdrs.first_usable_lba)
1246 edit_entry.last_lba_default = hdrs.first_usable_lba;
1247 if (edit_entry.last_lba_default > hdrs.last_usable_lba)
1248 edit_entry.last_lba_default = hdrs.last_usable_lba;
1249 break;
1251 ++idx;
1254 STATIC void *hdr_show(void)
1256 out_pf("header:\ndisk guid ");
1257 out_guid(&hdrs.disk_guid);
1258 out_pf("\nfirst usable lba %"PRIu64"\n", hdrs.first_usable_lba);
1259 out_pf("last usable lba %"PRIu64"\n", hdrs.last_usable_lba);
1260 out_pf("%s last lba is %"PRIu64"\n", blk_dev.path, blk_dev.last_lba);
1262 STATIC void guid_randomize(struct guid_t *guid)
1264 int fd;
1266 fd = open("/dev/urandom", O_RDONLY);
1267 if (fd == -1) {
1268 warning_pf("unable to open /dev/urandom for guid randomization\n");
1269 return;
1271 if (!read_full(fd, guid, sizeof(*guid)))
1272 warning_pf("something went wrong while reading from /dev/urandom, the guid is probably corrupted\n");
1273 close(fd);
1275 /* best effort */
1276 STATIC void guid_read_from_input(struct guid_t *guid, size_t lf)
1278 u8 *blk_start;
1279 u8 *blk_end;
1280 u8 save;
1281 u8 *end;
1283 input_line[lf] = 0;
1284 end = input_line + lf;
1286 blk_start = input_line;
1287 blk_end = strchr(blk_start, '-');
1288 if (blk_end == 0)
1289 return;
1290 *blk_end = 0;
1291 guid->blk_0 = (u32)strtou64(blk_start, 0, 16);
1292 blk_start = blk_end + 1;
1293 if (blk_start >= end)
1294 return;
1295 blk_end = strchr(blk_start, '-');
1296 if (blk_end == 0)
1297 return;
1298 *blk_end = 0;
1299 guid->blk_1 = (u16)strtou64(blk_start, 0, 16);
1300 blk_start = blk_end + 1;
1301 if (blk_start >= end)
1302 return;
1303 blk_end = strchr(blk_start, '-');
1304 if (blk_end == 0)
1305 return;
1306 *blk_end = 0;
1307 guid->blk_2 = (u16)strtou64(blk_start, 0, 16);
1308 blk_start = blk_end + 1;
1309 if (blk_start >= end)
1310 return;
1311 blk_end = strchr(blk_start, '-');
1312 if (blk_end == 0)
1313 return;
1314 *blk_end = 0;
1315 guid->blk_3 = (u16)strtou64(blk_start, 0, 16);
1316 /* blk_4 */
1317 blk_start = blk_end + 1;
1318 if (blk_start >= end)
1319 return;
1320 blk_end = blk_start + 12; /* 6 bytes */
1321 if (blk_end > end)
1322 return;
1323 save = blk_start[2];
1324 blk_start[2] = 0;
1325 guid->blk_4[0] = (u8)strtou64(&blk_start[0], 0, 16);
1326 blk_start[2] = save;
1328 save = blk_start[4];
1329 blk_start[4] = 0;
1330 guid->blk_4[1] = (u8)strtou64(&blk_start[2], 0, 16);
1331 blk_start[4] = save;
1333 save = blk_start[6];
1334 blk_start[6] = 0;
1335 guid->blk_4[2] = (u8)strtou64(&blk_start[4], 0, 16);
1336 blk_start[6] = save;
1338 save = blk_start[8];
1339 blk_start[8] = 0;
1340 guid->blk_4[3] = (u8)strtou64(&blk_start[6], 0, 16);
1341 blk_start[8] = save;
1343 save = blk_start[10];
1344 blk_start[10] = 0;
1345 guid->blk_4[4] = (u8)strtou64(&blk_start[8], 0, 16);
1346 blk_start[10] = save;
1348 guid->blk_4[5] = (u8)strtou64(&blk_start[10], 0, 16);
1350 STATIC void *edit_entry_to_entries(void)
1352 struct entry_t *dst_entry;
1354 dst_entry = &entries[edit_entry.num - 1];
1355 memcpy(&dst_entry->type, &types[edit_entry.type - 1].guid,
1356 sizeof(dst_entry->type));
1357 memcpy(&dst_entry->uniq, &edit_entry.uniq, sizeof(dst_entry->uniq));
1358 dst_entry->first = edit_entry.first_lba;
1359 dst_entry->last = edit_entry.last_lba;
1360 dst_entry->attrs = edit_entry.attrs;
1361 /* don't support name input yet */
1363 STATIC void gpt_write(void)
1365 entries_serialized_array_gen();
1366 /* crc32 on exactly the entries array size, not lba bounded */
1367 entries_serialized_array_crc32 = 0;
1368 le_crc32_update(entries_serialized_array, entry_bytes_n * entries_n,
1369 &entries_serialized_array_crc32);
1370 hdr_primary_serialized_gen();
1371 hdr_secondary_serialized_gen();
1372 protective_mbr_gen();
1373 protective_mbr_write();
1374 hdr_primary_serialized_write();
1375 hdr_secondary_serialized_write();
1376 entries_serialized_array_primary_write();
1377 entries_serialized_array_secondary_write();
1379 /*{{{ states */
1380 STATIC void *state_edit_entry_substate_attrs(size_t lf)
1382 if (lf == 0) {
1383 edit_entry.attrs = 0;
1384 goto exit;
1386 input_line[lf] = 0;
1387 edit_entry.attrs = strtou64(input_line, 0, 16);
1388 exit:
1390 * TODO: don't want to use iconv, will implement an utf8 to utf16
1391 * converter another time.
1393 edit_entry_to_entries();
1394 main_menu_prompt();
1395 return state_main_menu;
1397 STATIC void *state_edit_entry_substate_uniq_quid(size_t lf)
1399 if (lf == 1 && input_line[0] == 'r') {
1400 guid_randomize(&edit_entry.uniq);
1401 out_pf("generated guid is : ");
1402 goto exit;
1404 guid_read_from_input(&edit_entry.uniq, lf);
1405 out_pf("read guid is : ");
1406 exit:
1407 out_guid(&edit_entry.uniq);
1408 out_pf("\n");
1409 edit_entry_attrs_prompt();
1410 return state_edit_entry_substate_attrs;
1412 STATIC void *state_edit_entry_substate_type(size_t lf)
1414 if (lf == 1 && input_line[0] == 'l') {
1415 types_show();
1416 edit_entry_type_prompt();
1417 return state_edit_entry_substate_type;
1419 input_line[lf] = 0;
1420 edit_entry.type = (u32)strtou64(input_line, 0, 10);
1421 if (edit_entry.type == 0 || edit_entry.type > ARRAY_N(types)) {
1422 warning_pf("invalid type number\n");
1423 edit_entry_type_prompt();
1424 return state_edit_entry_substate_type;
1426 edit_entry_uniq_guid_prompt();
1427 return state_edit_entry_substate_uniq_quid;
1429 STATIC void *state_edit_entry_substate_last_lba(size_t lf)
1431 if (lf == 0)
1432 edit_entry.last_lba = edit_entry.last_lba_default;
1433 else {
1434 input_line[lf] = 0;
1435 edit_entry.last_lba = strtou64(input_line, 0, 10);
1437 edit_entry_type_prompt();
1438 return state_edit_entry_substate_type;
1440 STATIC void *state_edit_entry_substate_first_lba(size_t lf)
1442 if (lf == 0) {
1443 edit_entry.first_lba = edit_entry.first_lba_default;
1444 } else {
1445 input_line[lf] = 0;
1446 edit_entry.first_lba = strtou64(input_line, 0, 10);
1448 last_lba_default_select();
1449 edit_entry_last_lba_prompt();
1450 return state_edit_entry_substate_last_lba;
1452 STATIC void *state_edit_entry(size_t lf)
1454 input_line[lf] = 0;
1455 edit_entry.num = (u32)strtou64(input_line, 0, 10);
1456 if (1 <= edit_entry.num && edit_entry.num <= entries_n) {
1457 first_lba_default_select();
1458 edit_entry_first_lba_prompt();
1459 return state_edit_entry_substate_first_lba;
1460 } else
1461 warning_pf("%"PRIu32" is an invalid partition entry number\n", edit_entry.num);
1462 main_menu_prompt();
1463 return state_main_menu;
1465 STATIC void *state_delete_entry(size_t lf)
1467 u8 *end;
1468 u32 entry_num;
1470 input_line[lf] = 0;
1471 entry_num = (u32)strtou64(input_line, 0, 10);
1472 if (1 <= entry_num && entry_num <= entries_n) {
1473 out_bold_pf("deleting partition entry %"PRIu64"\n", entry_num);
1474 entry_delete((u32)(entry_num - 1));
1475 } else
1476 out_bold_pf("partition number out of range %"PRIu64"\n", entry_num);
1477 main_menu_prompt();
1478 return state_main_menu;
1480 STATIC void *state_change_disk_guid(size_t lf)
1482 if (lf == 1) {
1483 if (input_line[0] == 'r') {
1484 guid_randomize(&hdrs.disk_guid);
1485 out_pf("randomized disk guid is ");
1487 } else {
1488 guid_read_from_input(&hdrs.disk_guid, lf);
1489 out_pf("read disk guid is ");
1491 out_guid(&hdrs.disk_guid);
1492 out_pf("\n");
1493 main_menu_prompt();
1494 return state_main_menu;
1496 STATIC void *state_main_menu(size_t lf)
1498 if (lf == 1) {
1499 switch(input_line[0]) {
1500 case 'q':
1501 out_pf("quit\n");
1502 exit(0);
1503 case 'm':
1504 main_menu_show();
1505 break;
1506 case 'p':
1507 entries_show();
1508 break;
1509 case 'h':
1510 hdr_show();
1511 break;
1512 case 'd':
1513 delete_entry_prompt();
1514 return state_delete_entry;
1515 case 'n':
1516 edit_entry_prompt();
1517 return state_edit_entry;
1518 case 'u':
1519 change_disk_guid_prompt();
1520 return state_change_disk_guid;
1521 case 'w':
1522 gpt_write();
1523 exit(0);
1524 default:
1525 break;
1528 main_menu_prompt();
1529 return state_main_menu;
1531 /*}}} states -- end */
1532 /* end is the index of the byte past the last read byte */
1533 STATIC size_t input_line_consume(size_t lf, size_t end)
1535 input_state = input_state(lf);
1537 /* update the input line buffer and offsets */
1538 if (lf == (BUFSIZ - 1)) { /* lf is the last char in our line buffer */
1539 memset(input_line, 0, BUFSIZ);
1540 return 0;
1542 memmove(input_line, input_line + lf + 1, end - (lf + 1));
1543 return end - (lf + 1);
1545 STATIC void input_line_loop(void)
1547 bool discarding;
1548 size_t end;
1550 end = 0;
1551 memset(input_line, 0, BUFSIZ);
1552 discarding = false;
1553 input_state = state_main_menu;
1554 main_menu_prompt();
1555 loop {
1556 int r;
1557 size_t lf;
1559 errno = 0;
1560 r = read(0, &input_line[end], BUFSIZ - end);
1561 if (r == -1) {
1562 if (errno == EINTR)
1563 continue;
1564 error_pf("error reading input line from standard input\n");
1566 lf = end;
1567 end += (size_t)r;
1568 loop { /* scan what was read for lf */
1569 if (lf == end)
1570 break;
1571 if (input_line[lf] == '\n') {
1572 if (discarding) {
1573 discarding = false;
1574 end = 0;
1575 break;
1577 end = input_line_consume(lf, end);
1578 break;
1580 ++lf;
1582 if (end == BUFSIZ) {
1583 if (!discarding) {
1584 out_pf("your input line is too long, discarding...\n");
1585 discarding = true;
1587 end = 0;
1591 STATIC void init_once(void)
1593 le_crc32_tbl_gen();
1594 colors_on = true;
1595 load_previous_gpt = true;
1596 entries = 0;
1597 entries_n = 0;
1598 memset(&hdrs, 0, sizeof(hdrs));
1600 STATIC void usage(void)
1602 BOLD;
1603 out_pf("\
1604 nyangpt: line input oriented minimal GPT partition creator\n\
1606 usage:\n\
1607 nyangpt [-nc] [-nl] [-h|--help][ [--] device_path\n\
1609 -nc: disable ANSI terminal colors\n\
1610 -nl: don't load previous GPT partitions\n\
1611 -h|--help: this summary\n");
1612 RESTORE;
1613 exit(0);
1615 STATIC void options_parse(u32 argc, utf8 **argv)
1617 u32 arg;
1618 bool no_more_options;
1619 bool print_usage;
1621 if (argc == 1)
1622 usage();
1623 arg = 1;
1624 no_more_options = false;
1625 print_usage = false;
1626 blk_dev.path = 0;
1627 loop {
1628 if (arg == argc)
1629 break;
1630 if (no_more_options) {
1631 if (blk_dev.path == 0)
1632 blk_dev.path = argv[arg];
1633 } else if (strcmp(argv[arg], "--") == 0) {
1634 no_more_options = true;
1635 } else if (strcmp(argv[arg], "-nc") == 0) {
1636 colors_on = false;
1637 } else if (strcmp(argv[arg], "-nl") == 0) {
1638 load_previous_gpt = false;
1639 } else if (strcmp(argv[arg], "-h") == 0
1640 || strcmp(argv[arg], "--help") == 0) {
1641 print_usage = true;
1642 } else {
1643 if (blk_dev.path == 0)
1644 blk_dev.path = argv[arg];
1646 ++arg;
1648 if (print_usage || blk_dev.path == 0)
1649 usage();
1651 int nyangpt_main(int argc, utf8 **argv)
1653 u64 whole_dev_bytes_n;
1654 u64 entries_bytes_n;
1656 init_once();
1657 options_parse(argc, argv);
1658 out_pf("block device path is %s, opening...\n", blk_dev.path);
1659 blk_dev.fd = open(blk_dev.path, O_RDWR | O_SYNC);
1660 if (blk_dev.fd == -1)
1661 error_pf("%s:unable to open\n", blk_dev.path);
1662 out_pf("%s:opened\n", blk_dev.path);
1664 sysfs_infos_get();
1666 whole_dev_bytes_n = blk_dev.sz_512_n * 512;
1667 if ((whole_dev_bytes_n % blk_dev.logical_blk_bytes_n) != 0)
1668 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);
1669 /* the total number of lba, -1 to get the offset of the last one */
1670 blk_dev.last_lba = blk_dev.sz_512_n * 512 / blk_dev.logical_blk_bytes_n - 1;
1671 out_pf("%s:last lba is %"PRIu64"\n", blk_dev.path, blk_dev.last_lba);
1673 if (load_previous_gpt) {
1674 previous_gpt_load();
1675 build_data_from_previous_gpt();
1678 * Resizing the array of entries is expensive and dangerous, try to
1679 * keep it constant as much as possible.
1680 * If it was not initialized based from the previous gpt, init one
1681 * with sane defaults.
1683 if (entries_n == 0)
1684 entries_reset();
1685 if (hdrs.bytes_n == 0)
1686 hdrs.bytes_n = HDR_BYTES_N;
1687 if (guid_is_zero(&hdrs.disk_guid))
1688 guid_randomize(&hdrs.disk_guid);
1690 * Once we have "an array of entries", the first and last usable
1691 * lbas must be recomputed.
1692 * We ignore the values from the previous gpt headers, we prefer
1693 * to recompute them.
1695 hdrs_usable_lbas_compute();
1696 input_line_loop();
1697 return 0;
1699 /*{{{ preprocessor space cleanup */
1700 #undef ARRAY_N
1701 #undef BLK_0
1702 #undef BLK_1
1703 #undef BLK_2
1704 #undef BLK_3
1705 #undef BLK_4
1706 #undef BOLD_RED
1707 #undef BOLD_ORANGE
1708 #undef BOLD
1709 #undef RESTORE
1710 #undef BOOT_SIGNATURE_0X55
1711 #undef BOOT_SIGNATURE_0XAA
1712 #undef ENTRIES_ARRAY_MIN_BYTES_N
1713 #undef ENTRY_BYTES_N
1714 #undef HDR_BYTES_N
1715 #undef HDR_REVISION
1716 #undef HDR_SIGNATURE
1717 #undef HDR_SIGNATURE_BYTES_N
1718 #undef loop
1719 #undef PART_0
1720 #undef s32
1721 #undef STATIC
1722 #undef strtou64
1723 #undef u8
1724 #undef u16
1725 #undef u32
1726 #undef u64
1727 #undef utf16
1728 #undef utf8
1729 #undef X64_UTF8_BYTES_MAX
1730 /*}}} preprocessor space cleanup -- end */
1731 /*{{{ namespace cleanup ------------------------------------------------------*/
1732 #undef blk_dev
1733 #undef build_data_from_previous_gpt
1734 #undef change_disk_guid_prompt
1735 #undef colors_on
1736 #undef delete_entry_prompt
1737 #undef edit_entry
1738 #undef edit_entry_attrs_prompt
1739 #undef edit_entry_first_lba_prompt
1740 #undef edit_entry_last_lba_prompt
1741 #undef edit_entry_prompt
1742 #undef edit_entry_to_entries
1743 #undef edit_entry_type_prompt
1744 #undef edit_entry_uniq_guid_prompt
1745 #undef entries
1746 #undef entries_lbas_n
1747 #undef entries_load
1748 #undef entries_n
1749 #undef entries_reset
1750 #undef entries_serialized_array
1751 #undef entries_serialized_array_crc32
1752 #undef entries_serialized_array_gen
1753 #undef entries_serialized_array_gen_entry
1754 #undef entries_serialized_array_primary_write
1755 #undef entries_serialized_array_secondary_write
1756 #undef entries_show
1757 #undef entry_bytes_n
1758 #undef entry_delete
1759 #undef entry_is_used
1760 #undef entry_show
1761 #undef entry_t
1762 #undef error_pf
1763 #undef first_lba_default_select
1764 #undef gpt_write
1765 #undef guid_is_zero
1766 #undef guid_t
1767 #undef guid_randomize
1768 #undef guid_read
1769 #undef guid_read_from_input
1770 #undef guid_write
1771 #undef hdr_load
1772 #undef hdr_show
1773 #undef hdr_primary_serialized
1774 #undef hdr_primary_serialized_gen
1775 #undef hdr_primary_serialized_write
1776 #undef hdr_secondary_serialized
1777 #undef hdr_secondary_serialized_gen
1778 #undef hdr_secondary_serialized_write
1779 #undef hdr_t
1780 #undef hdr_validate
1781 #undef hdrs
1782 #undef hdrs_usable_lbas_compute
1783 #undef init_once
1784 #undef input_line
1785 #undef input_line_consume
1786 #undef input_line_loop
1787 #undef input_state
1788 #undef last_lba_default_select
1789 #undef le_crc32_tbl_gen
1790 #undef le_crc32_update
1791 #undef load_previous_gpt
1792 #undef main_menu_prompt
1793 #undef main_menu_show
1794 #undef options_parse
1795 #undef out_bold_pf
1796 #undef out_guid
1797 #undef out_pf
1798 #undef gpt_load
1799 #undef protective_mbr
1800 #undef protective_mbr_gen
1801 #undef protective_mbr_write
1802 #undef read_full
1803 #undef state_change_disk_guid
1804 #undef state_delete_entry
1805 #undef state_edit_entry
1806 #undef state_edit_entry_substate_attrs
1807 #undef state_edit_entry_substate_first_lba
1808 #undef state_edit_entry_substate_last_lba
1809 #undef state_edit_entry_substate_type
1810 #undef state_edit_entry_substate_uniq_quid
1811 #undef state_main_menu
1812 #undef sysfs_infos_get
1813 #undef type_guids_lookup_name
1814 #undef types
1815 #undef types_show
1816 #undef usage
1817 #undef utf16_strdup
1818 #undef warning_pf
1819 #undef write_full
1820 /*----------------------------------------------------------------------------*/
1821 #undef nyangpt_main
1822 /*}}} namespace cleanup -- end -----------------------------------------------*/
1823 #endif