Remove lib/x1000-installer

Carrying this library is somewhat of a maintenance burden because
some Rockbox APIs are mocked or duplicated in the test suite and
thus need to be fixed up when refactoring.

It's also unused & incomplete, so there's no good reason to keep
it in tree any more.

Change-Id: If39c62744b4edc0d81b1b6608ee5df69430e6581
This commit is contained in:
Aidan MacDonald 2022-12-19 21:46:52 +00:00
parent 6e794c9a2d
commit ba010851fa
35 changed files with 0 additions and 3381 deletions

View file

@ -1,5 +0,0 @@
xf_test
fakeNAND.bin
fakeNAND_meta.bin
*.profraw
*.profdata

View file

@ -1,82 +0,0 @@
OBJ = src/xf_error.o \
src/xf_flashmap.o \
src/xf_nandio.o \
src/xf_package.o \
src/xf_stream.o \
src/xf_update.o
LIB = libx1000-installer.a
TOBJ = test_lib/core_alloc.o \
test_lib/fakenand.o \
test_lib/file.o \
test_lib/pathfuncs.o \
test_lib/md5.o \
test_lib/strlcpy.o \
test/main.o \
test/test_flashmap.o \
test/test_stream.o
TBIN = xf_test
# dependency needs to be built manually
MTARINC = -I../microtar/src
MTARLIB = ../microtar/libmicrotar.a
CPPFLAGS = -Iinclude -Itest_lib $(MTARINC) -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=200809L
CFLAGS = -std=c99 -Wall -Wextra
LDFLAGS =
PROFRAW_FILE=$(TBIN).profraw
PROFDATA_FILE=$(TBIN).profdata
export LLVM_PROFILE_FILE=$(PROFRAW_FILE)
ifeq ($(COVERAGE),1)
CC = clang
CFLAGS += -g -Og -fprofile-instr-generate -fcoverage-mapping
LDFLAGS += -fprofile-instr-generate -fcoverage-mapping
else
CFLAGS += -O2
endif
ifeq ($(SANITIZE),1)
CFLAGS += -fsanitize=address -fsanitize=undefined
LDFLAGS += -fsanitize=address -fsanitize=undefined
endif
.PHONY: all
all: $(LIB) $(TBIN)
.PHONY: test
test: $(TBIN)
@./$(TBIN)
.PHONY: cov
cov: $(PROFDATA_FILE)
@llvm-cov report $(TBIN) -instr-profile=$(PROFDATA_FILE)
.PHONY: cov-show
cov-show: $(PROFDATA_FILE)
@llvm-cov show $(TBIN) --use-color -instr-profile=$(PROFDATA_FILE) $(f) | less -R
.PHONY: clean
clean:
rm -f $(LIB) $(OBJ)
rm -f $(TBIN) $(TOBJ)
rm -f $(PROFRAW_FILE) $(PROFDATA_FILE)
rm -f fakeNAND.bin fakeNAND_meta.bin
$(LIB): $(OBJ)
$(AR) rcs $@ $^ >/dev/null
$(TBIN): $(TOBJ) $(LIB) $(MTARLIB)
$(CC) -o $@ $^ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
# use separate rule instead of depending on 'test' to avoid re-running
$(PROFRAW_FILE): $(TBIN)
@./$(TBIN)
$(PROFDATA_FILE): $(PROFRAW_FILE)
@llvm-profdata merge -sparse $(PROFRAW_FILE) -o $(PROFDATA_FILE)

View file

@ -1,6 +0,0 @@
src/xf_error.c
src/xf_flashmap.c
src/xf_nandio.c
src/xf_package.c
src/xf_stream.c
src/xf_update.c

View file

@ -1,43 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _XF_ERROR_H_
#define _XF_ERROR_H_
enum {
XF_E_SUCCESS = 0,
XF_E_IO = -1,
XF_E_LINE_TOO_LONG = -2,
XF_E_FILENAME_TOO_LONG = -3,
XF_E_INT_OVERFLOW = -4,
XF_E_BUF_OVERFLOW = -5,
XF_E_SYNTAX_ERROR = -6,
XF_E_INVALID_PARAMETER = -7,
XF_E_NAND = -8,
XF_E_OUT_OF_MEMORY = -9,
XF_E_OUT_OF_RANGE = -10,
XF_E_VERIFY_FAILED = -11,
XF_E_CANNOT_OPEN_FILE = -12,
};
const char* xf_strerror(int err);
#endif /* _XF_ERROR_H_ */

View file

@ -1,91 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _XF_FLASHMAP_H_
#define _XF_FLASHMAP_H_
#include "xf_stream.h"
#include <stdint.h>
#define XF_MAP_NAMELEN 63
enum {
XF_MAP_HAS_MD5 = 0x01, /* 'md5' field is valid */
XF_MAP_HAS_FILE_LENGTH = 0x02, /* 'file_length' field is valid */
};
struct xf_map {
char name[XF_MAP_NAMELEN+1]; /* file name */
uint8_t md5[16]; /* MD5 sum of file */
uint32_t file_length; /* length of file in bytes */
uint32_t offset; /* offset in flash */
uint32_t length; /* region length in flash, in bytes */
int flags;
};
/* Parse a line with space- or tab-delimited fields of the form
* <name> <md5> <file_length> <offset> <length>
* <name> '-' <offset> <length>
*
* - name can be up to XF_FMAP_NAMELEN characters long
* - md5 is 32 hexadecimal characters (case insensitive)
* - file_length, offset, and length are 32-bit unsigned integers
* and can be given in decimal or (with '0x' prefix) hexadecimal
*
* Parsed data is written to *map. Returns zero on success and
* nonzero on error.
*/
int xf_map_parseline(const char* line, struct xf_map* map);
/* Parse a file calling xf_map_parseline() on each line to populate
* a map of up to 'maxnum' regions. Blank and comment lines are
* ignored (comments start with '#').
*
* Returns the number of regions in the resulting map if the file was
* parsed successfully, or a negative value on error.
*/
int xf_map_parse(struct xf_stream* s, struct xf_map* map, int maxnum);
/* Sort the map so its members are in ascending order with the lowest
* flash offset region first. After sorting, xf_map_validate() is used
* to check for overlapping regions.
*
* The return value is that of xf_map_validate().
*/
int xf_map_sort(struct xf_map* map, int num);
/* Check if the input map is sorted and contains no overlap.
*
* Returns 0 if the map is sorted and contains no overlapping regions,
* -1 if the map isn't sorted, or if an overlapping region is detected,
* the index of the first overlapping region. (A returned index i is
* always positive: the two overlapped entries are map[i] and map[i-1].)
*/
int xf_map_validate(const struct xf_map* map, int num);
/* Write the map to a stream. This does not check that the map is valid.
* Returns the number of bytes written to the stream or a negative value
* on error. The stream may be NULL, in which case the number of bytes
* that would be written are returned (similar to snprintf).
*/
int xf_map_write(struct xf_map* map, int num, struct xf_stream* s);
#endif /* _XF_FLASHMAP_H_ */

View file

@ -1,130 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _XF_NANDIO_H_
#define _XF_NANDIO_H_
#include "nand-x1000.h"
#include <stdint.h>
#include <stddef.h>
enum xf_nandio_mode {
XF_NANDIO_READ = 0,
XF_NANDIO_PROGRAM,
XF_NANDIO_VERIFY,
};
struct xf_nandio {
nand_drv* ndrv;
int nand_err; /* copy of the last NAND error code */
int alloc_handle;
enum xf_nandio_mode mode;
size_t block_size; /* size of a block, in bytes */
size_t page_size; /* size of a page, in bytes */
uint8_t* old_buf; /* contains the 'old' block data already on chip */
uint8_t* new_buf; /* contains the possibly modified 'new' block data */
nand_block_t cur_block; /* address of the current block */
size_t offset_in_block; /* byte offset in the current block */
unsigned block_valid: 1; /* 1 if buffered block data is valid */
};
int xf_nandio_init(struct xf_nandio* nio);
void xf_nandio_destroy(struct xf_nandio* nio);
/** Sets the operational mode, which determines read/write/flush semantics.
*
* - XF_NANDIO_READ: Accesses the chip in read-only mode. Writes are allowed,
* but should not be used. (Writes will modify a temporary buffer but this
* will not alter the flash contents.)
*
* - XF_NANDIO_PROGRAM: Writes are allowed to modify the flash contents.
* Writes within a block are accumulated in a temporary buffer. When
* crossing a block boundary, either by writing past the end the current
* block or by seeking to a new one, the data written to the temporary
* buffer is compared against the current flash contents. If the block
* has been modified, it is erased and any non-blank pages are programmed
* with the new data.
*
* - XF_NANDIO_VERIFY: This mode allows callers to easily check whether the
* flash contents match some expected contents. Callers "write" the expected
* contents as if programming it with XF_NANDIO_PROGRAM. When a block is
* flushed, if the written data doesn't match the block contents, an
* XF_E_VERIFY_FAILED error is returned. The flash contents will not be
* altered in this mode.
*
* \returns XF_E_SUCCESS or a negative error code on failure.
*/
int xf_nandio_set_mode(struct xf_nandio* nio, enum xf_nandio_mode mode);
/** Seek to a given byte offset in the NAND flash.
*
* If the new offset is outside the current block, the current block will
* be automatically flushed. Note this can result in verification or program
* failures as with any other flush.
*
* \returns XF_E_SUCCESS or a negative error code on failure.
*/
int xf_nandio_seek(struct xf_nandio* nio, size_t offset);
/** Read or write a contiguous sequence of bytes from flash.
*
* The read or write starts at the current position and continues for `count`
* bytes. Both reads and writes may cross block boundaries. Modified blocks
* will be flushed automatically if the operation crosses a block boundary.
*
* After a successful read or write, the current position is advanced by
* exactly `count` bytes. After a failure, the position is indeterminate.
*
* \returns XF_E_SUCCESS or a negative error code on failure.
*/
int xf_nandio_read(struct xf_nandio* nio, void* buf, size_t count);
int xf_nandio_write(struct xf_nandio* nio, const void* buf, size_t count);
/** Get a pointer to the block buffer for direct read/write access.
*
* These functions can be used to read or write data without intermediate
* buffers. The caller passes in the amount of data to be transferred in
* `*count`. A pointer to part of the block buffer is returned in `*buf`
* and the number of bytes available in `*buf` is returned in `*count`.
*
* Data at the current position can be read from the returned buffer and
* it may be modified by writing to the buffer. The buffer is only valid
* until the next call to an `xf_nandio` function.
*
* The read/write position is advanced by the returned `*count` on success,
* and is unchanged on failure.
*
* \returns XF_E_SUCCESS or a negative error code on failure.
*/
int xf_nandio_get_buffer(struct xf_nandio* nio, void** buf, size_t* count);
/** Flush the buffered block to ensure all outstanding program or verification
* operations have been performed. This should only be called to ensure the
* final modified block is flushed after you have finished writing all data.
*
* \returns XF_E_SUCCESS or a negative error code on failure.
*/
int xf_nandio_flush(struct xf_nandio* nio);
#endif /* _XF_NANDIO_H_ */

