/* * mkbootimg/fsZ.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 FS/Z file system driver * */ #include "main.h" #include "fsZ.h" int fsz_secsize = FSZ_SECSIZE, fsz_max = 0; unsigned char fsz_emptysec[FSZ_SECSIZE] = {0}; /* private functions */ static int fsz_direntcmp(const void *a, const void *b) { return strcmp((char *)((FSZ_DirEnt *)a)->name,(char *)((FSZ_DirEnt *)b)->name); } 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); in=(FSZ_Inode *)(fs_base+fs_len); memcpy(in->magic,FSZ_IN_MAGIC,4); memcpy((char*)&in->owner,"root",5); in->owner.access=FSZ_READ|FSZ_WRITE|FSZ_DELETE|(filetype && ( !strcmp(filetype,FSZ_FILETYPE_DIR) || !strcmp(filetype,FSZ_FILETYPE_UNION))? FSZ_EXEC : 0); if(filetype!=NULL){ i=strlen(filetype); memcpy(in->filetype,filetype,i>4?4:i); if(!strcmp(filetype,FSZ_FILETYPE_DIR)){ hdr=(FSZ_DirEntHeader *)(in->data.small.inlinedata); in->sec=hdr->fid=fs_len/fsz_secsize; in->flags=FSZ_IN_FLAG_INLINE; in->size=sizeof(FSZ_DirEntHeader); memcpy(in->data.small.inlinedata,FSZ_DIR_MAGIC,4); hdr->checksum=crc32_calc((unsigned char*)hdr + 16, in->size - 16); } } if(mimetype!=NULL){ if(!strcmp(filetype,FSZ_FILETYPE_UNION)){ for(i=1;imimetype:in->data.small.inlinedata,mimetype,i>j?j:i); if(j!=36) in->size=i; } in->changedate=in->createdate=t * 1000000; in->modifydate=t * 1000000; in->checksum=crc32_calc(in->filetype,1016); fs_len+=fsz_secsize; return fs_len/fsz_secsize-1; } void fsz_link_inode(int inode, char *path, int toinode) { unsigned int ns=0,cnt=0,l=strlen(path); FSZ_DirEntHeader *hdr; FSZ_DirEnt *ent; FSZ_Inode *in, *in2; if(toinode==0) toinode=((FSZ_SuperBlock *)fs_base)->rootdirfid; hdr=(FSZ_DirEntHeader *)(fs_base+toinode*fsz_secsize+1024); ent=(FSZ_DirEnt *)hdr; ent++; while(path[ns]!='/'&&path[ns]!=0) ns++; while(ent->fid!=0 && cnt<(unsigned int)((fsz_secsize-1024)/128-1)) { if(!strncmp((char *)(ent->name),path,ns+1)) { fsz_link_inode(inode,path+ns+1,ent->fid); return; } ent++; cnt++; } in=((FSZ_Inode *)(fs_base+toinode*fsz_secsize)); in2=((FSZ_Inode *)(fs_base+inode*fsz_secsize)); ent->fid=inode; if(l > 110) l = 110; memcpy(ent->name,path,l); if(!strncmp((char *)(((FSZ_Inode *)(fs_base+inode*fsz_secsize))->filetype),FSZ_FILETYPE_DIR,4)){ ent->name[l]='/'; } /* 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: partition #%d %s: %s\n", fs_no, lang[ERR_TOOMANY], path); exit(1); } hdr->numentries++; in->modifydate=t * 1000000; in->size+=sizeof(FSZ_DirEnt); qsort((char*)hdr+sizeof(FSZ_DirEntHeader), hdr->numentries, sizeof(FSZ_DirEnt), fsz_direntcmp); hdr->checksum=crc32_calc((unsigned char*)hdr + 16, in->size - 16); in->checksum=crc32_calc(in->filetype,1016); in2->numlinks++; in2->checksum=crc32_calc(in2->filetype,1016); } void fsz_add_file(char *name, unsigned char *data, unsigned long int size) { FSZ_Inode *in; unsigned char *ptr; long int i,j,k,l,s=((size+fsz_secsize-1)/fsz_secsize)*fsz_secsize; 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); in=(FSZ_Inode *)(fs_base+inode*fsz_secsize); in->changedate=t * 1000000; in->modifydate=t * 1000000; in->size=size; if(size<=(unsigned long int)fsz_secsize-1024) { in->sec=inode; in->flags=FSZ_IN_FLAG_INLINE; in->numblocks=0; memcpy(in->data.small.inlinedata,data,size); s=0; } else { 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: 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_SD0; in->numblocks=0; l=0; } else { ptr=fs_base+size; in->flags=FSZ_IN_FLAG_SD1; in->numblocks=1; l=1; } k=inode+1+l; for(i=0;isize?size%fsz_secsize:(unsigned long int)fsz_secsize); k++; in->numblocks++; } else { s-=fsz_secsize; } ptr+=16; } if(in->flags==FSZ_IN_FLAG_SD1) size+=fsz_secsize; } else { in->flags=FSZ_IN_FLAG_DIRECT; if(memcmp(data,fsz_emptysec,fsz_secsize)) { in->numblocks=1; memcpy(fs_base+fs_len,data,size); } else { in->sec=0; in->numblocks=0; } } } if(!strncmp((char*)data+1,"ELF",3) || !strncmp((char*)data,"OS/Z",4) || !strncmp((char*)data,"CSBC",4) || !strncmp((char*)data,"\000asm",4)) {memset(in->mimetype,0,36);memcpy(in->mimetype,"executable",10);in->owner.access|=FSZ_EXEC;} if(!strcmp(name+strlen(name)-3,".so")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"sharedlib",9);} else if(!strcmp(name+strlen(name)-2,".h")|| !strcmp(name+strlen(name)-2,".c")|| !strcmp(name+strlen(name)-3,".md")|| !strcmp(name+strlen(name)-4,".txt")|| !strcmp(name+strlen(name)-5,".conf") ) {memset(in->mimetype,0,36);memcpy(in->mimetype,"plain",5); memcpy(in->filetype,"text",4); } else if(!strcmp(name+strlen(name)-3,".sh")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"shellscript",11); memcpy(in->filetype,"text",4);in->owner.access|=FSZ_EXEC; } else if(!strcmp(name+strlen(name)-4,".htm")|| !strcmp(name+strlen(name)-5,".html") ) {memset(in->mimetype,0,36);memcpy(in->mimetype,"html",4); memcpy(in->filetype,"text",4); } else if(!strcmp(name+strlen(name)-4,".css")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"stylesheet",10); memcpy(in->filetype,"text",4); } else if(!strcmp(name+strlen(name)-4,".svg")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"svg",3); memcpy(in->filetype,"imag",4); } else if(!strcmp(name+strlen(name)-4,".gif")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"gif",3); memcpy(in->filetype,"imag",4); } else if(!strcmp(name+strlen(name)-4,".png")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"png",3); memcpy(in->filetype,"imag",4); } else if(!strcmp(name+strlen(name)-4,".jpg")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"jpeg",4); memcpy(in->filetype,"imag",4); } else if(!strcmp(name+strlen(name)-4,".bmp")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"bitmap",6); memcpy(in->filetype,"imag",4); } else if(!strcmp(name+strlen(name)-4,".sfn")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"ssfont",6); memcpy(in->filetype,"font",4); } else if(!strcmp(name+strlen(name)-4,".psf")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"pc-screen-font",14); memcpy(in->filetype,"font",4); } else if(!strcmp(name+strlen(name)-4,".ttf")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"sfnt",4); memcpy(in->filetype,"font",4); } else if(!strcmp(name+strlen(name)-4,".m3d")) {memset(in->mimetype,0,36);memcpy(in->mimetype,"3d-model",8); memcpy(in->filetype,data[1]=='d' ? "text" : "mode",4); } else { j=1; for(i=0;imimetype,0,36);memcpy(in->mimetype,"plain",5); memcpy(in->filetype,"text",4); } } in->checksum=crc32_calc(in->filetype,1016); fs_len+=s; fsz_link_inode(inode,name,0); } /*** mkbootimg interface ***/ void fsz_open(gpt_t *gpt_entry) { FSZ_SuperBlock *sb; fs_base = realloc(fs_base, 2*fsz_secsize); if(!fs_base) { fprintf(stderr,"mkbootimg: %s\r\n", lang[ERR_MEM]); exit(1); } memset(fs_base,0,2*fsz_secsize); sb=(FSZ_SuperBlock *)(fs_base); memcpy(sb->magic,FSZ_MAGIC,4); sb->version_major=FSZ_VERSION_MAJOR; sb->version_minor=FSZ_VERSION_MINOR; sb->logsec=fsz_secsize==2048?0:(fsz_secsize==4096?1:2); sb->maxmounts=255; sb->currmounts=0; sb->createdate=sb->lastmountdate=sb->lastumountdate=t * 1000000; if(gpt_entry) { memcpy(&sb->uuid, &gpt_entry->guid, 16); 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_max = 0; } memcpy(sb->magic2,FSZ_MAGIC,4); fs_len = fsz_secsize; /* don't use sb after this point because add inode will realloc */ ((FSZ_SuperBlock *)(fs_base))->rootdirfid = fsz_add_inode(FSZ_FILETYPE_DIR,FSZ_MIMETYPE_DIR_ROOT); ((FSZ_Inode*)(fs_base+fsz_secsize))->numlinks++; } void fsz_add(struct stat *st, char *name, unsigned char *content, int size) { int i; char *fn = strrchr(name, '/'); if(!fn) fn = name; else fn++; if(!strcmp(fn, ".") || !strcmp(fn, "..")) return; if(S_ISDIR(st->st_mode)) { i=fsz_add_inode(FSZ_FILETYPE_DIR,NULL); fsz_link_inode(i,name,0); } else if(S_ISREG(st->st_mode)) { fsz_add_file(name,content,size); } else if(S_ISLNK(st->st_mode) && content) { i=fsz_add_inode(FSZ_FILETYPE_SYMLINK,(char*)content); fsz_link_inode(i,name,0); } } void fsz_close() { FSZ_SuperBlock *sb=(FSZ_SuperBlock *)(fs_base); if(!sb) return; if(!sb->numsec) sb->numsec = fs_len / fsz_secsize; sb->freesec = fs_len / fsz_secsize; sb->checksum = crc32_calc((unsigned char *)&sb->magic,508); }