forked from jeelabs/esp-link
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathespfs.c
293 lines (257 loc) · 8.13 KB
/
espfs.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/*
This is a simple read-only implementation of a file system. It uses a block of data coming from the
mkespfsimg tool, and can use that block to do abstracted operations on the files that are in there.
It's written for use with httpd, but doesn't need to be used as such.
*/
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <[email protected]> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
//These routines can also be tested by comping them in with the espfstest tool. This
//simplifies debugging, but needs some slightly different headers. The #ifdef takes
//care of that.
#ifdef __ets__
//esp build
#include <esp8266.h>
#else
//Test build
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define os_malloc malloc
#define os_free free
#define os_memcpy memcpy
#define os_memset memset
#define os_strncmp strncmp
#define os_strcmp strcmp
#define os_strcpy strcpy
#define os_printf printf
#define ICACHE_FLASH_ATTR
#endif
#include "espfsformat.h"
#include "espfs.h"
EspFsContext espLinkCtxDef;
EspFsContext userPageCtxDef;
EspFsContext * espLinkCtx = &espLinkCtxDef;
EspFsContext * userPageCtx = &userPageCtxDef;
struct EspFsContext
{
char* data;
EspFsSource source;
uint8_t valid;
};
struct EspFsFile {
EspFsContext *ctx;
EspFsHeader *header;
char decompressor;
int32_t posDecomp;
char *posStart;
char *posComp;
void *decompData;
};
/*
Available locations, at least in my flash, with boundaries partially guessed. This
is using 0.9.1/0.9.2 SDK on a not-too-new module.
0x00000 (0x10000): Code/data (RAM data?)
0x10000 (0x02000): Gets erased by something?
0x12000 (0x2E000): Free (filled with zeroes) (parts used by ESPCloud and maybe SSL)
0x40000 (0x20000): Code/data (ROM data?)
0x60000 (0x1C000): Free
0x7c000 (0x04000): Param store
0x80000 - end of flash
Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All accesses
*must* be aligned 32-bit accesses. Reading a short, byte or unaligned word will result in
a memory exception, crashing the program.
*/
//Copies len bytes over from dst to src, but does it using *only*
//aligned 32-bit reads. Yes, it's no too optimized but it's short and sweet and it works.
//ToDo: perhaps os_memcpy also does unaligned accesses?
#ifdef __ets__
void ICACHE_FLASH_ATTR memcpyAligned(char *dst, const char *src, int len) {
int x;
int w, b;
for (x=0; x<len; x++) {
b=((int)src&3);
w=*((int *)(src-b));
if (b==0) *dst=(w>>0);
if (b==1) *dst=(w>>8);
if (b==2) *dst=(w>>16);
if (b==3) *dst=(w>>24);
dst++; src++;
}
}
#else
#define memcpyAligned memcpy
#endif
void ICACHE_FLASH_ATTR memcpyFromFlash(char *dst, const char *src, int len)
{
if( spi_flash_read( (int)src, (void *)dst, len ) != SPI_FLASH_RESULT_OK )
os_memset( dst, 0, len ); // if read was not successful, reply with zeroes
}
// memcpy on MEMORY/FLASH file systems
void espfs_memcpy( EspFsContext * ctx, void * dest, const void * src, int count )
{
if( ctx->source == ESPFS_MEMORY )
os_memcpy( dest, src, count );
else
memcpyFromFlash(dest, src, count);
}
// aligned memcpy on MEMORY/FLASH file systems
void espfs_memcpyAligned( EspFsContext * ctx, void * dest, const void * src, int count )
{
if( ctx->source == ESPFS_MEMORY )
memcpyAligned(dest, src, count);
else
memcpyFromFlash(dest, src, count);
}
// initializes an EspFs context
EspFsInitResult ICACHE_FLASH_ATTR espFsInit(EspFsContext *ctx, void *flashAddress, EspFsSource source) {
ctx->valid = 0;
ctx->source = source;
// base address must be aligned to 4 bytes
if (((int)flashAddress & 3) != 0) {
return ESPFS_INIT_RESULT_BAD_ALIGN;
}
// check if there is valid header at address
EspFsHeader testHeader;
espfs_memcpy(ctx, &testHeader, flashAddress, sizeof(EspFsHeader));
if (testHeader.magic != ESPFS_MAGIC) {
return ESPFS_INIT_RESULT_NO_IMAGE;
}
ctx->data = (char *)flashAddress;
ctx->valid = 1;
return ESPFS_INIT_RESULT_OK;
}
// Returns flags of opened file.
int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) {
if (fh == NULL) {
#ifdef ESPFS_DBG
os_printf("File handle not ready\n");
#endif
return -1;
}
int8_t flags;
espfs_memcpyAligned(fh->ctx, (char*)&flags, (char*)&fh->header->flags, 1);
return (int)flags;
}
// creates and initializes an iterator over the espfs file system
void ICACHE_FLASH_ATTR espFsIteratorInit(EspFsContext *ctx, EspFsIterator *iterator)
{
if( ctx->data == NULL )
{
iterator->ctx = NULL;
return;
}
iterator->ctx = ctx;
iterator->position = NULL;
}
// moves iterator to the next file on espfs
// returns 1 if iterator move was successful, otherwise 0 (last file)
// iterator->header and iterator->name will contain file information
int ICACHE_FLASH_ATTR espFsIteratorNext(EspFsIterator *iterator)
{
if( iterator->ctx == NULL )
return 0;
char * position = iterator->position;
if( position == NULL )
position = iterator->ctx->data; // first node
else
{
// jump the iterator to the next file
position+=sizeof(EspFsHeader) + iterator->header.nameLen+iterator->header.fileLenComp;
if ((int)position&3) position+=4-((int)position&3); //align to next 32bit val
}
iterator->position = position;
EspFsHeader * hdr = &iterator->header;
espfs_memcpy(iterator->ctx, hdr, position, sizeof(EspFsHeader));
if (hdr->magic!=ESPFS_MAGIC) {
#ifdef ESPFS_DBG
os_printf("Magic mismatch. EspFS image broken.\n");
#endif
return 0;
}
if (hdr->flags&FLAG_LASTFILE) {
//os_printf("End of image.\n");
iterator->ctx = NULL; // invalidate the iterator
return 0;
}
position += sizeof(EspFsHeader);
//Grab the name of the file.
espfs_memcpy(iterator->ctx, iterator->name, position, sizeof(iterator->name));
return 1;
}
//Open a file and return a pointer to the file desc struct.
EspFsFile ICACHE_FLASH_ATTR *espFsOpen(EspFsContext *ctx, char *fileName) {
EspFsIterator it;
espFsIteratorInit(ctx, &it);
if (it.ctx == NULL) {
#ifdef ESPFS_DBG
os_printf("Call espFsInit first!\n");
#endif
return NULL;
}
//Strip initial slashes
while(fileName[0]=='/') fileName++;
//Search the file
while( espFsIteratorNext(&it) )
{
if (os_strcmp(it.name, fileName)==0) {
//Yay, this is the file we need!
EspFsFile * r=(EspFsFile *)os_malloc(sizeof(EspFsFile)); //Alloc file desc mem
//os_printf("Alloc %p[%d]\n", r, sizeof(EspFsFile));
if (r==NULL) return NULL;
r->ctx = ctx;
r->header=(EspFsHeader *)it.position;
r->decompressor=it.header.compression;
r->posComp=it.position + it.header.nameLen + sizeof(EspFsHeader);
r->posStart=it.position + it.header.nameLen + sizeof(EspFsHeader);
r->posDecomp=0;
if (it.header.compression==COMPRESS_NONE) {
r->decompData=NULL;
} else {
#ifdef ESPFS_DBG
os_printf("Invalid compression: %d\n", h.compression);
#endif
return NULL;
}
return r;
}
}
return NULL;
}
//Read len bytes from the given file into buff. Returns the actual amount of bytes read.
int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) {
int flen, fdlen;
if (fh==NULL) return 0;
//Cache file length.
espfs_memcpyAligned(fh->ctx, (char*)&flen, (char*)&fh->header->fileLenComp, 4);
espfs_memcpyAligned(fh->ctx, (char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4);
//Do stuff depending on the way the file is compressed.
if (fh->decompressor==COMPRESS_NONE) {
int toRead;
toRead=flen-(fh->posComp-fh->posStart);
if (len>toRead) len=toRead;
// os_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp);
espfs_memcpyAligned(fh->ctx, buff, fh->posComp, len);
fh->posDecomp+=len;
fh->posComp+=len;
// os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp);
return len;
}
return 0;
}
//Close the file.
void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) {
if (fh==NULL) return;
//os_printf("Freed %p\n", fh);
os_free(fh);
}
// checks if the file system is valid (detect if the content is an espfs image or random data)
int ICACHE_FLASH_ATTR espFsIsValid(EspFsContext *ctx) {
return ctx->valid;
}