1
0
Fork 0
mirror of https://gitlab.com/bztsrc/bootboot.git synced 2023-02-13 20:54:32 -05:00

mkbootimg generating LFN FAT and Minix3 file systems

This commit is contained in:
bzt 2021-03-19 07:00:08 +01:00
parent d7e8e7d9fc
commit c74b2a0d67
18 changed files with 823 additions and 73 deletions

View file

@ -1,7 +1,7 @@
TARGET = mkbootimg
CC = gcc
CFLAGS = -Wall -Wextra -ansi -pedantic
CFLAGS = -Wall -Wextra -ansi -pedantic -g
SRCS = $(filter-out bin2h.c data.c,$(wildcard *.c)) data.c
ifneq ("$(wildcard /bin/*.exe)","")

View file

@ -7,7 +7,8 @@ Ez egy minden az egyben, többplatformos, függőség nélküli lemezkép kreál
forgatva). Egy lemezkonfigurációt kell megadni neki JSON-ben, és létrehozza az ESP FAT boot partíciót a szükséges betöltő
fájlokkal, GPT táblával, PMBR-el, stb. Továbbá képes létrehozni az induló memórialemezképet egy könyvtár tartalmából (jelenleg
`cpio`, `tar`, `jamesm` (James Molloy initrdje), `echfs` és az `FS/Z` támogatott, de a kód úgy lett megírva, hogy könnyű legyen
bővíteni).
bővíteni). Szemben ezzel, könyvtárból generálható partíció `fat` (hosszú fájlnevekkel), `minix` (Minix V3, POSIX
jogosultságokkal és eszközfájlokkal), `tar`, `echfs`, `FS/Z` fájlrendszerekhez.
A kigenerált képet leellenőriztem fdisk-el, valamint a gdisk verify funkciójával. A FAT partíció tesztelve lett fsck.vfat-al
és UEFI förmverrel, továbbá Raspberry Pi-n. Az ISO9660-es rész iat-vel (ISO9660 Analyzer Tool) és Linux mounttal lett tesztelve.
@ -23,7 +24,7 @@ BOOTBOOT mkbootimg utility - bztsrc@gitlab
Raspbery Pi Firmware Copyright (c) Broadcom Corp, Raspberry Pi (Trading) Ltd
Ellenőrzi, hogy az ELF vagy PE futtatható BOOTBOOT kompatíbilis-e, illetve
hibrid indító lemez képet generál a hobbi OS-edhez vagy Option ROM képet.
hibrid indító lemez képet vagy Option ROM képet generál a hobbi OS-edhez.
Használat:
./mkbootimg check <kernel elf / pe>
@ -121,22 +122,25 @@ fix "EFI System Partition" névvel. Ugyanezért a `size` méret megadása kötel
| size | szám | opcionális, a partíció mérete Megabájtban. Ha nincs megadva, kiszámolja |
| file | fájlnév | opcionális, a használandó partíciókép elérési útja |
| directory | mappa | opcionális, mappa elérési útja, a tarmalmából fogja generálni a partícióképet |
| driver | sztring | opcionális, ha a paríció típusa nem határozná meg egyértelműen a formátumot |
| type | sztring | a partíció formátuma. Érvénytelen esetén listázza a lehetőségeket |
| name | sztring | UTF-8 partíciónév, korlátozva a 32 és 65535 közötti UNICODE kódpontokra (BMP) |
Az első elem esetén a `type` lehetséges értékei: `boot` (vagy explicit `fat16` és `fat32`). A parancs igyekszik kényelmesen
használni ezeket, ha lehet FAT16-ot választva, helytakarékosság miatt. A boot partíció minimális mérete 16 Megabájt. Bár mind
a lemezkép készítő, mind a BOOTBOOT betöltő képes lenne kezelni kissebb méretet, néhány UEFI förmver helytelenül FAT12-nek
hiszi, ha túl kevés kluszter van a fájlrendszeren. Ha a partíció mérete meghaladja a 128 Megabájtot, akkor automatikusan
FAT32-t választ. Ha nem használsz `iso9660`-t, akkor kissebb méretű is lehet, de legalább 33 Megabájt (ez a FAT32 minimális
mérete). Ugyanakkor `iso9660` használata esetén garantálni kell, hogy minden kluszter 2048 bájtos címen kezdődjön, amit
4 szektor per kluszterrel a legegyszerűbb elérni. Itt is ugyanaz a probléma merül fel, mind a lemezkép készítő, mind a
BOOTBOOT betöltők képesek lennének kevessebb kluszterrel is használni a FAT32-t, de néhány UEFI förmver nem, és hibásan
FAT16-nak látná. Hogy ezt elkerüljük a minimális kluszterszámmal, az ISO9960 és FAT32 együttes használata esetén a
partíció minimális mérete 128 Megabájt (128\*1024\*1024/512/4 = 65536, ami pont eggyel több, mint ami még 16 bitbe belefér).
Az első elem esetén a `type` lehetséges értékei: `boot` (vagy explicit `fat16` és `fat32`). Csak 8+3 fájlneveket generál.
A parancs igyekszik kényelmesen kezelni ezt, ha lehet FAT16-ot választva, helytakarékosság miatt. A boot partíció
minimális mérete 16 Megabájt. Bár mind a lemezkép készítő, mind a BOOTBOOT betöltő képes lenne kezelni kissebb méretet,
néhány UEFI förmver helytelenül FAT12-nek hiszi, ha túl kevés kluszter van a fájlrendszeren. Ha a partíció mérete meghaladja
a 128 Megabájtot, akkor automatikusan FAT32-t választ. Ha nem használsz `iso9660`-t, akkor kissebb méretű is lehet, de
legalább 33 Megabájt (ez a FAT32 minimális mérete). Ugyanakkor `iso9660` használata esetén garantálni kell, hogy minden
kluszter 2048 bájtos címen kezdődjön, amit 4 szektor per kluszterrel a legegyszerűbb elérni. Itt is ugyanaz a probléma merül
fel, mind a lemezkép készítő, mind a BOOTBOOT betöltők képesek lennének kevessebb kluszterrel is használni a FAT32-t, de
néhány UEFI förmver nem, és hibásan FAT16-nak látná. Hogy ezt elkerüljük a minimális kluszterszámmal, az ISO9960 és FAT32
együttes használata esetén a partíció minimális mérete 128 Megabájt (128\*1024\*1024/512/4 = 65536, ami pont eggyel több,
mint ami még 16 bitbe belefér).
A többi (a másodiktól kezdve) bejegyzés esetén a `type` vagy egy GUID, vagy egy az előre definiált aliaszok közül. Érvénytelen
sztring esetén a parancs listázza az összes lehetséges értéket.
A többi (a másodiktól kezdve) bejegyzés esetén a `type` vagy egy GUID, vagy egy az előre definiált aliaszok közül. Itt a
`fat` meghajtó csakis a kluszterek száma alapján dönt, hogy FAT16 vagy FAT32 legyen-e, és hosszú fájlneveket is generál.
Érvénytelen sztring esetén a parancs listázza az összes lehetséges értéket.
Példa:
```
@ -167,10 +171,12 @@ akkor a kölönbséget nullákkal tölti fel. A partíció mérete mindig `align
a partíciók 1 Megabájtos címekre lesznek igazítva. Az első bejegyzés esetén csak a `size` használható, a `file` nem.
Alternatívaként esetleg használható a `directory` a `file` helyett, amennyiben a `type`-nál megadott típushoz van
fájlrendszer meghajtó implementálva. Ekkor a megadott mappa tartalmából generálódik a partíció tartalma. Mivel nem feltétlenül
van egy-az-egyhez megfeleltetés a fájlrendszer típus és a partíció típus között, ezért használható a `typeguid` az utóbbi
explicit megadására. Erre csak a `directory` direktíva használata esetén lehet szükség. Példa:
van egy-az-egyhez megfeleltetés a partíció típus és a fájlrendszer típus között, ezért használható a `driver` az utóbbi
explicit megadására. Erre csak a `directory` direktíva használata esetén lehet szükség. Példák:
```
{ "type": "FS/Z", "typeguid": "5A2F534F-8664-5346-2F5A-000075737200", "size": 32, "name": "MyOS usr", "directory": "myusr" },
{ "type": "5A2F534F-8664-5346-2F5A-000075737200", "driver": "FS/Z", "size": 32, "name": "usr", "directory": "myusr" },
{ "type": "Linux home", "driver": "minix", "size": 32, "name": "home", "directory": "myhome" },
{ "type": "Microsoft basic data", "driver": "fat", "size": 32, "name": "data", "directory": "mydata" },
```
Végezetül a `name` egy sima UTF-8 sztring, a partíció neve. Maximális hossza 35 karakter. Az első partíciónál nem használható.
@ -187,17 +193,20 @@ void somefs_add(struct stat *st, char *name, int pathlen, unsigned char *content
void somefs_close();
```
Az első akkor hívódik, amikor egy új fájlrendszert kell létrehozni. Itt a `gpt_entry` mutató NULL, ha memórialemezkép kreáláshoz
hívódik a meghajtó. Ahogy a megadott mappát rekurzívan bejárja, minden almappa és fájl esetén meghívódik az "add". Ez hozzá
kell adja a fájlt vagy mappát a fájlrendszer képéhez. Az `st` a stat struktúra, `name` a fájl neve teljes elérési úttal,
Az első, az "open" akkor hívódik, amikor egy új fájlrendszert kell létrehozni. Itt a `gpt_entry` mutató NULL, ha memórialemezkép
kreáláshoz hívódik a meghajtó. Ahogy a megadott mappát rekurzívan bejárja, minden almappa és fájl esetén meghívódik az "add". Ez
hozzá kell adja a fájlt vagy mappát a fájlrendszer képéhez. Az `st` a stat struktúra, `name` a fájl neve teljes elérési úttal,
a `content` és a `size` pedig a fájl tartalma, illetve szimbolikus hivatkozások esetén a mutatott elérési út. Végezetül amikor a
bejárásnak vége, a close hívódik meg, hogy lezárja és véglegesítse a lemezképet. Ezek közül csak az "add" a kötelező, a másik
bejárásnak vége, a "close" hívódik meg, hogy lezárja és véglegesítse a lemezképet. Ezek közül csak az "add" a kötelező, a másik
kettő opcionális.
Ezek a funkciók elérnek két globális változót, az `fs_base`-t és `fs_len`-t, amik a lemezkép memóriabeli bufferét jelölik.
Ezek a funkciók elérnek két globális változót, az `fs_base`-t és `fs_len`-t, amik a lemezkép memóriabeli bufferét jelölik
(ebből következik, hogy a partíciók mérete pár gigabájt lehet, amennyi szabad memória van a gépedben). Ha hibát kell jelenteni,
az `fs_no` változó tartalmazza annak a partíciónak a számát, amihez a meghajtó éppen generál.
Ezen függvények hiányában, a fájlrendszer továbbra is használható a partíciók `type` mezőjében, de ekkor csak a GPT bejegyzést
hozza létre, magát a partíció tartalmát nem.
hozza létre, magát a partíció tartalmát nem. A `driver` mezőben csak olyan fájlrendszer típus adható meg, ami rendelkezik ezekkel
a funkciókkal.
A beépített binárisok naprakészen tartása
-----------------------------------------

View file

@ -6,7 +6,9 @@ See [BOOTBOOT Protocol](https://gitlab.com/bztsrc/bootboot) for common details.
This is an all-in-one, multiplatform, dependency-free disk image creator tool. You pass a disk configuration to it in a very
flexible JSON, and it generates ESP FAT boot partition with the required loader files, GPT partitioning table, PMBR, etc. It
also creates an initrd from a directory (currently `cpio`, `tar`, `jamesm` (James Molloy's initrd), `echfs` and `FS/Z`
supported, but the code is written in a way that it is easily expandable).
supported, but the code is written in a way that it is easily expandable). In contrast, partitions can be generated from
directories for `fat` (with long file names), `minix` (Minix V3 with POSIX permissions and device files), `tar`, `echfs`,
`FS/Z` filesystems.
The generated image was tested with fdisk, and with the verify function of gdisk. The FAT partition was tested with fsck.vfat
and with TianoCore UEFI firmware and on Raspberry Pi. The ISO9660 part tested with iat (ISO9660 Analyzer Tool) and Linux mount.
@ -22,7 +24,7 @@ BOOTBOOT mkbootimg utility - bztsrc@gitlab
Raspbery Pi Firmware Copyright (c) Broadcom Corp, Raspberry Pi (Trading) Ltd
Validates ELF or PE executables for being BOOTBOOT compatible, otherwise
creates a bootable hybrid image for your hobby OS or Option ROM image.
creates a bootable hybrid image or Option ROM image for your hobby OS.
Usage:
./mkbootimg check <kernel elf / pe>
@ -120,21 +122,23 @@ for the first (boot) partition.
| size | integer | optional, the size of the partition in Megabytes. If not given, it is calculated |
| file | filename | optional, path to a partition image to be used |
| directory | folder | optional, path to a folder, its contents will be used to generate the partition |
| driver | string | optional, in case type can't specify the format without a doubt |
| type | string | format of the partition. When invalid value given, it lists the options |
| name | string | UTF-8 partition names, limited to UNICODE code points 32 to 65535 (BMP) |
For the first entry, valid values for `type` are: `boot` (or explicit `fat16` and `fat32`). The utility handles these
comfortably, it tries to use FAT16 if possible to save storage space. There's a minimal size for the boot partition,
16 Megabytes. Although both the image creator and BOOTBOOT is capable of handling smaller sizes, some UEFI firmware
incorrectly assumes FAT12 when there are too few clusters on the file system. If the partition size is bigger than
128 Megabytes, then it automatically switches to FAT32. If you don't use `iso9660`, then you can also set FAT32 for
smaller images, but at least 33 Megabytes (that's a hard lower limit for FAT32). With `iso9660`, each cluster must
For the first entry, valid values for `type` are: `boot` (or explicit `fat16` and `fat32`). Generates only 8+3 file names.
The utility handles this comfortably, it tries to use FAT16 if possible to save storage space. There's a minimal size
for the boot partition, 16 Megabytes. Although both the image creator and BOOTBOOT is capable of handling smaller sizes,
some UEFI firmware incorrectly assumes FAT12 when there are too few clusters on the file system. If the partition size is
bigger than 128 Megabytes, then it automatically switches to FAT32. If you don't use `iso9660`, then you can also set FAT32
for smaller images, but at least 33 Megabytes (that's a hard lower limit for FAT32). With `iso9660`, each cluster must
be 2048 bytes aligned, which is achieved by 4 sectors per cluster. The same problem applies here, both the image
creator and the BOOTBOOT loader capable of handling FAT32 with smaller cluster numbers, but some UEFI firmware don't,
and falsely assumes FAT16. To guarantee the minimum number of clusters, with ISO9660 and FAT32 the boot partition's
minimum size is 128 Megabytes (128\*1024\*1024/512/4 = 65536, just one larger than what fits in 16 bits).
For the other entries (starting from the second), `type` is either a GUID or one of a pre-defined file system aliases.
Here `fat` will decide between FAT16 and FAT32 based on the number of clusters, and it can generate long file names.
With an invalid string, the utility will list all possible values.
Example:
@ -164,12 +168,14 @@ If `file` given, then the partition is filled with data from that file. If `size
the file's size, then the file's size will be the partition's size. If both given, and `size` is larger, then the
difference is filled up with zeros. Partition sizes will always be multiple of `align` Kilobytes. Using 1024
as alignment gives you 1 Megabyte aligned partitions. For the first entry, only `size` is valid, `file` isn't.
Alternatively to `file`, you might also able to use `directory` to generate the partition image from the contents
Alternatively to `file`, you might also be able to use `directory` to generate the partition image from the contents
of a directory. This option is only available if the file system driver is implemented for `type`. Because there might
be no one-to-one relation between file system types and partition types, you can use `typeguid` to explicily set the
be no one-to-one relation between partition types and file system types, you can use `driver` to explicily set the
latter. This is only relevant when the `directory` directive is used. For example:
```
{ "type": "FS/Z", "typeguid": "5A2F534F-8664-5346-2F5A-000075737200", "size": 32, "name": "MyOS usr", "directory": "myusr" },
{ "type": "5A2F534F-8664-5346-2F5A-000075737200", "driver": "FS/Z", "size": 32, "name": "usr", "directory": "myusr" },
{ "type": "Linux home", "driver": "minix", "size": 32, "name": "home", "directory": "myhome" },
{ "type": "Microsoft basic data", "driver": "fat", "size": 32, "name": "data", "directory": "mydata" },
```
Finally, `name` is just an UTF-8 string, name of the partition. Maximum length is 35 characters. Not valid for the first entry.
@ -177,7 +183,7 @@ Finally, `name` is just an UTF-8 string, name of the partition. Maximum length i
Adding More File Systems
------------------------
These are listed in the fs registry, in the file `fs.h`. You can freely add new types. For file systems that you
Types are listed in the fs registry, in the file `fs.h`. You can freely add new file system types. For file systems that you
want to use for generating partition images or initrd as well, you must implement three functions, like:
```
@ -186,17 +192,18 @@ void somefs_add(struct stat *st, char *name, unsigned char *content, int size);
void somefs_close();
```
The first is called whenever a new file system is to be created. The `gpt_entry` is NULL when called for initrd creation.
As the given directory is recursively parsed, for each directory entry an "add" call is made. This should add the file or
directory to the file system image. Here `st` is the stat struct for the file, `name` is the filename with full path,
The first, the "open" is called whenever a new file system is to be created. The `gpt_entry` is NULL when called for initrd
creation. As the given directory is recursively parsed, for each directory entry an "add" call is made. This should add the
file or directory to the file system image. Here `st` is the stat struct for the file, `name` is the filename with full path,
`content` and `size` are the file's content, or in case of a symbolic link, the pointed path. Finally when the parsing is
done, the close function is called to finalize the image. Only the "add" function is mandatory, the other two are optional.
done, the "close" function is called to finalize the image. Only the "add" function is mandatory, the other two are optional.
These functions can use two global variables, `fs_base` and `fs_len` which holds the buffer for the filesystem image
in memory.
in memory (this implies that partitions are limited to few gigabytes, depending how much RAM you have). In case they want
to report error, `fs_no` is the number of the partition the driver is generating for.
In lack of these functions, the file system still can be used in the partition's `type` field, but then only the GPT entry
will be created, not the content of the partition.
will be created, not the content of the partition. The `driver` field only accepts file system types which have these functions.
Keeping the built-in binaries up-to-date
----------------------------------------

View file

@ -31,7 +31,7 @@
void cpio_open(gpt_t *gpt_entry)
{
(void)gpt_entry;
if(gpt_entry) { fprintf(stderr,"mkbootimg: partition #%d %s cpio.\r\n", fs_no, lang[ERR_INITRDTYPE]); exit(1); }
}
void cpio_add(struct stat *st, char *name, unsigned char *content, int size)

View file

@ -52,8 +52,11 @@ uint64_t ech_numblk;
void ech_open(gpt_t *gpt_entry)
{
if(gpt) {
if((gpt_entry->last - gpt_entry->start) < 1) { fprintf(stderr,"mkbootimg: %s\r\n", lang[ERR_NOSIZE]); exit(1); }
if(gpt_entry) {
if((gpt_entry->last - gpt_entry->start) < 1) {
fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_NOSIZE]);
exit(1);
}
memcpy(ech_uuid, &gpt_entry->guid, 16);
ech_numblk = gpt_entry->last - gpt_entry->start + 1;
ech_maxents = (ech_numblk * 5 / 100) * 512 / sizeof(ech_entry_t);
@ -84,10 +87,17 @@ void ech_add(struct stat *st, char *name, unsigned char *content, int size)
parent = ech_ents[i].payload;
fn = end + 1;
end = *end ? strchr(fn, '/') : NULL;
if(!end) { end = fn + strlen(name); break; }
if(!end) { end = fn + strlen(fn); break; }
}
}
if(ech_maxents && ech_numents + 1 >= ech_maxents) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_TOOMANY]); exit(1); }
if(ech_numblk && ech_numblk * 512 < ech_size + size) {
fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_TOOBIG]);
exit(1);
}
if(ech_maxents && ech_numents + 1 >= ech_maxents) {
fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_TOOMANY]);
exit(1);
}
ech_ents = (ech_entry_t*)realloc(ech_ents, (ech_numents + 1) * sizeof(ech_entry_t));
if(!ech_ents) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_MEM]); exit(1); }
memset(&ech_ents[ech_numents], 0, sizeof(ech_entry_t));

