490 lines
22 KiB
C++
490 lines
22 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
// Some parts of the following driver is made using this guide: https://elm-chan.org/docs/fat_e.html
|
|
#include "vybuild.hpp"
|
|
#include "common/versions.h"
|
|
#include "api.h"
|
|
#include <dlfcn.h>
|
|
#include <map>
|
|
using namespace std;
|
|
typedef int (*crypto_sign_func_ptr)(uint8_t*,size_t*,const uint8_t*,size_t,const uint8_t*);
|
|
typedef void* (*sha3_512_func_ptr)(const void*,size_t,void*,int);
|
|
struct common_crypto_struct {
|
|
crypto_sign_func_ptr crypto_sign;
|
|
sha3_512_func_ptr sha3_512;
|
|
};
|
|
bool is_crypto_lib_available=false;
|
|
struct common_crypto_struct common_crypto;
|
|
uint64_t vftm_target_offset=0;
|
|
vector<uint8_t> vftm_target_content;
|
|
uint8_t manifest_keypair[CRYPTO_PUBLICKEYBYTES+CRYPTO_SECRETKEYBYTES]={0};
|
|
bool vybuild::fat32::is_crypto_lib_loaded() {
|
|
return is_crypto_lib_available;
|
|
}
|
|
bool vybuild::fat32::is_vftm_target_set() {
|
|
return vftm_target_offset!=0;
|
|
}
|
|
uint8_t* vybuild::fat32::get_manifest_keypair_buffer() {
|
|
return manifest_keypair;
|
|
}
|
|
void vybuild::fat32::load_crypto_lib(string path) {
|
|
void *handle;
|
|
handle=dlopen(path.c_str(),RTLD_LAZY | RTLD_LOCAL);
|
|
if (!handle) {
|
|
throw std::runtime_error("Something went wrong during the loading of libcommoncrypto.so: "+string(dlerror()));
|
|
}
|
|
struct common_crypto_struct crypto;
|
|
crypto.crypto_sign=reinterpret_cast<crypto_sign_func_ptr>(dlsym(handle,"crypto_sign_signature"));
|
|
crypto.sha3_512=reinterpret_cast<sha3_512_func_ptr>(dlsym(handle,"sha3"));
|
|
char *error=dlerror();
|
|
if (error!=NULL) {
|
|
dlclose(handle);
|
|
throw std::runtime_error("Something went wrong during the loading of symbols from libcommoncrypto.so: "+string(error));
|
|
}
|
|
common_crypto=crypto;
|
|
is_crypto_lib_available=true;
|
|
return;
|
|
}
|
|
#pragma pack(1)
|
|
struct vystem_fat_trusted_manifest {
|
|
uint8_t sig[8]={'V','y','F','a','t','S','i','g'};
|
|
uint16_t manifest_version=VY_COMMON_VERSIONS_MANIFEST;
|
|
uint16_t bootloader_version=VY_COMMON_VERSIONS_BOOTLOADER;
|
|
uint32_t os_version=VY_COMMON_VERSIONS_OS;
|
|
uint8_t fat_hash[64];
|
|
uint8_t fat_build_id[32];
|
|
uint64_t clusters_count;
|
|
uint64_t bytes_per_sector;
|
|
uint64_t sectors_per_cluster;
|
|
uint64_t fat_size_bytes;
|
|
uint64_t fat_offset_bytes;
|
|
uint64_t bootloader_cluster_count;
|
|
uint64_t bootloader_cluster_offset;
|
|
uint64_t bootloader_build_id;
|
|
uint8_t fat_header_hash[64];
|
|
uint8_t bootloader_binary_hash[64];
|
|
uint64_t signature_offset_bytes;
|
|
uint64_t signature_size;
|
|
uint8_t gpt_disk_guid[16];
|
|
uint8_t gpt_efi_part_unique_guid[16];
|
|
uint8_t manifest_hash[64];
|
|
};
|
|
#pragma pack()
|
|
#pragma pack(1)
|
|
struct fat32_header {
|
|
uint8_t BS_JmpBoot[3]={0xeb,0x58,0x90};
|
|
uint8_t BS_OEMName[8]={'V','Y','B','U','I','L','D','1'};
|
|
uint16_t BPB_BytsPerSec=512;
|
|
uint8_t BPB_SecPerClus=1;
|
|
uint16_t BPB_RsvdSecCnt=32; // equivalent to 32 clusters
|
|
uint8_t BPB_NumFATs=2;
|
|
uint16_t BPB_RootEntCnt=0; // always 0 for FAT32
|
|
uint16_t BPB_TotSec16=0;
|
|
uint8_t BPB_Media=0xF8;
|
|
uint16_t BPB_FATSz16=0;
|
|
uint16_t BPB_SecPerTrk=0; // set to 0 because guide didn't indicate anything
|
|
uint16_t BPB_NumHeads=0; // also set to 0 for same reason, useless on modern hardware
|
|
uint32_t BPB_HiddSec=0; // i honestly don't know what value the UEFI firmware expect here, but i pray for it to work
|
|
uint32_t BPB_TotSec32; // finally a useful field, amount of SECTORS in the whole fs
|
|
uint32_t BPB_FATSz32; // amount of SECTORS in each FAT
|
|
uint16_t BPB_ExtFlags=0; // too complicated for now
|
|
uint16_t BPB_FSVer=0; // apparentaly not only FAT32 is very old and will never be updated, but current version is 0.0
|
|
uint32_t BPB_RootClus=2; // root directory cluster
|
|
uint16_t BPB_FSInfo=1; // this is an offset
|
|
uint16_t BPB_BkBootSec=6; // this is also an offset
|
|
uint8_t BPB_Reserved[12]={0}; // i wonder why so much useles space waster into reserved fields
|
|
uint8_t BS_DrvNum=0x80; // hope this will work on UEFI firmware, i hate retrocompatibility, it lead to useles thing
|
|
uint8_t BS_Reserved=0; // another useles field
|
|
uint8_t BS_BootSig=0x29; // another signature
|
|
uint32_t BS_VolID=0; // the guide want me to put a date and time here, but too bad, i'm lazy
|
|
uint8_t BS_VolLab[11]={'V','Y','A','U','T','O','F','A','T','3','2'}; // yay, my custom volume label fit!
|
|
uint8_t BS_FilSysType[8]={'F','A','T','3','2',' ',' ',' '}; // ok why not
|
|
uint8_t BS_BootCode32[420]={0}; // who the fuck think this is a good idea to put a field so large into a fs header ?!
|
|
uint16_t BS_Sign=0xAA55; // another signature
|
|
}; // yeah, exactly 512 bytes
|
|
#pragma pack()
|
|
#define ENTRY_FLAG_RO 0x01
|
|
#define ENTRY_FLAG_HIDDEN 0x02
|
|
#define ENTRY_FLAG_SYSTEM 0x04
|
|
#define ENTRY_FLAG_VOL_ID 0x08
|
|
#define ENTRY_FLAG_IS_DIR 0x10
|
|
#define ENTRY_FLAG_ARCHIVE 0x20
|
|
#define ENTRY_FLAG_LFN 0x0F
|
|
#pragma pack(1)
|
|
struct dir_entry {
|
|
char name[11];
|
|
uint8_t attributes;
|
|
uint8_t sfn_flags=0;
|
|
uint8_t creation_time_tenth=0;
|
|
uint16_t creation_time=0;
|
|
uint16_t creation_date=0;
|
|
uint16_t last_access_date=0;
|
|
uint16_t upper_part_first_cluster;
|
|
uint16_t last_time_change=0;
|
|
uint16_t last_data_change=0;
|
|
uint16_t lower_part_first_cluster=0;
|
|
uint32_t file_size=0;
|
|
};
|
|
#pragma pack()
|
|
#pragma pack(1)
|
|
struct fat32_fsinfo {
|
|
uint32_t FSI_LeadSig=0x41615252;
|
|
uint8_t FSI_Reserved1[480]={0}; // what the actual fuck is this... i think we have a new record
|
|
uint32_t FSI_StrucSig=0x61417272;
|
|
uint32_t FSI_Free_Count=0xFFFFFFFF;
|
|
uint32_t FSI_Nxt_Free=0xFFFFFFFF;
|
|
uint8_t FSI_Reserved2[12]={0};
|
|
uint32_t FSI_TrailSig=0xAA550000;
|
|
};
|
|
#pragma pack()
|
|
struct fat32_container {
|
|
fat32_header header;
|
|
uint64_t fat_start_sector=header.BPB_RsvdSecCnt;
|
|
uint64_t fat_total_sectors; // complete it with header.BPB_FATSz32 * header.BPB_NumFATs, amount of sectors in all FATs
|
|
uint64_t data_start_sector; // complete with header.BPB_RsvdSecCnt + fat_total_sectors
|
|
uint64_t data_total_sectors; // complete with header.BPB_TotSec32 - data_start_sector
|
|
uint64_t clusters_count; // complete with data_total_sectors / header.BPB_SecPerClus
|
|
uint64_t root_clusters_sector;
|
|
vector<uint8_t> fat;
|
|
vector<uint8_t> data;
|
|
fat32_fsinfo fsinfo;
|
|
};
|
|
map<string,fat32_container> fs_map;
|
|
vector<uint8_t> get_urandom(size_t n) {
|
|
vector<uint8_t> out(n);
|
|
ifstream urandom("/dev/urandom",ios::binary);
|
|
if (!urandom.is_open()) {
|
|
throw runtime_error("Couldn't open /dev/urandom.");
|
|
}
|
|
urandom.read(reinterpret_cast<char*>(out.data()),n);
|
|
if (urandom.gcount()!=static_cast<streamsize>(n)) {
|
|
throw runtime_error("Coudln't open /dev/urandom.");
|
|
}
|
|
return out;
|
|
}
|
|
void read_sector(uint8_t *buf_start_ptr,size_t sector_index,uint8_t* out_buf) {
|
|
size_t byte_offset=sector_index*512;
|
|
memcpy(out_buf,buf_start_ptr+byte_offset,512);
|
|
return;
|
|
}
|
|
void write_sector(uint8_t *buf_start_ptr,size_t sector_index,uint8_t* in_buf) {
|
|
size_t byte_offset=sector_index*512;
|
|
memcpy(buf_start_ptr+byte_offset,in_buf,512);
|
|
return;
|
|
}
|
|
uint32_t fat_read_entry(fat32_container& cont,uint32_t cluster) {
|
|
if (cluster>=cont.clusters_count+2) throw std::runtime_error("FAT cluster out of bounds");
|
|
size_t offset=cluster*4;
|
|
uint32_t value;
|
|
memcpy(&value,cont.fat.data()+offset,4);
|
|
return value & 0x0FFFFFFF;
|
|
}
|
|
void fat_write_entry(fat32_container& cont,uint32_t cluster,uint32_t value) {
|
|
if (cluster>=cont.clusters_count+2) throw std::runtime_error("FAT cluster out of bounds");
|
|
size_t offset=cluster*4;
|
|
value&=0x0FFFFFFF;
|
|
memcpy(cont.fat.data()+offset,&value,4);
|
|
return;
|
|
}
|
|
vystem_fat_trusted_manifest generate_manifest(fat32_container& cont,string disk_guid_var,string efi_part_unique_guid_var) {
|
|
if (!is_crypto_lib_available) throw std::runtime_error("Crypto library isn't available.");
|
|
if (vftm_target_offset==0) throw std::runtime_error("VFTM target isn't set.");
|
|
int zero_bytes=0;
|
|
for (int i=0;i<sizeof(manifest_keypair);i++) {
|
|
if (manifest_keypair[i]==0) {
|
|
zero_bytes++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (zero_bytes==sizeof(manifest_keypair)) throw std::runtime_error("VFTM keypair isn't set.");
|
|
vystem_fat_trusted_manifest vftm;
|
|
uint32_t *f1=(uint32_t*)cont.fat.data();
|
|
f1[0]=f1[1]=0;
|
|
common_crypto.sha3_512(cont.fat.data(),cont.fat.size(),vftm.fat_hash,sizeof(vftm.fat_hash));
|
|
fat_write_entry(cont,0,0x0FFFFFF8);
|
|
fat_write_entry(cont,1,0xFFFFFFFF);
|
|
auto fat_build_id=get_urandom(sizeof(vftm.fat_build_id));
|
|
memcpy(vftm.fat_build_id,fat_build_id.data(),fat_build_id.size());
|
|
vftm.clusters_count=cont.clusters_count;
|
|
vftm.bytes_per_sector=cont.header.BPB_BytsPerSec;
|
|
vftm.sectors_per_cluster=cont.header.BPB_SecPerClus;
|
|
vftm.fat_size_bytes=cont.fat.size();
|
|
vftm.fat_offset_bytes=cont.header.BPB_RsvdSecCnt*cont.header.BPB_BytsPerSec;
|
|
vftm.bootloader_cluster_count=vftm_target_content.size()/(cont.header.BPB_BytsPerSec*cont.header.BPB_SecPerClus);
|
|
vftm.bootloader_cluster_offset=vftm_target_offset;
|
|
auto bootloader_build_id=get_urandom(8);
|
|
memcpy(&vftm.bootloader_build_id,&bootloader_build_id,8);
|
|
common_crypto.sha3_512(&cont.header,sizeof(cont.header),vftm.fat_header_hash,sizeof(vftm.fat_header_hash));
|
|
common_crypto.sha3_512(vftm_target_content.data(),vftm_target_content.size(),vftm.bootloader_binary_hash,sizeof(vftm.bootloader_binary_hash));
|
|
vftm.signature_offset_bytes=cont.header.BPB_TotSec32*cont.header.BPB_BytsPerSec;
|
|
vftm.signature_size=CRYPTO_BYTES;
|
|
auto disk_guid=vybuild::vars::get_global_variable("\\$"+disk_guid_var+"\\$");
|
|
auto disk_guid_bytes=vybuild::disk::uuid4_to_bytes(disk_guid);
|
|
memcpy(vftm.gpt_disk_guid,disk_guid_bytes.data(),disk_guid_bytes.size());
|
|
auto efi_part_unique_guid=vybuild::vars::get_global_variable("\\$"+efi_part_unique_guid_var+"\\$");
|
|
auto efi_part_unique_guid_bytes=vybuild::disk::uuid4_to_bytes(efi_part_unique_guid);
|
|
memcpy(vftm.gpt_efi_part_unique_guid,efi_part_unique_guid_bytes.data(),efi_part_unique_guid_bytes.size());
|
|
common_crypto.sha3_512(&vftm,sizeof(vftm)-64,vftm.manifest_hash,sizeof(vftm.manifest_hash));
|
|
return vftm;
|
|
}
|
|
uint8_t* cluster_ptr(fat32_container& cont,uint32_t cluster) {
|
|
size_t sector=(cluster-2)*cont.header.BPB_SecPerClus;
|
|
return (uint8_t*)cont.data.data()+sector*512;
|
|
}
|
|
uint32_t fat_allocate_cluster(fat32_container& cont) {
|
|
for (uint32_t i=2;i<cont.clusters_count+2;i++) {
|
|
if (fat_read_entry(cont,i)==0) {
|
|
fat_write_entry(cont,i,0x0FFFFFFF);
|
|
memset(cluster_ptr(cont,i),0,cont.header.BPB_SecPerClus*512);
|
|
return i;
|
|
}
|
|
}
|
|
throw std::runtime_error("Fat32 image full.");
|
|
}
|
|
bool dir_add_entry(fat32_container& cont,uint32_t dir_cluster,dir_entry& entry) {
|
|
if (dir_cluster<2 || dir_cluster>=cont.clusters_count+2) throw std::runtime_error("Directory cluster out of bounds.");
|
|
dir_entry* entries=reinterpret_cast<dir_entry*>(cluster_ptr(cont,dir_cluster));
|
|
size_t max_entries=(cont.header.BPB_SecPerClus*512)/sizeof(dir_entry);
|
|
for (size_t i=0;i<max_entries;i++) {
|
|
if (entries[i].name[0]==0) {
|
|
entries[i]=entry;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
void vybuild::fat32::create_fat32(string key,size_t size) {
|
|
if (size%512!=0) throw std::runtime_error("Fat32 image size isn't a multiple of 512 bytes.");
|
|
fat32_container cont;
|
|
cont.header.BPB_TotSec32=size/512;
|
|
uint32_t fat_sectors=1;
|
|
while (true) {
|
|
uint64_t data_sectors=cont.header.BPB_TotSec32-cont.header.BPB_RsvdSecCnt-fat_sectors*cont.header.BPB_NumFATs;
|
|
uint64_t clusters=data_sectors/cont.header.BPB_SecPerClus;
|
|
uint32_t new_fat_sectors=((clusters+2)*4+511)/512;
|
|
if (new_fat_sectors==fat_sectors) break;
|
|
fat_sectors=new_fat_sectors;
|
|
}
|
|
cont.header.BPB_FATSz32=fat_sectors;
|
|
cont.fat_total_sectors=cont.header.BPB_FATSz32*cont.header.BPB_NumFATs;
|
|
cont.data_start_sector=cont.header.BPB_RsvdSecCnt+cont.fat_total_sectors;
|
|
cont.data_total_sectors=cont.header.BPB_TotSec32-cont.data_start_sector;
|
|
cont.clusters_count=cont.data_total_sectors/cont.header.BPB_SecPerClus;
|
|
cont.root_clusters_sector=cont.data_start_sector+(cont.header.BPB_RootClus-2)*cont.header.BPB_SecPerClus;
|
|
cont.fat.resize(cont.header.BPB_FATSz32*512);
|
|
memset(cont.fat.data(),0,cont.fat.size());
|
|
fat_write_entry(cont,0,0x0FFFFFF8);
|
|
fat_write_entry(cont,1,0xFFFFFFFF);
|
|
fat_write_entry(cont,2,0x0FFFFFFF);
|
|
cont.data.resize(cont.data_total_sectors*512);
|
|
memset(cont.data.data(),0,cont.data.size());
|
|
memset(cluster_ptr(cont,2),0,cont.header.BPB_SecPerClus*512);
|
|
dir_entry label{};
|
|
memset(label.name,' ',11);
|
|
memcpy(label.name,"VYAUTOFAT32",11);
|
|
label.attributes=ENTRY_FLAG_VOL_ID;
|
|
label.lower_part_first_cluster=0;
|
|
label.upper_part_first_cluster=0;
|
|
label.file_size=0;
|
|
dir_add_entry(cont,2,label);
|
|
fs_map[key]=cont;
|
|
return;
|
|
}
|
|
uint32_t path_find_dir_cluster(fat32_container& cont,string& path) {
|
|
if (path.empty() || path[0]!='/') throw std::runtime_error("Path must be absolute.");
|
|
if (path=="/") return 2;
|
|
uint32_t current_cluster=2;
|
|
size_t pos=1;
|
|
while (pos<path.size()) {
|
|
size_t next=path.find('/',pos);
|
|
string part=(next==string::npos)?path.substr(pos):path.substr(pos,next-pos);
|
|
char fat_name[11];
|
|
memset(fat_name,' ',11);
|
|
memcpy(fat_name,part.c_str(),min<size_t>(part.size(),8));
|
|
dir_entry* entries=reinterpret_cast<dir_entry*>(cluster_ptr(cont,current_cluster));
|
|
size_t max_entries=(cont.header.BPB_SecPerClus*512)/sizeof(dir_entry);
|
|
bool found=false;
|
|
for (size_t i=0;i<max_entries;i++) {
|
|
if (entries[i].name[0]==0) break;
|
|
if (!(entries[i].attributes & ENTRY_FLAG_IS_DIR)) continue;
|
|
if (memcmp(entries[i].name,fat_name,11)==0) {
|
|
current_cluster=(uint32_t(entries[i].upper_part_first_cluster)<<16)|entries[i].lower_part_first_cluster;
|
|
found=true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) return 0;
|
|
if (next==string::npos) break;
|
|
pos=next+1;
|
|
}
|
|
return current_cluster;
|
|
}
|
|
uint32_t create_dir_path(fat32_container& cont,string& parent_folder,string& dir_name) {
|
|
uint32_t parent_cluster=path_find_dir_cluster(cont,parent_folder);
|
|
if (parent_cluster==0) throw std::runtime_error("Parent folder not found: "+parent_folder);
|
|
uint32_t new_cluster=fat_allocate_cluster(cont);
|
|
dir_entry entry{};
|
|
memset(entry.name,' ', 11);
|
|
memcpy(entry.name,dir_name.c_str(),min<size_t>(dir_name.size(),8));
|
|
memcpy(entry.name+8," ",3);
|
|
entry.attributes=ENTRY_FLAG_IS_DIR;
|
|
entry.lower_part_first_cluster=new_cluster&0xFFFF;
|
|
entry.upper_part_first_cluster=new_cluster>>16;
|
|
entry.file_size=0;
|
|
if (!dir_add_entry(cont,parent_cluster,entry)) throw std::runtime_error("Parent directory full.");
|
|
dir_entry dot{};
|
|
memset(dot.name,' ',11);
|
|
memcpy(dot.name,". ",11);
|
|
dot.attributes=ENTRY_FLAG_IS_DIR;
|
|
dot.lower_part_first_cluster=new_cluster&0xFFFF;
|
|
dot.upper_part_first_cluster=new_cluster>>16;
|
|
dot.file_size=0;
|
|
dir_entry dotdot{};
|
|
memset(dotdot.name,' ',11);
|
|
memcpy(dotdot.name,".. ",11);
|
|
dotdot.attributes=ENTRY_FLAG_IS_DIR;
|
|
dotdot.lower_part_first_cluster=parent_cluster&0xFFFF;
|
|
dotdot.upper_part_first_cluster=parent_cluster>>16;
|
|
dotdot.file_size=0;
|
|
dir_add_entry(cont,new_cluster,dot);
|
|
dir_add_entry(cont,new_cluster,dotdot);
|
|
return new_cluster;
|
|
}
|
|
uint32_t create_dir(fat32_container& cont,string& full_path) {
|
|
if (full_path.empty() || full_path[0]!='/') throw std::runtime_error("Path must be absolute.");
|
|
size_t last_slash=full_path.find_last_of('/');
|
|
if (last_slash==string::npos) throw std::runtime_error("Invalid path.");
|
|
string parent=(last_slash==0)?"/":full_path.substr(0,last_slash);
|
|
string dir_name=full_path.substr(last_slash+1);
|
|
if (dir_name.empty()) throw std::runtime_error("Directory name is empty.");
|
|
return create_dir_path(cont, parent, dir_name);
|
|
}
|
|
void vybuild::fat32::add_directory_fat32(string key,string full_path) {
|
|
if (fs_map.find(key)==fs_map.end()) throw std::runtime_error(key+" isn't a valid Fat32 image key.");
|
|
fat32_container& fat=fs_map[key];
|
|
create_dir(fat,full_path);
|
|
return;
|
|
}
|
|
uint32_t fat_allocate_chain(fat32_container& cont,uint32_t count) {
|
|
if (count==0) throw std::runtime_error("Cluster chain size cannot be zero.");
|
|
uint32_t max_cluster=cont.clusters_count+2;
|
|
for (uint32_t start=2;start+count<=max_cluster;start++) {
|
|
bool free=true;
|
|
for (uint32_t i=0;i<count;i++) {
|
|
if (fat_read_entry(cont,start+i)!=0) {
|
|
free=false;
|
|
break;
|
|
}
|
|
}
|
|
if (!free) continue;
|
|
for (uint32_t i=0;i<count-1;i++) {
|
|
fat_write_entry(cont,start+i,start+i+1);
|
|
}
|
|
fat_write_entry(cont,start+count-1,0x0FFFFFFF);
|
|
size_t cluster_size=cont.header.BPB_SecPerClus*512;
|
|
memset(cluster_ptr(cont,start),0,cluster_size*count);
|
|
return start;
|
|
}
|
|
throw std::runtime_error("Not enough contiguous clusters.");
|
|
}
|
|
uint32_t create_file(fat32_container& cont,string full_path,uint8_t *buffer_in,size_t file_size,bool is_vftm_target) {
|
|
if (file_size!=0 && buffer_in==nullptr) throw std::runtime_error("Input buffer is null.");
|
|
if (full_path.empty() || full_path[0]!='/') throw std::runtime_error("Path must be absolute.");
|
|
size_t last_slash=full_path.find_last_of('/');
|
|
if (last_slash==string::npos) throw std::runtime_error("Invalid path.");
|
|
string parent=(last_slash==0)?"/":full_path.substr(0,last_slash);
|
|
string file_name=full_path.substr(last_slash+1);
|
|
if (file_name.empty()) throw std::runtime_error("File name is empty.");
|
|
if (file_name.size()>12) throw std::runtime_error("File name including extension is too long.");
|
|
size_t dot=file_name.find_last_of('.');
|
|
if (dot==string::npos || dot==0) throw std::runtime_error("Invalid file name.");
|
|
string name=file_name.substr(0,dot);
|
|
string ext=file_name.substr(dot+1);
|
|
if (ext.size()>3 || ext.size()==0) throw std::runtime_error("Invalid extension.");
|
|
if (name.size()>8 || ext.size()==0) throw std::runtime_error("Invalid file name.");
|
|
transform(name.begin(),name.end(),name.begin(),::toupper);
|
|
transform(ext.begin(),ext.end(),ext.begin(),::toupper);
|
|
uint32_t parent_cluster=path_find_dir_cluster(cont,parent);
|
|
if (parent_cluster==0) throw std::runtime_error("Parent folder doesn't exist.");
|
|
uint32_t cluster_size=cont.header.BPB_SecPerClus*512;
|
|
uint32_t cluster_count=max<uint32_t>(1,(file_size+cluster_size-1)/cluster_size);
|
|
uint32_t start_cluster=fat_allocate_chain(cont,cluster_count);
|
|
dir_entry entry{};
|
|
memset(entry.name,' ',11);
|
|
memcpy(entry.name,name.c_str(),min<size_t>(name.size(),8));
|
|
memcpy(entry.name+8,ext.data(),min<size_t>(ext.size(),3));
|
|
entry.attributes=ENTRY_FLAG_ARCHIVE;
|
|
entry.lower_part_first_cluster=start_cluster&0xFFFF;
|
|
entry.upper_part_first_cluster=start_cluster>>16;
|
|
entry.file_size=file_size;
|
|
if (!dir_add_entry(cont,parent_cluster,entry)) throw std::runtime_error("Parent directory full.");
|
|
memcpy(cluster_ptr(cont,start_cluster),buffer_in,file_size);
|
|
if (is_vftm_target) {
|
|
vftm_target_content.resize(cluster_count*cluster_size,0);
|
|
memcpy(vftm_target_content.data(),buffer_in,file_size);
|
|
vftm_target_offset=start_cluster;
|
|
}
|
|
return start_cluster;
|
|
}
|
|
void vybuild::fat32::add_file_fat32(string key,string full_path,uint8_t *buffer_in,size_t file_size,bool is_vftm_target) {
|
|
if (fs_map.find(key)==fs_map.end()) throw std::runtime_error(key+" isn't a valid Fat32 image key.");
|
|
fat32_container& fat=fs_map[key];
|
|
create_file(fat,full_path,buffer_in,file_size,is_vftm_target);
|
|
return;
|
|
}
|
|
void generate_vftm_sectors(const uint8_t* part_guid,int* s1,int* s2) {
|
|
uint32_t h=0x811c9dc5;
|
|
for (int i=0;i<16;i++) h=(h^part_guid[i])*0x01000193;
|
|
*s1=8+(h%24);
|
|
*s2=8+(((h>>16)%23+(*s1-7))%24);
|
|
}
|
|
void secure_erase(void *address,size_t size) {
|
|
explicit_bzero(address,size);
|
|
}
|
|
vector<uint8_t> export_fat32(fat32_container& cont,bool generate_vftm,string disk_guid_var,string efi_part_unique_guid_var) {
|
|
uint32_t total_sectors=cont.header.BPB_TotSec32;
|
|
vector<uint8_t> img(total_sectors*512,0);
|
|
auto write_sector=[&](uint32_t sector,const void* data,size_t size) {
|
|
memcpy(img.data()+sector*512,data,size);
|
|
};
|
|
write_sector(0,&cont.header,sizeof(cont.header));
|
|
write_sector(cont.header.BPB_BkBootSec,&cont.header,sizeof(cont.header));
|
|
write_sector(1,&cont.fsinfo,sizeof(cont.fsinfo));
|
|
write_sector(cont.header.BPB_BkBootSec+1,&cont.fsinfo,sizeof(cont.fsinfo));
|
|
uint32_t fat_size_bytes=cont.header.BPB_FATSz32*512;
|
|
uint32_t fat_sector_start=cont.header.BPB_RsvdSecCnt;
|
|
memcpy(img.data()+fat_sector_start*512,cont.fat.data(),fat_size_bytes);
|
|
uint32_t fat2_sector_start=fat_sector_start+cont.header.BPB_FATSz32;
|
|
memcpy(img.data()+fat2_sector_start*512,cont.fat.data(),fat_size_bytes);
|
|
uint32_t data_sector_start=cont.data_start_sector;
|
|
memcpy(img.data()+data_sector_start*512,cont.data.data(),cont.data.size());
|
|
if (generate_vftm) {
|
|
auto efi_part_unique_guid=vybuild::vars::get_global_variable("\\$"+efi_part_unique_guid_var+"\\$");
|
|
auto efi_part_unique_guid_bytes=vybuild::disk::uuid4_to_bytes(efi_part_unique_guid);
|
|
auto vftm=generate_manifest(cont,disk_guid_var,efi_part_unique_guid_var);
|
|
int manifest_sector,pk_manifest_sector;
|
|
generate_vftm_sectors((uint8_t*)efi_part_unique_guid_bytes.data(),&manifest_sector,&pk_manifest_sector);
|
|
uint8_t signature_manifest[CRYPTO_BYTES];
|
|
size_t siglen;
|
|
if (common_crypto.crypto_sign(signature_manifest,&siglen,(uint8_t*)&vftm,sizeof(vftm),manifest_keypair+CRYPTO_PUBLICKEYBYTES)) {
|
|
secure_erase(manifest_keypair,sizeof(manifest_keypair));
|
|
throw std::runtime_error("Couldn't sign VFTM.");
|
|
}
|
|
if (siglen!=CRYPTO_BYTES) {
|
|
secure_erase(manifest_keypair,sizeof(manifest_keypair));
|
|
throw std::runtime_error("The returned signature isn't the expected size.");
|
|
}
|
|
secure_erase(manifest_keypair+CRYPTO_PUBLICKEYBYTES,CRYPTO_SECRETKEYBYTES);
|
|
memcpy(img.data()+manifest_sector*cont.header.BPB_BytsPerSec,&vftm,sizeof(vftm));
|
|
memcpy(img.data()+pk_manifest_sector*cont.header.BPB_BytsPerSec,manifest_keypair,CRYPTO_PUBLICKEYBYTES);
|
|
img.resize(img.size()+CRYPTO_BYTES);
|
|
memcpy(img.data()+vftm.signature_offset_bytes,signature_manifest,CRYPTO_BYTES);
|
|
}
|
|
return img;
|
|
}
|
|
vector<uint8_t> vybuild::fat32::export_fat32(string key,bool generate_vftm,string disk_guid_var,string efi_part_unique_guid_var) {
|
|
if (fs_map.find(key)==fs_map.end()) throw std::runtime_error(key+" isn't a valid Fat32 image key.");
|
|
fat32_container& fat=fs_map[key];
|
|
return export_fat32(fat,generate_vftm,disk_guid_var,efi_part_unique_guid_var);
|
|
}
|