Skip to content

Commit

Permalink
Add support for delta-source-fat-* directives
Browse files Browse the repository at this point in the history
Closes #201
  • Loading branch information
Jean Parpaillon committed Mar 17, 2023
1 parent c9918f7 commit 8aec2cc
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 2 deletions.
69 changes: 69 additions & 0 deletions src/fatfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,44 @@ int fatfs_truncate(struct block_cache *output, off_t block_offset, const char *f
return 0;
}

/**
* @brief fatfs_pread Read a file
*
* @param fc the current FAT session
* @param filename an existing file
* @param offset offset inside file
* @param size number of bytes to read
* @param buffer buffer where to put read data
* @param br pointer to the variable that receives number of byters actually read
* @return 0 on success
*/
int fatfs_pread(struct block_cache *output, off_t block_offset, const char *filename, int offset, size_t size, void *buffer, size_t *br)
{
// Check if this is the same file as a previous pwrite call
if (current_file_ && strcmp(current_file_, filename) != 0)
close_open_files();

MAYBE_MOUNT(output, block_offset);

if (!current_file_) {
CHECK("fat_read can't open file", filename, f_open(&fil_, filename, FA_READ));
CHECK_SYNC(filename, &fil_);

// Assuming it opens ok, cache the filename for future reads.
current_file_ = strdup(filename);
}

// Check if this pread requires a seek.
FSIZE_t desired_offset = offset;
if (desired_offset != f_tell(&fil_))
CHECK("fat_read can't seek to end of file", filename, f_lseek(&fil_, f_size(&fil_)));

CHECK("fat_read can't read", filename, f_read(&fil_, buffer, (uint)size, (uint*)br));
CHECK_SYNC(filename, &fil_);

return 0;
}

