nodemcu-firmware/tools/spiffsimg/main.c

502 lines
13 KiB
C
Raw Permalink Normal View History

/*
* Copyright 2015 Dius Computing Pty Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
* - Neither the name of the copyright holders nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Johny Mattsson <jmattsson@dius.com.au>
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#ifdef _MSC_VER
#include "getopt.h" /* local copy from MingW project */
#include <io.h>
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
typedef ptrdiff_t off_t;
#else
#include <unistd.h>
#include <getopt.h>
#include <sys/mman.h>
#endif
#include <fcntl.h>
#include <errno.h>
#include "spiffs.h"
#define NO_CPU_ESP8266_INCLUDE
#include "../platform/cpu_esp8266.h"
static spiffs fs;
static uint8_t *flash;
static int retcode = 0;
#define LOG_PAGE_SIZE 256
// If the caclulated size is this or less, then use smaller blocks
// (1 page) rather than the normal 2 pages. This gives a little bit
// more flexibility for the file system, at the expense of a bit of
// efficiency
#define SMALL_FILESYSTEM (128 * 1024)
static int delete_on_die = 0;
static const char *delete_list[10];
static int delete_list_index = 0;
static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
static u8_t spiffs_fds[32*4];
static s32_t flash_read (u32_t addr, u32_t size, u8_t *dst) {
memcpy (dst, flash + addr, size);
return SPIFFS_OK;
}
static s32_t flash_write (u32_t addr, u32_t size, u8_t *src) {
memcpy (flash + addr, src, size);
return SPIFFS_OK;
}
static s32_t flash_erase (u32_t addr, u32_t size) {
memset (flash + addr, 0xff, size);
return SPIFFS_OK;
}
static void die (const char *what)
{
if (errno == 0) {
fprintf(stderr, "%s: fatal error\n", what);
} else {
perror (what);
}
if (delete_on_die) {
const char **p = delete_list;
while (*p) {
unlink(*p);
p++;
}
}
exit (1);
}
static void list (void)
{
spiffs_DIR dir;
if (!SPIFFS_opendir (&fs, "/", &dir))
die ("spiffs_opendir");
struct spiffs_dirent de;
while (SPIFFS_readdir (&dir, &de))
{
static const char types[] = "?fdhs"; // file, dir, hardlink, softlink
char name[sizeof(de.name)+1] = { 0 };
memcpy (name, de.name, sizeof(de.name));
printf("%c %6u %s\n", types[de.type], de.size, name);
}
SPIFFS_closedir (&dir);
}
static void cat (char *fname)
{
spiffs_file fh = SPIFFS_open (&fs, fname, SPIFFS_RDONLY, 0);
char buff[512];
s32_t n;
while ((n = SPIFFS_read (&fs, fh, buff, sizeof (buff))) > 0)
write (STDOUT_FILENO, buff, n);
SPIFFS_close (&fs, fh);
}
static void import (char *src, char *dst)
{
int fd = open (src, O_RDONLY);
if (fd < 0)
die (src);
spiffs_file fh = SPIFFS_open (&fs, dst, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0);
if (fh < 0)
die ("spiffs_open");
char buff[16384];
s32_t n;
while ((n = read (fd, buff, sizeof (buff))) > 0)
if (SPIFFS_write (&fs, fh, buff, n) < 0)
die ("spiffs_write");
if (SPIFFS_close (&fs, fh) < 0)
die("spiffs_close");
close (fd);
}
static void export (char *src, char *dst)
{
spiffs_file fh = SPIFFS_open (&fs, src, SPIFFS_RDONLY, 0);
if (fh < 0)
die ("spiffs_open");
int fd = open (dst, O_CREAT | O_TRUNC | O_WRONLY, 0664);
if (fd < 0)
die (dst);
char buff[512];
s32_t n;
while ((n = SPIFFS_read (&fs, fh, buff, sizeof (buff))) > 0)
if (write (fd, buff, n) < 0)
die ("write");
SPIFFS_close (&fs, fh);
close (fd);
}
char *trim (char *in)
{
if (!in)
return "";
char *out = 0;
while (*in)
{
if (!out && !isspace (*in))
out = in;
++in;
}
if (!out)
return "";
while (--in > out && isspace (*in))
;
in[1] = 0;
return out;
}
void syntax (void)
{
fprintf (stderr,
"Syntax: spiffsimg -f <filename> [-d] [-o <locationfilename>] [-c size] [-S flashsize] [-U usedsize] [-l | -i | -r <scriptname> ]\n\n"
);
exit (1);
}
static size_t getsize(const char *s)
{
char *end = 0;
size_t val = strtoul(s, &end, 0);
if (end) {
int factor;
if (*end == 'k' || *end == 'K') {
factor = 1 << 10;
end++;
} else if (*end == 'm' || *end == 'M') {
factor = 1 << 20;
end++;
} else {
factor = 1;
}
// Capital B is bytes
if (*end == 'B') {
factor = factor << 3;
}
// we want bytes
val = (val * factor) / 8;
}
return val;
}
int main (int argc, char *argv[])
{
if (argc == 1)
syntax ();
int opt;
const char *fname = 0;
bool create = false;
enum { CMD_NONE, CMD_LIST, CMD_INTERACTIVE, CMD_SCRIPT } command = CMD_NONE;
int sz = 0;
const char *script_name = 0;
const char *resolved = 0;
int flashsize = 0;
int used = 0;
while ((opt = getopt (argc, argv, "do:f:c:lir:S:U:")) != -1)
{
switch (opt)
{
case 'f': fname = optarg; break;
case 'o': resolved = optarg; break;
case 'c': create = true; sz = strtol(optarg, 0, 0); break;
case 'S': create = true; flashsize = getsize(optarg); break;
case 'U': create = true; used = strtol(optarg, 0, 0); break;
case 'd': delete_on_die = 1; break;
case 'l': command = CMD_LIST; break;
case 'i': command = CMD_INTERACTIVE; break;
case 'r': command = CMD_SCRIPT; script_name = optarg; break;
default: die ("unknown option");
}
}
if (!fname) {
die("Need a filename");
}
#ifdef _MSC_VER
_set_fmode( _O_BINARY ); //change default open mode to binary rather than text
#endif
int fd;
if (create)
{
if (!sz) {
if (!flashsize || !used) {
die("Missing flashSize or Used");
}
sz = flashsize - used - SYS_PARAM_SEC_NUM * 4096; // This leaves space for the parameter area
if (sz < 0x4000) {
die("Not enough space");
}
if (sz > SMALL_FILESYSTEM) {
used = (used + 0xffff) & ~0xffff;
} else {
used = (used + 0x1fff) & ~0x1fff;
}
sz = flashsize - used - SYS_PARAM_SEC_NUM * 4096;
}
sz &= ~(0x1fff);
if (used == 0) {
if (strchr(fname, '%')) {
die("Unable to calculate where to put the flash image, and % present in filename");
}
}
char fnamebuff[1024];
sprintf(fnamebuff, fname, used);
delete_list[delete_list_index++] = strdup(fnamebuff);;
fd = open (fnamebuff, (create ? (O_CREAT | O_TRUNC) : 0) | O_RDWR, 0664);
if (fd == -1)
die ("open");
if (resolved) {
delete_list[delete_list_index++] = resolved;
FILE *f = fopen(resolved, "w");
fprintf(f, "0x%x", used);
fclose(f);
}
if (lseek (fd, sz -1, SEEK_SET) == -1)
die ("lseek");
if (write (fd, "", 1) != 1)
die ("write");
}
else {
fd = open (fname, O_RDWR, 0664);
if (fd == -1)
die ("open");
if (!sz)
{
off_t offs = lseek (fd, 0, SEEK_END);
if (offs == -1)
die ("lseek");
sz = offs;
}
}
if (sz & (0x1000 -1))
die ("file size not multiple of erase block size");
#ifdef _MSC_VER
//We don't have a mmap(), so we simply allocate a file buffer we write out
//at the end of the program. This also means that we need to let the program
//end normally (i.e. not via Ctrl-C) or else that final write will not happen.
//If you are in interactive mode, there is not a command for 'exit', however
//EOF may be used to end the interactive loop by pressing Ctrl-Z, return.
flash = (uint8_t*)malloc( sz );
if ( lseek( fd, 0, SEEK_SET ) == -1 )
die( "lseek" );
if ( read( fd, flash, sz ) != sz )
die( "write" );
#else
flash = mmap( 0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
#endif
if (!flash)
die ("mmap");
if (create)
memset (flash, 0xff, sz);
spiffs_config cfg;
cfg.phys_size = sz;
cfg.phys_addr = 0;
cfg.phys_erase_block = 0x1000;
cfg.log_block_size = 0x1000 * (sz > SMALL_FILESYSTEM ? 2 : 1);
cfg.log_page_size = LOG_PAGE_SIZE;
cfg.hal_read_f = flash_read;
cfg.hal_write_f = flash_write;
cfg.hal_erase_f = flash_erase;
if (cfg.phys_size < 4 * cfg.log_block_size) {
die("disk not large enough for four blocks");
}
if (SPIFFS_mount (&fs, &cfg,
spiffs_work_buf,
spiffs_fds,
sizeof(spiffs_fds),
malloc(65536), 65536, 0) != 0) {
if (create) {
if (SPIFFS_format(&fs) != 0) {
die("spiffs_format");
}
if (SPIFFS_mount (&fs, &cfg,
spiffs_work_buf,
spiffs_fds,
sizeof(spiffs_fds),
malloc(65536), 65536, 0) != 0) {
die ("spiffs_mount");
}
if (command == CMD_INTERACTIVE) {
printf("Created filesystem -- size 0x%x, block_size=%d\n", cfg.phys_size, cfg.log_block_size);
}
} else {
die ("spiffs_mount");
}
}
if (command == CMD_NONE)
; // maybe just wanted to create an empty image?
else if (command == CMD_LIST)
list ();
else
{
FILE *in = (command == CMD_INTERACTIVE) ? stdin : fopen (script_name, "r");
if (!in)
die ("fopen");
char buff[128] = { 0 };
if (in == stdin)
printf("> ");
while (fgets (buff, sizeof (buff) -1, in))
{
char *line = trim (buff);
if (!line[0] || line[0] == '#')
continue;
if (strcmp (line, "ls") == 0)
list ();
else if (strncmp (line, "import ", 7) == 0)
{
char *src = 0, *dst = 0;
#ifdef _MSC_VER
src = (char*)malloc(260 + 1); //MAX_PATH
dst = (char*)malloc( 260 + 1 );
if (sscanf (line +7, " %260s %260s", src, dst) != 2)
#else
if (sscanf (line +7, " %ms %ms", &src, &dst) != 2)
#endif
{
fprintf (stderr, "SYNTAX ERROR: %s\n", line);
retcode = 1;
}
else
import (src, dst);
free (src);
free (dst);
}
else if (strncmp (line, "export ", 7) == 0)
{
char *src = 0, *dst = 0;
#ifdef _MSC_VER
src = (char*)malloc( 260 + 1 ); //MAX_PATH
dst = (char*)malloc( 260 + 1 );
if ( sscanf( line + 7, " %260s %260s", src, dst ) != 2 )
#else
if (sscanf (line + 7, " %ms %ms", &src, &dst) != 2)
#endif
{
fprintf (stderr, "SYNTAX ERROR: %s\n", line);
retcode = 1;
}
else
export (src, dst);
free (src);
free (dst);
}
else if (strncmp (line, "rm ", 3) == 0)
{
if (SPIFFS_remove (&fs, trim (line + 3)) < 0)
{
fprintf (stderr, "FAILED: %s\n", line);
retcode = 1;
}
}
else if (strncmp (line, "cat ", 4) == 0)
cat (trim (line + 4));
else if (strncmp (line, "info", 4) == 0)
{
u32_t total, used;
if (SPIFFS_info (&fs, &total, &used) < 0)
{
fprintf (stderr, "FAILED: %s\n", line);
retcode = 1;
}
else
printf ("Total: %u, Used: %u\n", total, used);
}
else
{
printf ("SYNTAX ERROR: %s\n", line);
retcode = 1;
}
if (in == stdin)
printf ("> ");
}
if (in == stdin)
printf ("\n");
}
SPIFFS_unmount (&fs);
#ifdef _MSC_VER
if ( lseek( fd, 0, SEEK_SET ) == -1 )
die( "lseek" );
if ( write( fd, flash, sz ) != sz )
die( "write" );
free( flash );
#else
munmap (flash, sz);
#endif
close (fd);
return retcode;
}