blob: 2a7323c1d2d32d079168f238afaa20c04f64d824 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <stdio.h>
#include <inttypes.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include "os/mynewt.h"
#include "../src/nffs_priv.h"
#include <fs/fs.h>
#include <bsp/bsp.h>
#include <nffs/nffs.h>
#include <hal/hal_flash.h>
#include <flash_map/flash_map.h>
#ifdef ARCH_sim
#include <mcu/mcu_sim.h>
#endif
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
struct log nffs_log;
static const char *copy_in_dir;
static const char *progname;
static int print_verbose;
char *file_flash_area;
size_t file_flash_size;
int file_scratch_idx;
#define MAX_AREAS 16
static struct nffs_area_desc area_descs[MAX_AREAS];
int nffs_version;
int force_version;
/** On-disk representation of a version 0 inode (file or directory). */
struct nffs_disk_V0inode {
uint32_t ndi_magic; /* NFFS_INODE_MAGIC */
uint32_t ndi_id; /* Unique object ID. */
uint32_t ndi_seq; /* Sequence number; greater supersedes
lesser. */
uint32_t ndi_parent_id; /* Object ID of parent directory inode. */
uint8_t reserved8;
uint8_t ndi_filename_len; /* Length of filename, in bytes. */
uint16_t ndi_crc16; /* Covers rest of header and filename. */
/* Followed by filename. */
};
#define NFFS_DISK_V0INODE_OFFSET_CRC 18
/** On-disk representation of a version 0 data block. */
struct nffs_disk_V0block {
uint32_t ndb_magic; /* NFFS_BLOCK_MAGIC */
uint32_t ndb_id; /* Unique object ID. */
uint32_t ndb_seq; /* Sequence number; greater supersedes lesser. */
uint32_t ndb_inode_id; /* Object ID of owning inode. */
uint32_t ndb_prev_id; /* Object ID of previous block in file;
NFFS_ID_NONE if this is the first block. */
uint16_t ndb_data_len; /* Length of data contents, in bytes. */
uint16_t ndb_crc16; /* Covers rest of header and data. */
/* Followed by 'ndb_data_len' bytes of data. */
};
#define NFFS_DISK_V0BLOCK_OFFSET_CRC 22
struct nffs_disk_V0object {
int ndo_type;
uint8_t ndo_area_idx;
uint32_t ndo_offset;
union {
struct nffs_disk_V0inode ndo_disk_V0inode;
struct nffs_disk_V0block ndo_disk_V0block;
} ndo_un_V0obj;
};
#define ndo_disk_V0inode ndo_un_V0obj.ndo_disk_V0inode
#define ndo_disk_V0block ndo_un_V0obj.ndo_disk_V0block
static void usage(int rc);
static void
copyfs(FILE *fp)
{
uint32_t dst_addr;
int rc;
int c;
dst_addr = area_descs[0].nad_offset;
while (1) {
c = getc(fp);
if (c == EOF) {
return;
}
rc = hal_flash_write(area_descs[0].nad_flash_id, dst_addr, &c, 1);
assert(rc == 0);
dst_addr++;
}
}
static void
print_inode_entry(struct nffs_inode_entry *inode_entry, int indent)
{
struct nffs_inode inode;
char name[NFFS_FILENAME_MAX_LEN + 1];
uint32_t area_offset;
uint8_t area_idx;
int rc;
rc = nffs_inode_from_entry(&inode, inode_entry);
/*
* Dummy inode
*/
if (rc == FS_ENOENT) {
printf(" DUMMY %d\n", rc);
return;
}
assert(rc == 0);
nffs_flash_loc_expand(inode_entry->nie_hash_entry.nhe_flash_loc,
&area_idx, &area_offset);
rc = nffs_flash_read(area_idx,
area_offset + sizeof (struct nffs_disk_inode),
name, inode.ni_filename_len);
assert(rc == 0);
name[inode.ni_filename_len] = '\0';
printf("%*s%s\n", indent, "", name[0] == '\0' ? "/" : name);
}
static void
process_inode_entry(struct nffs_inode_entry *inode_entry, int indent)
{
struct nffs_inode_entry *child;
print_inode_entry(inode_entry, indent);
if (nffs_hash_id_is_dir(inode_entry->nie_hash_entry.nhe_id)) {
SLIST_FOREACH(child, &inode_entry->nie_child_list, nie_sibling_next) {
process_inode_entry(child, indent + 2);
}
}
}
static int
print_nffs_inode(struct nffs_disk_inode *ndi, int idx, uint32_t off)
{
char filename[128];
int len;
int rc;
memset(filename, 0, sizeof(filename));
len = min(sizeof(filename) - 1, ndi->ndi_filename_len);
rc = nffs_flash_read(idx, off + sizeof(struct nffs_disk_inode),
filename, len);
printf(" %x-%d inode %d/%d",
off, ndi->ndi_filename_len, ndi->ndi_id, ndi->ndi_seq);
if (rc == 0)
printf(" %s\n", filename);
else
printf("\n");
return (sizeof(struct nffs_disk_inode) + ndi->ndi_filename_len);
}
static int
print_nffs_block(struct nffs_disk_block *ndb, int idx, uint32_t off)
{
printf(" %x-%d block %u/%u belongs to %u\n",
off, ndb->ndb_data_len, ndb->ndb_id, ndb->ndb_seq, ndb->ndb_inode_id);
return sizeof(struct nffs_disk_block) + ndb->ndb_data_len;
}
static int
print_nffs_object(int idx, uint32_t off)
{
struct nffs_disk_object dobj;
int rc;
rc = nffs_flash_read(idx, off, &dobj.ndo_un_obj, sizeof(dobj.ndo_un_obj));
assert(rc == 0);
if (nffs_hash_id_is_inode(dobj.ndo_disk_inode.ndi_id)) {
return print_nffs_inode(&dobj.ndo_disk_inode, idx, off);
} else if (nffs_hash_id_is_block(dobj.ndo_disk_inode.ndi_id)) {
return print_nffs_block(&dobj.ndo_disk_block, idx, off);
} else if (dobj.ndo_disk_inode.ndi_id == NFFS_ID_NONE) {
assert(0);
return 0;
} else {
printf(" %x Corruption\n", off);
return 1;
}
}
static void
print_nffs_darea(struct nffs_disk_area *darea)
{
printf("\tdarea: len %d ver %d gc_seq %d id %x\n",
darea->nda_length, darea->nda_ver,
darea->nda_gc_seq, darea->nda_id);
}
static void
print_nffs_area(int idx)
{
struct nffs_area *area;
struct nffs_disk_area darea;
int off;
int rc;
area = &nffs_areas[idx];
rc = nffs_flash_read(idx, 0, &darea, sizeof(darea));
assert(rc == 0);
print_nffs_darea(&darea);
if (!nffs_area_magic_is_set(&darea)) {
printf("Area header corrupt!\n");
return;
}
/*
* XXX Enhance to print but not restore unsupported formats
*/
if (!nffs_area_is_current_version(&darea)) {
printf("Area format is not supported!\n");
return;
}
off = sizeof (struct nffs_disk_area);
while (off < area->na_cur) {
off += print_nffs_object(idx, off);
}
}
static void
print_nffs_areas(void)
{
int i;
struct nffs_area *area;
for (i = 0; i < nffs_num_areas; i++) {
if (nffs_scratch_area_idx == i) {
printf(" sc ");
} else {
printf(" ");
}
area = &nffs_areas[i];
printf("%d: cur:%d id:%d 0x%x-0x%x\n",
i, area->na_cur, area->na_id,
area->na_offset, area->na_offset + area->na_length);
print_nffs_area(i);
}
}
static int
copy_in_file(char *src, char *dst)
{
struct fs_file *nf;
FILE *fp;
int rc;
char data[2048];
int ret = 0;
rc = fs_open(dst, FS_ACCESS_WRITE, &nf);
assert(rc == 0);
fp = fopen(src, "r");
if (!fp) {
perror("fopen()");
assert(0);
}
while ((rc = fread(data, 1, sizeof(data), fp))) {
rc = fs_write(nf, data, rc);
if (rc) {
ret = rc;
break;
}
}
rc = fs_close(nf);
assert(rc == 0);
fclose(fp);
return ret;
}
void
copy_in_directory(const char *src, const char *dst)
{
DIR *dr;
struct dirent *entry;
char src_name[1024];
char dst_name[1024];
int rc;
dr = opendir(src);
if (!dr) {
perror("opendir");
usage(1);
}
while ((entry = readdir(dr))) {
snprintf(src_name, sizeof(src_name), "%s/%s", src, entry->d_name);
snprintf(dst_name, sizeof(dst_name), "%s/%s", dst, entry->d_name);
if (entry->d_type == DT_DIR &&
strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
rc = fs_mkdir(dst_name);
copy_in_directory(src_name, dst_name);
assert(rc == 0);
} else if (entry->d_type == DT_REG) {
printf("Copying %s\n", dst_name);
rc = copy_in_file(src_name, dst_name);
if (rc) {
if (print_verbose) {
printf(" error code %d ", rc);
}
switch (rc) {
case FS_ENOMEM:
printf("out of memory\n");
break;
case FS_EFULL:
printf("disk is full\n");
break;
default:
printf("\n");
}
break;
}
} else {
if (print_verbose) {
printf("Skipping %s\n", src_name);
}
}
}
closedir(dr);
}
static int
file_flash_read(uint32_t addr, void *dst, int byte_cnt)
{
memcpy(dst, (void*)file_flash_area + addr, byte_cnt);
return 0;
}
/*
* Print NFFS V0 structures
* XXX could check for CRC errors
*/
static int
print_nffs_flash_V0inode(struct nffs_area_desc *area, uint32_t off)
{
struct nffs_disk_V0inode ndi;
char filename[128];
int len;
int rc;
rc = file_flash_read(area->nad_offset + off, &ndi, sizeof(ndi));
assert(rc == 0);
assert(nffs_hash_id_is_inode(ndi.ndi_id));
memset(filename, 0, sizeof(filename));
len = min(sizeof(filename) - 1, ndi.ndi_filename_len);
rc = file_flash_read(area->nad_offset + off + sizeof(ndi), filename, len);
filename[len] = '\0';
assert(rc == 0);
printf(" %s off %d id %x flen %d seq %d prnt %x %s\n",
(nffs_hash_id_is_file(ndi.ndi_id) ? "File" :
(nffs_hash_id_is_dir(ndi.ndi_id) ? "Dir" : "???")),
off, ndi.ndi_id, ndi.ndi_filename_len,
ndi.ndi_seq, ndi.ndi_parent_id, filename);
return sizeof(struct nffs_disk_V0inode) + ndi.ndi_filename_len;
}
static int
print_nffs_flash_V0block(struct nffs_area_desc *area, uint32_t off)
{
struct nffs_disk_V0block ndb;
int rc;
rc = file_flash_read(area->nad_offset + off, &ndb, sizeof(ndb));
assert(rc == 0);
assert(nffs_hash_id_is_block(ndb.ndb_id));
assert(!nffs_hash_id_is_inode(ndb.ndb_id));
printf(" Block off %d id %x len %d seq %d prev %x ino %x\n",
off, ndb.ndb_id, ndb.ndb_data_len, ndb.ndb_seq,
ndb.ndb_prev_id, ndb.ndb_inode_id);
return sizeof(struct nffs_disk_V0block) + ndb.ndb_data_len;
}
static int
print_nffs_flash_V0object(struct nffs_area_desc *area, uint32_t off)
{
uint32_t magic;
int rc;
printf("print_nffs_flash_V0object(area:%d off%d)\n", area->nad_flash_id, off);
rc = file_flash_read(area->nad_offset + off, &magic, sizeof magic);
assert(rc == 0);
switch (magic) {
case NFFS_INODE_MAGIC:
return print_nffs_flash_V0inode(area, off);
case NFFS_BLOCK_MAGIC:
return print_nffs_flash_V0block(area, off);
case 0xffffffff:
return area->nad_length;
default:
return 1;
}
}
static int
print_nffs_flash_inode(struct nffs_area_desc *area, uint32_t off)
{
struct nffs_disk_inode ndi;
char filename[128];
int len;
int rc;
uint16_t crc16;
int badcrc = 0;
rc = file_flash_read(area->nad_offset + off, &ndi, sizeof(ndi));
assert(rc == 0);
crc16 = crc16_ccitt(0, (void*)&ndi, NFFS_DISK_INODE_OFFSET_CRC);
memset(filename, 0, sizeof(filename));
len = min(sizeof(filename) - 1, ndi.ndi_filename_len);
rc = file_flash_read(area->nad_offset + off + sizeof(ndi), filename, len);
crc16 = crc16_ccitt(crc16, filename, ndi.ndi_filename_len);
if (crc16 != ndi.ndi_crc16) {
badcrc = 1;
}
printf(" off %x %s id %x flen %d seq %d last %x prnt %x flgs %x %s%s\n",
off,
(nffs_hash_id_is_file(ndi.ndi_id) ? "File" :
nffs_hash_id_is_dir(ndi.ndi_id) ? "Dir" : "???"),
ndi.ndi_id,
ndi.ndi_filename_len,
ndi.ndi_seq,
ndi.ndi_lastblock_id,
ndi.ndi_parent_id,
ndi.ndi_flags,
filename,
badcrc ? " (Bad CRC!)" : "");
return sizeof(ndi) + ndi.ndi_filename_len;
}
static int
print_nffs_flash_block(struct nffs_area_desc *area, uint32_t off)
{
struct nffs_disk_block ndb;
int rc;
uint16_t crc16;
int badcrc = 0;
int dataover = 0;
rc = file_flash_read(area->nad_offset + off, &ndb, sizeof(ndb));
assert(rc == 0);
if (off + ndb.ndb_data_len > area->nad_length) {
dataover++;
} else {
crc16 = crc16_ccitt(0, (void*)&ndb, NFFS_DISK_BLOCK_OFFSET_CRC);
crc16 = crc16_ccitt(crc16,
(void*)(file_flash_area + area->nad_offset + off + sizeof(ndb)),
ndb.ndb_data_len);
if (crc16 != ndb.ndb_crc16) {
badcrc++;
}
}
printf(" off %x Block id %x len %d seq %d prev %x own ino %x%s%s\n",
off,
ndb.ndb_id,
ndb.ndb_data_len,
ndb.ndb_seq,
ndb.ndb_prev_id,
ndb.ndb_inode_id,
dataover ? " (Bad data length)" : "",
badcrc ? " (Bad CRC!)" : "");
if (dataover) {
return 1;
}
return sizeof(ndb) + ndb.ndb_data_len;
}
static int
print_nffs_flash_object(struct nffs_area_desc *area, uint32_t off)
{
struct nffs_disk_inode *ndi;
struct nffs_disk_block *ndb;
ndi = (struct nffs_disk_inode*)(file_flash_area + area->nad_offset + off);
ndb = (struct nffs_disk_block*)ndi;
if (nffs_hash_id_is_inode(ndi->ndi_id)) {
return print_nffs_flash_inode(area, off);
} else if (nffs_hash_id_is_block(ndb->ndb_id)) {
return print_nffs_flash_block(area, off);
} else if (ndb->ndb_id == 0xffffffff) {
return area->nad_length;
} else {
return 1;
}
}
static void
print_nffs_file_flash(char *flash_area, size_t size)
{
char *daptr; /* Disk Area Pointer */
char *eoda; /* End Of Disk Area */
struct nffs_disk_area *nda;
int nad_cnt = 0; /* Nffs Area Descriptor count */
int off;
int objsz;
daptr = flash_area;
eoda = flash_area + size;
printf("\nNFFS Flash Areas:\n");
while (daptr < eoda) {
if (nffs_area_magic_is_set((struct nffs_disk_area*)daptr)) {
nda = (struct nffs_disk_area*)daptr;
area_descs[nad_cnt].nad_offset = (daptr - flash_area);
area_descs[nad_cnt].nad_length = nda->nda_length;
area_descs[nad_cnt].nad_flash_id = nda->nda_id;
if (force_version >= 0) {
nffs_version = force_version;
} else {
nffs_version = nda->nda_ver;
}
if (nda->nda_id == 0xff)
file_scratch_idx = nad_cnt;
printf("Area %d: off %x-%x len %d flshid %x gcseq %d ver %d id %x%s%s\n",
nad_cnt,
area_descs[nad_cnt].nad_offset,
area_descs[nad_cnt].nad_offset +
area_descs[nad_cnt].nad_length,
area_descs[nad_cnt].nad_length,
area_descs[nad_cnt].nad_flash_id,
nda->nda_gc_seq,
nda->nda_ver,
nda->nda_id,
nda->nda_ver != NFFS_AREA_VER ? " (V0)" : "",
nad_cnt == file_scratch_idx ? " (Scratch)" : "");
if (nffs_version == 0) {
objsz = sizeof (struct nffs_disk_V0object);
} else {
objsz = sizeof (struct nffs_disk_object);
}
off = sizeof (struct nffs_disk_area);
while (off + objsz < area_descs[nad_cnt].nad_length) {
if (nffs_version == 0) {
off += print_nffs_flash_V0object(&area_descs[nad_cnt], off);
} else if (nffs_version == NFFS_AREA_VER) {
off += print_nffs_flash_object(&area_descs[nad_cnt], off);
}
}
printf("\n");
nad_cnt++;
daptr = daptr + nda->nda_length;
} else {
daptr++;
}
}
nffs_num_areas = nad_cnt;
}
static void
printfs(void)
{
printf("\nNFFS directory:\n");
process_inode_entry(nffs_root_dir, print_verbose);
printf("\nNFFS areas:\n");
print_nffs_areas();
}
static void
usage(int rc)
{
printf("%s [-v][-c]|[-d dir][-s][-f flash_file]\n", progname);
printf(" Tool for operating on simulator flash image file\n");
printf(" -c: ...\n");
printf(" -v: verbose\n");
printf(" -d: use dir as root for NFFS portion and create flash image\n");
printf(" -f: flash_file is the name of the flash image file\n");
printf(" -s: use flash area layout in flash image file\n");
exit(rc);
}
int
main(int argc, char **argv)
{
FILE *fp;
int fd;
int rc;
int ch;
int cnt;
struct stat st;
int standalone = 0;
progname = argv[0];
force_version = -1;
while ((ch = getopt(argc, argv, "c:d:f:sv01")) != -1) {
switch (ch) {
case 'c':
fp = fopen(optarg, "rb");
assert(fp != NULL);
copyfs(fp);
fclose(fp);
break;
case 'd':
copy_in_dir = optarg;
break;
case 's':
standalone++;
break;
case 'f':
native_flash_file = optarg;
break;
case 'v':
print_verbose++;
break;
case '0':
force_version = 0;
break;
case '1':
force_version = 1;
break;
case '?':
default:
usage(0);
}
}
sysinit();
log_register("nffs-log", &nffs_log, &log_console_handler, NULL, LOG_SYSLEVEL);
file_scratch_idx = MAX_AREAS + 1;
if (standalone) {
fd = open(native_flash_file, O_RDWR);
if ((rc = fstat(fd, &st)))
perror("fstat failed");
file_flash_size = st.st_size;
if ((file_flash_area = mmap(0, (size_t)8192, PROT_READ,
MAP_FILE|MAP_SHARED, fd, 0)) == MAP_FAILED) {
perror("%s mmap failed");
}
print_nffs_file_flash(file_flash_area, file_flash_size);
return 0;
}
rc = nffs_misc_desc_from_flash_area(MYNEWT_VAL(NFFS_FLASH_AREA), &cnt,
area_descs);
assert(rc == 0);
if (copy_in_dir) {
/*
* Build filesystem from contents of directory
*/
rc = nffs_format(area_descs);
assert(rc == 0);
copy_in_directory(copy_in_dir, "");
} else {
rc = nffs_detect(area_descs);
if (rc) {
printf("nffs_detect() failed\n");
exit(0);
}
}
printfs();
return 0;
}