View file

@ -1,65 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _XF_PACKAGE_H_
#define _XF_PACKAGE_H_
/* package format
*
* - bootloader-info.txt (contains a version label and optional metadata)
* - flashmap.txt (describes the flash update map)
* - [package must contain any other files referenced by the flash map]
* - [other files may be present, but are ignored]
*/
#include "xf_flashmap.h"
#include "xf_stream.h"
#include "microtar.h"
struct xf_package {
int alloc_handle;
mtar_t* tar;
struct xf_map* map;
int map_size;
char* metadata;
size_t metadata_len;
};
/** Open an update package
*
* \param pkg Uninitialized package structure
* \param file Name of the package file
* \param dflt_map Default flash map for loading old format packages
* \param dflt_map_size Size of the default flash map
* \returns XF_E_SUCCESS or a negative error code
*/
int xf_package_open_ex(struct xf_package* pkg, const char* file,
const struct xf_map* dflt_map, int dflt_map_size);
/** Close a package which was previously opened successfully */
void xf_package_close(struct xf_package* pkg);
static inline int xf_package_open(struct xf_package* pkg, const char* file)
{
return xf_package_open_ex(pkg, file, NULL, 0);
}
#endif /* _XF_PACKAGE_H_ */

View file

@ -1,64 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _XF_STREAM_H_
#define _XF_STREAM_H_
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h> /* ssize_t */
#include "microtar.h"
struct xf_stream;
struct xf_stream_ops {
off_t(*xget_size)(struct xf_stream* s);
ssize_t(*xread)(struct xf_stream* s, void* buf, size_t len);
ssize_t(*xwrite)(struct xf_stream* s, const void* buf, size_t len);
int(*xclose)(struct xf_stream* s);
};
struct xf_stream {
intptr_t data;
const struct xf_stream_ops* ops;
};
inline size_t xf_stream_get_size(struct xf_stream* s)
{ return s->ops->xget_size(s); }
inline ssize_t xf_stream_read(struct xf_stream* s, void* buf, size_t len)
{ return s->ops->xread(s, buf, len); }
inline ssize_t xf_stream_write(struct xf_stream* s, const void* buf, size_t len)
{ return s->ops->xwrite(s, buf, len); }
inline int xf_stream_close(struct xf_stream* s)
{ return s->ops->xclose(s); }
int xf_open_file(const char* file, int flags, struct xf_stream* s);
int xf_open_tar(mtar_t* mtar, const char* file, struct xf_stream* s);
int xf_create_tar(mtar_t* mtar, const char* file, size_t size, struct xf_stream* s);
/* Utility function needed for a few things */
int xf_stream_read_lines(struct xf_stream* s, char* buf, size_t bufsz,
int(*callback)(int n, char* buf, void* arg), void* arg);
#endif /* _XF_STREAM_H_ */

View file

@ -1,53 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _XF_UPDATE_H_
#define _XF_UPDATE_H_
#include "xf_package.h"
#include "xf_nandio.h"
#include "xf_flashmap.h"
typedef int(*xf_update_open_stream_cb)(void* arg, const char* file,
struct xf_stream* stream);
enum xf_update_mode {
XF_UPDATE,
XF_BACKUP,
XF_VERIFY,
};
/** The main updater entry point
*
* \param mode Operational mode
* \param nio Initialized NAND I/O object.
* \param map Flash map describing what regions to update.
* \param map_size Number of entries in the map.
* \param open_stream Callback used to open a stream for each map entry.
* \param arg Argument passed to the `open_stream` callback.
*
* \returns XF_E_SUCCESS on success or a negative error code on failure.
*/
int xf_updater_run(enum xf_update_mode mode, struct xf_nandio* nio,
struct xf_map* map, int map_size,
xf_update_open_stream_cb open_stream, void* arg);
#endif /* _XF_UPDATE_H_ */

View file

@ -1,42 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "xf_error.h"
const char* xf_strerror(int err)
{
switch(err) {
case XF_E_SUCCESS: return "Success";
case XF_E_IO: return "I/O error";
case XF_E_LINE_TOO_LONG: return "Line too long";
case XF_E_FILENAME_TOO_LONG: return "Filename too long";
case XF_E_INT_OVERFLOW: return "Numeric overflow";
case XF_E_BUF_OVERFLOW: return "Buffer overflowed";
case XF_E_SYNTAX_ERROR: return "Syntax error";
case XF_E_INVALID_PARAMETER: return "Invalid parameter";
case XF_E_NAND: return "NAND flash error";
case XF_E_OUT_OF_MEMORY: return "Out of memory";
case XF_E_OUT_OF_RANGE: return "Out of range";
case XF_E_VERIFY_FAILED: return "Verification failed";
case XF_E_CANNOT_OPEN_FILE: return "Cannot open file";
default: return "Unknown error";
}
}

View file

@ -1,327 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "xf_flashmap.h"
#include "xf_error.h"
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
static int xdigit_to_int(char c)
{
if(c >= 'a' && c <= 'f')
return 10 + (c - 'a');
if(c >= 'A' && c <= 'F')
return 10 + (c - 'A');
if(c >= '0' && c <= '9')
return c - '0';
return -1;
}
static int isfilenamechar(char c)
{
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '$' || c == '%' || c == '\'' || c == '-' || c == '_' ||
c == '@' || c == '~' || c == '`' || c == '!' || c == '(' ||
c == ')' || c == '{' || c == '}' || c == '^' || c == '#' ||
c == '&' || c == '+' || c == ',' || c == ';' || c == '=' ||
c == '[' || c == ']' || c == '.';
}
int xf_map_parseline(const char* line, struct xf_map* map)
{
enum {
s_name,
s_md5,
s_first_num,
s_file_len = s_first_num,
s_offset,
s_length,
s_done,
};
#define skipws() do { while(*line == ' ' || *line == '\t') ++line; } while(0)
#define nextstate() do { ++state; length = 0; ++line; skipws(); } while(0)
int state = s_name;
int length = 0;
int digit_val;
uint32_t int_val;
uint32_t* num_ptr[3] = {&map->file_length, &map->offset, &map->length};
bool has_md5 = true;
skipws();
while(*line && *line != '\n') {
switch(state) {
case s_name:
if(*line == ' ' || *line == '\t') {
nextstate();
continue;
} else if(isfilenamechar(*line)) {
if(length == XF_MAP_NAMELEN)
return XF_E_FILENAME_TOO_LONG;
map->name[length++] = *line++;
map->name[length] = '\0';
continue;
} else {
return XF_E_SYNTAX_ERROR;
}
case s_md5:
if(*line == '-') {
memset(map->md5, 0, 16);
map->file_length = 0;
has_md5 = false;
++line;
} else {
for(int i = 0; i < 16; ++i) {
int_val = 0;
for(int j = 0; j < 2; ++j) {
digit_val = xdigit_to_int(*line++);
if(digit_val < 0)
return XF_E_SYNTAX_ERROR;
int_val <<= 4;
int_val |= digit_val;
}
map->md5[i] = int_val;
}
}
if(*line == ' ' || *line == '\t') {
/* skip file length if md5 is not present */
if(!has_md5)
++state;
nextstate();
continue;
} else {
return XF_E_SYNTAX_ERROR;
}
case s_file_len:
case s_offset:
case s_length:
int_val = 0;
if(*line == '0') {
++line;
if(*line == 'x' || *line == 'X') {
++line;
while((digit_val = xdigit_to_int(*line)) >= 0) {
++line;
if(int_val > UINT32_MAX/16)
return XF_E_INT_OVERFLOW;
int_val *= 16;
int_val |= digit_val;
}
}
} else if(*line >= '1' && *line <= '9') {
do {
if(int_val > UINT32_MAX/10)
return XF_E_INT_OVERFLOW;
int_val *= 10;
digit_val = *line++ - '0';
if(int_val > UINT32_MAX - digit_val)
return XF_E_INT_OVERFLOW;
int_val += digit_val;
} while(*line >= '0' && *line <= '9');
}
*num_ptr[state - s_first_num] = int_val;
if(*line == ' ' || *line == '\t') {
nextstate();
continue;
} else if(state+1 == s_done && *line == '\0') {
/* end of input */
continue;
} else {
return XF_E_SYNTAX_ERROR;
}
case s_done:
if(isspace(*line)) {
line++;
continue; /* swallow trailing spaces, carriage return, etc */
} else
return XF_E_SYNTAX_ERROR;
}
}
#undef skipws
#undef nextstate
/* one last overflow check - ensure mapped range is addressable */
if(map->offset > UINT32_MAX - map->length)
return XF_E_INT_OVERFLOW;
if(has_md5)
map->flags = XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH;
else
map->flags = 0;
return XF_E_SUCCESS;
}
struct map_parse_args {
struct xf_map* map;
int num;
int maxnum;
};
int map_parse_line_cb(int n, char* buf, void* arg)
{
(void)n;
struct map_parse_args* args = arg;
/* skip whitespace */
while(*buf && isspace(*buf))
++buf;
/* ignore comments and blank lines */
if(*buf == '#' || *buf == '\0')
return 0;
struct xf_map dummy_map;
struct xf_map* dst_map;
if(args->num < args->maxnum)
dst_map = &args->map[args->num];
else
dst_map = &dummy_map;
int rc = xf_map_parseline(buf, dst_map);
if(rc)
return rc;
args->num++;
return 0;
}
int xf_map_parse(struct xf_stream* s, struct xf_map* map, int maxnum)
{
char buf[200];
struct map_parse_args args;
args.map = map;
args.num = 0;
args.maxnum = maxnum;
int rc = xf_stream_read_lines(s, buf, sizeof(buf),
map_parse_line_cb, &args);
if(rc < 0)
return rc;
return args.num;
}
static int xf_map_compare(const void* a, const void* b)
{
const struct xf_map* mapA = a;
const struct xf_map* mapB = b;
if(mapA->offset < mapB->offset)
return -1;
else if(mapA->offset == mapB->offset)
return 0;
else
return 1;
}
int xf_map_sort(struct xf_map* map, int num)
{
qsort(map, num, sizeof(struct xf_map), xf_map_compare);
return xf_map_validate(map, num);
}
int xf_map_validate(const struct xf_map* map, int num)
{
for(int i = 1; i < num; ++i)
if(map[i].offset <= map[i-1].offset)
return -1;
for(int i = 1; i < num; ++i)
if(map[i-1].offset + map[i-1].length > map[i].offset)
return i;
return 0;
}
int xf_map_write(struct xf_map* map, int num, struct xf_stream* s)
{
static const char hex[] = "0123456789abcdef";
char buf[200];
char md5str[33];
int total_len = 0;
md5str[32] = '\0';
for(int i = 0; i < num; ++i) {
bool has_md5 = false;
if(map->flags & XF_MAP_HAS_MD5) {
if(!(map->flags & XF_MAP_HAS_FILE_LENGTH))
return XF_E_INVALID_PARAMETER;
has_md5 = true;
for(int j = 0; j < 16; ++j) {
uint8_t byte = map[i].md5[j];
md5str[2*j] = hex[(byte >> 4) & 0xf];
md5str[2*j+1] = hex[byte & 0xf];
}
}
int len;
if(!has_md5) {
len = snprintf(buf, sizeof(buf), "%s - %lx %lu\n",
map[i].name,
(unsigned long)map[i].offset,
(unsigned long)map[i].length);
} else {
len = snprintf(buf, sizeof(buf), "%s %s %lu 0x%lx %lu\n",
map[i].name, md5str,
(unsigned long)map[i].file_length,
(unsigned long)map[i].offset,
(unsigned long)map[i].length);
}
if(len < 0 || (size_t)len >= sizeof(buf))
return XF_E_LINE_TOO_LONG;
if(s) {
int rc = xf_stream_write(s, buf, len);
if(rc != len)
return XF_E_IO;
}
total_len += len;
}
return total_len;
}

