… | |
… | |
19 | # along with this program. If not, see <https://www.gnu.org/licenses/>. |
19 | # along with this program. If not, see <https://www.gnu.org/licenses/>. |
20 | # |
20 | # |
21 | |
21 | |
22 | use 5.016; # numerous features need 5.14, __SUB__ needs 5.16 |
22 | use 5.016; # numerous features need 5.14, __SUB__ needs 5.16 |
23 | |
23 | |
24 | our $VERSION = '1.2'; |
24 | our $VERSION = '1.4'; |
25 | our $JSON_VERSION = 2; # the version of the json objects generated by this program |
25 | our $JSON_VERSION = 3; # the version of the json objects generated by this program |
26 | |
26 | |
27 | our $CHANGELOG = <<EOF; |
27 | our $CHANGELOG = <<EOF; |
|
|
28 | |
|
|
29 | - work around lsblk bug sometimes giving dos pttype for gpt partitions. |
|
|
30 | - bootmenupolicy in synopsis must be set to 0 for text menu. |
|
|
31 | |
|
|
32 | 1.4 Thu Aug 22 10:48:22 CEST 2019 |
|
|
33 | - new "create" subcommand. |
|
|
34 | - "create" and "edit" try to save and restore ownership/permissions |
|
|
35 | of bcd hives when writing the new file. |
|
|
36 | - editorial fixes to the documentation. |
|
|
37 | - add mininmal hive creation example. |
|
|
38 | |
|
|
39 | 1.3 Sat Aug 17 07:04:15 CEST 2019 |
|
|
40 | - output of pbcdedit elements --json has changed, as it didn't |
|
|
41 | take the reorganisation by classes fully into account. |
|
|
42 | - json schema bumped to 3. |
|
|
43 | - new "bcd-device" and "bcd-legacy-device" subcommands. |
|
|
44 | - implement --json option for lsblk. |
|
|
45 | |
28 | 1.2 Fri Aug 16 00:20:41 CEST 2019 |
46 | 1.2 Fri Aug 16 00:20:41 CEST 2019 |
29 | - bcde element names now depend on the bcd object type they are in, |
47 | - bcd element names now depend on the bcd object type they are in, |
30 | also affects "elements" output. |
48 | also affects "elements" output. |
31 | - json schema bumped to 2. |
49 | - json schema bumped to 2. |
32 | - new version command. |
50 | - new version command. |
33 | - numerous minor bugfixes. |
51 | - numerous minor bugfixes. |
34 | |
52 | |
… | |
… | |
48 | pbcdedit edit path/to/BCD edit-instructions... |
66 | pbcdedit edit path/to/BCD edit-instructions... |
49 | |
67 | |
50 | pbcdedit objects # list all supported object aliases and types |
68 | pbcdedit objects # list all supported object aliases and types |
51 | pbcdedit elements # list all supported bcd element aliases |
69 | pbcdedit elements # list all supported bcd element aliases |
52 | |
70 | |
|
|
71 | # Example: enable text-based boot menu. |
|
|
72 | pbcdedit edit /my/BCD set '{default}' bootmenupolicy 0 |
|
|
73 | |
|
|
74 | # Example change system device to first partition containing winload. |
|
|
75 | pbcdedit edit /my/BCD \ |
|
|
76 | set '{default}' device 'locate=<null>,element,path' \ |
|
|
77 | set '{default}' osdevice 'locate=<null>,element,path' |
|
|
78 | |
|
|
79 | |
53 | =head1 DESCRIPTION |
80 | =head1 DESCRIPTION |
54 | |
81 | |
55 | This program allows you to create, read and modify Boot Configuration Data |
82 | This program allows you to create, read and modify Boot Configuration Data |
56 | (BCD) stores used by Windows Vista and newer versions of Windows. |
83 | (BCD) stores used by Windows Vista and newer versions of Windows. |
57 | |
84 | |
… | |
… | |
136 | =item C<parse> F<path> I<instructions...> |
163 | =item C<parse> F<path> I<instructions...> |
137 | |
164 | |
138 | Same as C<edit>, above, except it doesn't save the data store again. Can |
165 | Same as C<edit>, above, except it doesn't save the data store again. Can |
139 | be useful to extract some data from it. |
166 | be useful to extract some data from it. |
140 | |
167 | |
141 | =item C<lsblk> |
168 | =item C<create> F<path> I<instructions...> |
|
|
169 | |
|
|
170 | Same as C<edit>, above, except it creates a new data store from scratch if |
|
|
171 | needed. An existing store will be emptied completely. |
|
|
172 | |
|
|
173 | =item C<lsblk> [C<--json>] |
142 | |
174 | |
143 | On a GNU/Linux system, you can get a list of partition device descriptors |
175 | On a GNU/Linux system, you can get a list of partition device descriptors |
144 | using this command - the external C<lsblk> command is required, as well as |
176 | using this command - the external C<lsblk> command is required, as well as |
145 | a mounted C</sys> file system. |
177 | a mounted C</sys> file system. |
146 | |
178 | |
147 | The output will be a list of all partitions in the system and C<partition> |
179 | The output will be a list of all partitions in the system and C<partition> |
148 | descriptors for GPT and both C<legacypartition> and C<partition> |
180 | descriptors for GPT and both C<legacypartition> and C<partition> |
149 | descriptors for MBR partitions. |
181 | descriptors for MBR partitions. |
|
|
182 | |
|
|
183 | With C<--json> it will print similar information as C<lsblk --json>, but |
|
|
184 | with extra C<bcd_device> and C<bcd_legacy_device> attributes. |
|
|
185 | |
|
|
186 | =item C<bcd-device> F<path> |
|
|
187 | |
|
|
188 | Tries to find the BCD device element for the given device, which currently |
|
|
189 | must be a a partition of some kind. Prints the C<partition=> descriptor as |
|
|
190 | a result, or nothing. Exit status will be true on success, and false on |
|
|
191 | failure. |
|
|
192 | |
|
|
193 | Like C<lsblk>, above, this likely only works on GNU/Linux systems. |
|
|
194 | |
|
|
195 | Example: print the partition descriptor of tghe partition with label DATA. |
|
|
196 | |
|
|
197 | $ pbcdedit bcd-device /dev/disk/by-label/DATA |
|
|
198 | partition=<null>,harddisk,mbr,47cbc08a,213579202560 |
|
|
199 | |
|
|
200 | =item C<bcd-legacy-device> F<path> |
|
|
201 | |
|
|
202 | Like above, but uses a C<legacypartition> descriptor instead. |
150 | |
203 | |
151 | =item C<objects> [C<--json>] |
204 | =item C<objects> [C<--json>] |
152 | |
205 | |
153 | Outputs two tables: a table listing all type aliases with their hex BCD |
206 | Outputs two tables: a table listing all type aliases with their hex BCD |
154 | element ID, and all object name aliases with their GUID and default type |
207 | element ID, and all object name aliases with their GUID and default type |
… | |
… | |
256 | |
309 | |
257 | Note that minimal doesn't mean recommended - Windows itself will add stuff |
310 | Note that minimal doesn't mean recommended - Windows itself will add stuff |
258 | to this during or after boot, and you might or might not run into issues |
311 | to this during or after boot, and you might or might not run into issues |
259 | when installing updates as it might not be able to find the F<bootmgr>. |
312 | when installing updates as it might not be able to find the F<bootmgr>. |
260 | |
313 | |
|
|
314 | This is how you would create a minimal hive with PBCDEDIT from within |
|
|
315 | GNU/Linux, assuming F</dev/sdc3> is the windows partition, using |
|
|
316 | a random GUID for the osloader and using C<partition> instead of |
|
|
317 | C<legacypartition>: |
|
|
318 | |
|
|
319 | osldr="{$(uuidgen)}" |
|
|
320 | part=$(pbcdedit bcd-device /dev/sdc3) |
|
|
321 | pbcdedit create minimal.bcd \ |
|
|
322 | set '{bootmgr}' default "$osldr" \ |
|
|
323 | set "$osldr" type application::osloader \ |
|
|
324 | set "$osldr" description 'Windows Boot' \ |
|
|
325 | set "$osldr" device "$part" \ |
|
|
326 | set "$osldr" osdevice "$part" \ |
|
|
327 | set "$osldr" path '\Windows\system32\winload.exe' \ |
|
|
328 | set "$osldr" systemroot '\Windows' |
|
|
329 | |
261 | =head2 The C<meta> key |
330 | =head2 The C<meta> key |
262 | |
331 | |
263 | The C<meta> key is not stored in the BCD data store but is used only |
332 | The C<meta> key is not stored in the BCD data store but is used only |
264 | by PBCDEDIT. It is always generated when exporting, and importing will |
333 | by PBCDEDIT. It is always generated when exporting, and importing will |
265 | be refused when it exists and the version stored inside doesn't store |
334 | be refused when it exists and the version stored inside doesn't store |
… | |
… | |
352 | |
421 | |
353 | "displaybootmenu" : 0, |
422 | "displaybootmenu" : 0, |
354 | |
423 | |
355 | =item integer |
424 | =item integer |
356 | |
425 | |
357 | Again, very simple, this is a 64 bit integer. IT can be either specified |
426 | Again, very simple, this is a 64 bit integer. It can be either specified |
358 | as a decimal number, as a hex number (by prefixing it with C<0x>) or as a |
427 | as a decimal number, as a hex number (by prefixing it with C<0x>) or as a |
359 | binary number (prefix C<0b>). |
428 | binary number (prefix C<0b>). |
360 | |
429 | |
361 | For example, the boot C<timeout> is an integer, specifying the automatic |
430 | For example, the boot C<timeout> is an integer, specifying the automatic |
362 | boot delay in seconds: |
431 | boot delay in seconds: |
… | |
… | |
364 | "timeout" : 30, |
433 | "timeout" : 30, |
365 | |
434 | |
366 | =item integer list |
435 | =item integer list |
367 | |
436 | |
368 | This is a list of 64 bit integers separated by whitespace. It is not used |
437 | This is a list of 64 bit integers separated by whitespace. It is not used |
369 | much, so here is a somewhat artificial an untested example of using |
438 | much, so here is a somewhat artificial and untested example of using |
370 | C<customactions> to specify a certain custom, eh, action to be executed |
439 | C<customactions> to specify a certain custom, eh, action to be executed |
371 | when pressing C<F10> at boot: |
440 | when pressing C<F10> at boot: |
372 | |
441 | |
373 | "customactions" : "0x1000044000001 0x54000001", |
442 | "customactions" : "0x1000044000001 0x54000001", |
374 | |
443 | |
… | |
… | |
402 | This type is why I write I<most> are easy to explain earlier: This type |
471 | This type is why I write I<most> are easy to explain earlier: This type |
403 | is the pinnacle of Microsoft-typical hacks layered on top of other |
472 | is the pinnacle of Microsoft-typical hacks layered on top of other |
404 | hacks. Understanding this type took more time than writing all the rest of |
473 | hacks. Understanding this type took more time than writing all the rest of |
405 | PBCDEDIT, and because it is so complex, this type has its own subsection |
474 | PBCDEDIT, and because it is so complex, this type has its own subsection |
406 | below. |
475 | below. |
|
|
476 | |
407 | =back |
477 | =back |
408 | |
478 | |
409 | =head4 The BCD "device" element type |
479 | =head3 The BCD "device" element type |
410 | |
480 | |
411 | Device elements specify, well, devices. They are used for such diverse |
481 | Device elements specify, well, devices. They are used for such diverse |
412 | purposes such as finding a TFTP network boot image, serial ports or VMBUS |
482 | purposes such as finding a TFTP network boot image, serial ports or VMBUS |
413 | devices, but most commonly they are used to specify the disk (harddisk, |
483 | devices, but most commonly they are used to specify the disk (harddisk, |
414 | cdrom, ramdisk, vhd...) to boot from. |
484 | cdrom, ramdisk, vhd...) to boot from. |
… | |
… | |
422 | element, so almost everything known about it had to be researched first |
492 | element, so almost everything known about it had to be researched first |
423 | in the process of writing this script, and consequently, support for BCD |
493 | in the process of writing this script, and consequently, support for BCD |
424 | device elements is partial only. |
494 | device elements is partial only. |
425 | |
495 | |
426 | On the other hand, the expressive power of PBCDEDIT in specifying devices |
496 | On the other hand, the expressive power of PBCDEDIT in specifying devices |
427 | is much bigger than BCDEDIT and therefore more can be done with it. The |
497 | is much greater than BCDEDIT and therefore more can be done with it. The |
428 | downside is that BCD device elements are much more complicated than what |
498 | downside is that BCD device elements are much more complicated than what |
429 | you might think from reading the BCDEDIT documentation. |
499 | you might think from reading the BCDEDIT documentation. |
430 | |
500 | |
431 | In other words, simple things are complicated, and complicated things are |
501 | In other words, simple things are complicated, and complicated things are |
432 | possible. |
502 | possible. |
… | |
… | |
475 | the leading GUID, which it can always decode). In such cases, it will |
545 | the leading GUID, which it can always decode). In such cases, it will |
476 | convert the device into this type with a hexdump of the element data. |
546 | convert the device into this type with a hexdump of the element data. |
477 | |
547 | |
478 | =item C<null> |
548 | =item C<null> |
479 | |
549 | |
480 | This is another special type - sometimes, a device all zero-filled, which |
550 | This is another special type - sometimes, a device is all zero-filled, |
481 | is not valid. This can mark the absence of a device or something PBCDEDIT |
551 | which is not valid. This can mark the absence of a device or something |
482 | does not understand, so it decodes it into this special "all zero" type |
552 | PBCDEDIT does not understand, so it decodes it into this special "all |
483 | called C<null>. |
553 | zero" type called C<null>. |
484 | |
554 | |
485 | It's most commonly found in devices that can use an optional parent |
555 | It's most commonly found in devices that can use an optional parent |
486 | device, when no parent device is used. |
556 | device, when no parent device is used. |
487 | |
557 | |
488 | =item C<boot> |
558 | =item C<boot> |
… | |
… | |
599 | |
669 | |
600 | Last not least, the most complex type, C<block>, which... specifies block |
670 | Last not least, the most complex type, C<block>, which... specifies block |
601 | devices (which could be inside a F<vhdx> file for example). |
671 | devices (which could be inside a F<vhdx> file for example). |
602 | |
672 | |
603 | I<devicetypes> is one of C<harddisk>, C<floppy>, C<cdrom>, C<ramdisk>, |
673 | I<devicetypes> is one of C<harddisk>, C<floppy>, C<cdrom>, C<ramdisk>, |
604 | C<file> or C<vhd> - the same as for C<partiion=>. |
674 | C<file> or C<vhd> - the same as for C<partition=>. |
605 | |
675 | |
606 | The remaining arguments change depending on the I<devicetype>: |
676 | The remaining arguments change depending on the I<devicetype>: |
607 | |
677 | |
608 | =over |
678 | =over |
609 | |
679 | |
… | |
… | |
639 | |
709 | |
640 | Probably not yet implemented. Tell me of your needs... |
710 | Probably not yet implemented. Tell me of your needs... |
641 | |
711 | |
642 | =back |
712 | =back |
643 | |
713 | |
644 | =back5 Examples |
714 | =head4 Examples |
645 | |
715 | |
646 | This concludes the syntax overview for device elements, but probably |
716 | This concludes the syntax overview for device elements, but probably |
647 | leaves many questions open. I can't help with most of them, as I also ave |
717 | leaves many questions open. I can't help with most of them, as I also have |
648 | many questions, but I can walk you through some actual examples using more |
718 | many questions, but I can walk you through some actual examples using more |
649 | complex aspects. |
719 | complex aspects. |
650 | |
720 | |
651 | =item C<< locate=<block=vhd,<block=file,<locate=<null>,path,\disk.vhdx>,\disk.vhdx>>,element,path >> |
721 | =item C<< locate=<block=vhd,<block=file,<locate=<null>,path,\disk.vhdx>,\disk.vhdx>>,element,path >> |
652 | |
722 | |
… | |
… | |
866 | sub xxd($$) { |
936 | sub xxd($$) { |
867 | open my $xxd, "| xxd | sed -e 's/^/\Q$_[0]\E: /'"; |
937 | open my $xxd, "| xxd | sed -e 's/^/\Q$_[0]\E: /'"; |
868 | syswrite $xxd, $_[1]; |
938 | syswrite $xxd, $_[1]; |
869 | } |
939 | } |
870 | |
940 | |
|
|
941 | # get some meta info on a file (uid, gid, perms) |
|
|
942 | sub stat_get($) { |
|
|
943 | [(stat shift)[4, 5, 2]] |
|
|
944 | } |
|
|
945 | |
|
|
946 | # set stat info on a file |
|
|
947 | sub stat_set($$) { |
|
|
948 | my ($fh_or_path, $stat) = @_; |
|
|
949 | |
|
|
950 | return unless $stat; |
|
|
951 | chown $stat->[0], $stat->[1], $fh_or_path; |
|
|
952 | chmod +($stat->[2] & 07777), $fh_or_path; |
|
|
953 | } |
|
|
954 | |
871 | sub file_load($) { |
955 | sub file_load($) { |
872 | my ($path) = @_; |
956 | my ($path) = @_; |
873 | |
957 | |
874 | open my $fh, "<:raw", $path |
958 | open my $fh, "<:raw", $path |
875 | or die "$path: $!\n"; |
959 | or die "$path: $!\n"; |
876 | my $size = -s $fh; |
960 | my $size = -s $fh; |
877 | $size = read $fh, my $buf, $size |
961 | $size = read $fh, my $buf, $size |
878 | or die "$path: short read\n"; |
962 | or die "$path: short read\n"; |
879 | |
963 | |
880 | $buf |
964 | $buf |
|
|
965 | } |
|
|
966 | |
|
|
967 | sub file_save($$;$) { |
|
|
968 | my ($path, $data, $stat) = @_; |
|
|
969 | |
|
|
970 | open my $fh, ">:raw", "$path~" |
|
|
971 | or die "$path~: $!\n"; |
|
|
972 | print $fh $data |
|
|
973 | or die "$path~: short write\n"; |
|
|
974 | stat_set $fh, $stat; |
|
|
975 | $fh->sync; |
|
|
976 | close $fh; |
|
|
977 | |
|
|
978 | rename "$path~", $path; |
881 | } |
979 | } |
882 | |
980 | |
883 | # sources and resources used for writing pbcdedit |
981 | # sources and resources used for writing pbcdedit |
884 | # |
982 | # |
885 | # registry: |
983 | # registry: |
… | |
… | |
1071 | my ($rname, $root) = $decode_key->($rootcell); |
1169 | my ($rname, $root) = $decode_key->($rootcell); |
1072 | |
1170 | |
1073 | [$rname, $root] |
1171 | [$rname, $root] |
1074 | } |
1172 | } |
1075 | |
1173 | |
1076 | # return a binary windows fILETIME struct |
1174 | # return a binary windows FILETIME struct |
1077 | sub filetime_now { |
1175 | sub filetime_now { |
1078 | my ($s, $ms) = Time::HiRes::gettimeofday; |
1176 | my ($s, $ms) = Time::HiRes::gettimeofday; |
1079 | |
1177 | |
1080 | pack "Q<", $s = ($s * 1_000_000 + $ms) * 10 + 116_444_736_000_000_000 |
1178 | pack "Q<", ($s * 1_000_000 + $ms) * 10 |
|
|
1179 | + 116_444_736_000_000_000 # 1970-01-01 00:00:00 |
1081 | } |
1180 | } |
1082 | |
1181 | |
1083 | # encode a registry hive |
1182 | # encode a registry hive |
1084 | sub regf_encode($) { |
1183 | sub regf_encode($) { |
1085 | my ($hive) = @_; |
1184 | my ($hive) = @_; |
… | |
… | |
1242 | |
1341 | |
1243 | regf_decode file_load $path |
1342 | regf_decode file_load $path |
1244 | } |
1343 | } |
1245 | |
1344 | |
1246 | # encode and save registry to file |
1345 | # encode and save registry to file |
1247 | sub regf_save { |
1346 | sub regf_save($$;$) { |
1248 | my ($path, $hive) = @_; |
1347 | my ($path, $hive, $stat) = @_; |
1249 | |
1348 | |
1250 | $hive = regf_encode $hive; |
1349 | $hive = regf_encode $hive; |
1251 | |
1350 | |
1252 | open my $regf, ">:raw", "$path~" |
1351 | file_save $path, $hive, $stat; |
1253 | or die "$path~: $!\n"; |
|
|
1254 | print $regf $hive |
|
|
1255 | or die "$path~: short write\n"; |
|
|
1256 | $regf->sync; |
|
|
1257 | close $regf; |
|
|
1258 | |
|
|
1259 | rename "$path~", $path; |
|
|
1260 | } |
1352 | } |
1261 | |
1353 | |
1262 | ############################################################################# |
1354 | ############################################################################# |
1263 | # bcd stuff |
1355 | # bcd stuff |
1264 | |
1356 | |
… | |
… | |
2199 | or die "$_: malformed or missing vmbus interface instance guid\n"; |
2291 | or die "$_: malformed or missing vmbus interface instance guid\n"; |
2200 | my $instance = enc_guid $1; |
2292 | my $instance = enc_guid $1; |
2201 | |
2293 | |
2202 | $payload = pack "a16a16x24", $type, $instance; |
2294 | $payload = pack "a16a16x24", $type, $instance; |
2203 | |
2295 | |
|
|
2296 | # } elsif ($type eq "udp") { |
|
|
2297 | # $payload = pack "Va16", 1, "12345678"; |
|
|
2298 | |
2204 | } else { |
2299 | } else { |
2205 | die "$type: not a supported device type (binary, null, boot, legacypartition, partition, block, locate)\n"; |
2300 | die "$type: not a supported device type (binary, null, boot, legacypartition, partition, block, locate)\n"; |
2206 | } |
2301 | } |
2207 | |
2302 | |
2208 | return ( |
2303 | return ( |
… | |
… | |
2390 | } |
2485 | } |
2391 | |
2486 | |
2392 | } |
2487 | } |
2393 | |
2488 | |
2394 | ############################################################################# |
2489 | ############################################################################# |
2395 | # command line parser |
2490 | # other utilities |
2396 | |
2491 | |
2397 | # json to stdout |
2492 | # json to stdout |
2398 | sub prjson($) { |
2493 | sub prjson($) { |
2399 | print $json_coder->encode ($_[0]); |
2494 | print $json_coder->encode ($_[0]); |
2400 | } |
2495 | } |
… | |
… | |
2404 | my $json; |
2499 | my $json; |
2405 | 1 while read STDIN, $json, 65536, length $json; |
2500 | 1 while read STDIN, $json, 65536, length $json; |
2406 | $json_coder->decode ($json) |
2501 | $json_coder->decode ($json) |
2407 | } |
2502 | } |
2408 | |
2503 | |
2409 | # all subcommands |
2504 | sub lsblk() { |
|
|
2505 | my $lsblk = $json_coder->decode (scalar qx<lsblk --json -o PATH,KNAME,MAJ:MIN,TYPE,PTTYPE,PTUUID,PARTUUID,LABEL,FSTYPE>); |
|
|
2506 | |
|
|
2507 | for my $dev (@{ $lsblk->{blockdevices} }) { |
|
|
2508 | if ($dev->{type} eq "part") { |
|
|
2509 | |
|
|
2510 | # lsblk sometimes gives a bogus pttype, so we recreate it here |
|
|
2511 | $dev->{pttype} = $dev->{ptuuid} =~ /^$RE_GUID\z/ |
|
|
2512 | ? "gpt" : "dos"; |
|
|
2513 | |
|
|
2514 | if ($dev->{pttype} eq "gpt") { |
|
|
2515 | $dev->{bcd_device} = "partition=<null>,harddisk,gpt,$dev->{ptuuid},$dev->{partuuid}"; |
|
|
2516 | } elsif ($dev->{pttype} eq "dos") { # why not "mbr" :( |
|
|
2517 | if ($dev->{partuuid} =~ /^([0-9a-f]{8})-([0-9a-f]{2})\z/i) { |
|
|
2518 | my ($diskid, $partno) = ($1, hex $2); |
|
|
2519 | $dev->{bcd_legacy_device} = "legacypartition=<null>,harddisk,mbr,$diskid,$partno"; |
|
|
2520 | if (open my $fh, "/sys/class/block/$dev->{kname}/start") { |
|
|
2521 | my $start = 512 * readline $fh; |
|
|
2522 | $dev->{bcd_device} = "partition=<null>,harddisk,mbr,$diskid,$start"; |
|
|
2523 | } |
|
|
2524 | } |
|
|
2525 | } |
|
|
2526 | } |
|
|
2527 | } |
|
|
2528 | |
|
|
2529 | $lsblk->{blockdevices} |
|
|
2530 | } |
|
|
2531 | |
|
|
2532 | sub prdev($$) { |
|
|
2533 | my ($path, $attribute) = @_; |
|
|
2534 | |
|
|
2535 | # rather than stat'ing and guessing how devices are encoded, we use lsblk for this |
|
|
2536 | my $mm = $json_coder->decode (scalar qx<lsblk -d -o MAJ:MIN -J \Q$path\E>)->{blockdevices}[0]{"maj:min"}; |
|
|
2537 | |
|
|
2538 | my $lsblk = lsblk; |
|
|
2539 | |
|
|
2540 | for my $dev (@$lsblk) { |
|
|
2541 | if ($dev->{"maj:min"} eq $mm && $dev->{$attribute}) { |
|
|
2542 | say $dev->{$attribute}; |
|
|
2543 | exit 0; |
|
|
2544 | } |
|
|
2545 | } |
|
|
2546 | |
|
|
2547 | exit 1; |
|
|
2548 | } |
|
|
2549 | |
|
|
2550 | ############################################################################# |
|
|
2551 | # command line parser |
|
|
2552 | |
2410 | our %CMD = ( |
2553 | our %CMD = ( |
2411 | help => sub { |
2554 | help => sub { |
2412 | require Pod::Usage; |
2555 | require Pod::Usage; |
2413 | Pod::Usage::pod2usage (-verbose => 2); |
2556 | Pod::Usage::pod2usage (-verbose => 2); |
2414 | }, |
2557 | }, |
… | |
… | |
2465 | BCDE_FORMAT_INTEGER , "integer", |
2608 | BCDE_FORMAT_INTEGER , "integer", |
2466 | BCDE_FORMAT_BOOLEAN , "boolean", |
2609 | BCDE_FORMAT_BOOLEAN , "boolean", |
2467 | BCDE_FORMAT_INTEGER_LIST, "integer list", |
2610 | BCDE_FORMAT_INTEGER_LIST, "integer list", |
2468 | ); |
2611 | ); |
2469 | |
2612 | |
2470 | my %element; |
2613 | my @element; |
2471 | |
2614 | |
2472 | for my $class (sort keys %rbcde_byclass) { |
2615 | for my $class (sort keys %rbcde_byclass) { |
2473 | my $rbcde = $rbcde_byclass{$class}; |
2616 | my $rbcde = $rbcde_byclass{$class}; |
2474 | |
2617 | |
2475 | unless ($json) { |
2618 | unless ($json) { |
… | |
… | |
2478 | printf "%-9s %-12s %s\n", "Element", "Format", "Name Alias"; |
2621 | printf "%-9s %-12s %s\n", "Element", "Format", "Name Alias"; |
2479 | } |
2622 | } |
2480 | for my $name (sort keys %$rbcde) { |
2623 | for my $name (sort keys %$rbcde) { |
2481 | my $id = $rbcde->{$name}; |
2624 | my $id = $rbcde->{$name}; |
2482 | my $format = $format_name{$id & BCDE_FORMAT}; |
2625 | my $format = $format_name{$id & BCDE_FORMAT}; |
2483 | $id = sprintf "%08x", $id; |
|
|
2484 | |
2626 | |
2485 | if ($json) { |
2627 | if ($json) { |
2486 | $element{$id} = [$class, $format, $name]; |
2628 | push @element, [$class, $id * 1, $format, $name]; |
2487 | } else { |
2629 | } else { |
|
|
2630 | $id = sprintf "%08x", $id; |
2488 | printf "%-9s %-12s %s\n", $id, $format, $name; |
2631 | printf "%-9s %-12s %s\n", $id, $format, $name; |
2489 | } |
2632 | } |
2490 | } |
2633 | } |
2491 | } |
2634 | } |
2492 | print "\n" unless $json; |
2635 | print "\n" unless $json; |
2493 | |
2636 | |
2494 | prjson { |
2637 | prjson { |
2495 | version => $JSON_VERSION, |
2638 | version => $JSON_VERSION, |
2496 | element => \%element, |
2639 | element => \@element, |
2497 | class => \@bcde_typeclass, |
2640 | class => \@bcde_typeclass, |
2498 | } if $json; |
2641 | } if $json; |
2499 | |
2642 | |
2500 | }, |
2643 | }, |
2501 | |
2644 | |
… | |
… | |
2505 | |
2648 | |
2506 | import => sub { |
2649 | import => sub { |
2507 | regf_save shift, bcd_encode rdjson; |
2650 | regf_save shift, bcd_encode rdjson; |
2508 | }, |
2651 | }, |
2509 | |
2652 | |
|
|
2653 | create => sub { |
|
|
2654 | my $path = shift; |
|
|
2655 | my $stat = stat_get $path; # should actually be done at file load time |
|
|
2656 | my $bcd = { }; |
|
|
2657 | bcd_edit $path, $bcd, @_; |
|
|
2658 | regf_save $path, bcd_encode $bcd; |
|
|
2659 | stat_set $path, $stat; |
|
|
2660 | }, |
|
|
2661 | |
2510 | edit => sub { |
2662 | edit => sub { |
2511 | my $path = shift; |
2663 | my $path = shift; |
|
|
2664 | my $stat = stat_get $path; # should actually be done at file load time |
2512 | my $bcd = bcd_decode regf_load $path; |
2665 | my $bcd = bcd_decode regf_load $path; |
2513 | bcd_edit $path, $bcd, @_; |
2666 | bcd_edit $path, $bcd, @_; |
2514 | regf_save $path, bcd_encode $bcd; |
2667 | regf_save $path, bcd_encode $bcd; |
|
|
2668 | stat_set $path, $stat; |
2515 | }, |
2669 | }, |
2516 | |
2670 | |
2517 | parse => sub { |
2671 | parse => sub { |
2518 | my $path = shift; |
2672 | my $path = shift; |
2519 | my $bcd = bcd_decode regf_load $path; |
2673 | my $bcd = bcd_decode regf_load $path; |
… | |
… | |
2528 | "import-regf" => sub { |
2682 | "import-regf" => sub { |
2529 | regf_save shift, rdjson; |
2683 | regf_save shift, rdjson; |
2530 | }, |
2684 | }, |
2531 | |
2685 | |
2532 | lsblk => sub { |
2686 | lsblk => sub { |
|
|
2687 | my $json = $_[0] eq "--json"; |
|
|
2688 | |
|
|
2689 | my $lsblk = lsblk; |
|
|
2690 | |
|
|
2691 | if ($json) { |
|
|
2692 | prjson $lsblk; |
|
|
2693 | } else { |
2533 | printf "%-10s %-8.8s %-6.6s %-3s %s\n", "DEVICE", "LABEL", "FSTYPE", "PT", "DEVICE DESCRIPTOR"; |
2694 | printf "%-10s %-8.8s %-6.6s %-3s %s\n", "DEVICE", "LABEL", "FSTYPE", "PT", "DEVICE DESCRIPTOR"; |
2534 | |
2695 | for my $dev (@$lsblk) { |
2535 | my $lsblk = $json_coder->decode (scalar qx<lsblk --json -o PATH,KNAME,TYPE,PTTYPE,PTUUID,PARTUUID,LABEL,FSTYPE>); |
2696 | for my $bcd ($dev->{bcd_device}, $dev->{bcd_legacy_device}) { |
2536 | |
|
|
2537 | for my $dev (@{ $lsblk->{blockdevices} }) { |
|
|
2538 | my $pr = sub { |
|
|
2539 | printf "%-10s %-8.8s %-6.6s %-3s %s\n", |
2697 | printf "%-10s %-8.8s %-6.6s %-3s %s\n", |
2540 | $dev->{path}, $dev->{label}, $dev->{fstype}, $dev->{pttype}, $_[0]; |
2698 | $dev->{path}, $dev->{label}, $dev->{fstype}, $dev->{pttype}, $bcd |
2541 | }; |
|
|
2542 | |
|
|
2543 | if ($dev->{type} eq "part") { |
|
|
2544 | if ($dev->{pttype} eq "gpt") { |
|
|
2545 | $pr->("partition=<null>,harddisk,gpt,$dev->{ptuuid},$dev->{partuuid}"); |
|
|
2546 | } elsif ($dev->{pttype} eq "dos") { # why not "mbr" :( |
|
|
2547 | if ($dev->{partuuid} =~ /^([0-9a-f]{8})-([0-9a-f]{2})\z/i) { |
|
|
2548 | my ($diskid, $partno) = ($1, hex $2); |
|
|
2549 | $pr->("legacypartition=<null>,harddisk,mbr,$diskid,$partno"); |
|
|
2550 | if (open my $fh, "/sys/class/block/$dev->{kname}/start") { |
|
|
2551 | my $start = 512 * readline $fh; |
|
|
2552 | $pr->("partition=<null>,harddisk,mbr,$diskid,$start"); |
|
|
2553 | } |
2699 | if $bcd; |
2554 | } |
|
|
2555 | } |
2700 | } |
2556 | } |
2701 | } |
2557 | } |
2702 | } |
|
|
2703 | }, |
|
|
2704 | |
|
|
2705 | "bcd-device" => sub { |
|
|
2706 | prdev shift, "bcd_device"; |
|
|
2707 | }, |
|
|
2708 | |
|
|
2709 | "bcd-legacy-device" => sub { |
|
|
2710 | prdev shift, "bcd_legacy_device"; |
2558 | }, |
2711 | }, |
2559 | |
2712 | |
2560 | version => sub { |
2713 | version => sub { |
2561 | print "\n", |
2714 | print "\n", |
2562 | "PBCDEDIT version $VERSION, copyright 2019 Marc A. Lehmann <pbcdedit\@schmorp.de>.\n", |
2715 | "PBCDEDIT version $VERSION, copyright 2019 Marc A. Lehmann <pbcdedit\@schmorp.de>.\n", |