int fatfs_pwrite(struct block_cache *output, off_t block_offset, const char *filename, int offset, const char *buffer, off_t size)
{
// Check if this is the same file as a previous pwrite call
Expand Down Expand Up @@ -476,6 +514,37 @@ int fatfs_pwrite(struct block_cache *output, off_t block_offset, const char *fil
return 0;
}

/**
* @brief fatfs_size read file size
*
* @param fc the current FAT session
* @param filename an existing file
* @param size pointer to the variable that receives file size
* @return 0 on success
*/
int fatfs_size(struct block_cache *output, off_t block_offset, const char *filename, size_t *size)
{
// Check if this is the same file as a previous size call
if (current_file_ && strcmp(current_file_, filename) != 0)
close_open_files();

MAYBE_MOUNT(output, block_offset);

if (!current_file_) {
CHECK("fat_size can't open file", filename, f_open(&fil_, filename, FA_READ));
CHECK_SYNC(filename, &fil_);

// Assuming it opens ok, cache the filename for future calls.
current_file_ = strdup(filename);
}

size = malloc(sizeof(off_t));
*size = (size_t)f_size(&fil_);
CHECK_SYNC(filename, &fil_);

return 0;
}

void fatfs_closefs()
{
if (output_) {
Expand Down
2 changes: 2 additions & 0 deletions src/fatfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ int fatfs_setlabel(struct block_cache *output, off_t block_offset, const char *l
int fatfs_mv(struct block_cache *output, off_t block_offset, const char *cmd, const char *from_name, const char *to_name, bool force);
int fatfs_rm(struct block_cache *output, off_t block_offset, const char *cmd, const char *filename, bool file_must_exist);
int fatfs_truncate(struct block_cache *output, off_t block_offset, const char *filename);
int fatfs_pread(struct block_cache *output, off_t block_offset, const char *filename, int offset, size_t size, void *buffer, size_t *br);
int fatfs_pwrite(struct block_cache *output, off_t block_offset, const char *filename, int offset, const char *buffer, off_t size);
int fatfs_cp(struct block_cache *output, off_t block_offset, const char *from_name, const char *to_name);
int fatfs_touch(struct block_cache *output, off_t block_offset, const char *filename);
int fatfs_exists(struct block_cache *output, off_t block_offset, const char *filename);
int fatfs_file_matches(struct block_cache *output, off_t block_offset, const char *filename, const char *pattern);
int fatfs_size(struct block_cache *output, off_t block_offset, const char *filename, size_t *size);
void fatfs_closefs();

#endif // FATFS_H
1 change: 1 addition & 0 deletions src/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ struct fun_context {
struct xdelta_state *xd;
off_t xd_source_offset;
size_t xd_source_count;
const char *xd_source_path;

void *cookie;
};
Expand Down
27 changes: 26 additions & 1 deletion src/fwup_apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,17 @@ static int xdelta_read_source_callback(void *cookie, void *buf, size_t count, of
return block_cache_pread(fctx->output, buf, count, fctx->xd_source_offset + offset);
}

static int xdelta_read_fat_callback(void *cookie, void *buf, size_t count, off_t offset)
{
struct fun_context *fctx = (struct fun_context *) cookie;
size_t br;

if (offset < 0)
ERR_RETURN("xdelta tried to load outside of allowed byte range (0-%" PRId64 ")", fctx->xd_source_count);

return fatfs_pread(fctx->output, fctx->xd_source_offset, fctx->xd_source_path, offset, count, buf, &br);
}

static int read_callback_xdelta(struct fun_context *fctx, const void **buffer, size_t *len, off_t *offset)
{
struct fwup_apply_data *p = (struct fwup_apply_data *) fctx->cookie;
Expand Down Expand Up @@ -387,17 +398,31 @@ static int run_task(struct fun_context *fctx, struct fwup_apply_data *pd)
off_t expected_size_in_archive = sparse_file_data_size(&pd->sfm);

if (pd->sfm.map_len == 1 && archive_entry_size_is_set(ae) && size_in_archive != expected_size_in_archive) {
// Size in archive is different from expected size
const char *source_raw_offset_str = cfg_getstr(on_resource, "delta-source-raw-offset");
int source_raw_count = cfg_getint(on_resource, "delta-source-raw-count");

const char *source_fat_offset_str = cfg_getstr(on_resource, "delta-source-fat-offset");
const char *source_fat_path = cfg_getstr(on_resource, "delta-source-fat-path");

if (source_raw_count > 0 && source_raw_offset_str != NULL) {
// Found delta-source-raw-offset and delta-source-raw-count directives
off_t source_raw_offset = strtoul(source_raw_offset_str, NULL, 0);

fctx->xd = malloc(sizeof(struct xdelta_state));
xdelta_init(fctx->xd, xdelta_read_patch_callback, xdelta_read_source_callback, fctx);
fctx->xd_source_offset = source_raw_offset * FWUP_BLOCK_SIZE;
fctx->xd_source_count = source_raw_count * FWUP_BLOCK_SIZE;
} else if (source_fat_offset_str != NULL && source_fat_path != NULL) {
// Found delta-source-fat-offset and delta-source-fat-path directives
off_t source_fat_offset = strtoul(source_fat_offset_str, NULL, 0);

fctx->xd = malloc(sizeof(struct xdelta_state));
xdelta_init(fctx->xd, xdelta_read_patch_callback, xdelta_read_fat_callback, fctx);
fctx->xd_source_offset = source_fat_offset;
fctx->xd_source_path = source_fat_path;
} else {
ERR_CLEANUP_MSG("File '%s' isn't expected size (%d vs %d) and xdelta3 patch support not enabled on it. (Add delta-source-raw-offset or delta-source-raw-count at least)", resource_name, (int) size_in_archive, (int) expected_size_in_archive);
ERR_CLEANUP_MSG("File '%s' isn't expected size (%d vs %d) and xdelta3 patch support not enabled on it. (Add delta-source-raw-offset / delta-source-raw-count or delta-source-fat-offset / delta-source-fat-path)", resource_name, (int) size_in_archive, (int) expected_size_in_archive);
}
}
}
Expand Down
74 changes: 74 additions & 0 deletions tests/192_delta_fat_upgrade.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/bin/sh

#
# Write a firmware image and then test upgrading it with
# an xdelta3-encoded update, on FAT-stored files.
#
# brew install xdelta
#

. "$(cd "$(dirname "$0")" && pwd)/common.sh"

FWFILE2="${WORK}/fwup2.fw"

cat >"$CONFIG" <<EOF
define(BOOT_PART_OFFSET, 63)
define(BOOT_PART_COUNT, 77238)
file-resource 1k.cur {
host-path = "${TESTFILE_1K}"
}
file-resource 1k.new {
host-path = "${TESTFILE_1K}"
}
mbr mbr-a {
partition 0 {
block-offset = \${BOOT_PART_OFFSET}
block-count = \${BOOT_PART_COUNT}
type = 0xc # FAT32
boot = true
}
}
task complete {
on-init {
mbr_write(mbr-a)
fat_mkfs(\${BOOT_PART_OFFSET}, \${BOOT_PART_COUNT})
}
on-resource 1k.cur { fat_write(\${BOOT_PART_OFFSET}, "1k.cur") }
}
task upgrade {
on-resource 1k.new {
delta-source-fat-offset=\${BOOT_PART_OFFSET}
delta-source-fat-path="1k.cur"
fat_write(\${BOOT_PART_OFFSET}, "1k.new")
}
}
EOF

offset_bytes=$(( 63*512 ))

# Create the firmware file, then "burn it"
$FWUP_CREATE -c -f "$CONFIG" -o "$FWFILE"
$FWUP_APPLY -a -d "$IMGFILE" -i "$FWFILE" -t complete

# Check the content of current firmware content
mcopy -n -i "${IMGFILE}@@${offset_bytes}" ::/1k.cur "${WORK}/1k.cur"
diff "${TESTFILE_1K}" "${WORK}/1k.cur"

# Manually create the delta upgrade by replacing 1k.new
# with the delta3 version
mkdir -p "$WORK/data"
xdelta3 -A -S -f -s "$TESTFILE_1K" "$TESTFILE_1K" "$WORK/data/1k.new"
cp "$FWFILE" "$FWFILE2"
(cd "$WORK" && zip "$FWFILE2" data/1k.new)

# Now upgrade the IMGFILE file
$FWUP_APPLY -a -d "$IMGFILE" -i "$FWFILE2" -t upgrade

# Check the content of new firmware content
mcopy -i "${IMGFILE}@@${offset_bytes}" ::/1k.new "${WORK}/1k.new"
diff "${TESTFILE_1K}" "${WORK}/1k.new"

# Check that the verify logic works on both files
$FWUP_VERIFY -V -i "$FWFILE"
$FWUP_VERIFY -V -i "$FWFILE2"
3 changes: 2 additions & 1 deletion tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ TESTS = 001_simple_fw.test \
188_uboot_redundant_bad_param.test \
189_uboot_redundant_recover.test \
190_one_metadata_cmdline.test \
191_disk_crypto_via_env.test
191_disk_crypto_via_env.test \
192_delta_fat_upgrade.test

EXTRA_DIST = $(TESTS) common.sh 1K.bin 1K-corrupt.bin 150K.bin

0 comments on commit 8aec2cc

Please sign in to comment.