View file

@ -1,292 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "xf_nandio.h"
#include "xf_error.h"
#include "core_alloc.h"
#include "system.h"
#include <string.h>
#include <stdbool.h>
int xf_nandio_init(struct xf_nandio* nio)
{
int rc;
memset(nio, 0, sizeof(*nio));
/* open NAND */
nio->ndrv = nand_init();
nand_lock(nio->ndrv);
rc = nand_open(nio->ndrv);
if(rc != NAND_SUCCESS) {
nio->nand_err = rc;
rc = XF_E_NAND;
goto out;
}
/* read chip parameters */
nio->page_size = nio->ndrv->chip->page_size;
nio->block_size = nio->page_size << nio->ndrv->chip->log2_ppb;
/* allocate memory */
size_t alloc_size = 0;
alloc_size += CACHEALIGN_SIZE - 1;
alloc_size += nio->block_size * 2;
nio->alloc_handle = core_alloc_ex("xf_nandio", alloc_size, &buflib_ops_locked);
if(nio->alloc_handle < 0) {
rc = XF_E_OUT_OF_MEMORY;
goto out_nclose;
}
uint8_t* buffer = core_get_data(nio->alloc_handle);
CACHEALIGN_BUFFER(buffer, alloc_size);
nio->old_buf = buffer;
nio->new_buf = &buffer[nio->block_size];
rc = XF_E_SUCCESS;
goto out;
out_nclose:
nand_close(nio->ndrv);
out:
nand_unlock(nio->ndrv);
return rc;
}
void xf_nandio_destroy(struct xf_nandio* nio)
{
nio->alloc_handle = core_free(nio->alloc_handle);
if(nio->ndrv) {
nand_lock(nio->ndrv);
nand_close(nio->ndrv);
nand_unlock(nio->ndrv);
nio->ndrv = NULL;
}
}
static bool is_page_blank(const uint8_t* buf, uint32_t length)
{
for(uint32_t i = 0; i < length; ++i)
if(buf[i] != 0xff)
return false;
return true;
}
static int flush_block(struct xf_nandio* nio, bool invalidate)
{
/* no block, or only reading - flush is a no-op */
if(!nio->block_valid || nio->mode == XF_NANDIO_READ)
return XF_E_SUCCESS;
/* nothing to do if new data is same as old data */
if(!memcmp(nio->old_buf, nio->new_buf, nio->block_size))
return XF_E_SUCCESS;
/* data mismatch during verification - report the error */
if(nio->mode == XF_NANDIO_VERIFY)
return XF_E_VERIFY_FAILED;
/* erase the block */
int rc = nand_block_erase(nio->ndrv, nio->cur_block);
if(rc != NAND_SUCCESS) {
nio->block_valid = false;
nio->nand_err = rc;
return XF_E_NAND;
}
size_t oob_size = nio->ndrv->chip->oob_size;
unsigned page = 0;
nand_page_t page_addr = nio->cur_block;
for(; page < nio->ndrv->ppb; ++page, ++page_addr) {
/* skip programming blank pages to go faster & reduce wear */
uint8_t* page_data = &nio->new_buf[page * nio->page_size];
if(is_page_blank(page_data, nio->page_size))
continue;
/* copy page and write blank OOB data */
memcpy(nio->ndrv->page_buf, page_data, nio->page_size);
memset(&nio->ndrv->page_buf[nio->page_size], 0xff, oob_size);
/* program the page */
rc = nand_page_program(nio->ndrv, page_addr, nio->ndrv->page_buf);
if(rc != NAND_SUCCESS) {
nio->block_valid = false;
nio->nand_err = rc;
return XF_E_NAND;
}
}
if(invalidate)
nio->block_valid = false;
else {
/* update our 'old' buffer so a subsequent flush
* will not reprogram the same block */
memcpy(nio->old_buf, nio->new_buf, nio->block_size);
}
return XF_E_SUCCESS;
}
static int seek_to_block(struct xf_nandio* nio, nand_block_t block_addr,
size_t offset_in_block)
{
/* already on this block? */
if(nio->block_valid && block_addr == nio->cur_block) {
nio->offset_in_block = offset_in_block;
return XF_E_SUCCESS;
}
/* ensure new block is within range */
if(block_addr >= (nio->ndrv->chip->nr_blocks << nio->ndrv->chip->log2_ppb))
return XF_E_OUT_OF_RANGE;
/* flush old block */
int rc = flush_block(nio, true);
if(rc)
return rc;
nio->block_valid = false;
/* read the new block */
unsigned page = 0;
nand_page_t page_addr = block_addr;
for(; page < nio->ndrv->ppb; ++page, ++page_addr) {
rc = nand_page_read(nio->ndrv, page_addr, nio->ndrv->page_buf);
if(rc != NAND_SUCCESS) {
nio->nand_err = rc;
return XF_E_NAND;
}
memcpy(&nio->old_buf[page * nio->page_size], nio->ndrv->page_buf, nio->page_size);
}
/* copy to 2nd buffer */
memcpy(nio->new_buf, nio->old_buf, nio->block_size);
/* update position */
nio->cur_block = block_addr;
nio->offset_in_block = offset_in_block;
nio->block_valid = true;
return XF_E_SUCCESS;
}
int xf_nandio_set_mode(struct xf_nandio* nio, enum xf_nandio_mode mode)
{
nand_lock(nio->ndrv);
/* flush the current block before switching to the new mode,
* to ensure consistency */
int rc = flush_block(nio, false);
if(rc)
goto err;
nio->mode = mode;
rc = XF_E_SUCCESS;
err:
nand_unlock(nio->ndrv);
return rc;
}
static int nandio_rdwr(struct xf_nandio* nio, void* buf, size_t count, bool write)
{
while(count > 0) {
void* ptr;
size_t amount = count;
int rc = xf_nandio_get_buffer(nio, &ptr, &amount);
if(rc)
return rc;
if(write)
memcpy(ptr, buf, amount);
else
memcpy(buf, ptr, amount);
count -= amount;
}
return XF_E_SUCCESS;
}
int xf_nandio_seek(struct xf_nandio* nio, size_t offset)
{
uint32_t block_nr = offset / nio->block_size;
size_t offset_in_block = offset % nio->block_size;
nand_block_t block_addr = block_nr << nio->ndrv->chip->log2_ppb;
nand_lock(nio->ndrv);
int rc = seek_to_block(nio, block_addr, offset_in_block);
nand_unlock(nio->ndrv);
return rc;
}
int xf_nandio_read(struct xf_nandio* nio, void* buf, size_t count)
{
return nandio_rdwr(nio, buf, count, false);
}
int xf_nandio_write(struct xf_nandio* nio, const void* buf, size_t count)
{
return nandio_rdwr(nio, (void*)buf, count, true);
}
int xf_nandio_get_buffer(struct xf_nandio* nio, void** buf, size_t* count)
{
nand_lock(nio->ndrv);
/* make sure the current block data is read in */
int rc = seek_to_block(nio, nio->cur_block, nio->offset_in_block);
if(rc)
goto err;
size_t amount_left = nio->block_size - nio->offset_in_block;
if(amount_left == 0) {
amount_left = nio->block_size;
rc = seek_to_block(nio, nio->cur_block + nio->ndrv->ppb, 0);
if(rc)
goto err;
}
*buf = &nio->new_buf[nio->offset_in_block];
*count = MIN(*count, amount_left);
nio->offset_in_block += *count;
rc = XF_E_SUCCESS;
err:
nand_unlock(nio->ndrv);
return rc;
}
int xf_nandio_flush(struct xf_nandio* nio)
{
nand_lock(nio->ndrv);
int rc = flush_block(nio, false);
nand_unlock(nio->ndrv);
return rc;
}

View file

