style
[RRG-proxmark3.git] / client / src / cmdflashmemspiffs.c
blobfef35bb4df4ecb70a43495ee6e4cc2811ac8a1d7
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Proxmark3 RDV40 Flash memory commands
17 //-----------------------------------------------------------------------------
18 #include "cmdflashmemspiffs.h"
19 #include "cmdtrace.h"
20 #include <ctype.h>
21 #include "cmdparser.h" // command_t
22 #include "pmflash.h"
23 #include "fileutils.h" //saveFile
24 #include "comms.h" //getfromdevice
25 #include "cliparser.h"
27 static int CmdHelp(const char *Cmd);
29 int flashmem_spiffs_load(const char *destfn, const uint8_t *data, size_t datalen) {
31 int ret_val = PM3_SUCCESS;
33 // We want to mount before multiple operation so the lazy writes/append will not
34 // trigger a mount + umount each loop iteration (lazy ops device side)
35 SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0);
37 // Send to device
38 uint32_t bytes_sent = 0;
39 uint32_t bytes_remaining = datalen;
41 // fast push mode
42 g_conn.block_after_ACK = true;
44 while (bytes_remaining > 0) {
46 uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining);
48 flashmem_write_t *payload = calloc(1, sizeof(flashmem_write_t) + bytes_in_packet);
50 payload->append = (bytes_sent > 0);
52 uint8_t fnlen = MIN(sizeof(payload->fn), strlen(destfn));
54 payload->fnlen = fnlen;
55 memcpy(payload->fn, destfn, fnlen);
57 payload->bytes_in_packet = bytes_in_packet;
58 memset(payload->data, 0, bytes_in_packet);
59 memcpy(payload->data, data + bytes_sent, bytes_in_packet);
61 PacketResponseNG resp;
62 clearCommandBuffer();
63 SendCommandNG(CMD_SPIFFS_WRITE, (uint8_t *)payload, sizeof(flashmem_write_t) + bytes_in_packet);
65 free(payload);
67 bytes_remaining -= bytes_in_packet;
68 bytes_sent += bytes_in_packet;
70 uint8_t retry = 3;
71 while (WaitForResponseTimeout(CMD_SPIFFS_WRITE, &resp, 2000) == false) {
72 PrintAndLogEx(WARNING, "timeout while waiting for reply.");
73 retry--;
74 if (retry == 0) {
75 ret_val = PM3_ETIMEOUT;
76 goto out;
81 out:
82 clearCommandBuffer();
84 // turn off fast push mode
85 g_conn.block_after_ACK = false;
87 // We want to unmount after these to set things back to normal but more than this
88 // unmouting ensure that SPIFFS CACHES are all flushed so our file is actually written on memory
89 SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0);
90 return ret_val;
93 int flashmem_spiffs_download(char *fn, uint8_t fnlen, void **pdest, size_t *destlen) {
94 // get size from spiffs itself !
95 clearCommandBuffer();
96 SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)fn, fnlen);
97 PacketResponseNG resp;
98 if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) {
99 PrintAndLogEx(WARNING, "timeout while waiting for reply.");
100 return PM3_ETIMEOUT;
103 uint32_t len = resp.data.asDwords[0];
104 if (len == 0) {
105 PrintAndLogEx(ERR, "error, failed to retrieve file stats on SPIFFSS");
106 return PM3_EFAILED;
109 *pdest = calloc(len, sizeof(uint8_t));
110 if (*pdest == false) {
111 PrintAndLogEx(ERR, "error, cannot allocate memory ");
112 return PM3_EMALLOC;
115 uint32_t start_index = 0;
116 PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len, fn);
118 if (GetFromDevice(SPIFFS, *pdest, len, start_index, (uint8_t *)fn, fnlen, NULL, -1, true) == 0) {
119 PrintAndLogEx(FAILED, "error, downloading from spiffs");
120 free(*pdest);
121 return PM3_EFLASH;
124 *destlen = len;
125 return PM3_SUCCESS;
128 static int CmdFlashMemSpiFFSMount(const char *Cmd) {
129 CLIParserContext *ctx;
130 CLIParserInit(&ctx, "mem spiffs mount",
131 "Mount the SPIFFS file system if not already mounted",
132 "mem spiffs mount");
134 void *argtable[] = {
135 arg_param_begin,
136 arg_param_end
138 CLIExecWithReturn(ctx, Cmd, argtable, true);
139 CLIParserFree(ctx);
140 clearCommandBuffer();
141 SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0);
142 return PM3_SUCCESS;
145 static int CmdFlashMemSpiFFSUnmount(const char *Cmd) {
146 CLIParserContext *ctx;
147 CLIParserInit(&ctx, "mem spiffs unmount",
148 "Un-mount the SPIFFS file system",
149 "mem spiffs unmount");
151 void *argtable[] = {
152 arg_param_begin,
153 arg_param_end
155 CLIExecWithReturn(ctx, Cmd, argtable, true);
156 CLIParserFree(ctx);
157 clearCommandBuffer();
158 SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0);
159 return PM3_SUCCESS;
162 static int CmdFlashMemSpiFFSTest(const char *Cmd) {
163 CLIParserContext *ctx;
164 CLIParserInit(&ctx, "mem spiffs test",
165 "Test SPIFFS Operations, require wiping pages 0 and 1",
166 "mem spiffs test");
168 void *argtable[] = {
169 arg_param_begin,
170 arg_param_end
172 CLIExecWithReturn(ctx, Cmd, argtable, true);
173 CLIParserFree(ctx);
174 clearCommandBuffer();
175 SendCommandNG(CMD_SPIFFS_TEST, NULL, 0);
176 return PM3_SUCCESS;
179 static int CmdFlashMemSpiFFSCheck(const char *Cmd) {
180 CLIParserContext *ctx;
181 CLIParserInit(&ctx, "mem spiffs check",
182 "Check/try to defrag faulty/fragmented SPIFFS file system",
183 "mem spiffs check");
185 void *argtable[] = {
186 arg_param_begin,
187 arg_param_end
189 CLIExecWithReturn(ctx, Cmd, argtable, true);
190 CLIParserFree(ctx);
191 clearCommandBuffer();
192 SendCommandNG(CMD_SPIFFS_CHECK, NULL, 0);
193 return PM3_SUCCESS;
196 static int CmdFlashMemSpiFFSTree(const char *Cmd) {
197 CLIParserContext *ctx;
198 CLIParserInit(&ctx, "mem spiffs tree",
199 "Print the Flash memory file system tree",
200 "mem spiffs tree");
202 void *argtable[] = {
203 arg_param_begin,
204 arg_param_end
206 CLIExecWithReturn(ctx, Cmd, argtable, true);
207 CLIParserFree(ctx);
209 PrintAndLogEx(INFO, "--- " _CYAN_("Flash Memory tree (SPIFFS)") " -----------------");
210 clearCommandBuffer();
211 SendCommandNG(CMD_SPIFFS_PRINT_TREE, NULL, 0);
212 return PM3_SUCCESS;
215 static int CmdFlashMemSpiFFSInfo(const char *Cmd) {
216 CLIParserContext *ctx;
217 CLIParserInit(&ctx, "mem spiffs info",
218 "Print file system info and usage statistics",
219 "mem spiffs info");
221 void *argtable[] = {
222 arg_param_begin,
223 arg_param_end
225 CLIExecWithReturn(ctx, Cmd, argtable, true);
226 CLIParserFree(ctx);
228 PrintAndLogEx(INFO, "--- " _CYAN_("Flash Memory info (SPIFFS)") " -----------------");
229 clearCommandBuffer();
230 SendCommandNG(CMD_SPIFFS_PRINT_FSINFO, NULL, 0);
231 return PM3_SUCCESS;
234 static int CmdFlashMemSpiFFSRemove(const char *Cmd) {
235 CLIParserContext *ctx;
236 CLIParserInit(&ctx, "mem spiffs remove",
237 "Remove a file from SPIFFS filesystem",
238 "mem spiffs remove -f lasttag.bin"
241 void *argtable[] = {
242 arg_param_begin,
243 arg_str1("f", "file", "<fn>", "file to remove"),
244 arg_param_end
246 CLIExecWithReturn(ctx, Cmd, argtable, false);
248 int fnlen = 0;
249 char filename[32] = {0};
250 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, 32, &fnlen);
251 CLIParserFree(ctx);
253 PrintAndLogEx(DEBUG, "Removing `" _YELLOW_("%s") "`", filename);
254 struct {
255 uint8_t len;
256 uint8_t fn[32];
257 } PACKED payload;
258 payload.len = fnlen;
259 memcpy(payload.fn, filename, fnlen);
261 PacketResponseNG resp;
262 clearCommandBuffer();
263 SendCommandNG(CMD_SPIFFS_REMOVE, (uint8_t *)&payload, sizeof(payload));
264 WaitForResponse(CMD_SPIFFS_REMOVE, &resp);
265 if (resp.status == PM3_SUCCESS) {
266 PrintAndLogEx(INFO, "Done!");
268 return PM3_SUCCESS;
271 static int CmdFlashMemSpiFFSRename(const char *Cmd) {
272 CLIParserContext *ctx;
273 CLIParserInit(&ctx, "mem spiffs rename",
274 "Rename/move a file from SPIFFS filesystem.",
275 "mem spiffs rename -s aaa.bin -d bbb.bin"
278 void *argtable[] = {
279 arg_param_begin,
280 arg_str1("s", "src", "<fn>", "source file name"),
281 arg_str1("d", "dest", "<fn>", "destination file name"),
282 arg_param_end
284 CLIExecWithReturn(ctx, Cmd, argtable, false);
286 int slen = 0;
287 char src[32] = {0};
288 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen);
290 int dlen = 0;
291 char dest[32] = {0};
292 CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen);
293 CLIParserFree(ctx);
295 PrintAndLogEx(DEBUG, "Rename from `" _YELLOW_("%s") "` -> `" _YELLOW_("%s") "`", src, dest);
297 struct {
298 uint8_t slen;
299 uint8_t src[32];
300 uint8_t dlen;
301 uint8_t dest[32];
302 } PACKED payload;
303 payload.slen = slen;
304 payload.dlen = dlen;
306 memcpy(payload.src, src, slen);
307 memcpy(payload.dest, dest, dlen);
309 PacketResponseNG resp;
310 clearCommandBuffer();
311 SendCommandNG(CMD_SPIFFS_RENAME, (uint8_t *)&payload, sizeof(payload));
312 WaitForResponse(CMD_SPIFFS_RENAME, &resp);
313 if (resp.status == PM3_SUCCESS) {
314 PrintAndLogEx(INFO, "Done!");
317 PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
318 return PM3_SUCCESS;
321 static int CmdFlashMemSpiFFSCopy(const char *Cmd) {
323 CLIParserContext *ctx;
324 CLIParserInit(&ctx, "mem spiffs copy",
325 "Copy a file to another (destructively) in SPIFFS file system",
326 "mem spiffs copy -s aaa.bin -d aaa_cpy.bin"
329 void *argtable[] = {
330 arg_param_begin,
331 arg_str1("s", "src", "<fn>", "source file name"),
332 arg_str1("d", "dest", "<fn>", "destination file name"),
333 arg_param_end
335 CLIExecWithReturn(ctx, Cmd, argtable, false);
337 int slen = 0;
338 char src[32] = {0};
339 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen);
341 int dlen = 0;
342 char dest[32] = {0};
343 CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen);
344 CLIParserFree(ctx);
346 struct {
347 uint8_t slen;
348 uint8_t src[32];
349 uint8_t dlen;
350 uint8_t dest[32];
351 } PACKED payload;
352 payload.slen = slen;
353 payload.dlen = dlen;
355 memcpy(payload.src, src, slen);
356 memcpy(payload.dest, dest, dlen);
358 PacketResponseNG resp;
359 clearCommandBuffer();
360 SendCommandNG(CMD_SPIFFS_COPY, (uint8_t *)&payload, sizeof(payload));
361 WaitForResponse(CMD_SPIFFS_COPY, &resp);
362 if (resp.status == PM3_SUCCESS) {
363 PrintAndLogEx(INFO, "Done!");
366 PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
367 return PM3_SUCCESS;
370 static int CmdFlashMemSpiFFSDump(const char *Cmd) {
371 CLIParserContext *ctx;
372 CLIParserInit(&ctx, "mem spiffs dump",
373 "Dumps device SPIFFS file to a local file\n"
374 "Size is handled by first sending a STAT command against file to verify existence",
375 "mem spiffs dump -s tag.bin --> download binary file from device, saved as `tag.bin`\n"
376 "mem spiffs dump -s tag.bin -d a001 --> download tag.bin, save as `a001.bin`\n"
377 "mem spiffs dump -s tag.bin -t --> download tag.bin into trace buffer"
380 void *argtable[] = {
381 arg_param_begin,
382 arg_str1("s", "src", "<fn>", "SPIFFS file to save"),
383 arg_str0("d", "dest", "<fn>", "file name to save to <w/o .bin>"),
384 arg_lit0("t", "trace", "download into trace buffer"),
385 arg_param_end
387 CLIExecWithReturn(ctx, Cmd, argtable, false);
388 int slen = 0;
389 char src[32] = {0};
390 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen);
392 int dlen = 0;
393 char dest[FILE_PATH_SIZE] = {0};
394 CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, FILE_PATH_SIZE, &dlen);
396 bool to_trace = arg_get_lit(ctx, 3);
397 CLIParserFree(ctx);
399 // get size from spiffs itself !
400 clearCommandBuffer();
401 SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)src, slen);
402 PacketResponseNG resp;
403 if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) {
404 PrintAndLogEx(WARNING, "timeout while waiting for reply.");
405 return PM3_ETIMEOUT;
408 uint32_t len = resp.data.asDwords[0];
409 uint8_t *dump = calloc(len, sizeof(uint8_t));
410 if (dump == NULL) {
411 PrintAndLogEx(ERR, "error, cannot allocate memory ");
412 return PM3_EMALLOC;
415 // download from device
416 uint32_t start_index = 0;
417 PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len, src);
418 if (GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)src, slen, NULL, -1, true) == false) {
419 PrintAndLogEx(FAILED, "error, downloading from spiffs");
420 free(dump);
421 return PM3_EFLASH;
424 if (to_trace) {
425 // copy to client trace buffer
426 if (ImportTraceBuffer(dump, len) == false) {
427 PrintAndLogEx(FAILED, "error, copying to trace buffer");
428 free(dump);
429 return PM3_EMALLOC;
431 PrintAndLogEx(HINT, "Use 'trace list -1 -t ...' to view, 'trace save -f ...' to save");
435 if (dlen || slen) {
437 // save to file
438 char fn[FILE_PATH_SIZE] = {0};
440 // prefer dest name
441 // else source name
442 if (dlen) {
443 strncpy(fn, dest, dlen);
444 } else {
445 strncpy(fn, src, slen);
448 // set file extension
449 const char *suffix = strchr(fn, '.');
450 if (suffix) {
451 saveFile(fn, suffix, dump, len);
452 } else {
453 saveFile(fn, ".bin", dump, len);
456 free(dump);
457 return PM3_SUCCESS;
460 static int CmdFlashMemSpiFFSWipe(const char *Cmd) {
461 CLIParserContext *ctx;
462 CLIParserInit(&ctx, "mem spiffs wipe",
463 _RED_("* * * Warning * * *") " \n"
464 _CYAN_("This command wipes all files on the device SPIFFS file system"),
465 "mem spiffs wipe");
467 void *argtable[] = {
468 arg_param_begin,
469 arg_param_end
471 CLIExecWithReturn(ctx, Cmd, argtable, true);
472 CLIParserFree(ctx);
474 PrintAndLogEx(INFO, "Wiping all files from SPIFFS file system");
475 PacketResponseNG resp;
476 clearCommandBuffer();
477 SendCommandNG(CMD_SPIFFS_WIPE, NULL, 0);
478 WaitForResponse(CMD_SPIFFS_WIPE, &resp);
479 if (resp.status == PM3_SUCCESS) {
480 PrintAndLogEx(INFO, "Done!");
483 PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
484 return PM3_SUCCESS;
487 static int CmdFlashMemSpiFFSUpload(const char *Cmd) {
488 CLIParserContext *ctx;
489 CLIParserInit(&ctx, "mem spiffs upload",
490 "Uploads binary-wise file into device file system\n"
491 "Warning: mem area to be written must have been wiped first.\n"
492 "This is already taken care when loading dictionaries.\n"
493 "File names can only be 32 bytes long on device SPIFFS",
494 "mem spiffs upload -s local.bin -d dest.bin"
497 void *argtable[] = {
498 arg_param_begin,
499 arg_str1("s", "src", "<fn>", "source file name"),
500 arg_str1("d", "dest", "<fn>", "destination file name"),
501 arg_param_end
503 CLIExecWithReturn(ctx, Cmd, argtable, false);
505 int slen = 0;
506 char src[FILE_PATH_SIZE] = {0};
507 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, FILE_PATH_SIZE, &slen);
509 int dlen = 0;
510 char dest[32] = {0};
511 CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen);
512 CLIParserFree(ctx);
514 PrintAndLogEx(DEBUG, "Upload `" _YELLOW_("%s") "` -> `" _YELLOW_("%s") "`", src, dest);
516 size_t datalen = 0;
517 uint8_t *data = NULL;
519 int res = loadFile_safe(src, "", (void **)&data, &datalen);
520 if (res != PM3_SUCCESS) {
521 free(data);
522 return PM3_EFILE;
525 res = flashmem_spiffs_load(dest, data, datalen);
526 free(data);
528 if (res == PM3_SUCCESS)
529 PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu") " bytes to file "_GREEN_("%s"), datalen, dest);
531 PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
532 return res;
535 static int CmdFlashMemSpiFFSView(const char *Cmd) {
537 CLIParserContext *ctx;
538 CLIParserInit(&ctx, "mem spiffs view",
539 "View a file on flash memory on device in console",
540 "mem spiffs view -f tag.bin"
543 void *argtable[] = {
544 arg_param_begin,
545 arg_str1("f", "file", "<fn>", "SPIFFS file to view"),
546 arg_int0("c", "cols", "<dec>", "column breaks (def 16)"),
547 arg_param_end
549 CLIExecWithReturn(ctx, Cmd, argtable, false);
551 int fnlen = 0;
552 char fn[32] = {0};
553 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)fn, 32, &fnlen);
555 int breaks = arg_get_int_def(ctx, 2, 16);
556 CLIParserFree(ctx);
558 uint8_t *dump = NULL;
559 size_t dumplen = 0;
560 int res = flashmem_spiffs_download(fn, fnlen, (void **)&dump, &dumplen);
561 if (res != PM3_SUCCESS) {
562 return res;
565 PrintAndLogEx(NORMAL, "");
566 print_hex_break(dump, dumplen, breaks);
567 PrintAndLogEx(NORMAL, "");
568 free(dump);
569 return PM3_SUCCESS;
572 static command_t CommandTable[] = {
573 {"help", CmdHelp, AlwaysAvailable, "This help"},
574 {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS file system"},
575 {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented file system"},
576 {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS file system"},
577 {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print file system info and usage statistics"},
578 {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS file system if not already mounted"},
579 {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS file system"},
580 {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS file system"},
581 {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations"},
582 {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash memory file system tree"},
583 {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS file system"},
584 {"upload", CmdFlashMemSpiFFSUpload, IfPm3Flash, "Upload file into SPIFFS file system"},
585 {"view", CmdFlashMemSpiFFSView, IfPm3Flash, "View file on SPIFFS file system"},
586 {"wipe", CmdFlashMemSpiFFSWipe, IfPm3Flash, "Wipe all files from SPIFFS file system * " _RED_("dangerous") " *" },
587 {NULL, NULL, NULL, NULL}
590 static int CmdHelp(const char *Cmd) {
591 (void)Cmd; // Cmd is not used so far
592 CmdsHelp(CommandTable);
593 return PM3_SUCCESS;
596 int CmdFlashMemSpiFFS(const char *Cmd) {
597 clearCommandBuffer();
598 return CmdsParse(CommandTable, Cmd);