bztsrc--bootboot/mkbootimg/lean.c

348 lines
13 KiB
C

/*
* mkbootimg/lean.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 LeanFS file system driver
* See http://freedos-32.sourceforge.net/lean/specification.php (v0.6, original)
* See http://www.fysnet.net/leanfs/specification.php (v0.7 and later, forked by fys)
*
* The FS we create here does not use any v0.8 or v0.7 specific features, hence it is v0.6 compatible
* (no hidden or undeleted files, no bad sectors, sector size 512 bytes, six extents per inode etc.)
*
*/
#include "main.h"
#define LEAN_SUPER_MAGIC 0x4E41454C
#define LEAN_SUPER_VERSION 0x0008 /* could be 6 or 7 as well */
#define LEAN_INODE_MAGIC 0x45444F4E
#define LEAN_INODE_EXTENT_CNT 6
#define LEAN_FT_MT 0
#define LEAN_FT_REG 1
#define LEAN_FT_DIR 2
#define LEAN_FT_LNK 3
#define LEAN_ATTR_PREALLOC (1 << 18)
#define LEAN_ATTR_INLINEXTATTR (1 << 19)
#define LEAN_ATTR_IFMT (7 << 29)
#define LEAN_ATTR_IFTYPE(x) ((uint32_t)(x) << 29)
#define LEAN_LOG_BANDSIZE 12
#define LEAN_BITMAPSIZE (1 << (LEAN_LOG_BANDSIZE - 12))
#define LEAN_INODE_SIZE 176
typedef struct {
uint8_t loader[16384];
uint32_t checksum;
uint32_t magic;
uint16_t fs_version;
uint8_t pre_alloc_count;
uint8_t log_sectors_per_band;
uint32_t state;
uint8_t uuid[16];
uint8_t volume_label[64];
uint64_t sector_count;
uint64_t free_sector_count;
uint64_t primary_super;
uint64_t backup_super;
uint64_t bitmap_start;
uint64_t root_inode;
uint64_t bad_inode;
uint64_t journal_inode;
uint8_t log_block_size;
uint8_t reserved2[344];
} __attribute__((packed)) lean_super_t;
typedef struct {
uint32_t checksum;
uint32_t magic;
uint8_t extent_count;
uint8_t reserved[3];
uint32_t indirect_count;
uint32_t links_count;
uint32_t uid;
uint32_t gid;
uint32_t attributes;
uint64_t file_size;
uint64_t sector_count;
uint64_t atime;
uint64_t ctime;
uint64_t mtime;
uint64_t btime;
uint64_t first_indirect;
uint64_t last_indirect;
uint64_t fork;
uint64_t extent_start[LEAN_INODE_EXTENT_CNT];
uint32_t extent_size[LEAN_INODE_EXTENT_CNT];
} __attribute__((packed)) lean_inode_t;
typedef struct {
uint64_t inode;
uint8_t type;
uint8_t rec_len;
uint16_t name_len;
} __attribute__((packed)) lean_dirent_t;
int len_numblk, len_nextblk;
lean_super_t *len_sb;
uint32_t len_checksum(void *data, int size)
{
uint32_t ret = 0, *ptr = (uint32_t*)data;
int i;
for(i = 1; i < size; i++)
ret = (ret << 31) + (ret >> 1) + ptr[i];
return ret;
}
int len_alloc_blk()
{
int r, g = len_nextblk / (1 << LEAN_LOG_BANDSIZE), o = len_nextblk % (1 << LEAN_LOG_BANDSIZE);
while((uint64_t)len_nextblk < len_sb->sector_count &&
fs_base[(g * (1 << LEAN_LOG_BANDSIZE) + (!g ? len_sb->bitmap_start : 0)) * 512 + o / 8] & (1 << (o & 7))) {
o++; len_nextblk++;
if(o >= (1 << LEAN_LOG_BANDSIZE)) { o = 0; g++; len_nextblk += LEAN_BITMAPSIZE; }
}
if((uint64_t)len_nextblk + 1 >= len_sb->sector_count || len_sb->free_sector_count < 1) {
fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_TOOBIG]);
exit(1);
}
fs_base[(g * (1 << LEAN_LOG_BANDSIZE) + (!g ? len_sb->bitmap_start : 0)) * 512 + o / 8] |= 1 << (o & 7);
len_sb->free_sector_count--;
r = len_nextblk++;
if(!(len_nextblk % (1 << LEAN_LOG_BANDSIZE))) len_nextblk += LEAN_BITMAPSIZE;
return r;
}
void len_add_to_inode(uint32_t ino, uint32_t blk, char *name)
{
lean_inode_t *inode = (lean_inode_t*)(fs_base + ino * 512);
inode->sector_count++;
if(inode->extent_start[inode->extent_count - 1] + inode->extent_size[inode->extent_count - 1] == blk) {
inode->extent_size[inode->extent_count - 1]++;
} else {
inode->extent_count++;
if(inode->extent_count < LEAN_INODE_EXTENT_CNT) {
inode->extent_start[inode->extent_count - 1] = blk;
inode->extent_size[inode->extent_count - 1] = 1;
} else {
fprintf(stderr,"mkbootimg: partition #%d %s: %s\r\n", fs_no, lang[ERR_TOOBIG], name);
exit(1);
}
}
inode->checksum = len_checksum(inode, LEAN_INODE_SIZE / 4);
}
int len_alloc_inode(uint16_t mode, uint8_t type, uint64_t size, time_t t)
{
lean_inode_t *inode;
int n = len_alloc_blk(), i;
inode = (lean_inode_t*)(fs_base + n * 512);
inode->magic = LEAN_INODE_MAGIC;
inode->attributes = (mode & 0xFFF) | LEAN_ATTR_IFTYPE(type) | LEAN_ATTR_INLINEXTATTR |
(type == LEAN_FT_DIR ? LEAN_ATTR_PREALLOC : 0);
inode->atime = inode->ctime = inode->mtime = inode->btime = (uint64_t)t * 1000000;
inode->extent_count = 1;
inode->extent_start[0] = n;
inode->extent_size[0] = 1;
inode->sector_count = 1;
if(type == LEAN_FT_DIR)
for(i = 0; i < len_sb->pre_alloc_count; i++)
len_add_to_inode(n, len_alloc_blk(), NULL);
else
inode->file_size = size;
inode->checksum = len_checksum(inode, LEAN_INODE_SIZE / 4);
return n;
}
uint8_t *len_add_dirent(uint8_t *dir, uint64_t toinode, uint64_t ino, uint8_t type, char *name, int len)
{
lean_dirent_t *de;
lean_inode_t *inode;
uint8_t *end = NULL;
int l = 16 + (len < 4 ? 0 : ((len + 11) & ~15)), i = 0;
uint64_t j = 0;
inode = (lean_inode_t*)(fs_base + ino * 512);
inode->links_count++;
inode->checksum = len_checksum(inode, LEAN_INODE_SIZE / 4);
inode = (lean_inode_t*)(fs_base + toinode * 512);
if(!dir) {
if(!inode->extent_count || inode->extent_size[0] == 1)
len_add_to_inode(toinode, len_alloc_blk(), NULL);
dir = fs_base + inode->extent_start[0] * 512 + 512;
end = fs_base + (inode->extent_start[0] + inode->extent_size[0]) * 512;
while(j < inode->file_size && ((lean_dirent_t*)dir)->inode && ((lean_dirent_t*)dir)->rec_len) {
j += ((lean_dirent_t*)dir)->rec_len * 16;
dir += ((lean_dirent_t*)dir)->rec_len * 16;
if(dir >= end) {
dir = fs_base + inode->extent_start[++i] * 512 + ((uintptr_t)(dir - end) & 511);
end = fs_base + (inode->extent_start[i] + inode->extent_size[i]) * 512;
}
}
}
inode->file_size += l;
if(inode->file_size > inode->sector_count * 512) {
fprintf(stderr,"mkbootimg: partition #%d %s: %s\r\n", fs_no, lang[ERR_TOOMANY], name);
exit(1);
}
inode->checksum = len_checksum(inode, LEAN_INODE_SIZE / 4);
de = (lean_dirent_t*)dir;
de->inode = ino;
de->type = type;
de->rec_len = l >> 4;
de->name_len = len;
if(end && len > 4) {
memcpy(dir + 12, name, 4);
dir += 16;
name += 4;
len -= 4;
while(len) {
if(dir >= end) {
dir = fs_base + inode->extent_start[++i] * 512;
end = fs_base + (inode->extent_start[i] + inode->extent_size[i]) * 512;
}
memcpy(dir, name, len > 16 ? 16 : len);
name += 16;
dir += 16;
if(len > 16) len -= 16; else break;
}
} else {
memcpy(dir + 12, name, len);
dir += l;
}
return dir;
}
/*** mkbootimg interface ***/
void len_open(gpt_t *gpt_entry)
{
int i, j, numband;
if(!gpt_entry) { fprintf(stderr,"mkbootimg: %s lean.\r\n", lang[ERR_BADINITRDTYPE]); exit(1); }
len_numblk = (gpt_entry->last - gpt_entry->start + 1);
if(len_numblk < 32 + LEAN_BITMAPSIZE) { fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_NOSIZE]); exit(1); }
fs_len = len_numblk * 512;
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);
numband = len_numblk / ((1 << LEAN_LOG_BANDSIZE) * 512);
if(numband < 1) numband = 1;
len_sb = (lean_super_t*)fs_base;
len_sb->magic = LEAN_SUPER_MAGIC;
len_sb->fs_version = LEAN_SUPER_VERSION;
len_sb->log_sectors_per_band = LEAN_LOG_BANDSIZE;
len_sb->pre_alloc_count = 7;
len_sb->state = 1;
memcpy(&len_sb->uuid, &gpt_entry->guid, sizeof(guid_t));
memcpy(&len_sb->volume_label, "NO NAME", 7);
len_sb->log_block_size = 9;
len_sb->sector_count = len_numblk;
len_sb->free_sector_count = len_numblk - 34 - numband * LEAN_BITMAPSIZE; /* loader, superblock, backup, bitmaps */
len_sb->primary_super = 32;
len_sb->backup_super = (len_numblk < (1 << LEAN_LOG_BANDSIZE) ? len_numblk : (1 << LEAN_LOG_BANDSIZE)) - 1;
len_sb->bitmap_start = len_sb->primary_super + 1;
for(j = 0; j < numband; j++) {
for(i = 0; i < LEAN_BITMAPSIZE + (!j ? (int)len_sb->bitmap_start : 0); i++)
fs_base[(j * (1 << LEAN_LOG_BANDSIZE) + (!j ? len_sb->bitmap_start : 0)) * 512 + i / 8] |= 1 << (i & 7);
}
fs_base[len_sb->bitmap_start * 512 + len_sb->backup_super / 8] |= 1 << (len_sb->backup_super & 7);
len_nextblk = len_sb->bitmap_start + LEAN_BITMAPSIZE;
len_sb->root_inode = len_alloc_inode(0755, LEAN_FT_DIR, 0, t);
len_add_dirent(len_add_dirent(NULL,
len_sb->root_inode, len_sb->root_inode, LEAN_FT_DIR, ".", 1),
len_sb->root_inode, len_sb->root_inode, LEAN_FT_DIR, "..", 2);
}
void len_add(struct stat *st, char *name, unsigned char *content, int size)
{
uint64_t parent = len_sb->root_inode, ino, n, j;
lean_inode_t *inode;
uint8_t *dir, *end = NULL, type = LEAN_FT_REG;
char d_name[MAXPATH], *dn, *nend, *fn = strrchr(name, '/');
int i, k, l;
if(!fn) fn = name; else fn++;
if(!strcmp(fn, ".") || !strcmp(fn, "..") || (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode) && !S_ISLNK(st->st_mode))) return;
type = S_ISDIR(st->st_mode) ? LEAN_FT_DIR : (S_ISLNK(st->st_mode) ? LEAN_FT_LNK : LEAN_FT_REG);
n = len_alloc_inode(st->st_mode, type, size, st->st_mtime);
/* Enter name in directory */
fn = name;
nend = strchr(name, '/');
if(!nend) nend = name + strlen(name);
again:
i = 0; j = 0;
inode = (lean_inode_t*)(fs_base + parent * 512);
if(i < inode->extent_count) {
dir = fs_base + inode->extent_start[i] * 512 + (!i ? 512 : 0);
end = fs_base + (inode->extent_start[i] + inode->extent_size[i]) * 512;
while(j < inode->file_size) {
ino = ((lean_dirent_t*)dir)->inode;
k = ((lean_dirent_t*)dir)->rec_len - 1;
l = ((lean_dirent_t*)dir)->name_len;
dn = d_name;
memset(d_name, 0, sizeof(d_name));
memcpy(dn, dir + 12, 4);
dn += 4;
dir += 16;
j += 16;
while(j < inode->file_size && k--) {
if(dir >= end) {
dir = fs_base + inode->extent_start[++i] * 512;
end = fs_base + (inode->extent_start[i] + inode->extent_size[i]) * 512;
}
memcpy(dn, dir, 16);
dn += 16;
dir += 16;
j += 16;
}
if(l == nend - fn && !memcmp(d_name, fn, nend - fn)) {
parent = ino;
fn = nend + 1;
nend = *nend ? strchr(fn, '/') : NULL;
if(!nend) { nend = fn + strlen(fn); break; }
goto again;
}
}
}
len_add_dirent(NULL, parent, n, type, fn, nend - fn);
if(type == LEAN_FT_DIR) {
len_add_dirent(len_add_dirent(NULL,
n, n, LEAN_FT_DIR, ".", 1),
n, parent, LEAN_FT_DIR, "..", 2);
} else {
/* works for both regular files and symlinks */
while(size) {
k = size > 512 ? 512 : size;
i = len_alloc_blk();
memcpy(fs_base + i * 512, content, k);
len_add_to_inode(n, i, name);
content += k;
size -= k;
}
}
}
void len_close()
{
if(!fs_base || (uint64_t)fs_len < (len_sb->backup_super + 1) * 512) return;
len_sb->checksum = len_checksum(fs_base + len_sb->primary_super * 512, 128);
memcpy(fs_base + len_sb->backup_super * 512, fs_base + len_sb->primary_super * 512, 512);
}