@ -1,261 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "xf_package.h"
#include "xf_error.h"
#include "pathfuncs.h"
#include "file.h"
#include "core_alloc.h"
#include "md5.h"
#include "system.h"
#include <stdbool.h>
#include <string.h>
#ifdef ROCKBOX
# include "microtar-rockbox.h"
#else
# include "microtar-stdio.h"
#endif
#define METADATA_SIZE 4096 /* size of the metadata buffer */
#define MAX_MAP_SIZE 32 /* maximum number of map entries */
static int pkg_alloc(struct xf_package* pkg)
{
memset(pkg, 0, sizeof(*pkg));
/* calculate allocation size */
size_t alloc_size = 0;
alloc_size += ALIGN_UP_P2(sizeof(mtar_t), 3);
alloc_size += ALIGN_UP_P2(sizeof(struct xf_map) * MAX_MAP_SIZE, 3);
alloc_size += ALIGN_UP_P2(METADATA_SIZE, 3);
alloc_size += 7; /* for alignment */
pkg->alloc_handle = core_alloc_ex("xf_package", alloc_size, &buflib_ops_locked);
if(pkg->alloc_handle < 0)
return XF_E_OUT_OF_MEMORY;
/* distribute memory */
uint8_t* buf = (uint8_t*)core_get_data(pkg->alloc_handle);
memset(buf, 0, alloc_size);
ALIGN_BUFFER(buf, alloc_size, 8);
pkg->tar = (mtar_t*)buf;
buf += ALIGN_UP_P2(sizeof(mtar_t), 3);
pkg->map = (struct xf_map*)buf;
buf += ALIGN_UP_P2(sizeof(struct xf_map) * MAX_MAP_SIZE, 3);
pkg->metadata = (char*)buf;
buf += ALIGN_UP_P2(METADATA_SIZE, 3);
return XF_E_SUCCESS;
}
static int read_meta_line_cb(int n, char* buf, void* arg)
{
struct xf_package* pkg = (struct xf_package*)arg;
size_t length = strlen(buf);
/* skip blank lines and the first line (it's reserved for old format) */
if(n == 0 || length == 0)
return 0;
/* metadata lines require an '=' sign to separate key and value */
if(!strchr(buf, '='))
return XF_E_SYNTAX_ERROR;
/* we need space to copy the key-value pair plus a null terminator */
if(length + 1 >= METADATA_SIZE - pkg->metadata_len)
return XF_E_BUF_OVERFLOW;
memcpy(&pkg->metadata[pkg->metadata_len], buf, length + 1);
pkg->metadata_len += length + 1;
return 0;
}
static int pkg_read_meta(struct xf_package* pkg)
{
struct xf_stream stream;
int rc = xf_open_tar(pkg->tar, "bootloader-info.txt", &stream);
if(rc)
return XF_E_CANNOT_OPEN_FILE;
char buf[200];
rc = xf_stream_read_lines(&stream, buf, sizeof(buf), read_meta_line_cb, pkg);
xf_stream_close(&stream);
return rc;
}
static int pkg_read_map(struct xf_package* pkg,
const struct xf_map* dflt_map, int dflt_map_size)
{
/* Attempt to load and parse the map file */
struct xf_stream stream;
int rc = xf_open_tar(pkg->tar, "flashmap.txt", &stream);
/* If the flash map is absent but a default map has been provided,
* then the update is in the old fixed format. */
if(rc == MTAR_ENOTFOUND && dflt_map) {
if(dflt_map_size > MAX_MAP_SIZE)
return XF_E_INVALID_PARAMETER;
for(int i = 0; i < dflt_map_size; ++i) {
pkg->map[i] = dflt_map[i];
pkg->map[i].flags &= ~(XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH);
}
pkg->map_size = dflt_map_size;
return XF_E_SUCCESS;
}
if(rc != MTAR_ESUCCESS)
return XF_E_CANNOT_OPEN_FILE;
rc = xf_map_parse(&stream, pkg->map, MAX_MAP_SIZE);
if(rc < 0)
goto err;
/* Sort the map; reject it if there is any overlap. */
pkg->map_size = rc;
if(xf_map_sort(pkg->map, pkg->map_size)) {
rc = XF_E_INVALID_PARAMETER;
goto err;
}
/* All packages in the 'new' format are required to have MD5 sums. */
for(int i = 0; i < pkg->map_size; ++i) {
if(!(pkg->map[i].flags & XF_MAP_HAS_MD5) ||
!(pkg->map[i].flags & XF_MAP_HAS_FILE_LENGTH)) {
rc = XF_E_VERIFY_FAILED;
goto err;
}
}
rc = XF_E_SUCCESS;
err:
xf_stream_close(&stream);
return rc;
}
static int pkg_verify(struct xf_package* pkg)
{
struct xf_stream stream;
md5_context ctx;
uint8_t buffer[128];
for(int i = 0; i < pkg->map_size; ++i) {
/* At a bare minimum, check that the file exists. */
int rc = xf_open_tar(pkg->tar, pkg->map[i].name, &stream);
if(rc)
return XF_E_VERIFY_FAILED;
/* Also check that it isn't bigger than the update region.
* That would normally indicate a problem. */
off_t streamsize = xf_stream_get_size(&stream);
if(streamsize > (off_t)pkg->map[i].length) {
rc = XF_E_VERIFY_FAILED;
goto err;
}
/* Check against the listed file length. */
if(pkg->map[i].flags & XF_MAP_HAS_FILE_LENGTH) {
if(streamsize != (off_t)pkg->map[i].file_length) {
rc = XF_E_VERIFY_FAILED;
goto err;
}
}
/* Check the MD5 sum if we have it. */
if(pkg->map[i].flags & XF_MAP_HAS_MD5) {
md5_starts(&ctx);
while(1) {
ssize_t n = xf_stream_read(&stream, buffer, sizeof(buffer));
if(n < 0) {
rc = XF_E_IO;
goto err;
}
md5_update(&ctx, buffer, n);
if((size_t)n < sizeof(buffer))
break;
}
md5_finish(&ctx, buffer);
if(memcpy(buffer, pkg->map[i].md5, 16)) {
rc = XF_E_VERIFY_FAILED;
goto err;
}
}
err:
xf_stream_close(&stream);
if(rc)
return rc;
}
/* All files passed verification */
return XF_E_SUCCESS;
}
int xf_package_open_ex(struct xf_package* pkg, const char* file,
const struct xf_map* dflt_map, int dflt_map_size)
{
int rc = pkg_alloc(pkg);
if(rc)
return rc;
#ifdef ROCKBOX
rc = mtar_open(pkg->tar, file, O_RDONLY);
#else
rc = mtar_open(pkg->tar, file, "r");
#endif
if(rc != MTAR_ESUCCESS) {
rc = XF_E_CANNOT_OPEN_FILE;
goto err;
}
rc = pkg_read_meta(pkg);
if(rc)
goto err;
rc = pkg_read_map(pkg, dflt_map, dflt_map_size);
if(rc)
goto err;
rc = pkg_verify(pkg);
if(rc)
goto err;
err:
if(rc)
xf_package_close(pkg);
return rc;
}
void xf_package_close(struct xf_package* pkg)
{
if(mtar_is_open(pkg->tar))
mtar_close(pkg->tar);
pkg->alloc_handle = core_free(pkg->alloc_handle);
}

View file

@ -1,211 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "xf_stream.h"
#include "xf_error.h"
#include "file.h"
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
/*
* File streams
*/
static off_t file_stream_get_size(struct xf_stream* s)
{
return filesize(s->data);
}
static ssize_t file_stream_read(struct xf_stream* s, void* buf, size_t len)
{
return read(s->data, buf, len);
}
static ssize_t file_stream_write(struct xf_stream* s, const void* buf, size_t len)
{
return write(s->data, buf, len);
}
static int file_stream_close(struct xf_stream* s)
{
return close(s->data);
}
static const struct xf_stream_ops file_stream_ops = {
.xget_size = file_stream_get_size,
.xread = file_stream_read,
.xwrite = file_stream_write,
.xclose = file_stream_close,
};
int xf_open_file(const char* file, int flags, struct xf_stream* s)
{
s->data = open(file, flags, 0666);
s->ops = &file_stream_ops;
return (s->data >= 0) ? 0 : -1;
}
/*
* Tar streams
*/
static off_t tar_stream_get_size(struct xf_stream* s)
{
mtar_t* mtar = (mtar_t*)s->data;
return mtar_get_header(mtar)->size;
}
static ssize_t tar_stream_read(struct xf_stream* s, void* buffer, size_t count)
{
mtar_t* mtar = (mtar_t*)s->data;
int ret = mtar_read_data(mtar, buffer, count);
if(ret < 0)
return -1;
return ret;
}
static ssize_t tar_stream_write(struct xf_stream* s, const void* buffer, size_t count)
{
mtar_t* mtar = (mtar_t*)s->data;
int ret = mtar_write_data(mtar, buffer, count);
if(ret < 0)
return -1;
return ret;
}
static int tar_stream_close(struct xf_stream* s)
{
mtar_t* mtar = (mtar_t*)s->data;
if(mtar_access_mode(mtar) == MTAR_WRITE) {
if(mtar_update_file_size(mtar) != MTAR_ESUCCESS)
return -1;
if(mtar_end_data(mtar) != MTAR_ESUCCESS)
return -1;
}
return 0;
}
static const struct xf_stream_ops tar_stream_ops = {
.xget_size = tar_stream_get_size,
.xread = tar_stream_read,
.xwrite = tar_stream_write,
.xclose = tar_stream_close,
};
int xf_open_tar(mtar_t* mtar, const char* file, struct xf_stream* s)
{
int err = mtar_find(mtar, file);
if(err != MTAR_ESUCCESS)
return err;
/* must only read normal files */
const mtar_header_t* h = mtar_get_header(mtar);
if(h->type != 0 && h->type != MTAR_TREG)
return MTAR_EFAILURE;
s->data = (intptr_t)mtar;
s->ops = &tar_stream_ops;
return MTAR_ESUCCESS;
}
int xf_create_tar(mtar_t* mtar, const char* file, size_t size, struct xf_stream* s)
{
int err = mtar_write_file_header(mtar, file, size);
if(err)
return err;
s->data = (intptr_t)mtar;
s->ops = &tar_stream_ops;
return MTAR_ESUCCESS;
}
/*
* Utility functions
*/
int xf_stream_read_lines(struct xf_stream* s, char* buf, size_t bufsz,
int(*callback)(int n, char* buf, void* arg), void* arg)
{
char* startp, *endp;
ssize_t bytes_read;
int rc;
int n = 0;
size_t pos = 0;
bool at_eof = false;
if(bufsz <= 1)
return XF_E_LINE_TOO_LONG;
while(!at_eof) {
bytes_read = xf_stream_read(s, &buf[pos], bufsz - pos - 1);
if(bytes_read < 0)
return XF_E_IO;
/* short read is end of file */
if((size_t)bytes_read < bufsz - pos - 1)
at_eof = true;
pos += bytes_read;
buf[pos] = '\0';
startp = endp = buf;
while(endp != &buf[pos]) {
endp = strchr(startp, '\n');
if(endp) {
*endp = '\0';
} else {
if(!at_eof) {
if(startp == buf)
return XF_E_LINE_TOO_LONG;
else
break; /* read ahead to look for newline */
} else {
if(startp == &buf[pos])
break; /* nothing left to do */
else
endp = &buf[pos]; /* last line missing a newline -
* treat EOF as newline */
}
}
rc = callback(n++, startp, arg);
if(rc != 0)
return rc;
startp = endp + 1;
}
if(startp <= &buf[pos]) {
memmove(buf, startp, &buf[pos] - startp);
pos = &buf[pos] - startp;
}
}
return 0;
}

