lean.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /*
  2. * mkbootimg/lean.c
  3. *
  4. * Copyright (C) 2017 - 2021 bzt (bztsrc@gitlab)
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * This file is part of the BOOTBOOT Protocol package.
  27. * @brief LeanFS file system driver
  28. * See http://freedos-32.sourceforge.net/lean/specification.php (v0.6, original)
  29. * See http://www.fysnet.net/leanfs/specification.php (v0.7 and later, forked by fys)
  30. *
  31. * The FS we create here does not use any v0.8 or v0.7 specific features, hence it is v0.6 compatible
  32. * (no hidden or undeleted files, no bad sectors, sector size 512 bytes, six extents per inode etc.)
  33. *
  34. */
  35. #include "main.h"
  36. #define LEAN_SUPER_MAGIC 0x4E41454C
  37. #define LEAN_SUPER_VERSION 0x0008 /* could be 6 or 7 as well */
  38. #define LEAN_INODE_MAGIC 0x45444F4E
  39. #define LEAN_INODE_EXTENT_CNT 6
  40. #define LEAN_FT_MT 0
  41. #define LEAN_FT_REG 1
  42. #define LEAN_FT_DIR 2
  43. #define LEAN_FT_LNK 3
  44. #define LEAN_ATTR_PREALLOC (1 << 18)
  45. #define LEAN_ATTR_INLINEXTATTR (1 << 19)
  46. #define LEAN_ATTR_IFMT (7 << 29)
  47. #define LEAN_ATTR_IFTYPE(x) ((uint32_t)(x) << 29)
  48. #define LEAN_LOG_BANDSIZE 12
  49. #define LEAN_BITMAPSIZE (1 << (LEAN_LOG_BANDSIZE - 12))
  50. #define LEAN_INODE_SIZE 176
  51. typedef struct {
  52. uint8_t loader[16384];
  53. uint32_t checksum;
  54. uint32_t magic;
  55. uint16_t fs_version;
  56. uint8_t pre_alloc_count;
  57. uint8_t log_sectors_per_band;
  58. uint32_t state;
  59. uint8_t uuid[16];
  60. uint8_t volume_label[64];
  61. uint64_t sector_count;
  62. uint64_t free_sector_count;
  63. uint64_t primary_super;
  64. uint64_t backup_super;
  65. uint64_t bitmap_start;
  66. uint64_t root_inode;
  67. uint64_t bad_inode;
  68. uint64_t journal_inode;
  69. uint8_t log_block_size;
  70. uint8_t reserved2[344];
  71. } __attribute__((packed)) lean_super_t;
  72. typedef struct {
  73. uint32_t checksum;
  74. uint32_t magic;
  75. uint8_t extent_count;
  76. uint8_t reserved[3];
  77. uint32_t indirect_count;
  78. uint32_t links_count;
  79. uint32_t uid;
  80. uint32_t gid;
  81. uint32_t attributes;
  82. uint64_t file_size;
  83. uint64_t sector_count;
  84. uint64_t atime;
  85. uint64_t ctime;
  86. uint64_t mtime;
  87. uint64_t btime;
  88. uint64_t first_indirect;
  89. uint64_t last_indirect;
  90. uint64_t fork;
  91. uint64_t extent_start[LEAN_INODE_EXTENT_CNT];
  92. uint32_t extent_size[LEAN_INODE_EXTENT_CNT];
  93. } __attribute__((packed)) lean_inode_t;
  94. typedef struct {
  95. uint64_t inode;
  96. uint8_t type;
  97. uint8_t rec_len;
  98. uint16_t name_len;
  99. } __attribute__((packed)) lean_dirent_t;
  100. int len_numblk, len_nextblk;
  101. lean_super_t *len_sb;
  102. uint32_t len_checksum(void *data, int size)
  103. {
  104. uint32_t ret = 0, *ptr = (uint32_t*)data;
  105. int i;
  106. for(i = 1; i < size; i++)
  107. ret = (ret << 31) + (ret >> 1) + ptr[i];
  108. return ret;
  109. }
  110. int len_alloc_blk()
  111. {
  112. int r, g = len_nextblk / (1 << LEAN_LOG_BANDSIZE), o = len_nextblk % (1 << LEAN_LOG_BANDSIZE);
  113. while((uint64_t)len_nextblk < len_sb->sector_count &&
  114. fs_base[(g * (1 << LEAN_LOG_BANDSIZE) + (!g ? len_sb->bitmap_start : 0)) * 512 + o / 8] & (1 << (o & 7))) {
  115. o++; len_nextblk++;
  116. if(o >= (1 << LEAN_LOG_BANDSIZE)) { o = 0; g++; len_nextblk += LEAN_BITMAPSIZE; }
  117. }
  118. if((uint64_t)len_nextblk + 1 >= len_sb->sector_count || len_sb->free_sector_count < 1) {
  119. fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_TOOBIG]);
  120. exit(1);
  121. }
  122. fs_base[(g * (1 << LEAN_LOG_BANDSIZE) + (!g ? len_sb->bitmap_start : 0)) * 512 + o / 8] |= 1 << (o & 7);
  123. len_sb->free_sector_count--;
  124. r = len_nextblk++;
  125. if(!(len_nextblk % (1 << LEAN_LOG_BANDSIZE))) len_nextblk += LEAN_BITMAPSIZE;
  126. return r;
  127. }
  128. void len_add_to_inode(uint32_t ino, uint32_t blk, char *name)
  129. {
  130. lean_inode_t *inode = (lean_inode_t*)(fs_base + ino * 512);
  131. inode->sector_count++;
  132. if(inode->extent_start[inode->extent_count - 1] + inode->extent_size[inode->extent_count - 1] == blk) {
  133. inode->extent_size[inode->extent_count - 1]++;
  134. } else {
  135. inode->extent_count++;
  136. if(inode->extent_count < LEAN_INODE_EXTENT_CNT) {
  137. inode->extent_start[inode->extent_count - 1] = blk;
  138. inode->extent_size[inode->extent_count - 1] = 1;
  139. } else {
  140. fprintf(stderr,"mkbootimg: partition #%d %s: %s\r\n", fs_no, lang[ERR_TOOBIG], name);
  141. exit(1);
  142. }
  143. }
  144. inode->checksum = len_checksum(inode, LEAN_INODE_SIZE / 4);
  145. }
  146. int len_alloc_inode(uint16_t mode, uint8_t type, uint64_t size, time_t t)
  147. {
  148. lean_inode_t *inode;
  149. int n = len_alloc_blk(), i;
  150. inode = (lean_inode_t*)(fs_base + n * 512);
  151. inode->magic = LEAN_INODE_MAGIC;
  152. inode->attributes = (mode & 0xFFF) | LEAN_ATTR_IFTYPE(type) | LEAN_ATTR_INLINEXTATTR |
  153. (type == LEAN_FT_DIR ? LEAN_ATTR_PREALLOC : 0);
  154. inode->atime = inode->ctime = inode->mtime = inode->btime = (uint64_t)t * 1000000;
  155. inode->extent_count = 1;
  156. inode->extent_start[0] = n;
  157. inode->extent_size[0] = 1;
  158. inode->sector_count = 1;
  159. if(type == LEAN_FT_DIR)
  160. for(i = 0; i < len_sb->pre_alloc_count; i++)
  161. len_add_to_inode(n, len_alloc_blk(), NULL);
  162. else
  163. inode->file_size = size;
  164. inode->checksum = len_checksum(inode, LEAN_INODE_SIZE / 4);
  165. return n;
  166. }
  167. uint8_t *len_add_dirent(uint8_t *dir, uint64_t toinode, uint64_t ino, uint8_t type, char *name, int len)
  168. {
  169. lean_dirent_t *de;
  170. lean_inode_t *inode;
  171. uint8_t *end = NULL;
  172. int l = 16 + (len < 4 ? 0 : ((len + 11) & ~15)), i = 0;
  173. uint64_t j = 0;
  174. inode = (lean_inode_t*)(fs_base + ino * 512);
  175. inode->links_count++;
  176. inode->checksum = len_checksum(inode, LEAN_INODE_SIZE / 4);
  177. inode = (lean_inode_t*)(fs_base + toinode * 512);
  178. if(!dir) {
  179. if(!inode->extent_count || inode->extent_size[0] == 1)
  180. len_add_to_inode(toinode, len_alloc_blk(), NULL);
  181. dir = fs_base + inode->extent_start[0] * 512 + 512;
  182. end = fs_base + (inode->extent_start[0] + inode->extent_size[0]) * 512;
  183. while(j < inode->file_size && ((lean_dirent_t*)dir)->inode && ((lean_dirent_t*)dir)->rec_len) {
  184. j += ((lean_dirent_t*)dir)->rec_len * 16;
  185. dir += ((lean_dirent_t*)dir)->rec_len * 16;
  186. if(dir >= end) {
  187. dir = fs_base + inode->extent_start[++i] * 512 + ((uintptr_t)(dir - end) & 511);
  188. end = fs_base + (inode->extent_start[i] + inode->extent_size[i]) * 512;
  189. }
  190. }
  191. }
  192. inode->file_size += l;
  193. if(inode->file_size > inode->sector_count * 512) {
  194. fprintf(stderr,"mkbootimg: partition #%d %s: %s\r\n", fs_no, lang[ERR_TOOMANY], name);
  195. exit(1);
  196. }
  197. inode->checksum = len_checksum(inode, LEAN_INODE_SIZE / 4);
  198. de = (lean_dirent_t*)dir;
  199. de->inode = ino;
  200. de->type = type;
  201. de->rec_len = l >> 4;
  202. de->name_len = len;
  203. if(end && len > 4) {
  204. memcpy(dir + 12, name, 4);
  205. dir += 16;
  206. name += 4;
  207. len -= 4;
  208. while(len) {
  209. if(dir >= end) {
  210. dir = fs_base + inode->extent_start[++i] * 512;
  211. end = fs_base + (inode->extent_start[i] + inode->extent_size[i]) * 512;
  212. }
  213. memcpy(dir, name, len > 16 ? 16 : len);
  214. name += 16;
  215. dir += 16;
  216. if(len > 16) len -= 16; else break;
  217. }
  218. } else {
  219. memcpy(dir + 12, name, len);
  220. dir += l;
  221. }
  222. return dir;
  223. }
  224. /*** mkbootimg interface ***/
  225. void len_open(gpt_t *gpt_entry)
  226. {
  227. int i, j, numband;
  228. if(!gpt_entry) { fprintf(stderr,"mkbootimg: %s lean.\r\n", lang[ERR_BADINITRDTYPE]); exit(1); }
  229. len_numblk = (gpt_entry->last - gpt_entry->start + 1);
  230. if(len_numblk < 32 + LEAN_BITMAPSIZE) { fprintf(stderr,"mkbootimg: partition #%d %s\r\n", fs_no, lang[ERR_NOSIZE]); exit(1); }
  231. fs_len = len_numblk * 512;
  232. fs_base = realloc(fs_base, fs_len);
  233. if(!fs_base) { fprintf(stderr,"mkbootimg: %s\r\n",lang[ERR_MEM]); exit(1); }
  234. memset(fs_base, 0, fs_len);
  235. numband = len_numblk / ((1 << LEAN_LOG_BANDSIZE) * 512);
  236. if(numband < 1) numband = 1;
  237. len_sb = (lean_super_t*)fs_base;
  238. len_sb->magic = LEAN_SUPER_MAGIC;
  239. len_sb->fs_version = LEAN_SUPER_VERSION;
  240. len_sb->log_sectors_per_band = LEAN_LOG_BANDSIZE;
  241. len_sb->pre_alloc_count = 7;
  242. len_sb->state = 1;
  243. memcpy(&len_sb->uuid, &gpt_entry->guid, sizeof(guid_t));
  244. memcpy(&len_sb->volume_label, "NO NAME", 7);
  245. len_sb->log_block_size = 9;
  246. len_sb->sector_count = len_numblk;
  247. len_sb->free_sector_count = len_numblk - 34 - numband * LEAN_BITMAPSIZE; /* loader, superblock, backup, bitmaps */
  248. len_sb->primary_super = 32;
  249. len_sb->backup_super = (len_numblk < (1 << LEAN_LOG_BANDSIZE) ? len_numblk : (1 << LEAN_LOG_BANDSIZE)) - 1;
  250. len_sb->bitmap_start = len_sb->primary_super + 1;
  251. for(j = 0; j < numband; j++) {
  252. for(i = 0; i < LEAN_BITMAPSIZE + (!j ? (int)len_sb->bitmap_start : 0); i++)
  253. fs_base[(j * (1 << LEAN_LOG_BANDSIZE) + (!j ? len_sb->bitmap_start : 0)) * 512 + i / 8] |= 1 << (i & 7);
  254. }
  255. fs_base[len_sb->bitmap_start * 512 + len_sb->backup_super / 8] |= 1 << (len_sb->backup_super & 7);
  256. len_nextblk = len_sb->bitmap_start + LEAN_BITMAPSIZE;
  257. len_sb->root_inode = len_alloc_inode(0755, LEAN_FT_DIR, 0, t);
  258. len_add_dirent(len_add_dirent(NULL,
  259. len_sb->root_inode, len_sb->root_inode, LEAN_FT_DIR, ".", 1),
  260. len_sb->root_inode, len_sb->root_inode, LEAN_FT_DIR, "..", 2);
  261. }
  262. void len_add(struct stat *st, char *name, unsigned char *content, int size)
  263. {
  264. uint64_t parent = len_sb->root_inode, ino, n, j;
  265. lean_inode_t *inode;
  266. uint8_t *dir, *end = NULL, type = LEAN_FT_REG;
  267. char d_name[MAXPATH], *dn, *nend, *fn = strrchr(name, '/');
  268. int i, k, l;
  269. if(!fn) fn = name; else fn++;
  270. if(!strcmp(fn, ".") || !strcmp(fn, "..") || (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode) && !S_ISLNK(st->st_mode))) return;
  271. type = S_ISDIR(st->st_mode) ? LEAN_FT_DIR : (S_ISLNK(st->st_mode) ? LEAN_FT_LNK : LEAN_FT_REG);
  272. n = len_alloc_inode(st->st_mode, type, size, st->st_mtime);
  273. /* Enter name in directory */
  274. fn = name;
  275. nend = strchr(name, '/');
  276. if(!nend) nend = name + strlen(name);
  277. again:
  278. i = 0; j = 0;
  279. inode = (lean_inode_t*)(fs_base + parent * 512);
  280. if(i < inode->extent_count) {
  281. dir = fs_base + inode->extent_start[i] * 512 + (!i ? 512 : 0);
  282. end = fs_base + (inode->extent_start[i] + inode->extent_size[i]) * 512;
  283. while(j < inode->file_size) {
  284. ino = ((lean_dirent_t*)dir)->inode;
  285. k = ((lean_dirent_t*)dir)->rec_len - 1;
  286. l = ((lean_dirent_t*)dir)->name_len;
  287. dn = d_name;
  288. memset(d_name, 0, sizeof(d_name));
  289. memcpy(dn, dir + 12, 4);
  290. dn += 4;
  291. dir += 16;
  292. j += 16;
  293. while(j < inode->file_size && k--) {
  294. if(dir >= end) {
  295. dir = fs_base + inode->extent_start[++i] * 512;
  296. end = fs_base + (inode->extent_start[i] + inode->extent_size[i]) * 512;
  297. }
  298. memcpy(dn, dir, 16);
  299. dn += 16;
  300. dir += 16;
  301. j += 16;
  302. }
  303. if(l == nend - fn && !memcmp(d_name, fn, nend - fn)) {
  304. parent = ino;
  305. fn = nend + 1;
  306. nend = *nend ? strchr(fn, '/') : NULL;
  307. if(!nend) { nend = fn + strlen(fn); break; }
  308. goto again;
  309. }
  310. }
  311. }
  312. len_add_dirent(NULL, parent, n, type, fn, nend - fn);
  313. if(type == LEAN_FT_DIR) {
  314. len_add_dirent(len_add_dirent(NULL,
  315. n, n, LEAN_FT_DIR, ".", 1),
  316. n, parent, LEAN_FT_DIR, "..", 2);
  317. } else {
  318. /* works for both regular files and symlinks */
  319. while(size) {
  320. k = size > 512 ? 512 : size;
  321. i = len_alloc_blk();
  322. memcpy(fs_base + i * 512, content, k);
  323. len_add_to_inode(n, i, name);
  324. content += k;
  325. size -= k;
  326. }
  327. }
  328. }
  329. void len_close()
  330. {
  331. if(!fs_base || (uint64_t)fs_len < (len_sb->backup_super + 1) * 512) return;
  332. len_sb->checksum = len_checksum(fs_base + len_sb->primary_super * 512, 128);
  333. memcpy(fs_base + len_sb->backup_super * 512, fs_base + len_sb->primary_super * 512, 512);
  334. }