302
mkbootimg/fat.c Normal file
View file

@ -0,0 +1,302 @@
/*
* mkbootimg/fat.c
*
* Copyright (C) 2017 - 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the BOOTBOOT Protocol package.
* @brief normal (non-ESP) FAT16/32 file system driver with long filename support
*
*/
#include "main.h"
#define SECTOR_PER_CLUSTER 1
struct tm *fat_ts;
int fat_nextcluster, fat_bpc, fat_spf, fat_lfncnt, fat_numclu;
unsigned char *fat_rootdir, *fat_data, fat_lfn[769];
uint16_t *fat_fat16_1, *fat_fat16_2;
uint32_t *fat_fat32_1, *fat_fat32_2;
unsigned char *fat_newclu(int parent)
{
int clu;
if(fat_fat16_1) {
while(parent != fat_nextcluster && fat_fat16_1[parent] && fat_fat16_1[parent] != 0xFFFF)
parent = fat_fat16_1[parent];
fat_fat16_1[parent] = fat_fat16_2[parent] = fat_nextcluster;
fat_fat16_1[fat_nextcluster] = fat_fat16_2[fat_nextcluster] = 0xFFFF;
} else {
while(parent != fat_nextcluster && fat_fat32_1[parent] && fat_fat32_1[parent] != 0xFFFFFFF)
parent = fat_fat32_1[parent];
fat_fat32_1[parent] = fat_fat32_2[parent] = fat_nextcluster;
fat_fat32_1[fat_nextcluster] = fat_fat32_2[fat_nextcluster] = 0xFFFFFFF;
}
clu = fat_nextcluster++;
if(fat_nextcluster >= fat_numclu) { fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_TOOBIG]); exit(1); }
return fat_data + clu * fat_bpc;
}
unsigned char *fat_readlfn(unsigned char *dir, int *clu, int *size, int parent)
{
uint16_t uc2[256], *u;
unsigned char *s, *d;
int i, n;
memset(fat_lfn, 0, sizeof(fat_lfn));
if(!dir[0]) return dir;
while(dir[0] == '.') dir += 32;
fat_lfncnt++;
if(parent != 2 && !((uint64_t)(dir - fs_base) & (fat_bpc - 1))) {
if(fat_fat16_1) {
parent = fat_fat16_1[parent];
if(!parent || parent == 0xFFFF) return NULL;
} else {
parent = fat_fat32_1[parent];
if(!parent || parent == 0xFFFFFFF) return NULL;
}
dir = fat_data + parent * fat_bpc;
}
if(dir[0xB] != 0xF) {
for(s = dir, d = fat_lfn; *s && *s != ' ' && i < 8; i++)
*d++ = *s++;
if(dir[8] && dir[8] != ' ') {
*d++ = '.';
for(s = dir + 8; *s != ' ' && i < 3; i++)
*d++ = *s++;
}
} else {
memset(uc2, 0, sizeof(uc2));
n = dir[0] & 0x3F;
u = uc2 + (n - 1) * 13;
while(n--) {
for(i = 0; i < 5; i++)
u[i] = dir[i*2+2] << 8 | dir[i*2+1];
for(i = 0; i < 6; i++)
u[i+5] = dir[i*2+0xF] << 8 | dir[i*2+0xE];
u[11] = dir[0x1D] << 8 | dir[0x1C];
u[12] = dir[0x1F] << 8 | dir[0x1E];
u -= 13;
dir += 32;
if(!((uint64_t)(dir - fs_base) & (fat_bpc - 1))) {
if(fat_fat16_1) {
parent = fat_fat16_1[parent];
if(!parent || parent == 0xFFFF) return NULL;
} else {
parent = fat_fat32_1[parent];
if(!parent || parent == 0xFFFFFFF) return NULL;
}
dir = fat_data + parent * fat_bpc;
}
}
for(d = fat_lfn, u = uc2; *u; u++)
if(*u < 0x80) {
*d++ = *u;
} else if(*u < 0x800) {
*d++ = ((*u>>6)&0x1F)|0xC0;
*d++ = (*u&0x3F)|0x80;
} else {
*d++ = ((*u>>12)&0x0F)|0xE0;
*d++ = ((*u>>6)&0x3F)|0x80;
*d++ = (*u&0x3F)|0x80;
}
}
*clu = (dir[0x15] << 24) | (dir[0x14] << 16) | (dir[0x1B] << 8) | dir[0x1A];
*size = (dir[0x1F] << 24) | (dir[0x1E] << 16) | (dir[0x1D] << 8) | dir[0x1C];
return dir + 32;
}
unsigned char *fat_writelfn(unsigned char *dir, char *name, int type, int size, int parent, int clu)
{
uint16_t uc2[256], *u;
unsigned char *s, c = 0, sfn[12];
int i, n;
if(name[0] == '.') {
memset(dir, ' ', 11);
memcpy(dir, name, strlen(name));
} else {
memset(uc2, 0, sizeof(uc2));
for(n = 0, u = uc2, s = (unsigned char*)name; *s; n++, u++) {
if((*s & 128) != 0) {
if((*s & 32) == 0) { *u = ((*s & 0x1F)<<6)|(*(s+1) & 0x3F); s += 2; } else
if((*s & 16) == 0) { *u = ((*s & 0xF)<<12)|((*(s+1) & 0x3F)<<6)|(*(s+2) & 0x3F); s += 3; }
else { fprintf(stderr,"mkbootimg: partition #%d %s '%s'\r\n", fs_no, lang[ERR_WRITE], name); exit(1); }
} else
*u = *s++;
}
/* don't convert "Microsoft" to "MICROS~1 ", that's patented... */
sprintf((char*)sfn, "~%07xLFN", fat_lfncnt++);
for(i = 0; i < 11; i++)
c = (((c & 1) << 7) | ((c & 0xfe) >> 1)) + sfn[i];
n = (n + 12) / 13;
u = uc2 + (n - 1) * 13;
i = 0x40;
while(n--) {
if(parent > 2 && !((uint64_t)(dir - fs_base) & (fat_bpc - 1)))
dir = fat_newclu(parent);
dir[0] = i | (n + 1);
dir[11] = 0xF;
dir[0xD] = c;
memcpy(dir + 1, (unsigned char*)u, 10);
memcpy(dir + 14, (unsigned char*)u + 10, 12);
memcpy(dir + 28, (unsigned char*)u + 22, 4);
i = 0;
u -= 13;
dir += 32;
}
if(parent > 2 && !((uint64_t)(dir - fs_base) & (fat_bpc - 1)))
dir = fat_newclu(parent);
memcpy(dir, sfn, 11);
}
if(type) {
dir[0xB] = 0x10;
} else {
dir[0x1C] = size & 0xFF; dir[0x1D] = (size >> 8) & 0xFF;
dir[0x1E] = (size >> 16) & 0xFF; dir[0x1F] = (size >> 24) & 0xFF;
}
if(!clu) clu = fat_nextcluster;
if(clu < 3) clu = 0;
dir[0x1A] = clu & 0xFF; dir[0x1B] = (clu >> 8) & 0xFF;
dir[0x14] = (clu >> 16) & 0xFF; dir[0x15] = (clu >> 24) & 0xFF;
i = (fat_ts->tm_hour << 11) | (fat_ts->tm_min << 5) | (fat_ts->tm_sec/2);
dir[0xE] = dir[0x16] = i & 0xFF; dir[0xF] = dir[0x17] = (i >> 8) & 0xFF;
i = ((fat_ts->tm_year+1900-1980) << 9) | ((fat_ts->tm_mon+1) << 5) | (fat_ts->tm_mday);
return dir + 32;
}
void fat_open(gpt_t *gpt_entry)
{
int i;
if(!gpt_entry) { fprintf(stderr,"mkbootimg: %s fat.\r\n", lang[ERR_BADINITRDTYPE]); exit(1); }
fat_numclu = (gpt_entry->last - gpt_entry->start + 1) / SECTOR_PER_CLUSTER;
if(fat_numclu < 4085) { fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_NOSIZE]); exit(1); }
/* "format" the partition to either FAT16 or FAT32 */
fs_len = fat_numclu * 512 * SECTOR_PER_CLUSTER;
fs_base = realloc(fs_base, fs_len);
if(!fs_base) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_MEM]); exit(1); }
memset(fs_base, 0, fs_len);
memcpy(fs_base + 3, "MSWIN4.1", 8);
fs_base[0xC] = 2; fs_base[0x10] = 2; fs_base[0x15] = 0xF8; fs_base[0x1FE] = 0x55; fs_base[0x1FF] = 0xAA;
fs_base[0x18] = 0x20; fs_base[0x1A] = 0x40;
memcpy(fs_base + 0x1C, &gpt_entry->start, 4);
if(fat_numclu > 65535)
memcpy(fs_base + 0x20, &fat_numclu, 4);
else
memcpy(fs_base + 0x13, &fat_numclu, 2);
if(fat_numclu < 65525) {
/* FAT16 */
fat_spf = (fat_numclu*2 + 511) / 512;
fs_base[0xD] = SECTOR_PER_CLUSTER; fs_base[0xE] = 4; fs_base[0x12] = 2;
fs_base[0x16] = fat_spf & 0xFF; fs_base[0x17] = (fat_spf >> 8) & 0xFF;
fs_base[0x24] = 0x80; fs_base[0x26] = 0x29;
memcpy(fs_base + 0x27, &gpt_entry->guid, 4);
memcpy(fs_base + 0x2B, "NO NAME FAT16 ", 19);
fat_bpc = fs_base[0xD] * 512;
fat_rootdir = fs_base + (fat_spf*fs_base[0x10]+fs_base[0xE]) * 512;
fat_data = fat_rootdir + ((((fs_base[0x12]<<8)|fs_base[0x11])*32 - 2*fat_bpc) & ~(fat_bpc-1));
fat_fat16_1 = (uint16_t*)(&fs_base[fs_base[0xE] * 512]);
fat_fat16_2 = (uint16_t*)(&fs_base[(fs_base[0xE]+fat_spf) * 512]);
fat_fat16_1[0] = fat_fat16_2[0] = 0xFFF8; fat_fat16_1[1] = fat_fat16_2[1] = 0xFFFF;
fat_fat32_1 = fat_fat32_2 = NULL;
} else {
/* FAT32 */
fat_spf = (fat_numclu*4) / 512 - 8;
fs_base[0xD] = SECTOR_PER_CLUSTER; fs_base[0xE] = 8;
fs_base[0x24] = fat_spf & 0xFF; fs_base[0x25] = (fat_spf >> 8) & 0xFF;
fs_base[0x26] = (fat_spf >> 16) & 0xFF; fs_base[0x27] = (fat_spf >> 24) & 0xFF;
fs_base[0x2C] = 2; fs_base[0x30] = 1; fs_base[0x32] = 6; fs_base[0x40] = 0x80; fs_base[0x42] = 0x29;
memcpy(fs_base + 0x43, &gpt_entry->guid, 4);
memcpy(fs_base + 0x47, "NO NAME FAT32 ", 19);
memcpy(fs_base + 0x200, "RRaA", 4); memcpy(fs_base + 0x3E4, "rrAa", 4);
for(i = 0; i < 8; i++) fs_base[0x3E8 + i] = 0xFF;
fs_base[0x3FE] = 0x55; fs_base[0x3FF] = 0xAA;
fat_bpc = fs_base[0xD] * 512;
fat_rootdir = fs_base + (fat_spf*fs_base[0x10]+fs_base[0xE]) * 512;
fat_data = fat_rootdir - 2*fat_bpc;
fat_fat32_1 = (uint32_t*)(&fs_base[fs_base[0xE] * 512]);
fat_fat32_2 = (uint32_t*)(&fs_base[(fs_base[0xE]+fat_spf) * 512]);
fat_fat32_1[0] = fat_fat32_2[0] = fat_fat32_1[2] = fat_fat32_2[2] = 0x0FFFFFF8;
fat_fat32_1[1] = fat_fat32_2[1] = 0x0FFFFFFF;
fat_fat16_1 = fat_fat16_2 = NULL;
}
fat_nextcluster = 3;
}
void fat_add(struct stat *st, char *name, unsigned char *content, int size)
{
int parent = 2, clu, i;
unsigned char *dir = fat_rootdir;
char *end, *fn = strrchr(name, '/');
if(!fn) fn = name; else fn++;
if(!strcmp(fn, ".") || !strcmp(fn, "..")) return;
if(!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) return;
fat_ts = gmtime(&st->st_mtime);
fn = name;
end = strchr(name, '/');
if(!end) end = name + strlen(name);
fat_lfncnt = 1;
do {
dir = fat_readlfn(dir, &clu, &size, parent);
if(!dir) return;
if(!memcmp(fat_lfn, fn, end - fn) && !fat_lfn[end - fn]) {
fat_lfncnt = 1;
parent = clu;
dir = fat_data + parent * fat_bpc + 64;
fn = end + 1;
end = *end ? strchr(fn, '/') : NULL;
if(!end) { end = fn + strlen(fn); break; }
}
} while(dir[0]);
dir = fat_writelfn(dir, fn, S_ISDIR(st->st_mode), size, parent, 0);
if(S_ISDIR(st->st_mode)) {
dir = fat_newclu(fat_nextcluster);
dir = fat_writelfn(dir, ".", 1, 0, 2, fat_nextcluster - 1);
dir = fat_writelfn(dir, "..", 1, 0, 2, parent);
} else if(content && size > 0) {
if(fat_nextcluster * fat_bpc + size >= fs_len) {
fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_TOOBIG]);
exit(1);
}
memcpy(fat_data + fat_nextcluster * fat_bpc, content, size);
for(i = 0; i < ((size + fat_bpc-1) & ~(fat_bpc-1)); i += fat_bpc, fat_nextcluster++) {
if(fat_fat16_1) fat_fat16_1[fat_nextcluster] = fat_fat16_2[fat_nextcluster] = fat_nextcluster+1;
else fat_fat32_1[fat_nextcluster] = fat_fat32_2[fat_nextcluster] = fat_nextcluster+1;
}
if(fat_fat16_1) fat_fat16_1[fat_nextcluster-1] = fat_fat16_2[fat_nextcluster-1] = 0xFFFF;
else fat_fat32_1[fat_nextcluster-1] = fat_fat32_2[fat_nextcluster-1] = 0xFFFFFFF;
}
}
void fat_close()
{
int i;
if(!fs_base || fs_len < 512) return;
if(fat_fat32_1) {
fat_nextcluster -= 2;
i = ((fs_len - (fat_spf*fs_base[0x10]+fs_base[0xE]) * 512)/fat_bpc) - fat_nextcluster;
fs_base[0x3E8] = i & 0xFF; fs_base[0x3E9] = (i >> 8) & 0xFF;
fs_base[0x3EA] = (i >> 16) & 0xFF; fs_base[0x3EB] = (i >> 24) & 0xFF;
fs_base[0x3EC] = fat_nextcluster & 0xFF; fs_base[0x3ED] = (fat_nextcluster >> 8) & 0xFF;
fs_base[0x3EE] = (fat_nextcluster >> 16) & 0xFF; fs_base[0x3EF] = (fat_nextcluster >> 24) & 0xFF;
/* copy backup boot sectors */
memcpy(fs_base + (fs_base[0x32]*512), fs_base, 1024);
}
}