View file

@ -1,149 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "xf_update.h"
#include "xf_error.h"
#include "file.h"
#include "md5.h"
#include <string.h>
static int process_stream(struct xf_nandio* nio,
struct xf_map* map,
struct xf_stream* stream)
{
void* buffer;
size_t count;
int rc;
/* calculate MD5 on the fly if taking a backup */
md5_context md5_ctx;
if(nio->mode == XF_NANDIO_READ)
md5_starts(&md5_ctx);
/* first deal with the file data */
size_t bytes_left = map->length;
while(bytes_left > 0) {
count = bytes_left;
rc = xf_nandio_get_buffer(nio, &buffer, &count);
if(rc)
return rc;
if(nio->mode == XF_NANDIO_READ) {
md5_update(&md5_ctx, buffer, count);
rc = xf_stream_write(stream, buffer, count);
} else {
rc = xf_stream_read(stream, buffer, count);
}
bytes_left -= count;
if(rc < 0 || (size_t)rc > count)
return XF_E_IO;
if((size_t)rc < count) {
/* backup - we could not write all the data */
if(nio->mode == XF_NANDIO_READ)
return XF_E_IO;
/* update - clear rest of buffer to 0xff */
memset(buffer + rc, 0xff, count - rc);
break;
}
}
/* if updating - write blanks to the remainder of the region */
while(bytes_left > 0) {
count = bytes_left;
rc = xf_nandio_get_buffer(nio, &buffer, &count);
if(rc)
return rc;
memset(buffer, 0xff, count);
bytes_left -= count;
}
/* finalize the MD5 sum */
if(nio->mode == XF_NANDIO_READ) {
md5_finish(&md5_ctx, map->md5);
map->file_length = map->length;
map->flags |= XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH;
}
return XF_E_SUCCESS;
}
static int process_map(struct xf_nandio* nio, struct xf_map* map, int map_size,
xf_update_open_stream_cb open_stream, void* os_arg)
{
int rc, rc2;
struct xf_stream stream;
/* ensure the map is sequential and non-overlapping before continuing */
if(xf_map_validate(map, map_size) != 0)
return XF_E_INVALID_PARAMETER;
for(int i = 0; i < map_size; ++i) {
/* seek to initial offset */
rc = xf_nandio_seek(nio, map[i].offset);
if(rc)
return rc;
rc = open_stream(os_arg, map[i].name, &stream);
if(rc)
return XF_E_CANNOT_OPEN_FILE;
/* process the stream and be sure to close it even on error */
rc = process_stream(nio, &map[i], &stream);
rc2 = xf_stream_close(&stream);
/* bail if either operation raised an error */
if(rc)
return rc;
if(rc2)
return rc2;
}
/* now flush to ensure all data was written */
rc = xf_nandio_flush(nio);
if(rc)
return rc;
return XF_E_SUCCESS;
}
static const enum xf_nandio_mode update_mode_to_nandio[] = {
[XF_UPDATE] = XF_NANDIO_PROGRAM,
[XF_BACKUP] = XF_NANDIO_READ,
[XF_VERIFY] = XF_NANDIO_VERIFY,
};
int xf_updater_run(enum xf_update_mode mode, struct xf_nandio* nio,
struct xf_map* map, int map_size,
xf_update_open_stream_cb open_stream, void* arg)
{
/* Switch NAND I/O into the correct mode */
int rc = xf_nandio_set_mode(nio, update_mode_to_nandio[mode]);
if(rc)
return rc;
/* This does all the real work */
return process_map(nio, map, map_size, open_stream, arg);
}

View file

@ -1,108 +0,0 @@
1234567890abcdefghijklmnopqrstuvw
1234567890abcdefghijklmnopqrst
123
1234567890abcdef
1234567890abcdefghijklm
1234567890abcdefghijklmn
1234567890abcdefghijk
1234567890abcdefgh
123456
1234567
1234567890a
1234567890abcdefghijklm
1
1234567890abcdefghijklmnopq
1234567890abcdefghijkl
1234567890abcdefghijklmno
1234567890abcdefghijklmnopqrstuvwxyz
1234567890abcdefghijklmnopq
1234567890abcdefghijklmno
1234567890abcdefghijklmnopq
1234567890abcdefghijklmn
1234
1234567890abcdefghijklmnop
123456789
1234
12345
1234567890ab
1234567890abcdefghijklmnopqrstuvwxy
1234567890abcdefghijklmnopqrst
1234567890abcdefghijklmnopqrs
1234567890abcdefghijklmnopqrs
12345678
1234567890ab
1234567890a
1234567890abcdefghijklmnopqr
123456
1234567890abcdefghijkl
1234567890abcdefghijklm
1234567890abcdefghijklmnopqrstuvwxy
1234567890abcdefghijklmnopqrstuvwxy
1234567890abcdefghijklmnopqrstuv
1234567890abcdefghijklmnopqrstuv
1234567890abcdefghijklmnopqrstuvwx
1234567890abcde
1
1234567890abcdefgh
1234567890abcdef
1234567890abcdefghi
1234567890
1
12345
123456789
1234567
1234567890abcdefghi
1234567890abcdefghijklmnopqrstu
1234567890abcdefghijklmnopqrstuvwx
1234567890abcdefghijklmnopqrstuv
1234567890abcd
12345
1234567890abcdefg
1234567890abcd
1234567890abcdefghijklmnopqrstuvw
1234567890abcdefghijklmnopqrstuvwxyz
12345678
1234567890abcd
1234567890abcdefghijklmnop
123
1234567890abcdefg
123456
1234567890abcdefghij
1234567890abcdefg
1234567890abcdefghi
1234567890
1234567890abcdefghijklmno
1234567890abcdefghij
1234567890abcdefghijklmn
1234567890abcde
1234567890abc
123456789
1234567890abcdefgh
1234567890abcdefghij
1234567890abcdefghijklmnopqrstuvwx
1234567890abcdefghijklmnopqr
1234567890abcdefghijklmnopqrst
1234567890abcdefghijklmnopqrs
1234567890abcdefghijklmnopqrstuvwxyz
1234567890abcdefghijkl
1234
12
12
12345678
1234567
1234567890
123
1234567890abc
1234567890abcdefghijklmnopqr
12
1234567890abcde
1234567890abcdef
1234567890a
1234567890ab
1234567890abcdefghijk
1234567890abcdefghijklmnopqrstu
1234567890abcdefghijklmnop
1234567890abcdefghijk
1234567890abcdefghijklmnopqrstu
1234567890abcdefghijklmnopqrstuvw
1234567890abc

View file

@ -1,36 +0,0 @@
1
12
123
1234
12345
123456
1234567
12345678
123456789
1234567890
1234567890a
1234567890ab
1234567890abc
1234567890abcd
1234567890abcde
1234567890abcdef
1234567890abcdefg
1234567890abcdefgh
1234567890abcdefghi
1234567890abcdefghij
1234567890abcdefghijk
1234567890abcdefghijkl
1234567890abcdefghijklm
1234567890abcdefghijklmn
1234567890abcdefghijklmno
1234567890abcdefghijklmnop
1234567890abcdefghijklmnopq
1234567890abcdefghijklmnopqr
1234567890abcdefghijklmnopqrs
1234567890abcdefghijklmnopqrst
1234567890abcdefghijklmnopqrstu
1234567890abcdefghijklmnopqrstuv
1234567890abcdefghijklmnopqrstuvw
1234567890abcdefghijklmnopqrstuvwx
1234567890abcdefghijklmnopqrstuvwxy
1234567890abcdefghijklmnopqrstuvwxyz

View file

@ -1,86 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
static int test_num_executed = 0;
static int test_num_failed = 0;
int test_num_asserts_executed = 0;
int test_num_asserts_failed = 0;
void test_failure(const char* file, int line, const char* msg)
{
fprintf(stderr, "%s:%d: ASSERTION FAILED: %s\n", file, line, msg);
++test_num_asserts_failed;
}
typedef void(*test_t)(void);
struct test_info {
const char* name;
test_t func;
};
extern void test_stream_read_lines(void);
extern void test_stream_read_line_too_long(void);
extern void test_flashmap_parseline(void);
#define TEST(x) {#x, x}
static const struct test_info all_tests[] = {
TEST(test_stream_read_lines),
TEST(test_stream_read_line_too_long),
TEST(test_flashmap_parseline),
};
#undef TEST
void run_test(const struct test_info* tinfo)
{
int asserts_now = test_num_asserts_failed;
++test_num_executed;
fprintf(stderr, "RUN %s\n", tinfo->name);
tinfo->func();
if(test_num_asserts_failed > asserts_now) {
fprintf(stderr, " %s: FAILED!\n", tinfo->name);
++test_num_failed;
}
}
int main(int argc, char* argv[])
{
(void)argc;
(void)argv;
size_t num_tests = sizeof(all_tests) / sizeof(struct test_info);
for(size_t i = 0; i < num_tests; ++i)
run_test(&all_tests[i]);
fprintf(stderr, "------------------------------------------\n");
fprintf(stderr, "TEST COMPLETE\n");
fprintf(stderr, " Tests %d failed / %d executed\n",
test_num_failed, test_num_executed);
fprintf(stderr, " Assertions %d failed / %d executed\n",
test_num_asserts_failed, test_num_asserts_executed);
if(test_num_failed > 0)
return 1;
}

View file

