1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <stdio.h> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <stdint.h> 7 #include <sys/types.h> 8 #include <unistd.h> 9 #include <byteswap.h> 10 #include <endian.h> 11 #include <getopt.h> 12 13 14 #if !defined(__BYTE_ORDER) 15 #error "Unknown byte order" 16 #endif 17 18 #if __BYTE_ORDER == __BIG_ENDIAN 19 #define cpu_to_be16(x) (x) 20 #define cpu_to_be32(x) (x) 21 #define be16_to_cpu(x) (x) 22 #define be32_to_cpu(x) (x) 23 #elif __BYTE_ORDER == __LITTLE_ENDIAN 24 #define cpu_to_be16(x) bswap_16(x) 25 #define cpu_to_be32(x) bswap_32(x) 26 #define be16_to_cpu(x) bswap_16(x) 27 #define be32_to_cpu(x) bswap_32(x) 28 #else 29 #error "Unsupported endianness" 30 #endif 31 32 33 #define FAT_PTR_FLAGS_GET(x) ((be32_to_cpu(x) & 0xff000000) >> 24) 34 #define FAT_PTR_FLAGS_SET(x, y) (x = cpu_to_be32((be32_to_cpu(x) & 0x00ffffff) | ((y & 0x000000ff) << 24))) 35 36 #define FAT_PTR_VAL_GET(x) (be32_to_cpu(x) & 0x00ffffff) 37 #define FAT_PTR_VAL_SET(x, y) (x = cpu_to_be32((be32_to_cpu(x) & 0xff000000) | (y & 0x00ffffff))) 38 39 40 struct fat_entry { 41 /* first byte contains flags */ 42 uint32_t previous; 43 44 /* first byte is reserved */ 45 uint32_t next; 46 } __attribute__ ((packed)); 47 48 struct file_entry { 49 uint8_t flags; 50 51 uint8_t res0[5]; 52 53 uint16_t year; 54 uint8_t month; 55 uint8_t day; 56 uint8_t hour; 57 uint8_t minute; 58 uint8_t second; 59 60 uint8_t res1[3]; 61 62 uint32_t length; 63 64 uint32_t parent_block; 65 uint16_t parent_index; 66 67 uint8_t res2[2]; 68 69 uint32_t data_block; 70 71 char name[96]; 72 } __attribute__ ((packed)); 73 74 75 #define ERASEBLOCK_SIZE 0x10000 76 #define BLOCK_SIZE 0x400 77 78 #define BLOCKS_PER_ERASEBLOCK (ERASEBLOCK_SIZE / BLOCK_SIZE) 79 #define FILE_ENTRIES_PER_BLOCK (BLOCK_SIZE / sizeof(struct file_entry)) 80 81 82 #define FLAG_FREE 0x80 83 #define FLAG_VALID 0x40 84 #define FLAG_INVALID 0x20 85 #define FLAG_HIDE 0x10 86 #define FLAG_DIRECTORY 0x02 87 #define FLAG_READONLY 0x01 88 89 90 static FILE *f; 91 static size_t file_size = 0; 92 93 static int dir_block = 1; 94 static int dir_count = 0; 95 96 static int next_data_block = 2; 97 98 99 static inline size_t fat_entry_offset(int block) { 100 return ERASEBLOCK_SIZE * (block / (BLOCKS_PER_ERASEBLOCK-1)) 101 + sizeof(struct fat_entry) * (block % (BLOCKS_PER_ERASEBLOCK-1)); 102 } 103 104 static inline size_t block_offset(int block) { 105 return ERASEBLOCK_SIZE * (block / (BLOCKS_PER_ERASEBLOCK-1)) 106 + BLOCK_SIZE * (1 + (block % (BLOCKS_PER_ERASEBLOCK-1))); 107 } 108 109 static int init_eraseblock(size_t offset) { 110 size_t end = offset - (offset % ERASEBLOCK_SIZE) + ERASEBLOCK_SIZE; 111 char *fill = "\xff"; 112 int i; 113 114 while (file_size < end) { 115 if (fseek(f, file_size, SEEK_SET)) { 116 fprintf(stderr, "failed to seek to end\n"); 117 return -1; 118 } 119 120 for (i = 0; i < ERASEBLOCK_SIZE; i++) { 121 if (fwrite(fill, 1, 1, f) != 1) { 122 fprintf(stderr, "failed to write eraseblock\n"); 123 return -1; 124 } 125 } 126 127 file_size += ERASEBLOCK_SIZE; 128 } 129 130 return 0; 131 } 132 133 static inline void init_fat_entry(struct fat_entry *out) { 134 memset(out, '\xff', sizeof(struct fat_entry)); 135 } 136 137 static int read_fat_entry(struct fat_entry *out, int block) { 138 size_t offset = fat_entry_offset(block); 139 140 if (init_eraseblock(offset)) { 141 return -1; 142 } 143 144 if (fseek(f, offset, SEEK_SET)) { 145 fprintf(stderr, "failed to seek to fat entry\n"); 146 return -1; 147 } 148 149 if (fread(out, sizeof(struct fat_entry), 1, f) != 1) { 150 fprintf(stderr, "failed to read fat entry\n"); 151 return -1; 152 } 153 154 return 0; 155 } 156 157 static int write_fat_entry(struct fat_entry *in, int block) { 158 size_t offset = fat_entry_offset(block); 159 160 if (init_eraseblock(offset)) { 161 return -1; 162 } 163 164 if (fseek(f, offset, SEEK_SET)) { 165 fprintf(stderr, "failed to seek to fat entry\n"); 166 return -1; 167 } 168 169 if (fwrite(in, sizeof(struct fat_entry), 1, f) != 1) { 170 fprintf(stderr, "failed to write fat entry\n"); 171 return -1; 172 } 173 174 return 0; 175 } 176 177 static inline void init_file_entry(struct file_entry *out) { 178 memset(out, '\xff', sizeof(struct file_entry)); 179 } 180 181 static int write_file_entry(struct file_entry *in, int block, int index) { 182 size_t offset = block_offset(block) + sizeof(struct file_entry) * index; 183 184 if (init_eraseblock(offset)) { 185 return -1; 186 } 187 188 if (fseek(f, offset, SEEK_SET)) { 189 fprintf(stderr, "failed to seek to file entry\n"); 190 return -1; 191 } 192 193 if (fwrite(in, sizeof(struct file_entry), 1, f) != 1) { 194 fprintf(stderr, "failed to write file entry\n"); 195 return -1; 196 } 197 198 return 0; 199 } 200 201 static int write_block(void *in, size_t len, int block) { 202 size_t offset = block_offset(block); 203 204 if (init_eraseblock(offset)) { 205 return -1; 206 } 207 208 if (fseek(f, offset, SEEK_SET)) { 209 fprintf(stderr, "failed to seek to block\n"); 210 return -1; 211 } 212 213 if (fwrite(in, len, 1, f) != 1) { 214 fprintf(stderr, "failed to write block\n"); 215 return -1; 216 } 217 218 return 0; 219 } 220 221 static int create_root_directory() { 222 struct fat_entry fat; 223 struct file_entry file; 224 225 /* write format flag / FAT entry for block 0 (contains root file entry) */ 226 init_fat_entry(&fat); 227 fat.previous = cpu_to_be32((ERASEBLOCK_SIZE << 12) | BLOCK_SIZE); 228 if (write_fat_entry(&fat, 0)) { 229 return -1; 230 } 231 232 /* write root file entry */ 233 init_file_entry(&file); 234 file.flags = ~(FLAG_FREE | FLAG_VALID) & 0xff; 235 file.parent_block = cpu_to_be32(0); 236 file.data_block = cpu_to_be32(1); 237 if (write_file_entry(&file, 0, 0)) { 238 return -1; 239 } 240 241 /* write FAT entry for block 1 (contains first file entries of root directory) */ 242 init_fat_entry(&fat); 243 FAT_PTR_FLAGS_SET(fat.previous, ~(FLAG_FREE | FLAG_VALID)); 244 if (write_fat_entry(&fat, 1)) { 245 return -1; 246 } 247 248 return 0; 249 } 250 251 static int write_file(char *name, char *path) { 252 int ret = -1; 253 struct fat_entry fat; 254 struct file_entry file; 255 FILE *fin; 256 char buf[BLOCK_SIZE]; 257 size_t len; 258 size_t total = 0; 259 int first_data_block = next_data_block; 260 int data_block = 0; 261 262 fin = fopen(path, "r"); 263 if (fin == NULL) { 264 fprintf(stderr, "failed to open input file\n"); 265 return ret; 266 } 267 268 while ((len = fread(buf, 1, BLOCK_SIZE, fin)) != 0 || !data_block) { 269 total += len; 270 271 /* update next pointer of previous FAT entry */ 272 if (data_block) { 273 if (read_fat_entry(&fat, data_block)) { 274 goto err; 275 } 276 FAT_PTR_VAL_SET(fat.next, next_data_block); 277 if (write_fat_entry(&fat, data_block)) { 278 goto err; 279 } 280 } 281 282 /* write FAT entry for new block */ 283 init_fat_entry(&fat); 284 FAT_PTR_FLAGS_SET(fat.previous, ~(FLAG_FREE | FLAG_VALID)); 285 if (data_block) { 286 FAT_PTR_VAL_SET(fat.previous, data_block); 287 } 288 if (write_fat_entry(&fat, next_data_block)) { 289 goto err; 290 } 291 292 /* write data block */ 293 if (write_block(buf, len, next_data_block)) { 294 goto err; 295 } 296 297 data_block = next_data_block; 298 next_data_block++; 299 } 300 301 /* create new file entries block if necessary */ 302 if (dir_count == FILE_ENTRIES_PER_BLOCK) { 303 /* update next pointer of previous FAT entry */ 304 if (read_fat_entry(&fat, dir_block)) { 305 goto err; 306 } 307 FAT_PTR_VAL_SET(fat.next, next_data_block); 308 if (write_fat_entry(&fat, dir_block)) { 309 goto err; 310 } 311 312 /* write FAT entry for new block */ 313 init_fat_entry(&fat); 314 FAT_PTR_FLAGS_SET(fat.previous, ~(FLAG_FREE | FLAG_VALID)); 315 FAT_PTR_VAL_SET(fat.previous, dir_block); 316 if (write_fat_entry(&fat, next_data_block)) { 317 goto err; 318 } 319 320 dir_block = next_data_block; 321 dir_count = 0; 322 next_data_block++; 323 } 324 325 /* write file entry */ 326 init_file_entry(&file); 327 328 file.flags = ~(FLAG_FREE | FLAG_VALID) & 0xff; 329 330 file.year = cpu_to_be16(1970); 331 file.month = 1; 332 file.day = 1; 333 file.hour = 0; 334 file.minute = 0; 335 file.second = 0; 336 337 file.length = cpu_to_be32(total); 338 339 file.parent_block = cpu_to_be32(0); 340 file.parent_index = cpu_to_be16(0); 341 342 file.data_block = cpu_to_be32(first_data_block); 343 344 snprintf(file.name, sizeof(file.name), "%s", name); 345 346 if (write_file_entry(&file, dir_block, dir_count)) { 347 goto err; 348 } 349 350 dir_count++; 351 352 ret = 0; 353 err: 354 fclose(fin); 355 return ret; 356 } 357 358 static void usage(char* argv[]) { 359 printf("Usage: %s [OPTIONS...]\n" 360 "\n" 361 "Options:\n" 362 " -f <filename> filename in image\n" 363 " -i <file> input filename\n" 364 " -o <file> output filename\n" 365 , argv[0]); 366 } 367 368 int main(int argc, char* argv[]) { 369 int ret = EXIT_FAILURE; 370 371 static char *filename = NULL; 372 static char *input_filename = NULL; 373 static char *output_filename = NULL; 374 375 while ( 1 ) { 376 int c; 377 378 c = getopt(argc, argv, "f:i:o:"); 379 if (c == -1) 380 break; 381 382 switch (c) { 383 case 'f': 384 filename = optarg; 385 break; 386 case 'i': 387 input_filename = optarg; 388 break; 389 case 'o': 390 output_filename = optarg; 391 break; 392 default: 393 usage(argv); 394 goto err; 395 } 396 } 397 398 if (!filename || strlen(filename) == 0 || 399 !input_filename || strlen(input_filename) == 0 || 400 !output_filename || strlen(output_filename) == 0) { 401 402 usage(argv); 403 goto err; 404 } 405 406 f = fopen(output_filename, "w+"); 407 if (f == NULL) { 408 fprintf(stderr, "failed to open output file\n"); 409 goto err; 410 } 411 412 if (create_root_directory()) { 413 goto err_close; 414 } 415 416 if (write_file(filename, input_filename)) { 417 goto err_close; 418 } 419 420 ret = EXIT_SUCCESS; 421 422 err_close: 423 fclose(f); 424 err: 425 return ret; 426 } 427
This page was automatically generated by LXR 0.3.1. • OpenWrt