View file

@ -44,17 +44,28 @@ void ech_open(gpt_t *gpt_entry);
void ech_add(struct stat *st, char *name, unsigned char *content, int size);
void ech_close();
void mnx_open(gpt_t *gpt_entry);
void mnx_add(struct stat *st, char *name, unsigned char *content, int size);
void mnx_close();
void fat_open(gpt_t *gpt_entry);
void fat_add(struct stat *st, char *name, unsigned char *content, int size);
void fat_close();
void jamesm_open(gpt_t *gpt_entry);
void jamesm_add(struct stat *st, char *name, unsigned char *content, int size);
void jamesm_close();
/*** specify file system drivers and GPT file system types here ***/
/* for simplicity, first list the ones with drivers, and types only afterwards */
fsdrv_t fsdrv[] = {
{ "jamesm", {0}, jamesm_open, jamesm_add, jamesm_close },
{ "cpio", {0}, cpio_open, cpio_add, cpio_close },
{ "tar", { 0x65706154, 0x4120, 0x6372, { 0x68,0x69,0x76,0x65,0x20,0x46,0x53,0x20} }, tar_open, tar_add, tar_close },
{ "FS/Z", { 0x5A2F534F, 0x0000, 0x5346, { 0x2F,0x5A,0x00,0x00,0x00,0x00,0x00,0x00} }, fsz_open, fsz_add, fsz_close },
{ "echfs", { 0x66686365, 0x6973, 0x7673, { 0x65,0x72,0x79,0x6C,0x61,0x6D,0x65,0x00} }, ech_open, ech_add, ech_close },
{ "minix", { 0xB7AADF00, 0xDE27, 0x11CA, { 0xA5,0x74,0x56,0x72,0x69,0x6A,0x65,0x55} }, mnx_open, mnx_add, mnx_close },
{ "fat", { 0xEBD0A0A2, 0xB9E5, 0x4433, { 0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7} }, fat_open, fat_add, fat_close },
{ "FS/Z", { 0x5A2F534F, 0x0000, 0x5346, { 0x2F,0x5A,0x00,0x00,0x00,0x00,0x00,0x00} }, fsz_open, fsz_add, fsz_close },
{ "OS/Z usr (x86_64)", { 0x5A2F534F, 0x8664, 0x5346, { 0x2F,0x5A,0x00,0x00,0x75,0x73,0x72,0x00} }, NULL, NULL, NULL },
{ "OS/Z usr (AArch64)", { 0x5A2F534F, 0xAA64, 0x5346, { 0x2F,0x5A,0x00,0x00,0x75,0x73,0x72,0x00} }, NULL, NULL, NULL },
{ "OS/Z var", { 0x5A2F534F, 0x0000, 0x5346, { 0x2F,0x5A,0x00,0x00,0x76,0x61,0x72,0x00} }, NULL, NULL, NULL },

View file

@ -30,7 +30,7 @@
#include "main.h"
#include "fsZ.h"
int fsz_secsize = FSZ_SECSIZE, fsz_isinitrd = 0;
int fsz_secsize = FSZ_SECSIZE, fsz_max = 0;
unsigned char fsz_emptysec[FSZ_SECSIZE] = {0};
/* private functions */
@ -44,6 +44,10 @@ int fsz_add_inode(char *filetype, char *mimetype)
unsigned int i,j=!strcmp(filetype,FSZ_FILETYPE_SYMLINK)||!strcmp(filetype,FSZ_FILETYPE_UNION)?fsz_secsize-1024:36;
FSZ_Inode *in;
FSZ_DirEntHeader *hdr;
if(fsz_max && fs_len+fsz_secsize > fsz_max) {
fprintf(stderr,"mkbootimg: partition #%d %s\n", fs_no, lang[ERR_TOOBIG]);
exit(1);
}
fs_base=realloc(fs_base,fs_len+fsz_secsize);
if(!fs_base) { fprintf(stderr,"mkbootimg: %s\r\n", lang[ERR_MEM]); exit(1); }
memset(fs_base+fs_len,0,fsz_secsize);
@ -110,7 +114,7 @@ void fsz_link_inode(int inode, char *path, int toinode)
}
/* the format can hold 2^127 directory entries, but we only implement directories embedded in inodes here, up to 23 */
if(hdr->numentries >= (fsz_secsize - 1024 - sizeof(FSZ_DirEntHeader)) / sizeof(FSZ_DirEnt)) {
fprintf(stderr,"mkbootimg: %s: %s\n",lang[ERR_TOOMANY],path); exit(1);
fprintf(stderr,"mkbootimg: partition #%d %s: %s\n", fs_no, lang[ERR_TOOMANY], path); exit(1);
}
hdr->numentries++;
in->modifydate=t * 1000000;
@ -130,6 +134,10 @@ void fsz_add_file(char *name, unsigned char *data, unsigned long int size)
int inode=fsz_add_inode(data[0]==0x55 && data[1]==0xAA &&
data[3]==0xE9 && data[8]=='B' &&
data[12]=='B'?"boot":"application","octet-stream");
if(fsz_max && fs_len+fsz_secsize+s > fsz_max) {
fprintf(stderr,"mkbootimg: partition #%d %s: %s\n", fs_no, lang[ERR_TOOBIG], name);
exit(1);
}
fs_base=realloc(fs_base,fs_len+fsz_secsize+s);
if(!fs_base) { fprintf(stderr,"mkbootimg: %s\r\n", lang[ERR_MEM]); exit(1); }
memset(fs_base+fs_len,0,fsz_secsize);
@ -147,7 +155,7 @@ void fsz_add_file(char *name, unsigned char *data, unsigned long int size)
in->sec=fs_len/fsz_secsize;
if(size>(unsigned long int)fsz_secsize) {
j=s/fsz_secsize;
if(j*16>fsz_secsize){ fprintf(stderr,"mkbootimg: %s: %s\n",lang[ERR_TOOBIG],name); exit(1); }
if(j*16>fsz_secsize){ fprintf(stderr,"mkbootimg: partition #%d %s: %s\n", fs_no, lang[ERR_TOOBIG], name); exit(1); }
if(j*16<=fsz_secsize-1024) {
ptr=(unsigned char*)&in->data.small.inlinedata;
in->flags=FSZ_IN_FLAG_SDINLINE;
@ -162,7 +170,7 @@ void fsz_add_file(char *name, unsigned char *data, unsigned long int size)
k=inode+1+l;
for(i=0;i<j;i++){
/* no spare blocks allowed in initrd, there we must save a sector full of zeros */
if(fsz_isinitrd || memcmp(data+i*fsz_secsize,fsz_emptysec,fsz_secsize)) {
if(!fsz_max || memcmp(data+i*fsz_secsize,fsz_emptysec,fsz_secsize)) {
memcpy(ptr,&k,4);
memcpy(fs_base+size+(i+l)*fsz_secsize,data+i*fsz_secsize,
(unsigned long int)(i+l)*fsz_secsize>size?size%fsz_secsize:(unsigned long int)fsz_secsize);
@ -294,12 +302,12 @@ void fsz_open(gpt_t *gpt_entry)
sb->createdate=sb->lastchangedate=t * 1000000;
if(gpt_entry) {
memcpy(&sb->uuid, (void*)(gpt_entry + 16), sizeof(guid_t));
sb->numsec = (gpt_entry->last - gpt_entry->start + 1) * 512 / fsz_secsize;
fsz_isinitrd = 0;
fsz_max = (gpt_entry->last - gpt_entry->start + 1) * 512;
sb->numsec = fsz_max / fsz_secsize;
} else {
memcpy(&sb->uuid, (void*)&diskguid, sizeof(guid_t));
sb->uuid[15]--;
fsz_isinitrd = 1;
fsz_max = 0;
}
memcpy(sb->magic2,FSZ_MAGIC,4);
fs_len = fsz_secsize;

View file

@ -70,10 +70,6 @@ void gpt_maketable()
for(i = 0; fsdrv[i].name; i++)
if(fsdrv[i].type.Data1 && !strcmp(tmp, fsdrv[i].name)) { memcpy(&typeguid, &fsdrv[i].type, sizeof(guid_t)); break; }
free(tmp);
/* override with specific GUID if type was an fsname */
sprintf(key, "partitions.%d.%s", np, "typeguid");
tmp = json_get(json, key);
if(tmp && *tmp) { getguid(tmp, &typeguid); free(tmp); }
/* if there's still no type GUID */
if(!typeguid.Data1 && !typeguid.Data2 && !typeguid.Data3 && !typeguid.Data4[0]) {
fprintf(stderr,"mkbootimg: partition #%d %s. %s:\r\n", np+1, lang[ERR_TYPE], lang[ERR_ACCEPTVALUES]);
@ -216,6 +212,7 @@ void gpt_maketable()
else u[i] = 0;
}
}
free(tmp);
p += 128;
}

View file

@ -35,7 +35,7 @@
void img_write(char *fn)
{
FILE *f, *d;
int i, n, lastpercent, k;
int i, j, n, lastpercent, k;
char key[64], *tmp, *dir, *buf;
unsigned long int size, pos;
size_t s;
@ -82,9 +82,13 @@ void img_write(char *fn)
sprintf(key, "partitions.%d.%s", k, "directory");
dir = json_get(json, key);
if(dir && *dir) {
fs_base = NULL; fs_len = 0;
sprintf(key, "partitions.%d.%s", k, "type");
fs_base = NULL; fs_len = 0; fs_no = k + 1;
sprintf(key, "partitions.%d.%s", k, "driver");
tmp = json_get(json, key);
if(!tmp || !*tmp) {
sprintf(key, "partitions.%d.%s", k, "type");
tmp = json_get(json, key);
}
if(tmp && *tmp) {
rd_open = NULL; rd_add = NULL; rd_close = NULL;
for(i = 0; fsdrv[i].name && fsdrv[i].add; i++)
@ -95,6 +99,16 @@ void img_write(char *fn)
if(rd_open) (*rd_open)((gpt_t*)(gpt + 1024 + k * 128));
parsedir(dir, 0);
if(rd_close) (*rd_close)();
} else {
fprintf(stderr,"mkbootimg: partition #%d %s. %s:\r\n", np+1, lang[ERR_TYPE], lang[ERR_ACCEPTVALUES]);
for(i = 0; fsdrv[i].name; i++)
if(fsdrv[i].add) {
fprintf(stderr," \"%08X-%04X-%04X-%02X%02X-",fsdrv[i].type.Data1,fsdrv[i].type.Data2,
fsdrv[i].type.Data3, fsdrv[i].type.Data4[0],fsdrv[i].type.Data4[1]);
for(j = 2; j < 8; j++) fprintf(stderr,"%02X",fsdrv[i].type.Data4[j]);
fprintf(stderr,"\" / \"%s\"\r\n",fsdrv[i].name);
}
exit(1);
}
}
free(dir);

View file

@ -31,7 +31,7 @@
void jamesm_open(gpt_t *gpt_entry)
{
(void)gpt_entry;
if(gpt_entry) { fprintf(stderr,"mkbootimg: partition #%d %s jamesm.\r\n", fs_no, lang[ERR_INITRDTYPE]); exit(1); }
fs_len = 4 + 64 * 73;
fs_base = realloc(fs_base, fs_len);
if(!fs_base) { fprintf(stderr,"mkbootimg: %s\r\n", lang[ERR_MEM]); exit(1); }

View file

@ -42,6 +42,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = {
"initrd not specified in json",
"initrd type not specified in json",
"invalid initrd type",
"initrd-only type",
"Accepted values",
"unable to read BOOTBOOT configuration from",
"BOOTBOOT configuration file is bigger than 4095 bytes",
@ -71,7 +72,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = {
"must use valid static addresses",
"valid dynamic addresses",
"Validates ELF or PE executables for being BOOTBOOT compatible, otherwise",
"creates a bootable hybrid image for your hobby OS or Option ROM image",
"creates a bootable hybrid image or Option ROM image for your hobby OS",
"Usage",
"configuration json",
"output disk image name",
@ -87,6 +88,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = {
"initrd nincs megadva a json-ben",
"initrd type nincs megadva a json-ben",
"érvénytelen initrd type",
"csak initrd-nél használható type",
"Lehetséges értékek",
"nem tudom beolvasni a BOOTBOOT konfigurációt innen",
"a BOOTBOOT konfiguráció több, mint 4095 bájt",
@ -116,7 +118,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = {
"helyes statikus címeket kell használnia",
"érvényes dinamikus címek",
"Ellenőrzi, hogy az ELF vagy PE futtatható BOOTBOOT kompatíbilis-e, illetve",
"hibrid indító lemez képet generál a hobbi OS-edhez vagy Option ROM képet",
"hibrid indító lemez képet vagy Option ROM képet generál a hobbi OS-edhez",
"Használat",
"konfigurációs json",
"kimeneti lemezkép neve",
@ -132,6 +134,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = {
"initrd nie jest opisany w pliku json",
"typ initrd nie jest opisany w pliku json",
"niepoprawny typ initrd",
"nie ma poprawnego typu",
"Akceptowane wartości",
"nie udało się załadować konfiguracji BOOTBOOT z",
"plik z konfiguracją BOOTBOOT jest większy niż 4095 bajtów",
@ -161,7 +164,7 @@ char *dict[NUMLANGS][NUMTEXTS + 1] = {
"musi używać poprawnych statycznych adresów",
"poprawny dynamiczny adres",
"Sprawdza czy plik wykonywalny ELF lub PE jest kompatybilny z BOOTBOOT, w przeciwnym wypadku",
"tworzy boot-owalny obraz hybrydoway z twoim hobbistycznym OS lub \"Option ROM image\"",
"tworzy boot-owalny obraz hybrydoway lub \"Option ROM image\" z twoim hobbistycznym OS",
"Sposoby użytkowania",
"plik konfiguracyjny json",
"nazwa wyjściowego pliku obrazu dysku",

View file

@ -34,6 +34,7 @@ enum {
ERR_NOINITRD,
ERR_NOINITRDTYPE,
ERR_BADINITRDTYPE,
ERR_INITRDTYPE,
ERR_ACCEPTVALUES,
ERR_NOCONF,
ERR_BIGCONF,

View file

@ -463,13 +463,14 @@ int main(int argc, char **argv)
parsekernel(i, data, 0);
free(data);
skipbytes = strlen(initrd_dir[i]) + 1;
fs_base = NULL; fs_len = 0;
fs_base = NULL; fs_len = 0; fs_no = 0;
if(rd_open) (*rd_open)(NULL);
parsedir(initrd_dir[i], 0);
if(rd_close) (*rd_close)();
initrdcompress();
initrd_buf[i] = fs_base;
initrd_size[i] = fs_len;
free(initrd_dir[i]);
} else
if(initrd_buf[i]) {
fs_base = initrd_buf[i]; fs_len = initrd_size[i];
@ -519,6 +520,8 @@ int main(int argc, char **argv)
free(kernelname);
free(initrd_buf[0]);
if(initrd_buf[1]) free(initrd_buf[1]);
if(initrd_buf[2]) free(initrd_buf[2]);
if(config) free(config);
free(json);
}
return 0;

View file

@ -185,8 +185,8 @@ extern time_t t;
extern struct tm *ts;
extern guid_t diskguid;
extern char *json, *config, *kernelname, *initrd_dir[NUMARCH], initrd_arch[NUMARCH];
extern int fs_len, initrd_size[NUMARCH], initrd_gzip, boot_size, boot_fat, disk_size, esp_size, esp_bbs, bbp_start, bbp_end;
extern int iso9660, skipbytes, np;
extern int fs_len, fs_no, initrd_size[NUMARCH], initrd_gzip, boot_size, boot_fat, disk_size, esp_size, esp_bbs;
extern int iso9660, skipbytes, np, bbp_start, bbp_end;
extern unsigned char *esp, *gpt, gpt2[512], *fs_base, *initrd_buf[NUMARCH];
extern unsigned long int tsize, es, esiz, disk_align, gpt_parts[248];
extern fsdrv_t fsdrv[];

382
mkbootimg/minix.c Normal file
View file

@ -0,0 +1,382 @@
/*
* mkbootimg/minix.c
*
* Copyright (C) 2017 - 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the BOOTBOOT Protocol package.
* @brief Minix3 file system driver
*
*/
#include "main.h"
typedef uint32_t zone_t; /* zone number */
typedef uint32_t block_t; /* block number */
typedef uint32_t bit_t; /* bit number in a bit map */
typedef uint32_t bitchunk_t; /* collection of bits in a bitmap */
#define DEFAULT_BLOCK_SIZE 4096
#define SUPER_V3 0x4d5a /* magic # for V3 file systems */
#define MFS_DIRSIZ 60
#define NR_DZONES 7 /* # direct zone numbers in a V2 inode */
#define NR_TZONES 10 /* total # zone numbers in a V2 inode */
#define NR_DIR_ENTRIES (int)(DEFAULT_BLOCK_SIZE/sizeof(direct_t)) /* # dir entries/blk */
#define INDIRECTS (int)(DEFAULT_BLOCK_SIZE/sizeof(zone_t)) /* # zones/indir block */
#define FS_BITMAP_CHUNKS (int)(DEFAULT_BLOCK_SIZE/sizeof(bitchunk_t)) /*# map chunks/blk*/
#define FS_BITCHUNK_BITS (sizeof(bitchunk_t) * 8)
#define FS_BITS_PER_BLOCK (FS_BITMAP_CHUNKS * FS_BITCHUNK_BITS)
typedef struct {
uint32_t s_ninodes; /* # usable inodes on the minor device */
uint16_t s_nzones; /* total device size, including bit maps etc */
int16_t s_imap_blocks; /* # of blocks used by inode bit map */
int16_t s_zmap_blocks; /* # of blocks used by zone bit map */
uint16_t s_firstdatazone_old; /* number of first data zone (small) */
uint16_t s_log_zone_size; /* log2 of blocks/zone */
uint16_t s_flags; /* FS state flags */
int32_t s_max_size; /* maximum file size on this device */
uint32_t s_zones; /* number of zones (replaces s_nzones in V2) */
int16_t s_magic; /* magic number to recognize super-blocks */
/* The following items are valid on disk only for V3 and above */
int16_t s_pad2; /* try to avoid compiler-dependent padding */
/* The block size in bytes. Minimum MIN_BLOCK SIZE. SECTOR_SIZE multiple.*/
uint16_t s_block_size; /* block size in bytes. */
int8_t s_disk_version; /* filesystem format sub-version */
} __attribute__((packed)) superblock_t;
typedef struct {
uint32_t d_ino;
char d_name[MFS_DIRSIZ];
} __attribute__((packed)) direct_t;
typedef struct { /* V2/V3 disk inode */
uint16_t i_mode; /* file type, protection, etc. */
uint16_t i_nlinks; /* how many links to this file. */
int16_t i_uid; /* user id of the file's owner. */
uint16_t i_gid; /* group number */
uint32_t i_size; /* current file size in bytes */
uint32_t i_atime; /* when was file data last accessed */
uint32_t i_mtime; /* when was file data last changed */
uint32_t i_ctime; /* when was inode data last changed */
uint32_t i_zone[NR_TZONES]; /* zone nums for direct, ind, and dbl ind */
} __attribute__((packed)) inode_t;
block_t mnx_numblk, mnx_inode_offset, mnx_next_zone, mnx_next_inode, mnx_zone_map, mnx_root_inum;
zone_t mnx_zoff;
/* Insert one bit into the bitmap */
void mnx_insert_bit(block_t map, bit_t bit)
{
int boff, w, s;
block_t map_block = map + bit / FS_BITS_PER_BLOCK;
boff = bit % FS_BITS_PER_BLOCK;
w = boff / FS_BITCHUNK_BITS;
s = boff % FS_BITCHUNK_BITS;
*((uint32_t*)(fs_base + map_block * DEFAULT_BLOCK_SIZE + w)) |= (1 << s);
}
/* Increment the link count to inode n */
void mnx_incr_link(ino_t n)
{
inode_t *inodes = (inode_t*)(fs_base + mnx_inode_offset * DEFAULT_BLOCK_SIZE + (n-1) * sizeof(inode_t));
inodes[0].i_nlinks++;
}
/* Increment the file-size in inode n */
void mnx_incr_size(ino_t n, size_t count)
{
inode_t *inodes = (inode_t*)(fs_base + mnx_inode_offset * DEFAULT_BLOCK_SIZE + (n-1) * sizeof(inode_t));
inodes[0].i_size += count;
}
/* allocate an inode */
static ino_t mnx_alloc_inode(int mode, int usrid, int grpid)
{
ino_t num;
inode_t *inodes;
superblock_t *sup = (superblock_t*)(fs_base + 1024);
num = mnx_next_inode++;
if(num > sup->s_ninodes) {
fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_TOOMANY]);
exit(1);
}
inodes = (inode_t*)(fs_base + mnx_inode_offset * DEFAULT_BLOCK_SIZE + (num-1) * sizeof(inode_t));
inodes[0].i_mode = mode;
inodes[0].i_uid = usrid;
inodes[0].i_gid = grpid;
/* Set the bit in the bit map. */
mnx_insert_bit((block_t)2, num);
return(num);
}
/* Allocate a new zone */
static zone_t mnx_alloc_zone(void)
{
zone_t z = mnx_next_zone++;
mnx_insert_bit(mnx_zone_map, z - mnx_zoff);
return z;
}
void mnx_add_zone(ino_t n, zone_t z, size_t bytes, time_t mtime, char *name)
{
/* Add zone z to inode n. The file has grown by 'bytes' bytes. */
int i, j;
inode_t *p;
zone_t indir, dindir, *blk, *dblk;
p = (inode_t*)(fs_base + mnx_inode_offset * DEFAULT_BLOCK_SIZE + (n-1) * sizeof(inode_t));
p->i_size += bytes;
p->i_mtime = mtime;
for (i = 0; i < NR_DZONES; i++)
if (p->i_zone[i] == 0) {
p->i_zone[i] = z;
return;
}
/* File has grown beyond a small file. */
if (p->i_zone[NR_DZONES] == 0)
p->i_zone[NR_DZONES] = mnx_alloc_zone();
indir = p->i_zone[NR_DZONES];
--indir; /* Compensate for ++indir below */
for (i = 0; i < INDIRECTS; i++) {
if (i % INDIRECTS == 0)
blk = (zone_t*)(fs_base + ++indir * DEFAULT_BLOCK_SIZE);
if (blk[i % INDIRECTS] == 0) {
blk[i] = z;
return;
}
}
/* File has grown beyond single indirect; we need a double indirect */
if (p->i_zone[NR_DZONES+1] == 0)
p->i_zone[NR_DZONES+1] = mnx_alloc_zone();
dindir = p->i_zone[NR_DZONES+1];
--dindir; /* Compensate for ++indir below */
for (j = 0; j < INDIRECTS; j++) {
if (j % INDIRECTS == 0)
dblk = (zone_t*)(fs_base + ++dindir * DEFAULT_BLOCK_SIZE);
if (dblk[j % INDIRECTS] == 0)
dblk[j % INDIRECTS] = mnx_alloc_zone();
indir = dblk[j % INDIRECTS];
--indir; /* Compensate for ++indir below */
for (i = 0; i < INDIRECTS; i++) {
if (i % INDIRECTS == 0)
blk = (zone_t*)(fs_base + ++indir * DEFAULT_BLOCK_SIZE);
if (blk[i % INDIRECTS] == 0) {
blk[i] = z;
return;
}
}
}
fprintf(stderr,"mkbootimg: partition #%d %s: %s\r\n", fs_no, lang[ERR_TOOBIG], name);
exit(1);
}
int mnx_dir_try_enter(zone_t z, ino_t child, char const *name)
{
direct_t *dir_entry;
int i;
dir_entry = (direct_t*)(fs_base + z * DEFAULT_BLOCK_SIZE);
for (i = 0; i < NR_DIR_ENTRIES; i++)
if (!dir_entry[i].d_ino)
break;
if(i < NR_DIR_ENTRIES) {
dir_entry[i].d_ino = child;
strncpy(dir_entry[i].d_name, name, MFS_DIRSIZ);
return 1;
}
return 0;
}
void mnx_enter_dir(ino_t parent, char const *name, ino_t child)
{
/* Enter child in parent directory */
/* Works for dir > 1 block and zone > block */
unsigned int k;
block_t indir;
zone_t z;
inode_t *ino;
zone_t *indirblock;
/* Obtain the inode structure */
ino = (inode_t*)(fs_base + mnx_inode_offset * DEFAULT_BLOCK_SIZE + (parent-1) * sizeof(inode_t));
for (k = 0; k < NR_DZONES; k++) {
z = ino->i_zone[k];
if (z == 0) {
z = mnx_alloc_zone();
ino->i_zone[k] = z;
}
if(mnx_dir_try_enter(z, child, name))
return;
}
/* no space in directory using just direct blocks; try indirect */
if (ino->i_zone[NR_DZONES] == 0)
ino->i_zone[NR_DZONES] = mnx_alloc_zone();
indir = ino->i_zone[NR_DZONES];
--indir; /* Compensate for ++indir below */
for(k = 0; k < INDIRECTS; k++) {
if (k % INDIRECTS == 0)
indirblock = (zone_t*)(fs_base + ++indir * DEFAULT_BLOCK_SIZE);
z = indirblock[k % INDIRECTS];
if(!z)
z = indirblock[k % INDIRECTS] = mnx_alloc_zone();
if(mnx_dir_try_enter(z, child, name))
return;
}
fprintf(stderr,"mkbootimg: partition #%d %s: %s\r\n", fs_no, lang[ERR_TOOBIG], name);
exit(1);
}
void mnx_open(gpt_t *gpt_entry)
{
zone_t z;
superblock_t *sup;
int i, kb;
if(!gpt_entry) { fprintf(stderr,"mkbootimg: %s minix.\r\n", lang[ERR_BADINITRDTYPE]); exit(1); }
mnx_numblk = (gpt_entry->last - gpt_entry->start + 1) * 512 / DEFAULT_BLOCK_SIZE;
if(mnx_numblk < 8) { fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_NOSIZE]); exit(1); }
/* "format" the partition to Minix3FS */
fs_len = mnx_numblk * DEFAULT_BLOCK_SIZE;
fs_base = realloc(fs_base, fs_len);
if(!fs_base) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_MEM]); exit(1); }
memset(fs_base, 0, fs_len);
sup = (superblock_t*)(fs_base + 1024);
kb = fs_len / 1024;
sup->s_ninodes = kb / 2;
if (kb >= 100000) sup->s_ninodes = kb / 4;
if (kb >= 1000000) sup->s_ninodes = kb / 6;
if (kb >= 10000000) sup->s_ninodes = kb / 8;
if (kb >= 100000000) sup->s_ninodes = kb / 10;
if (kb >= 1000000000) sup->s_ninodes = kb / 12;
sup->s_ninodes += (DEFAULT_BLOCK_SIZE/sizeof(inode_t)) - 1;
sup->s_ninodes &= ~((DEFAULT_BLOCK_SIZE/sizeof(inode_t)) - 1);
if(sup->s_ninodes < 1) { fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_NOSIZE]); exit(1); }
sup->s_zones = mnx_numblk;
sup->s_imap_blocks = (1 + sup->s_ninodes + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE;
sup->s_zmap_blocks = (mnx_numblk + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE;
mnx_inode_offset = 2 + sup->s_imap_blocks + sup->s_zmap_blocks;
sup->s_magic = SUPER_V3;
sup->s_block_size = DEFAULT_BLOCK_SIZE;
i = NR_DZONES+(DEFAULT_BLOCK_SIZE/sizeof(inode_t))+(DEFAULT_BLOCK_SIZE/sizeof(inode_t))*(DEFAULT_BLOCK_SIZE/sizeof(inode_t));
if(INT32_MAX/DEFAULT_BLOCK_SIZE < i)
sup->s_max_size = INT32_MAX;
else
sup->s_max_size = i * DEFAULT_BLOCK_SIZE;
mnx_next_zone = mnx_inode_offset + (sup->s_ninodes+(DEFAULT_BLOCK_SIZE/sizeof(inode_t))-1)/(DEFAULT_BLOCK_SIZE/sizeof(inode_t));
mnx_zoff = mnx_next_zone - 1;
mnx_next_inode = 1;
mnx_zone_map = 2 + sup->s_imap_blocks;
mnx_insert_bit(mnx_zone_map, 0); /* bit zero must always be allocated */
mnx_insert_bit((block_t)2, 0); /* inode zero not used but must be allocated */
mnx_root_inum = mnx_alloc_inode(0755, 0, 0);
z = mnx_alloc_zone();
mnx_add_zone(mnx_root_inum, z, 2 * sizeof(direct_t), t, "rootdir");
mnx_enter_dir(mnx_root_inum, ".", mnx_root_inum);
mnx_enter_dir(mnx_root_inum, "..", mnx_root_inum);
mnx_incr_link(mnx_root_inum);
mnx_incr_link(mnx_root_inum);
}
void mnx_add(struct stat *st, char *name, unsigned char *content, int size)
{
ino_t n, parent = mnx_root_inum;
inode_t *ino;
zone_t z;
direct_t *dir_entry;
int i, k;
char *end, *fn = strrchr(name, '/');
if(!fn) fn = name; else fn++;
if(!strcmp(fn, ".") || !strcmp(fn, "..")) return;
if(!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode) && !S_ISLNK(st->st_mode) && !S_ISCHR(st->st_mode) && !S_ISBLK(st->st_mode))
return;
n = mnx_alloc_inode(st->st_mode, st->st_uid, st->st_gid);
/* Enter name in directory and update directory's size. */
fn = name;
end = strchr(name, '/');
if(!end) end = name + strlen(name);
i = k = 0;
do {
/* FIXME: this doesn't handle indirect and double indirect data */
ino = (inode_t*)(fs_base + mnx_inode_offset * DEFAULT_BLOCK_SIZE + (parent-1) * sizeof(inode_t));
dir_entry = (direct_t*)(fs_base + ino->i_zone[k] * DEFAULT_BLOCK_SIZE);
if(!memcmp(dir_entry[i].d_name, fn, end - fn) && !dir_entry[i].d_name[end - fn]) {
parent = dir_entry[i].d_ino; i = k = 0;
fn = end + 1;
end = *end ? strchr(fn, '/') : NULL;
if(!end) break;
}
i++;
if(i == NR_DIR_ENTRIES) { i = 0; k++; }
if((k * NR_DIR_ENTRIES + i) * sizeof(direct_t) >= ino->i_size) break;
} while(1);
mnx_enter_dir(parent, fn, n);
mnx_incr_size(parent, sizeof(direct_t));
/* Check to see if file is directory or special. */
mnx_incr_link(n);
if (S_ISDIR(st->st_mode)) {
/* This is a directory. */
z = mnx_alloc_zone(); /* zone for new directory */
mnx_add_zone(n, z, 2 * sizeof(direct_t), st->st_mtime, name);
mnx_enter_dir(n, ".", n);
mnx_enter_dir(n, "..", parent);
mnx_incr_link(parent);
mnx_incr_link(n);
} else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
/* Special file. */
mnx_add_zone(n, (zone_t)st->st_rdev, st->st_size, st->st_mtime, name);
} else if (S_ISLNK(st->st_mode)) {
if(size > DEFAULT_BLOCK_SIZE - 1) {
fprintf(stderr,"mkbootimg: partition #%d %s: %s\r\n", fs_no, lang[ERR_TOOBIG], name);
exit(1);
}
z = mnx_alloc_zone();
memcpy(fs_base + z * DEFAULT_BLOCK_SIZE, content, size + 1);
mnx_add_zone(n, z, size, st->st_mtime, name);
} else {
/* Regular file. Go read it. */
while(size) {
z = mnx_alloc_zone();
memcpy(fs_base + z * DEFAULT_BLOCK_SIZE, content, DEFAULT_BLOCK_SIZE);
mnx_add_zone(n, z, size < DEFAULT_BLOCK_SIZE ? size : DEFAULT_BLOCK_SIZE, st->st_mtime, name);
if(size > DEFAULT_BLOCK_SIZE) {
content += DEFAULT_BLOCK_SIZE;
size -= DEFAULT_BLOCK_SIZE;
} else
break;
}
}
}
void mnx_close()
{
FILE *f;
if(!fs_base || fs_len < 2048) return;
f = fopen("test.bin", "w");
fwrite(fs_base, fs_len, 1, f);
fclose(f);
}

View file

@ -31,7 +31,10 @@
void tar_open(gpt_t *gpt_entry)
{
if(gpt && (gpt_entry->last - gpt_entry->start) < 1) { fprintf(stderr,"mkbootimg: %s\r\n", lang[ERR_NOSIZE]); exit(1); }
if(gpt_entry && (gpt_entry->last - gpt_entry->start) < 1) {
fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_NOSIZE]);
exit(1);
}
}
void tar_add(struct stat *st, char *name, unsigned char *content, int size)

View file

@ -45,7 +45,7 @@ struct tm *ts;
guid_t diskguid;
char *json = NULL, *config = NULL, *kernelname = NULL, *initrd_dir[NUMARCH] = {0}, *initrd_file[NUMARCH] = {0};
char initrd_arch[NUMARCH] = {0};
int fs_len, initrd_size[NUMARCH] = {0}, initrd_gzip = 1, boot_size = 0, boot_fat = 16, disk_size = 0;
int fs_len, fs_no, initrd_size[NUMARCH] = {0}, initrd_gzip = 1, boot_size = 0, boot_fat = 16, disk_size = 0;
int iso9660 = 0, skipbytes = 0;
unsigned char *fs_base = NULL, *initrd_buf[NUMARCH] = {0};
unsigned long int tsize = 0, es = 0, esiz = 0, disk_align = 0;
@ -171,8 +171,8 @@ void initrdcompress()
memcpy(fs_base + 10 + initrdgz_len - 2, &crc, 4);
memcpy(fs_base + 14 + initrdgz_len - 2, &fs_len, 4);
fs_len = initrdgz_len - 2 + 18;
} else
free(initrdgz);
}
free(initrdgz);
}
/**