@ -1,39 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef TEST_H
#define TEST_H
extern int test_num_asserts_executed;
extern int test_num_asserts_failed;
extern void test_failure(const char* file, int line, const char* msg);
#define T_ASSERT(cond) \
do { \
++test_num_asserts_executed; \
if(!(cond)) { \
test_failure(__FILE__, __LINE__, #cond); \
goto cleanup; \
} \
} while(0)
#endif

View file

@ -1,140 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "test.h"
#include "xf_stream.h"
#include "xf_flashmap.h"
#include "xf_error.h"
#include "file.h"
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#define MD5SUM \
"abcdef012345ABCDEF012345abcdef01"
#define MD5SUM_L \
"abcdef012345abcdef012345abcdef01"
#define LONG_NAME \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
#define OVERLONG_NAME \
LONG_NAME "a"
static const struct parseline_test {
const char* line;
int rc;
const char* name;
const char* md5;
uint32_t file_length;
uint32_t offset;
uint32_t length;
int flags;
} parseline_tests[] = {
{"test1 " MD5SUM " 1234 0x9876 42",
XF_E_SUCCESS, "test1", MD5SUM_L, 1234, 0x9876, 42,
XF_MAP_HAS_FILE_LENGTH|XF_MAP_HAS_MD5},
{" test1 " MD5SUM " 1234 0x9876 42",
XF_E_SUCCESS, "test1", MD5SUM_L, 1234, 0x9876, 42,
XF_MAP_HAS_FILE_LENGTH|XF_MAP_HAS_MD5},
{"test1 " MD5SUM " 1234 0x9876 42 \r",
XF_E_SUCCESS, "test1", MD5SUM_L, 1234, 0x9876, 42,
XF_MAP_HAS_FILE_LENGTH|XF_MAP_HAS_MD5},
{"test1 " MD5SUM " 1234 0x9876 42 # junk",
XF_E_SYNTAX_ERROR, NULL, NULL, 0, 0, 0, 0},
{OVERLONG_NAME,
XF_E_FILENAME_TOO_LONG, NULL, NULL, 0, 0, 0, 0},
{LONG_NAME " " MD5SUM " 1234 0x9876 42",
XF_E_SUCCESS, LONG_NAME, MD5SUM_L, 1234, 0x9876, 42,
XF_MAP_HAS_FILE_LENGTH|XF_MAP_HAS_MD5},
{"test1 " MD5SUM " 4294967295 0 0xffffffff",
XF_E_SUCCESS, "test1", MD5SUM_L, 4294967295, 0, 0xfffffffful,
XF_MAP_HAS_FILE_LENGTH|XF_MAP_HAS_MD5},
{"test1 " MD5SUM " 4294967295 1 0xffffffff",
XF_E_INT_OVERFLOW, NULL, NULL, 0, 0, 0, 0},
{"test1 " MD5SUM " 4294967296 0 0xffffffff",
XF_E_INT_OVERFLOW, NULL, NULL, 0, 0, 0, 0},
{"test1 " MD5SUM " 100294967295 0 0xffffffff",
XF_E_INT_OVERFLOW, NULL, NULL, 0, 0, 0, 0},
{"test1 " MD5SUM " 0 0 0xffffffff0",
XF_E_INT_OVERFLOW, NULL, NULL, 0, 0, 0, 0},
{"test1 " MD5SUM " 0 0 0x100000000",
XF_E_INT_OVERFLOW, NULL, NULL, 0, 0, 0, 0},
{"test1 " MD5SUM " 0 0 0x100000001",
XF_E_INT_OVERFLOW, NULL, NULL, 0, 0, 0, 0},
{"<",
XF_E_SYNTAX_ERROR, NULL, NULL, 0, 0, 0, 0},
{"test1 abcXdef",
XF_E_SYNTAX_ERROR, NULL, NULL, 0, 0, 0, 0},
{"test1 " MD5SUM "0",
XF_E_SYNTAX_ERROR, NULL, NULL, 0, 0, 0, 0},
{"test1 - 4567 0xabcd",
XF_E_SUCCESS, "test1", NULL, 0, 4567, 0xabcd, 0},
{"test1 - 0 0x123456789",
XF_E_INT_OVERFLOW, NULL, NULL, 0, 0, 0, 0},
{"test1 - 4f",
XF_E_SYNTAX_ERROR, NULL, NULL, 0, 0, 0, 0},
};
static void md5_to_string(char* buf, const uint8_t* md5)
{
const char* hex = "0123456789abcdef";
for(int i = 0; i < 16; ++i) {
uint8_t byte = md5[i];
buf[2*i] = hex[(byte >> 4) & 0xf];
buf[2*i+1] = hex[byte & 0xf];
}
buf[32] = '\0';
}
int test_flashmap_parseline(void)
{
int rc;
struct xf_map map;
char md5string[33];
size_t num_cases = sizeof(parseline_tests) / sizeof(struct parseline_test);
const struct parseline_test* test = &parseline_tests[0];
for(; num_cases > 0; --num_cases, ++test) {
rc = xf_map_parseline(test->line, &map);
T_ASSERT(rc == test->rc);
if(rc != XF_E_SUCCESS)
continue;
T_ASSERT(strcmp(map.name, test->name) == 0);
T_ASSERT(map.offset == test->offset);
T_ASSERT(map.length == test->length);
T_ASSERT(map.flags == test->flags);
if(test->flags & XF_MAP_HAS_MD5) {
md5_to_string(md5string, map.md5);
T_ASSERT(strcmp(md5string, test->md5) == 0);
}
if(test->flags & XF_MAP_HAS_FILE_LENGTH) {
T_ASSERT(map.file_length == test->file_length);
}
}
cleanup:
return rc;
}

View file

@ -1,108 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "test.h"
#include "xf_stream.h"
#include "xf_error.h"
#include "file.h"
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#define TESTDATADIR "test/data/"
#define TESTFILE_SHUFFLED TESTDATADIR "lines_shuffled.txt"
#define TESTFILE_SORTED TESTDATADIR "lines_sorted.txt"
const char line_string[] = "1234567890abcdefghijklmnopqrstuvwxyz";
static int read_line_cb(int n, char* buf, void* arg)
{
int* max_n = (int*)arg;
*max_n = n;
T_ASSERT(strlen(buf) > 0);
T_ASSERT(strncmp(line_string, buf, strlen(buf)) == 0);
cleanup:
return 0;
}
void test_stream_read_lines(void)
{
struct xf_stream s;
int rc;
char buf[100];
bool s_open = false;
for(int bufsz = 38; bufsz <= 100; bufsz += 1) {
rc = xf_open_file(TESTFILE_SHUFFLED, O_RDONLY, &s);
T_ASSERT(rc == 0);
s_open = true;
int max_n = 0;
rc = xf_stream_read_lines(&s, buf, bufsz, &read_line_cb, &max_n);
T_ASSERT(rc == 0);
T_ASSERT(max_n+1 == 108);
xf_stream_close(&s);
s_open = false;
}
cleanup:
if(s_open)
xf_stream_close(&s);
return;
}
void test_stream_read_line_too_long(void)
{
struct xf_stream s;
int rc;
char buf[38];
bool s_open = false;
for(int bufsz = 0; bufsz <= 38; bufsz += 1) {
rc = xf_open_file(TESTFILE_SORTED, O_RDONLY, &s);
T_ASSERT(rc == 0);
s_open = true;
int max_n = -1;
rc = xf_stream_read_lines(&s, buf, bufsz, &read_line_cb, &max_n);
if(bufsz == 38) {
T_ASSERT(rc == 0);
T_ASSERT(max_n+1 == 36);
} else {
T_ASSERT(rc == XF_E_LINE_TOO_LONG);
if(bufsz <= 1)
T_ASSERT(max_n == -1);
else
T_ASSERT(max_n+1 == bufsz-2);
}
xf_stream_close(&s);
s_open = false;
}
cleanup:
if(s_open)
xf_stream_close(&s);
return;
}

View file

@ -1,72 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "core_alloc.h"
#include <stdlib.h>
#define N_POINTERS 100
static void* pointers[N_POINTERS];
struct buflib_callbacks buflib_ops_locked = {NULL, NULL, NULL};
int core_alloc(const char* name, size_t size)
{
(void)name;
void* mem = malloc(size);
if(!mem)
return -1;
for(int i = 0; i < N_POINTERS; ++i) {
if(pointers[i])
continue;
pointers[i] = mem;
return i + 1;
}
free(mem);
return -1;
}
int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks* cb)
{
(void)cb;
return core_alloc(name, size);
}
int core_free(int handle)
{
if(handle > 0) {
free(pointers[handle-1]);
pointers[handle-1] = NULL;
}
return 0;
}
void* core_get_data(int handle)
{
if(handle > 0)
return pointers[handle-1];
return NULL;
}

View file

@ -1,43 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/* fake core_alloc implementation for testing */
#ifndef CORE_ALLOC_H
#define CORE_ALLOC_H
#include <stddef.h>
#include <stdbool.h>
struct buflib_callbacks {
int (*move_callback)(int handle, void* current, void* new);
int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size);
void (*sync_callback)(int handle, bool sync_on);
};
extern struct buflib_callbacks buflib_ops_locked;
int core_alloc(const char* name, size_t size);
int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks* cb);
int core_free(int handle);
void* core_get_data(int handle);
#endif

View file

