From af1b908d77bde30beab4b3284a415edb83475dcb Mon Sep 17 00:00:00 2001 From: KrekBuk Date: Fri, 1 May 2020 17:19:29 +0200 Subject: [PATCH] FAT32 support --- Makefile | 21 ++++ qloader2.bin | Bin 32768 -> 32768 bytes src/fs/fat32.c | 313 +++++++++++++++++++++++++++++++++++++++++++++++++ src/fs/fat32.h | 32 +++++ src/fs/file.c | 18 +++ src/lib/blib.h | 2 + 6 files changed, 386 insertions(+) create mode 100644 src/fs/fat32.c create mode 100644 src/fs/fat32.h diff --git a/Makefile b/Makefile index d6c4687b..837e1145 100644 --- a/Makefile +++ b/Makefile @@ -39,3 +39,24 @@ ext2-test: all rm -rf test_image loopback_dev ./qloader2-install src/qloader2.bin test.img 2048 qemu-system-x86_64 -hda test.img -debugcon stdio + +fat32-test: all + $(MAKE) -C test + rm -rf test.img test_image/ + mkdir test_image + dd if=/dev/zero bs=1M count=0 seek=64 of=test.img + parted -s test.img mklabel gpt + parted -s test.img mkpart primary 2048s 6143s + parted -s test.img mkpart primary 6144s 131038s + sudo losetup -Pf --show test.img > loopback_dev + sudo mkfs.fat -F 32 `cat loopback_dev`p2 + sudo mount `cat loopback_dev`p2 test_image + sudo mkdir test_image/boot + sudo cp test/test.elf test_image/boot/ + sudo cp test/qloader2.cfg test_image/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + ./qloader2-install src/qloader2.bin test.img 2048 + qemu-system-x86_64 -hda test.img -debugcon stdio \ No newline at end of file diff --git a/qloader2.bin b/qloader2.bin index cbd41ac0386cddccb4dbde9ba24f8bcefbc880ce..59ae094d9691f02f647b4da027b8f355cf8c4e2d 100644 GIT binary patch delta 7408 zcma)Bdt6jy-k&)zGT`8h0xAeNBBCIupprKf(?MHQ=Hd*90huy8vZj_ZW)~c681ry; zSl6f8-O@f9)%v#X8|4ixR}f>sG%>BTTE)xO!BN*uGcvdHet%~G-S_Q}cRrtUp5OER zU7z3cdoBm6s<^5uu0AYuP{(N%?0K`noz>29cF}u#Q3!#bb?dX_&hE}7oB1$|tMmBTD(5iIi35F}$eMddlG}6Rf5RxX6 zp5#&+dBax6rlW9uY^XxvS5b5lets3jOO)rRUNCI+HahzSR`7E4{6yjjhI+xeXSAVl zi1nyYxpTv}(opuiDqTxa8wEpSh(Ti%7ma57RgSQC$iam~meOaUdwm9lrP1#5Ui-Hs z8PQ&+*3n2ZLZU*kxJdlMLjr``YZ4N%HIoye(&W5nHU|27nz_yp%3-VIE5E(C zw5+6g$8F@NCZ_c~Q-|w0G-=^+5yyckoEq@hg787vt>`u=s`@Hg6#7j@rKge0+5Wq} z3fE1)inQt17TE7sk(v^nz+Z@FuZ0^1{Fo{`7b@?yHCy?Akj3x9GpCVS@#2!sC z;_<5*@cs}_=Oa+}v?6SS+LEZXoaf zqbkJ6R*!fF-5tC>^59_DOg+NOpxGP(Lu^K0z5D-$kU$~)O%CBKLb!A4Dt~H+Y^vSU zRj^*uP(YU2<%o>N2Lz2FktD_+3%vSE@%@tB$!`E8lIb0k z#kqC#{$Cyt5+F(~D#EWK3Hk-J6fzP_0LAUep%4cXBol66CX+OgI?_av>ijz@JD5A) z!d-yEjWi>BcLmvx{Diacyw9583hziUO`r%wt#hiPR z6y*O>ak8&95oWoh5J{UXY?XqM7rn=uMU5SJB{u)Xnp3 zQe>)*%o5P~8eUY>Pe!J4XtqYi<R#k#E`1WK?RS_PIh++}6Z^)uU|kr_8pT6NY@K#8*PO(5 zYeyx>uy-yd(kRZuc8#4Hn`0!lR>z1I)}?)g<0i1px;MFVdY0XPP1p>W5v@_IzJGb_ zb6F@VKmT$vw0)t-IJ%+9Vt5Q2GvE&Q()iseQ5j0EJd0VQKj3y|vH>w8!WNPK_Wo>o zOcysjo=qC~2hOfzrv@J8-qNxygO(1O9tSJlxtMG0rKM=d&HJuk;wlsl1$94f zx)Bz#dQ{Ul;h9pQ&G)hhp4n#%HA~~fh->os;B9mb_FQ%gFKr{{hD#WoR*;SG{d@Mk zpbO8n)x5iz|D4zP)`gnPh2m-~W}F5Uii%=9$ak%=&?fE-pJ~dqVGT6< zfdXTN+EFb}ONUXsJ*YeRu8TF+*EU(AwfpC9we$G{YpkztvJ4g~-{GH&7hLc2YxIKk zAa6-l{>bN#((ZFxU)R3-o$?cYP5J}Dd9FjbOD=iP-W64M~Yd*7sj@@~hQ=96l{Vlg^=2($A8Bv3F!3qImjO;e9`sU@$Q zI)P5X+m2Sd|7??}CZtEhH(_#3^rY%fUGb(uqtAODGDh*s7=@yxI)^$` zdx0rccgMQY&G!WkJRso9Q}VJ+NVa%igN`!b*3H=xmIqkle8$oH`Jrd$Q3nKGVTus88I>OCH z-(IQ*o6lPh+$impllG;%vnOkqTx8i>3*$9?svEV{22fS&`)Xb5cIG+HYDqUZ9^-+UG6n|13j_==nmytOa>MA6!mJ|BgyY(1btUyOo(pRJ#-I^ioC zMjyi55=fsd9^gd+F-Hn90IKr zz3-7PAI*>OGQGRYH38wHT&Ds-u!+4hm~)*X+?WC_zg*4mA{mX6E86icZ9b2O$zBBT z*u?=+ZlOdi7_bbYc|$vIJtG(f3)Xnv(8^nnYxk{*#!ey38pj*1@UBMwW>eQt-f&c~ zUYYD_7Yt{H7>+)r_sze;pA|RAacI4`mtm1K3f7~khW1$NnZT!hLjy2{-|!(df~!%e zJUZIdKBV%@WLK-;HXM)bTGK}WS$W*VyXyUhJtQZ&_DHUJTy*84#CCv+oR!{Nj>7zQ ze*R9sVHe4U7LqZ&s*L8ZTyl+r6DRwHTV&EV-^YZX*MMvQ$@VY~^vK ztD%48him6ycLk{TvUtLiTS}~ zS!}q>UlGUL>>h;H`w85#hS+3x=Ubr6W*GS0kHjVYhh^WSU-KXOMR&UOU26o)Q-Rt) zN7MNd7REz-S*Y+xG+&@=`f(^<5FM}>jj*^CgU4<1s1BE0oWs*$wqlx?{h-a!;wuo6 zSG#LyuhD&f>M?4Xz!&QLb5P8PcYIvdAS+U05s&uou5Q;Lcyg5b+4FJfk^8s)+Af7G z-gYAQJi`G19tv62z>-P?hCICeeP)Ht$sw4Z0(u%yTx zB=9a(Uks4UVfhgboY#wrY>tC|wsF|Rq{AdI7bEQ{MDJJ5#(X&~hlrK&4g34BxG*_I zns3<8!?L+UewLl|ND!+22iusGp=p9BZVM&t7O{jKPSR&xBHi2AjvXgumPJgH3yn(g z1WG3`gX&SCgVacbU#Gah`VZG9D=ttaD1MO3C$q69rr>RYHlj!fT89^KXx<(^g_=vl zmr!F!9!^a~@)KOgd3G*&3N24sYA7OC8pu-G##X1#LL|PwkeFp!T%3 z-OJNvaGKSpXcppe2eY=ZJ?RVP>+MFn#cr~j?KXR%-D=+|3u^U^8l^U=@v6Ii_cLR5DBZ5pxQKiujzvS` zFGWJcQ#WwFg(fd2`XkUtLFOHkLxJ?$9D3B5c>6mU&2^?uzUAs?Hp{5vdCI27XbkqM zNqu8bJulGcz2lOv{=X%7E^WPVw^P~1UL9v~zw#-{{Tpy(Cl&ZC(_e?!taW_+F+ zMtd{yD8x(4ERMURTYwJILTM0PI9}=~Bmfb|0Azs~%#+FHm~o7KotYZFh#23?H-BAV zF&SNTjKyXR(QF__3mA^=$5?LGH2o+5fpU`ANi1hj$9u<_W;EIy?;K-WvqqV&6Du1m z3Iadzn0$cga)XJzN$i^l;};UC{FfXZ64{#)N1u;b%=o0tsE_I9SfMBs4+qUbNUrW= zFap0vOoie=d}ZK1gx$t5>tp5^uXh)K*=fUibu1_STzru19fhtT)9chEqRIo9mhBMaS? z_d9Hjt^`~tAjI?&hHwWzW;qk4aG!n5o|!P4yZtdcJRz3T9cN!oDCfo|I9r&4sY&4<5drpE#E`FqC`^BACYqg6F3Y=b(I7B7sW*20Dq_NYn+Q z-X-cYWIytGn3MLdBcs<(F#aL0I}-OE@fuDr@pY6{oe?R*i90tLEWN zKfh{qFXil}sOG=&dmn|>b$}e>VX`9E;xp_tiVS4~KE7P4AEBL8R#$prWL?CT?)W3D zim!_pbBiw*A9s&Ya5G@SQ3a00D4JvwddK`v*nvq&%3SvKq|NS93cD8dv#5;xI4Y^X z9{r+XX|HTz3n44y3e!kyL#1rTp9v}MK>(^99}+YN&@2Sm`5LNH^mHH%4vfRETG2f~ z!Jw(jm66%qh)LXa8)8-H@-fwT+Xcf5_PWPG35M#rWoYjhs+7(YO1~XF2SEIjC?VdJ8ujFzsv(l+)+?GSEX6jhGgVreTj2? z))f8HRV$t4>zw-KE0;U<_9e?#xSlH4j~cNoH6kd%a%ac5yJd-8zD)02rGKh?$uj-Y z6|ObT@~8A8*6Hm}t$ITL#FEwBN>gM@LD3bf{!ZxaXWo2VQ+v$4O%JOxq z%a=N#T)uLdew7_=G(kU09ZQz4jM(j-tyJR6PZ5v9cO;&1cxK}HRqO68hMGQH`g<&K z-Wcw$@3RH-rgMLJpViLGb6fUG(f}yYPlsn59uu%k0`MbjHlCHh1`>c@Jf0#vtAULt zU}itHmwPx;wellPD0XeqS%=31zJ~bt5%ya=&jF)rG5vrIQEUgX7T{n)@YCa=ucv4C zvX%3blB&TPif*ru#X%o>KwWq|d)fQ*lQ=Y=&(G%453qg=kR*N2`kSJevLK4PeTWS#7^C{}uq2&fzn>S)N(u&X+m5ic1xeh&Bka|J zY-U`jwzeFRB+6;8`ZNbHd_`=c?6`&%pPXG&8|X_KU@kS(Z{LDBov* zS&y-7a~fB2j6H5nQn6!_bdGIU7{#`l2QQg+T$1pY%KPaCNmH>JXWta?W<1DyE*K&4 zH-o3YZBLXn!S$Rxznr#&DW>d4S;9{*iyL8(`o zur~@zRYhN7Nb+imEgH)``8E4hQJ%_pQIh7c>qT+w-J;1{?j?4$$kt~W&UQ(`ZWP6` zvPI>p4s=swnX*M0Ojm4Ft+*mdZ9wALhT=JKJ%5SEjLLtx4?|1zrpvqkRXir)e*jvl Bi_-uA delta 5324 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 Fe*lr!l1Tsn diff --git a/src/fs/fat32.c b/src/fs/fat32.c new file mode 100644 index 00000000..d2183cb8 --- /dev/null +++ b/src/fs/fat32.c @@ -0,0 +1,313 @@ +#include +#include +#include +#include +#include + +#define FAT32_LFN_MAX_ENTRIES 20 +#define FAT32_LFN_MAX_FILENAME_LENGTH (FAT32_LFN_MAX_ENTRIES * 13 + 1) + +#define FAT32_VALID_SIGNATURE_1 0x28 +#define FAT32_VALID_SIGNATURE_2 0x29 +#define FAT32_VALID_SYSTEM_IDENTIFIER "FAT32 " +#define FAT32_SECTOR_SIZE 512 +#define FAT32_ATTRIBUTE_SUBDIRECTORY 0x10 +#define FAT32_LFN_ATTRIBUTE 0x0F + +struct fat32_bpb { + uint8_t jump[3]; + char oem[8]; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t fats_count; + uint16_t directory_entries_count; + uint16_t sector_totals; + uint8_t media_descriptor_type; + uint16_t sectors_per_fat_16; + uint16_t sectors_per_track; + uint16_t heads_count; + uint32_t hidden_sectors_count; + uint32_t large_sectors_count; + uint32_t sectors_per_fat_32; + uint16_t flags; + uint16_t fat_version_number; + uint32_t root_directory_cluster; + uint16_t fs_info_sector; + uint16_t backup_boot_sector; + uint8_t reserved[12]; + uint8_t drive_number; + uint8_t nt_flags; + uint8_t signature; + uint32_t volume_serial_number; + char label[11]; + char system_identifier[8]; +} __attribute__((packed)); + +struct fat32_directory_entry { + char file_name_and_ext[8 + 3]; + uint8_t attribute; + uint8_t file_data_1[8]; + uint16_t cluster_num_high; + uint8_t file_data_2[4]; + uint16_t cluster_num_low; + uint32_t file_size_bytes; +} __attribute__((packed)); + +struct fat32_lfn_entry { + uint8_t sequence_number; + char name1[10]; + uint8_t attribute; + uint8_t type; + uint8_t dos_checksum; + char name2[12]; + uint16_t first_cluster; + char name3[4]; +} __attribute__((packed)); + +static int fat32_init_context(struct fat32_context* context, int disk, int partition) { + context->drive = disk; + get_part(&context->part, disk, partition); + + struct fat32_bpb bpb; + read_partition(disk, &context->part, &bpb, 0, sizeof(struct fat32_bpb)); + + if (bpb.signature != FAT32_VALID_SIGNATURE_1 && bpb.signature != FAT32_VALID_SIGNATURE_2) { + return 1; + } + + if (strncmp(bpb.system_identifier, FAT32_VALID_SYSTEM_IDENTIFIER, SIZEOF_ARRAY(bpb.system_identifier)) != 0) { + return 1; + } + + context->sectors_per_cluster = bpb.sectors_per_cluster; + context->reserved_sectors = bpb.reserved_sectors; + context->number_of_fats = bpb.fats_count; + context->hidden_sectors = bpb.hidden_sectors_count; + context->sectors_per_fat = bpb.sectors_per_fat_32; + context->root_directory_cluster = bpb.root_directory_cluster; + context->fat_start_lba = bpb.reserved_sectors + bpb.hidden_sectors_count; + context->data_start_lba = context->fat_start_lba + bpb.fats_count * bpb.sectors_per_fat_32; + + return 0; +} + +static int fat32_read_cluster_from_map(struct fat32_context* context, uint32_t cluster, uint32_t* out) { + const uint32_t sector = cluster / (FAT32_SECTOR_SIZE / 4); + const uint32_t offset = cluster % (FAT32_SECTOR_SIZE / 4); + + uint32_t clusters[FAT32_SECTOR_SIZE / sizeof(uint32_t)]; + int r = read_partition(context->drive, &context->part, &clusters[0], (context->fat_start_lba + sector) * FAT32_SECTOR_SIZE, sizeof(clusters)); + + if (r) { + return r; + } + + *out = clusters[offset] & 0x0FFFFFFF; + return 0; +} + +static int fat32_load_fat_cluster_to_memory(struct fat32_context* context, uint32_t cluster_number, void* buffer, uint32_t offset, uint32_t limit) { + const uint32_t sector = context->data_start_lba + (cluster_number - 2) * context->sectors_per_cluster; + return read_partition(context->drive, &context->part, buffer, ((uint64_t) sector) * FAT32_SECTOR_SIZE + offset, limit); +} + +// Copy ucs-2 characters to char* +static void fat32_lfncpy(char* destination, const void* source, unsigned int size) { + for (unsigned int i = 0; i < size; i++) { + // ignore high bytes + *(((uint8_t*) destination) + i) = *(((uint8_t*) source) + (i * 2)); + } +} + +static int fat32_open_in(struct fat32_context* context, struct fat32_directory_entry* directory, struct fat32_directory_entry* file, const char* name) { + int error; + uint32_t current_cluster_number = directory->cluster_num_high << 16 | directory->cluster_num_low; + + char current_lfn[FAT32_LFN_MAX_FILENAME_LENGTH] = {0}; + bool has_lfn = false; + + do { + struct fat32_directory_entry directory_entries[FAT32_SECTOR_SIZE / sizeof(struct fat32_directory_entry)]; + error = fat32_load_fat_cluster_to_memory(context, current_cluster_number, directory_entries, 0, sizeof(directory_entries)); + + if (error != 0) { + return error; + } + + for (unsigned int i = 0; i < SIZEOF_ARRAY(directory_entries); i++) { + if (directory_entries[i].file_name_and_ext[0] == 0x00) { + // no more entries here + break; + } + + if (directory_entries[i].attribute == FAT32_LFN_ATTRIBUTE) { + has_lfn = true; + + struct fat32_lfn_entry* lfn = (struct fat32_lfn_entry*) &directory_entries[i]; + + if (lfn->sequence_number & 0b01000000) { + // this lfn is the first entry in the table, clear the lfn buffer + memset(current_lfn, ' ', sizeof(current_lfn)); + } + + const unsigned int lfn_index = ((lfn->sequence_number & 0b00011111) - 1U) * 13U; + if (lfn_index >= FAT32_LFN_MAX_ENTRIES * 13) { + continue; + } + + fat32_lfncpy(current_lfn + lfn_index + 00, lfn->name1, 5); + fat32_lfncpy(current_lfn + lfn_index + 05, lfn->name2, 6); + fat32_lfncpy(current_lfn + lfn_index + 11, lfn->name3, 2); + continue; + } + + if (has_lfn) { + // remove trailing spaces + for (int j = SIZEOF_ARRAY(current_lfn) - 2; j >= -1; j--) { + if (j == -1 || current_lfn[j] != ' ') { + current_lfn[j + 1] = 0; + break; + } + } + } + + if ((has_lfn && strcmp(current_lfn, name) == 0) || strncmp(directory_entries[i].file_name_and_ext, name, 8 + 3) == 0) { + *file = directory_entries[i]; + return 0; + } + + if (has_lfn) { + has_lfn = false; + } + } + + error = fat32_read_cluster_from_map(context, current_cluster_number, ¤t_cluster_number); + + if (error != 0) { + return error; + } + } while (current_cluster_number >= 0x00000002 && current_cluster_number <= 0x0FFFFEF); + + // file not found + return -1; +} + +int fat32_check_signature(int disk, int partition) { + struct fat32_context context; + return fat32_init_context(&context, disk, partition) == 0; +} + +int fat32_open(struct fat32_file_handle* ret, int disk, int partition, const char* path) { + struct fat32_context context; + int r = fat32_init_context(&context, disk, partition); + + if (r) { + print("fat32: context init failure (%d)\n", r); + return r; + } + + struct fat32_directory_entry current_directory; + struct fat32_directory_entry current_file; + unsigned int current_index = 0; + char current_part[FAT32_LFN_MAX_FILENAME_LENGTH]; + + // skip trailing slashes + while (path[current_index] == '/') { + current_index++; + } + + // walk down the directory tree + current_directory.cluster_num_low = context.root_directory_cluster & 0xFFFF; + current_directory.cluster_num_high = context.root_directory_cluster >> 16; + + for (;;) { + bool expect_directory = false; + + for (unsigned int i = 0; i < SIZEOF_ARRAY(current_part); i++) { + if (path[i + current_index] == 0) { + memcpy(current_part, path + current_index, i); + current_part[i] = 0; + expect_directory = false; + break; + } + + if (path[i + current_index] == '/') { + memcpy(current_part, path + current_index, i); + current_part[i] = 0; + current_index += i + 1; + expect_directory = true; + break; + } + } + + if ((r = fat32_open_in(&context, ¤t_directory, ¤t_file, current_part)) != 0) { + print("fat32: file %s not found\n", path); + return r; + } + + if (expect_directory) { + current_directory = current_file; + } else { + ret->context = context; + ret->first_cluster = current_file.cluster_num_high << 16 | current_file.cluster_num_low; + ret->size_clusters = DIV_ROUNDUP(current_file.file_size_bytes, FAT32_SECTOR_SIZE); + ret->size_bytes = current_file.file_size_bytes; + return 0; + } + } +} + +int fat32_read(struct fat32_file_handle* file, void* buf, uint64_t loc, uint64_t count) { + int r; + uint32_t cluster_size = file->context.sectors_per_cluster * FAT32_SECTOR_SIZE; + uint32_t current_cluster_number = file->first_cluster; + + // skip first clusters + while (loc >= cluster_size) { + r = fat32_read_cluster_from_map(&file->context, current_cluster_number, ¤t_cluster_number); + + if (r != 0) { + print("fat32: failed to read cluster %x from map\n", current_cluster_number); + return r; + } + + loc -= cluster_size; + } + + uint64_t readTotal = 0; + + do { + // find largest read size + uint64_t current_read = count; + if (current_read > cluster_size - loc) { + current_read = cluster_size - loc; + } + + r = fat32_load_fat_cluster_to_memory(&file->context, current_cluster_number, buf + readTotal, loc, current_read); + + if (r != 0) { + print("fat32: failed to load cluster %x to memory\n", current_cluster_number); + return r; + } + + loc = 0; + count -= current_read; + readTotal += current_read; + + if (count == 0) { + return 0; + } + + // fetch next cluster number + r = fat32_read_cluster_from_map(&file->context, current_cluster_number, ¤t_cluster_number); + + if (r != 0) { + print("fat32: failed to read cluster %x from map\n", current_cluster_number); + return r; + } + } while (current_cluster_number >= 0x00000002 && current_cluster_number <= 0x0FFFFEF); + + print("fat32: read failed, unexpected end of cluster chain\n"); + return 0; +} diff --git a/src/fs/fat32.h b/src/fs/fat32.h new file mode 100644 index 00000000..145a1728 --- /dev/null +++ b/src/fs/fat32.h @@ -0,0 +1,32 @@ +#ifndef __FS__FAT32_H__ +#define __FS__FAT32_H__ + +#include +#include + +struct fat32_context { + int drive; + struct part part; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t number_of_fats; + uint32_t hidden_sectors; + uint32_t sectors_per_fat; + uint32_t root_directory_cluster; + uint32_t fat_start_lba; + uint32_t data_start_lba; +}; + +struct fat32_file_handle { + struct fat32_context context; + uint32_t first_cluster; + uint32_t size_bytes; + uint32_t size_clusters; +}; + +int fat32_check_signature(int disk, int partition); + +int fat32_open(struct fat32_file_handle *ret, int disk, int partition, const char *path); +int fat32_read(struct fat32_file_handle *file, void *buf, uint64_t loc, uint64_t count); + +#endif diff --git a/src/fs/file.c b/src/fs/file.c index 38528c15..038e61c1 100644 --- a/src/fs/file.c +++ b/src/fs/file.c @@ -3,6 +3,7 @@ #include #include #include +#include #include int fopen(struct file_handle *ret, int disk, int partition, const char *filename) { @@ -38,6 +39,23 @@ int fopen(struct file_handle *ret, int disk, int partition, const char *filename return 0; } + if (fat32_check_signature(disk, partition)) { + struct fat32_file_handle *fd = balloc(sizeof(struct fat32_file_handle)); + + int r = fat32_open(fd, disk, partition, filename); + + if (r) + return r; + + ret->fd = (void *)fd; + ret->read = (void *)fat32_read; + ret->disk = disk; + ret->partition = partition; + ret->size = fd->size_bytes; + + return 0; + } + return -1; } diff --git a/src/lib/blib.h b/src/lib/blib.h index a69d6d90..250979dd 100644 --- a/src/lib/blib.h +++ b/src/lib/blib.h @@ -32,4 +32,6 @@ uint64_t strtoui(const char *s); typedef void *symbol[]; +#define SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0])) + #endif