1 // SPDX-License-Identifier: GPL-2.0-only
14 #if !defined(__BYTE_ORDER)
15 #error "Unknown byte order"
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)
29 #error "Unsupported endianness"
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)))
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)))
41 /* first byte contains flags */
44 /* first byte is reserved */
46 } __attribute__ ((packed
));
64 uint32_t parent_block
;
65 uint16_t parent_index
;
72 } __attribute__ ((packed
));
75 #define ERASEBLOCK_SIZE 0x10000
76 #define BLOCK_SIZE 0x400
78 #define BLOCKS_PER_ERASEBLOCK (ERASEBLOCK_SIZE / BLOCK_SIZE)
79 #define FILE_ENTRIES_PER_BLOCK (BLOCK_SIZE / sizeof(struct file_entry))
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
91 static size_t file_size
= 0;
93 static int dir_block
= 1;
94 static int dir_count
= 0;
96 static int next_data_block
= 2;
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));
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)));
109 static int init_eraseblock(size_t offset
) {
110 size_t end
= offset
- (offset
% ERASEBLOCK_SIZE
) + ERASEBLOCK_SIZE
;
114 while (file_size
< end
) {
115 if (fseek(f
, file_size
, SEEK_SET
)) {
116 fprintf(stderr
, "failed to seek to end\n");
120 for (i
= 0; i
< ERASEBLOCK_SIZE
; i
++) {
121 if (fwrite(fill
, 1, 1, f
) != 1) {
122 fprintf(stderr
, "failed to write eraseblock\n");
127 file_size
+= ERASEBLOCK_SIZE
;
133 static inline void init_fat_entry(struct fat_entry
*out
) {
134 memset(out
, '\xff', sizeof(struct fat_entry
));
137 static int read_fat_entry(struct fat_entry
*out
, int block
) {
138 size_t offset
= fat_entry_offset(block
);
140 if (init_eraseblock(offset
)) {
144 if (fseek(f
, offset
, SEEK_SET
)) {
145 fprintf(stderr
, "failed to seek to fat entry\n");
149 if (fread(out
, sizeof(struct fat_entry
), 1, f
) != 1) {
150 fprintf(stderr
, "failed to read fat entry\n");
157 static int write_fat_entry(struct fat_entry
*in
, int block
) {
158 size_t offset
= fat_entry_offset(block
);
160 if (init_eraseblock(offset
)) {
164 if (fseek(f
, offset
, SEEK_SET
)) {
165 fprintf(stderr
, "failed to seek to fat entry\n");
169 if (fwrite(in
, sizeof(struct fat_entry
), 1, f
) != 1) {
170 fprintf(stderr
, "failed to write fat entry\n");
177 static inline void init_file_entry(struct file_entry
*out
) {
178 memset(out
, '\xff', sizeof(struct file_entry
));
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
;
184 if (init_eraseblock(offset
)) {
188 if (fseek(f
, offset
, SEEK_SET
)) {
189 fprintf(stderr
, "failed to seek to file entry\n");
193 if (fwrite(in
, sizeof(struct file_entry
), 1, f
) != 1) {
194 fprintf(stderr
, "failed to write file entry\n");
201 static int write_block(void *in
, size_t len
, int block
) {
202 size_t offset
= block_offset(block
);
204 if (init_eraseblock(offset
)) {
208 if (fseek(f
, offset
, SEEK_SET
)) {
209 fprintf(stderr
, "failed to seek to block\n");
213 if (fwrite(in
, len
, 1, f
) != 1) {
214 fprintf(stderr
, "failed to write block\n");
221 static int create_root_directory() {
222 struct fat_entry fat
;
223 struct file_entry file
;
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)) {
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)) {
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)) {
251 static int write_file(char *name
, char *path
) {
253 struct fat_entry fat
;
254 struct file_entry file
;
256 char buf
[BLOCK_SIZE
];
259 int first_data_block
= next_data_block
;
262 fin
= fopen(path
, "r");
264 fprintf(stderr
, "failed to open input file\n");
268 while ((len
= fread(buf
, 1, BLOCK_SIZE
, fin
)) != 0 || !data_block
) {
271 /* update next pointer of previous FAT entry */
273 if (read_fat_entry(&fat
, data_block
)) {
276 FAT_PTR_VAL_SET(fat
.next
, next_data_block
);
277 if (write_fat_entry(&fat
, data_block
)) {
282 /* write FAT entry for new block */
283 init_fat_entry(&fat
);
284 FAT_PTR_FLAGS_SET(fat
.previous
, ~(FLAG_FREE
| FLAG_VALID
));
286 FAT_PTR_VAL_SET(fat
.previous
, data_block
);
288 if (write_fat_entry(&fat
, next_data_block
)) {
292 /* write data block */
293 if (write_block(buf
, len
, next_data_block
)) {
297 data_block
= next_data_block
;
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
)) {
307 FAT_PTR_VAL_SET(fat
.next
, next_data_block
);
308 if (write_fat_entry(&fat
, dir_block
)) {
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
)) {
320 dir_block
= next_data_block
;
325 /* write file entry */
326 init_file_entry(&file
);
328 file
.flags
= ~(FLAG_FREE
| FLAG_VALID
) & 0xff;
330 file
.year
= cpu_to_be16(1970);
337 file
.length
= cpu_to_be32(total
);
339 file
.parent_block
= cpu_to_be32(0);
340 file
.parent_index
= cpu_to_be16(0);
342 file
.data_block
= cpu_to_be32(first_data_block
);
344 snprintf(file
.name
, sizeof(file
.name
), "%s", name
);
346 if (write_file_entry(&file
, dir_block
, dir_count
)) {
358 static void usage(char* argv
[]) {
359 printf("Usage: %s [OPTIONS...]\n"
362 " -f <filename> filename in image\n"
363 " -i <file> input filename\n"
364 " -o <file> output filename\n"
368 int main(int argc
, char* argv
[]) {
369 int ret
= EXIT_FAILURE
;
371 static char *filename
= NULL
;
372 static char *input_filename
= NULL
;
373 static char *output_filename
= NULL
;
378 c
= getopt(argc
, argv
, "f:i:o:");
387 input_filename
= optarg
;
390 output_filename
= optarg
;
398 if (!filename
|| strlen(filename
) == 0 ||
399 !input_filename
|| strlen(input_filename
) == 0 ||
400 !output_filename
|| strlen(output_filename
) == 0) {
406 f
= fopen(output_filename
, "w+");
408 fprintf(stderr
, "failed to open output file\n");
412 if (create_root_directory()) {
416 if (write_file(filename
, input_filename
)) {