@ -1,270 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "nand-x1000.h"
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
const char* nand_backing_file = "fakeNAND.bin";
const char* nand_meta_file = "fakeNAND_meta.bin";
struct nand_trace* nand_trace = NULL;
size_t nand_trace_capacity = 0;
size_t nand_trace_length = 0;
static struct nand_trace* nand_trace_cur = NULL;
static int injected_err = 0;
#define METAF_PROGRAMMED 1
static const nand_chip fake_chip = {
/* ATO25D1GA */
.log2_ppb = 6, /* 64 pages */
.page_size = 2048,
.oob_size = 64,
.nr_blocks = 1024,
};
void nand_trace_reset(size_t size)
{
nand_trace = realloc(nand_trace, size);
nand_trace_capacity = size;
nand_trace_length = 0;
nand_trace_cur = nand_trace;
}
void nand_inject_error(int rc)
{
injected_err = rc;
}
nand_drv* nand_init(void)
{
static bool inited = false;
static uint8_t scratch_buf[NAND_DRV_SCRATCHSIZE];
static uint8_t page_buf[NAND_DRV_MAXPAGESIZE];
static nand_drv d;
if(!inited) {
d.scratch_buf = scratch_buf;
d.page_buf = page_buf;
d.chip = &fake_chip;
inited = true;
}
return &d;
}
static void lock_assert(bool cond, const char* msg)
{
if(!cond) {
fprintf(stderr, "%s\n", msg);
fflush(stderr);
abort();
}
}
void nand_lock(nand_drv* drv)
{
drv->lock_count++;
}
void nand_unlock(nand_drv* drv)
{
lock_assert(drv->lock_count > 0, "nand_unlock() called when not locked");
drv->lock_count--;
}
#define CHECK_INJECTED_ERROR \
do { int __err = injected_err; injected_err = 0; if(__err) return __err; } while(0)
int nand_open(nand_drv* drv)
{
lock_assert(drv->lock_count > 0, "nand_open(): lock not held");
CHECK_INJECTED_ERROR;
if(drv->refcount > 0) {
drv->refcount++;
return NAND_SUCCESS;
}
/* leaks an fd on error but this is only testing... */
drv->fd = open(nand_backing_file, O_RDWR|O_CREAT, 0644);
drv->metafd = open(nand_meta_file, O_RDWR|O_CREAT, 0644);
if(drv->fd < 0 || drv->metafd < 0)
goto err;
drv->ppb = 1 << drv->chip->log2_ppb;
drv->fpage_size = drv->chip->page_size + drv->chip->oob_size;
/* make backing file the correct size */
if(ftruncate(drv->fd, drv->chip->page_size * drv->ppb * drv->chip->nr_blocks) < 0)
goto err;
if(ftruncate(drv->metafd, drv->chip->nr_blocks * drv->ppb) < 0)
goto err;
drv->refcount++;
return NAND_SUCCESS;
err:
if(drv->fd >= 0)
close(drv->fd);
if(drv->metafd >= 0)
close(drv->metafd);
return NAND_ERR_OTHER;
}
void nand_close(nand_drv* drv)
{
lock_assert(drv->lock_count > 0, "nand_close(): lock not held");
if(--drv->refcount > 0)
return;
close(drv->fd);
close(drv->metafd);
drv->fd = -1;
drv->metafd = -1;
}
static int read_meta(nand_drv* drv, nand_page_t page)
{
/* probably won't fail */
if(lseek(drv->metafd, page, SEEK_SET) < 0)
return NAND_ERR_OTHER;
if(read(drv->metafd, drv->scratch_buf, 1) != 1)
return NAND_ERR_OTHER;
return drv->scratch_buf[0];
}
static int write_meta(nand_drv* drv, nand_page_t page, int val)
{
drv->scratch_buf[0] = val;
if(lseek(drv->metafd, page, SEEK_SET) < 0)
return NAND_ERR_OTHER;
if(write(drv->metafd, drv->scratch_buf, 1) != 1)
return NAND_ERR_OTHER;
return NAND_SUCCESS;
}
static int upd_meta(nand_drv* drv, nand_page_t page, uint8_t clr, uint8_t set)
{
int meta = read_meta(drv, page);
if(meta < 0)
return meta;
meta &= ~clr;
meta |= set;
return write_meta(drv, page, meta);
}
static int page_program(nand_drv* drv, nand_page_t page, const void* buffer,
uint8_t clr, uint8_t set)
{
if(lseek(drv->fd, page * drv->chip->page_size, SEEK_SET) < 0)
return NAND_ERR_OTHER;
if(write(drv->fd, buffer, drv->chip->page_size) != (ssize_t)drv->chip->page_size)
return NAND_ERR_PROGRAM_FAIL;
return upd_meta(drv, page, clr, set);
}
static void trace(enum nand_trace_type ty, enum nand_trace_exception ex, nand_page_t addr)
{
if(nand_trace_length < nand_trace_capacity) {
nand_trace_cur->type = ty;
nand_trace_cur->exception = ex;
nand_trace_cur->addr = addr;
nand_trace_cur++;
nand_trace_length++;
}
}
int nand_block_erase(nand_drv* drv, nand_block_t block)
{
lock_assert(drv->lock_count > 0, "nand_block_erase(): lock not held");
CHECK_INJECTED_ERROR;
trace(NTT_ERASE, NTE_NONE, block);
memset(drv->page_buf, 0xff, drv->fpage_size);
for(unsigned i = 0; i < drv->ppb; ++i) {
int rc = page_program(drv, block + i, drv->page_buf, METAF_PROGRAMMED, 0);
if(rc < 0)
return NAND_ERR_ERASE_FAIL;
}
return NAND_SUCCESS;
}
int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer)
{
lock_assert(drv->lock_count > 0, "nand_page_program(): lock not held");
CHECK_INJECTED_ERROR;
int meta = read_meta(drv, page);
if(meta < 0)
return meta;
enum nand_trace_exception exception = NTE_NONE;
if(meta & METAF_PROGRAMMED)
exception = NTE_DOUBLE_PROGRAMMED;
trace(NTT_PROGRAM, exception, page);
return page_program(drv, page, buffer, 0, METAF_PROGRAMMED);
}
int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer)
{
lock_assert(drv->lock_count > 0, "nand_page_read(): lock not held");
CHECK_INJECTED_ERROR;
enum nand_trace_exception exception = NTE_NONE;
int meta = read_meta(drv, page);
if(meta < 0)
return meta;
if(meta & METAF_PROGRAMMED) {
if(lseek(drv->fd, page * drv->chip->page_size, SEEK_SET) < 0)
return NAND_ERR_OTHER;
if(read(drv->fd, buffer, drv->chip->page_size) != (ssize_t)drv->chip->page_size)
return NAND_ERR_OTHER;
} else {
memset(buffer, 0xff, drv->chip->page_size);
exception = NTE_CLEARED;
}
trace(NTT_READ, exception, page);
memset(buffer + drv->chip->page_size, 0xff, drv->chip->oob_size);
return NAND_SUCCESS;
}

View file

@ -1,11 +0,0 @@
#include "file.h"
off_t filesize(int osfd)
{
struct stat sb;
if (!fstat(osfd, &sb))
return sb.st_size;
else
return -1;
}

View file

@ -1,18 +0,0 @@
#ifndef FILE_H
#define FILE_H
#include <sys/types.h>
#include <sys/statfs.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef MAX_PATH
# undef MAX_PATH
#endif
#define MAX_PATH 260
off_t filesize(int fd);
#endif

View file

@ -1,245 +0,0 @@
/*
* RFC 1321 compliant MD5 implementation
*
* Copyright (C) 2001-2003 Christophe Devine
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include "md5.h"
#define GET_UINT32(n,b,i) \
{ \
(n) = ( (uint32_t) (b)[(i) ] ) \
| ( (uint32_t) (b)[(i) + 1] << 8 ) \
| ( (uint32_t) (b)[(i) + 2] << 16 ) \
| ( (uint32_t) (b)[(i) + 3] << 24 ); \
}
#define PUT_UINT32(n,b,i) \
{ \
(b)[(i) ] = (uint8_t) ( (n) ); \
(b)[(i) + 1] = (uint8_t) ( (n) >> 8 ); \
(b)[(i) + 2] = (uint8_t) ( (n) >> 16 ); \
(b)[(i) + 3] = (uint8_t) ( (n) >> 24 ); \
}
void md5_starts( md5_context *ctx )
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
}
static void md5_process( md5_context *ctx, uint8_t data[64] )
{
uint32_t X[16], A, B, C, D;
GET_UINT32( X[0], data, 0 );
GET_UINT32( X[1], data, 4 );
GET_UINT32( X[2], data, 8 );
GET_UINT32( X[3], data, 12 );
GET_UINT32( X[4], data, 16 );
GET_UINT32( X[5], data, 20 );
GET_UINT32( X[6], data, 24 );
GET_UINT32( X[7], data, 28 );
GET_UINT32( X[8], data, 32 );
GET_UINT32( X[9], data, 36 );
GET_UINT32( X[10], data, 40 );
GET_UINT32( X[11], data, 44 );
GET_UINT32( X[12], data, 48 );
GET_UINT32( X[13], data, 52 );
GET_UINT32( X[14], data, 56 );
GET_UINT32( X[15], data, 60 );
#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define P(a,b,c,d,k,s,t) \
{ \
a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
#define F(x,y,z) (z ^ (x & (y ^ z)))
P( A, B, C, D, 0, 7, 0xD76AA478 );
P( D, A, B, C, 1, 12, 0xE8C7B756 );
P( C, D, A, B, 2, 17, 0x242070DB );
P( B, C, D, A, 3, 22, 0xC1BDCEEE );
P( A, B, C, D, 4, 7, 0xF57C0FAF );
P( D, A, B, C, 5, 12, 0x4787C62A );
P( C, D, A, B, 6, 17, 0xA8304613 );
P( B, C, D, A, 7, 22, 0xFD469501 );
P( A, B, C, D, 8, 7, 0x698098D8 );
P( D, A, B, C, 9, 12, 0x8B44F7AF );
P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
P( B, C, D, A, 11, 22, 0x895CD7BE );
P( A, B, C, D, 12, 7, 0x6B901122 );
P( D, A, B, C, 13, 12, 0xFD987193 );
P( C, D, A, B, 14, 17, 0xA679438E );
P( B, C, D, A, 15, 22, 0x49B40821 );
#undef F
#define F(x,y,z) (y ^ (z & (x ^ y)))
P( A, B, C, D, 1, 5, 0xF61E2562 );
P( D, A, B, C, 6, 9, 0xC040B340 );
P( C, D, A, B, 11, 14, 0x265E5A51 );
P( B, C, D, A, 0, 20, 0xE9B6C7AA );
P( A, B, C, D, 5, 5, 0xD62F105D );
P( D, A, B, C, 10, 9, 0x02441453 );
P( C, D, A, B, 15, 14, 0xD8A1E681 );
P( B, C, D, A, 4, 20, 0xE7D3FBC8 );
P( A, B, C, D, 9, 5, 0x21E1CDE6 );
P( D, A, B, C, 14, 9, 0xC33707D6 );
P( C, D, A, B, 3, 14, 0xF4D50D87 );
P( B, C, D, A, 8, 20, 0x455A14ED );
P( A, B, C, D, 13, 5, 0xA9E3E905 );
P( D, A, B, C, 2, 9, 0xFCEFA3F8 );
P( C, D, A, B, 7, 14, 0x676F02D9 );
P( B, C, D, A, 12, 20, 0x8D2A4C8A );
#undef F
#define F(x,y,z) (x ^ y ^ z)
P( A, B, C, D, 5, 4, 0xFFFA3942 );
P( D, A, B, C, 8, 11, 0x8771F681 );
P( C, D, A, B, 11, 16, 0x6D9D6122 );
P( B, C, D, A, 14, 23, 0xFDE5380C );
P( A, B, C, D, 1, 4, 0xA4BEEA44 );
P( D, A, B, C, 4, 11, 0x4BDECFA9 );
P( C, D, A, B, 7, 16, 0xF6BB4B60 );
P( B, C, D, A, 10, 23, 0xBEBFBC70 );
P( A, B, C, D, 13, 4, 0x289B7EC6 );
P( D, A, B, C, 0, 11, 0xEAA127FA );
P( C, D, A, B, 3, 16, 0xD4EF3085 );
P( B, C, D, A, 6, 23, 0x04881D05 );
P( A, B, C, D, 9, 4, 0xD9D4D039 );
P( D, A, B, C, 12, 11, 0xE6DB99E5 );
P( C, D, A, B, 15, 16, 0x1FA27CF8 );
P( B, C, D, A, 2, 23, 0xC4AC5665 );
#undef F
#define F(x,y,z) (y ^ (x | ~z))
P( A, B, C, D, 0, 6, 0xF4292244 );
P( D, A, B, C, 7, 10, 0x432AFF97 );
P( C, D, A, B, 14, 15, 0xAB9423A7 );
P( B, C, D, A, 5, 21, 0xFC93A039 );
P( A, B, C, D, 12, 6, 0x655B59C3 );
P( D, A, B, C, 3, 10, 0x8F0CCC92 );
P( C, D, A, B, 10, 15, 0xFFEFF47D );
P( B, C, D, A, 1, 21, 0x85845DD1 );
P( A, B, C, D, 8, 6, 0x6FA87E4F );
P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
P( C, D, A, B, 6, 15, 0xA3014314 );
P( B, C, D, A, 13, 21, 0x4E0811A1 );
P( A, B, C, D, 4, 6, 0xF7537E82 );
P( D, A, B, C, 11, 10, 0xBD3AF235 );
P( C, D, A, B, 2, 15, 0x2AD7D2BB );
P( B, C, D, A, 9, 21, 0xEB86D391 );
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
}
void md5_update( md5_context *ctx, uint8_t *input, uint32_t length )
{
uint32_t left, fill;
if( ! length ) return;
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += length;
ctx->total[0] &= 0xFFFFFFFF;
if( ctx->total[0] < length )
ctx->total[1]++;
if( left && length >= fill )
{
memcpy( (void *) (ctx->buffer + left),
(void *) input, fill );
md5_process( ctx, ctx->buffer );
length -= fill;
input += fill;
left = 0;
}
while( length >= 64 )
{
md5_process( ctx, input );
length -= 64;
input += 64;
}
if( length )
{
memcpy( (void *) (ctx->buffer + left),
(void *) input, length );
}
}
static uint8_t md5_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
void md5_finish( md5_context *ctx, uint8_t digest[16] )
{
uint32_t last, padn;
uint32_t high, low;
uint8_t msglen[8];
high = ( ctx->total[0] >> 29 )
| ( ctx->total[1] << 3 );
low = ( ctx->total[0] << 3 );
PUT_UINT32( low, msglen, 0 );
PUT_UINT32( high, msglen, 4 );
last = ctx->total[0] & 0x3F;
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
md5_update( ctx, md5_padding, padn );
md5_update( ctx, msglen, 8 );
PUT_UINT32( ctx->state[0], digest, 0 );
PUT_UINT32( ctx->state[1], digest, 4 );
PUT_UINT32( ctx->state[2], digest, 8 );
PUT_UINT32( ctx->state[3], digest, 12 );
}

View file

@ -1,18 +0,0 @@
#ifndef _MD5_H
#define _MD5_H
#include <stdint.h>
typedef struct
{
uint32_t total[2];
uint32_t state[4];
uint8_t buffer[64];
}
md5_context;
void md5_starts( md5_context *ctx );
void md5_update( md5_context *ctx, uint8_t *input, uint32_t length );
void md5_finish( md5_context *ctx, uint8_t digest[16] );
#endif /* md5.h */

View file

@ -1,112 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/* Stripped down fake version of X1000 NAND API for testing purposes,
* uses a normal file to store the data */
#ifndef __NAND_X1000_H__
#define __NAND_X1000_H__
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define NAND_SUCCESS 0
#define NAND_ERR_UNKNOWN_CHIP (-1)
#define NAND_ERR_PROGRAM_FAIL (-2)
#define NAND_ERR_ERASE_FAIL (-3)
#define NAND_ERR_UNALIGNED (-4)
#define NAND_ERR_OTHER (-5)
#define NAND_ERR_INJECTED (-6)
/* keep max page size in sync with the NAND chip table in the .c file */
#define NAND_DRV_SCRATCHSIZE 32
#define NAND_DRV_MAXPAGESIZE 2112
typedef uint32_t nand_block_t;
typedef uint32_t nand_page_t;
enum nand_trace_type {
NTT_READ,
NTT_PROGRAM,
NTT_ERASE,
};
enum nand_trace_exception {
NTE_NONE,
NTE_DOUBLE_PROGRAMMED,
NTE_CLEARED,
};
struct nand_trace {
enum nand_trace_type type;
enum nand_trace_exception exception;
nand_page_t addr;
};
typedef struct nand_chip {
/* Base2 logarithm of the number of pages per block */
unsigned log2_ppb;
/* Size of a page's main / oob areas, in bytes. */
unsigned page_size;
unsigned oob_size;
/* Total number of blocks in the chip */
unsigned nr_blocks;
} nand_chip;
typedef struct nand_drv {
/* Backing file */
int fd;
int metafd;
int lock_count;
unsigned refcount;
uint8_t* scratch_buf;
uint8_t* page_buf;
const nand_chip* chip;
unsigned ppb;
unsigned fpage_size;
} nand_drv;
extern const char* nand_backing_file;
extern const char* nand_meta_file;
extern struct nand_trace* nand_trace;
extern size_t nand_trace_capacity;
extern size_t nand_trace_length;
extern void nand_trace_reset(size_t size);
extern void nand_inject_error(int rc);
extern nand_drv* nand_init(void);
extern void nand_lock(nand_drv* drv);
extern void nand_unlock(nand_drv* drv);
extern int nand_open(nand_drv* drv);
extern void nand_close(nand_drv* drv);
extern int nand_block_erase(nand_drv* drv, nand_block_t block);
extern int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer);
extern int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer);
#endif /* __NAND_X1000_H__ */

View file

@ -1,130 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "pathfuncs.h"
#include "strlcpy.h"
#include "system.h"
#include <string.h>
static const char* GOBBLE_PATH_SEPCH(const char* p)
{
int c;
while((c = *p) == PATH_SEPCH)
++p;
return p;
}
static const char* GOBBLE_PATH_COMP(const char* p)
{
int c;
while((c = *p) && c != PATH_SEPCH)
++p;
return p;
}
/* Strips the trailing component from the path
* "" *nameptr->NUL, len=0: ""
* "/" *nameptr->/, len=1: "/"
* "//" *nameptr->2nd /, len=1: "/"
* "/a" *nameptr->/, len=1: "/"
* "a/" *nameptr->a, len=0: ""
* "/a/bc" *nameptr->/, len=2: "/a"
* "d" *nameptr->d, len=0: ""
* "ef/gh" *nameptr->e, len=2: "ef"
*
* Notes: * Interpret len=0 as ".".
* * In the same string, path_dirname() returns a pointer with the
* same or lower address as path_basename().
* * Pasting a separator between the returns of path_dirname() and
* path_basename() will result in a path equivalent to the input.
*
*/
size_t path_dirname(const char *name, const char **nameptr)
{
const char *p = GOBBLE_PATH_SEPCH(name);
const char *q = name;
const char *r = p;
while (*(p = GOBBLE_PATH_COMP(p)))
{
const char *s = p;
if (!*(p = GOBBLE_PATH_SEPCH(p)))
break;
q = s;
}
if (q == name && r > name)
name = r, q = name--; /* root - return last slash */
*nameptr = name;
return q - name;
}
/* Appends one path to another, adding separators between components if needed.
* Return value and behavior is otherwise as strlcpy so that truncation may be
* detected.
*
* For basepath and component:
* PA_SEP_HARD adds a separator even if the base path is empty
* PA_SEP_SOFT adds a separator only if the base path is not empty
*/
size_t path_append(char *buf, const char *basepath,
const char *component, size_t bufsize)
{
const char *base = basepath && basepath[0] ? basepath : buf;
if (!base)
return bufsize; /* won't work to get lengths from buf */
if (!buf)
bufsize = 0;
if (path_is_absolute(component))
{
/* 'component' is absolute; replace all */
basepath = component;
component = "";
}
/* if basepath is not null or empty, buffer contents are replaced,
otherwise buf contains the base path */
size_t len = base == buf ? strlen(buf) : my_strlcpy(buf, basepath, bufsize);
bool separate = false;
if (!basepath || !component)
separate = !len || base[len-1] != PATH_SEPCH;
else if (component[0])
separate = len && base[len-1] != PATH_SEPCH;
/* caller might lie about size of buf yet use buf as the base */
if (base == buf && bufsize && len >= bufsize)
buf[bufsize - 1] = '\0';
buf += len;
bufsize -= MIN(len, bufsize);
if (separate && (len++, bufsize > 0) && --bufsize > 0)
*buf++ = PATH_SEPCH;
return len + my_strlcpy(buf, component ?: "", bufsize);
}

View file

@ -1,39 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _PATHFUNCS_H_
#define _PATHFUNCS_H_
#include <stddef.h>
#include <stdbool.h>
#define PATH_SEPCH '/'
/* return true if path begins with a root '/' component and is not NULL */
static inline bool path_is_absolute(const char *path)
{
return path && path[0] == PATH_SEPCH;
}
size_t path_dirname(const char *name, const char **nameptr);
size_t path_append(char *buf, const char *basepath,
const char *component, size_t bufsize);
#endif

View file

@ -1,50 +0,0 @@
/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
my_strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}

View file

@ -1,4 +0,0 @@
#ifndef STRLCPY_H
#define STRLCPY_H
size_t my_strlcpy(char *dst, const char *src, size_t siz);
#endif

View file

@ -1,10 +0,0 @@
#ifndef SYSTEM_H
#define SYSTEM_H
#define CACHEALIGN_SIZE 1
#define CACHEALIGN_BUFFER(x,y) do { } while(0)
#define MIN(a, b) (((a)<(b))?(a):(b))
#define ALIGN_BUFFER(ptr, size, align) do { } while(0)
#define ALIGN_UP_P2(x, p) (((x) + ((1 << (p)) - 1)) & ~((1 << (p)) - 1))
#endif

View file

@ -1,21 +0,0 @@
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
#
X1000INSTALLER_DIR = $(ROOTDIR)/lib/x1000-installer
X1000INSTALLER_SRC = $(call preprocess, $(X1000INSTALLER_DIR)/SOURCES)
X1000INSTALLER_OBJ := $(call c2obj, $(X1000INSTALLER_SRC))
X1000INSTALLERLIB = $(BUILDDIR)/lib/libx1000-installer.a
INCLUDES += -I$(X1000INSTALLER_DIR)/include
OTHER_SRC += $(X1000INSTALLER_SRC)
CORE_LIBS += $(X1000INSTALLERLIB)
$(X1000INSTALLERLIB): $(X1000INSTALLER_OBJ)
$(SILENT)$(shell rm -f $@)
$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null