Adds tools to access tffs3 and pre-calibration data required for ipq4019 based FritBoxes. Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>master
parent
d5b10bb560
commit
aa47f5623a
@ -0,0 +1,30 @@ |
||||
include $(TOPDIR)/rules.mk |
||||
|
||||
PKG_NAME:=fritz-tools
|
||||
PKG_RELEASE:=1
|
||||
CMAKE_INSTALL:=1
|
||||
PKG_FLAGS:=nonshared
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk |
||||
include $(INCLUDE_DIR)/cmake.mk |
||||
|
||||
define Package/fritz-tools |
||||
SECTION:=firmware
|
||||
CATEGORY:=Firmware
|
||||
DEPENDS:=@TARGET_ipq806x +zlib
|
||||
TITLE:=Utilites for accessing AVM Calibration data and TFFS
|
||||
endef |
||||
|
||||
define Package/fritz-tools/description |
||||
This package contains:
|
||||
fritz_cal_extract.c: tool to extract WLAN calibration data.
|
||||
fritz_tffs_read.c: partially read the TFFS filesystems.
|
||||
endef |
||||
|
||||
define Package/fritz-tools/install |
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/fritz_cal_extract $(1)/usr/bin/
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/fritz_tffs_read $(1)/usr/bin/
|
||||
endef |
||||
|
||||
$(eval $(call BuildPackage,fritz-tools)) |
@ -0,0 +1,50 @@ |
||||
Userspace utilties for accessing TFFS (a name-value storage usually found in AVM Fritz!Box based devices) |
||||
|
||||
## Building |
||||
|
||||
``` |
||||
mkdir build |
||||
cd build |
||||
cmake /path/to/fritz_tffs_tools |
||||
make |
||||
``` |
||||
|
||||
## Usage |
||||
|
||||
All command line parameters are documented: |
||||
``` |
||||
fritz_tffs_read -h |
||||
``` |
||||
|
||||
Show all entries from a TFFS partition dump (in the format: name=value): |
||||
``` |
||||
fritz_tffs_read -i /path/to/tffs.dump -a |
||||
``` |
||||
|
||||
Read a TFFS partition and show all entries (in the format: name=value): |
||||
``` |
||||
fritz_tffs_read -i /dev/mtdX -a |
||||
``` |
||||
|
||||
Output only the value of a specific key (this will only show the value): |
||||
``` |
||||
fritz_tffs_read -i /dev/mtdX -n my_ipaddress |
||||
``` |
||||
|
||||
## LICENSE |
||||
|
||||
See `LICENSE`: |
||||
|
||||
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., |
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
@ -0,0 +1,12 @@ |
||||
cmake_minimum_required(VERSION 2.6) |
||||
|
||||
PROJECT(fritz-tools C) |
||||
ADD_DEFINITIONS(-Wall -Werror --std=gnu99 -Wmissing-declarations) |
||||
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") |
||||
|
||||
ADD_EXECUTABLE(fritz_tffs_read fritz_tffs_read.c) |
||||
ADD_EXECUTABLE(fritz_cal_extract fritz_cal_extract.c) |
||||
TARGET_LINK_LIBRARIES(fritz_cal_extract z) |
||||
|
||||
INSTALL(TARGETS fritz_tffs_read fritz_cal_extract RUNTIME DESTINATION bin) |
@ -0,0 +1,259 @@ |
||||
/*
|
||||
* A tool for reading the zlib compressed calibration data |
||||
* found in AVM Fritz!Box based devices). |
||||
* |
||||
* Copyright (c) 2017 Christian Lamparter <chunkeey@googlemail.com> |
||||
* |
||||
* Based on zpipe, which is an example of proper use of zlib's inflate(). |
||||
* that is Not copyrighted -- provided to the public domain |
||||
* Version 1.4 11 December 2005 Mark Adler |
||||
* |
||||
* 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., |
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
#include <unistd.h> |
||||
#include <stdint.h> |
||||
#include <stdlib.h> |
||||
#include <endian.h> |
||||
#include <errno.h> |
||||
#include "zlib.h" |
||||
|
||||
#define CHUNK 1024 |
||||
|
||||
static inline size_t special_min(size_t a, size_t b) |
||||
{ |
||||
return a == 0 ? b : (a < b ? a : b); |
||||
} |
||||
|
||||
/* Decompress from file source to file dest until stream ends or EOF.
|
||||
inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be |
||||
allocated for processing, Z_DATA_ERROR if the deflate data is |
||||
invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and |
||||
the version of the library linked do not match, or Z_ERRNO if there |
||||
is an error reading or writing the files. */ |
||||
static int inf(FILE *source, FILE *dest, size_t limit, size_t skip) |
||||
{ |
||||
int ret; |
||||
size_t have; |
||||
z_stream strm; |
||||
unsigned char in[CHUNK]; |
||||
unsigned char out[CHUNK]; |
||||
|
||||
/* allocate inflate state */ |
||||
strm.zalloc = Z_NULL; |
||||
strm.zfree = Z_NULL; |
||||
strm.opaque = Z_NULL; |
||||
strm.avail_in = 0; |
||||
strm.next_in = Z_NULL; |
||||
ret = inflateInit(&strm); |
||||
if (ret != Z_OK) |
||||
return ret; |
||||
|
||||
/* decompress until deflate stream ends or end of file */ |
||||
do { |
||||
strm.avail_in = fread(in, 1, CHUNK, source); |
||||
if (ferror(source)) { |
||||
(void)inflateEnd(&strm); |
||||
return Z_ERRNO; |
||||
} |
||||
if (strm.avail_in == 0) |
||||
break; |
||||
strm.next_in = in; |
||||
|
||||
/* run inflate() on input until output buffer not full */ |
||||
do { |
||||
strm.avail_out = CHUNK; |
||||
strm.next_out = out; |
||||
ret = inflate(&strm, Z_NO_FLUSH); |
||||
assert(ret != Z_STREAM_ERROR); /* state not clobbered */ |
||||
switch (ret) { |
||||
case Z_NEED_DICT: |
||||
ret = Z_DATA_ERROR; /* and fall through */ |
||||
case Z_DATA_ERROR: |
||||
case Z_MEM_ERROR: |
||||
(void)inflateEnd(&strm); |
||||
return ret; |
||||
} |
||||
have = special_min(limit, CHUNK - strm.avail_out) - skip; |
||||
if (fwrite(&out[skip], have, 1, dest) != 1 || ferror(dest)) { |
||||
(void)inflateEnd(&strm); |
||||
return Z_ERRNO; |
||||
} |
||||
skip = 0; |
||||
limit -= have; |
||||
} while (strm.avail_out == 0 && limit > 0); |
||||
|
||||
/* done when inflate() says it's done */ |
||||
} while (ret != Z_STREAM_END && limit > 0); |
||||
|
||||
/* clean up and return */ |
||||
(void)inflateEnd(&strm); |
||||
return (limit == 0 ? Z_OK : (ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR)); |
||||
} |
||||
|
||||
/* report a zlib or i/o error */ |
||||
static void zerr(int ret) |
||||
{ |
||||
switch (ret) { |
||||
case Z_ERRNO: |
||||
if (ferror(stdin)) |
||||
fputs("error reading stdin\n", stderr); |
||||
if (ferror(stdout)) |
||||
fputs("error writing stdout\n", stderr); |
||||
break; |
||||
case Z_STREAM_ERROR: |
||||
fputs("invalid compression level\n", stderr); |
||||
break; |
||||
case Z_DATA_ERROR: |
||||
fputs("invalid or incomplete deflate data\n", stderr); |
||||
break; |
||||
case Z_MEM_ERROR: |
||||
fputs("out of memory\n", stderr); |
||||
break; |
||||
case Z_VERSION_ERROR: |
||||
fputs("zlib version mismatch!\n", stderr); |
||||
} |
||||
} |
||||
|
||||
static unsigned int get_num(char *str) |
||||
{ |
||||
if (!strncmp("0x", str, 2)) |
||||
return strtoul(str+2, NULL, 16); |
||||
else |
||||
return strtoul(str, NULL, 10); |
||||
} |
||||
|
||||
static void usage(void) |
||||
{ |
||||
fprintf(stderr, "Usage: fritz_cal_extract [-s seek offset] [-i skip] [-o output file] [-l limit] [infile] -e entry_id\n" |
||||
"Finds and extracts zlib compressed calibration data in the EVA loader\n"); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
struct cal_entry { |
||||
uint16_t id; |
||||
uint16_t len; |
||||
} __attribute__((packed)); |
||||
|
||||
/* compress or decompress from stdin to stdout */ |
||||
int main(int argc, char **argv) |
||||
{ |
||||
struct cal_entry cal = { .len = 0 }; |
||||
FILE *in = stdin; |
||||
FILE *out = stdout; |
||||
size_t limit = 0, skip = 0; |
||||
int initial_offset = 0; |
||||
int entry = -1; |
||||
int ret; |
||||
int opt; |
||||
|
||||
while ((opt = getopt(argc, argv, "s:e:o:l:i:")) != -1) { |
||||
switch (opt) { |
||||
case 's': |
||||
initial_offset = (int)get_num(optarg); |
||||
if (errno) { |
||||
perror("Failed to parse seek offset"); |
||||
goto out_bad; |
||||
} |
||||
break; |
||||
case 'e': |
||||
entry = (int) htobe16(get_num(optarg)); |
||||
if (errno) { |
||||
perror("Failed to entry id"); |
||||
goto out_bad; |
||||
} |
||||
break; |
||||
case 'o': |
||||
out = fopen(optarg, "w"); |
||||
if (!out) { |
||||
perror("Failed to create output file"); |
||||
goto out_bad; |
||||
} |
||||
break; |
||||
case 'l': |
||||
limit = (size_t)get_num(optarg); |
||||
if (errno) { |
||||
perror("Failed to parse limit"); |
||||
goto out_bad; |
||||
} |
||||
break; |
||||
case 'i': |
||||
skip = (size_t)get_num(optarg); |
||||
if (errno) { |
||||
perror("Failed to parse skip"); |
||||
goto out_bad; |
||||
} |
||||
break; |
||||
default: /* '?' */ |
||||
usage(); |
||||
} |
||||
} |
||||
|
||||
if (entry == -1) |
||||
usage(); |
||||
|
||||
if (argc > 1 && optind <= argc) { |
||||
in = fopen(argv[optind], "r"); |
||||
if (!in) { |
||||
perror("Failed to create output file"); |
||||
goto out_bad; |
||||
} |
||||
} |
||||
|
||||
if (initial_offset) { |
||||
ret = fseek(in, initial_offset, SEEK_CUR); |
||||
if (ret) { |
||||
perror("Failed to seek to calibration table"); |
||||
goto out_bad; |
||||
} |
||||
} |
||||
|
||||
do { |
||||
ret = fseek(in, be16toh(cal.len), SEEK_CUR); |
||||
if (feof(in)) { |
||||
fprintf(stderr, "Reached end of file, but didn't find the matching entry\n"); |
||||
goto out_bad; |
||||
} else if (ferror(in)) { |
||||
perror("Failure during seek"); |
||||
goto out_bad; |
||||
} |
||||
|
||||
ret = fread(&cal, 1, sizeof cal, in); |
||||
if (ret != sizeof cal) |
||||
goto out_bad; |
||||
} while (entry != cal.id || cal.id == 0xffff); |
||||
|
||||
if (cal.id == 0xffff) { |
||||
fprintf(stderr, "Reached end of filesystem, but didn't find the matching entry\n"); |
||||
goto out_bad; |
||||
} |
||||
|
||||
ret = inf(in, out, limit, skip); |
||||
if (ret == Z_OK) |
||||
goto out; |
||||
|
||||
zerr(ret); |
||||
|
||||
out_bad: |
||||
ret = EXIT_FAILURE; |
||||
|
||||
out: |
||||
fclose(in); |
||||
fclose(out); |
||||
return ret; |
||||
} |
@ -0,0 +1,375 @@ |
||||
/*
|
||||
* A tool for reading the TFFS partitions (a name-value storage usually |
||||
* found in AVM Fritz!Box based devices). |
||||
* |
||||
* Copyright (c) 2015-2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com> |
||||
* |
||||
* Based on the TFFS 2.0 kernel driver from AVM: |
||||
* Copyright (c) 2004-2007 AVM GmbH <fritzbox_info@avm.de> |
||||
* and the OpenWrt TFFS kernel driver: |
||||
* Copyright (c) 2013 John Crispin <blogic@openwrt.org> |
||||
* |
||||
* 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., |
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
||||
*/ |
||||
|
||||
#include <stdbool.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdint.h> |
||||
#include <string.h> |
||||
#include <libgen.h> |
||||
#include <getopt.h> |
||||
#include <unistd.h> |
||||
#include <sys/types.h> |
||||
#include <sys/stat.h> |
||||
#include <arpa/inet.h> |
||||
|
||||
#define DEFAULT_TFFS_SIZE (256 * 1024) |
||||
|
||||
#define TFFS_ID_END 0xffff |
||||
#define TFFS_ID_TABLE_NAME 0x01ff |
||||
|
||||
static char *progname; |
||||
static char *input_file; |
||||
static unsigned long tffs_size = DEFAULT_TFFS_SIZE; |
||||
static char *name_filter = NULL; |
||||
static bool show_all = false; |
||||
static bool print_all_key_names = false; |
||||
static bool swap_bytes = false; |
||||
|
||||
struct tffs_entry_header { |
||||
uint16_t id; |
||||
uint16_t len; |
||||
}; |
||||
|
||||
struct tffs_entry { |
||||
const struct tffs_entry_header *header; |
||||
char *name; |
||||
uint8_t *val; |
||||
}; |
||||
|
||||
struct tffs_name_table_entry { |
||||
const uint32_t *id; |
||||
const char *val; |
||||
}; |
||||
|
||||
struct tffs_key_name_table { |
||||
uint32_t size; |
||||
struct tffs_name_table_entry *entries; |
||||
}; |
||||
|
||||
static inline uint16_t get_header_len(const struct tffs_entry_header *header) |
||||
{ |
||||
if (swap_bytes) |
||||
return ntohs(header->len); |
||||
|
||||
return header->len; |
||||
} |
||||
|
||||
static inline uint16_t get_header_id(const struct tffs_entry_header *header) |
||||
{ |
||||
if (swap_bytes) |
||||
return ntohs(header->id); |
||||
|
||||
return header->id; |
||||
} |
||||
|
||||
static inline uint16_t to_entry_header_id(uint32_t name_id) |
||||
{ |
||||
if (swap_bytes) |
||||
return ntohl(name_id) & 0xffff; |
||||
|
||||
return name_id & 0xffff; |
||||
} |
||||
|
||||
static inline uint32_t get_walk_size(uint32_t entry_len) |
||||
{ |
||||
return (entry_len + 3) & ~0x03; |
||||
} |
||||
|
||||
static void print_entry_value(const struct tffs_entry *entry) |
||||
{ |
||||
int i; |
||||
|
||||
/* These are NOT NULL terminated. */ |
||||
for (i = 0; i < get_header_len(entry->header); i++) |
||||
fprintf(stdout, "%c", entry->val[i]); |
||||
} |
||||
|
||||
static void parse_entry(uint8_t *buffer, uint32_t pos, |
||||
struct tffs_entry *entry) |
||||
{ |
||||
entry->header = (struct tffs_entry_header *) &buffer[pos]; |
||||
entry->val = &buffer[pos + sizeof(struct tffs_entry_header)]; |
||||
} |
||||
|
||||
static int find_entry(uint8_t *buffer, uint16_t id, struct tffs_entry *entry) |
||||
{ |
||||
uint32_t pos = 0; |
||||
|
||||
do { |
||||
parse_entry(buffer, pos, entry); |
||||
|
||||
if (get_header_id(entry->header) == id) |
||||
return 1; |
||||
|
||||
pos += sizeof(struct tffs_entry_header); |
||||
pos += get_walk_size(get_header_len(entry->header)); |
||||
} while (pos < tffs_size && entry->header->id != TFFS_ID_END); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void parse_key_names(struct tffs_entry *names_entry, |
||||
struct tffs_key_name_table *key_names) |
||||
{ |
||||
uint32_t pos = 0, i = 0; |
||||
struct tffs_name_table_entry *name_item; |
||||
|
||||
key_names->entries = calloc(sizeof(*name_item), 1); |
||||
|
||||
do { |
||||
name_item = &key_names->entries[i]; |
||||
|
||||
name_item->id = (uint32_t *) &names_entry->val[pos]; |
||||
pos += sizeof(*name_item->id); |
||||
name_item->val = (const char *) &names_entry->val[pos]; |
||||
|
||||
/*
|
||||
* There is no "length" field because the string values are |
||||
* simply NULL-terminated -> strlen() gives us the size. |
||||
*/ |
||||
pos += get_walk_size(strlen(name_item->val) + 1); |
||||
|
||||
++i; |
||||
key_names->entries = realloc(key_names->entries, |
||||
sizeof(*name_item) * (i + 1)); |
||||
} while (pos < get_header_len(names_entry->header)); |
||||
|
||||
key_names->size = i; |
||||
} |
||||
|
||||
static void show_all_key_names(struct tffs_key_name_table *key_names) |
||||
{ |
||||
int i; |
||||
|
||||
for (i = 0; i < key_names->size; i++) |
||||
printf("%s\n", key_names->entries[i].val); |
||||
} |
||||
|
||||
static int show_all_key_value_pairs(uint8_t *buffer, |
||||
struct tffs_key_name_table *key_names) |
||||
{ |
||||
int i, has_value = 0; |
||||
uint16_t id; |
||||
struct tffs_entry tmp; |
||||
|
||||
for (i = 0; i < key_names->size; i++) { |
||||
id = to_entry_header_id(*key_names->entries[i].id); |
||||
|
||||
if (find_entry(buffer, id, &tmp)) { |
||||
printf("%s=", key_names->entries[i].val); |
||||
print_entry_value(&tmp); |
||||
printf("\n"); |
||||
has_value++; |
||||
} |
||||
} |
||||
|
||||
if (!has_value) { |
||||
fprintf(stderr, "ERROR: no values found!\n"); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
return EXIT_SUCCESS; |
||||
} |
||||
|
||||
static int show_matching_key_value(uint8_t *buffer, |
||||
struct tffs_key_name_table *key_names) |
||||
{ |
||||
int i; |
||||
uint16_t id; |
||||
struct tffs_entry tmp; |
||||
const char *name; |
||||
|
||||
for (i = 0; i < key_names->size; i++) { |
||||
name = key_names->entries[i].val; |
||||
|
||||
if (strncmp(name, name_filter, strlen(name)) == 0) { |
||||
id = to_entry_header_id(*key_names->entries[i].id); |
||||
|
||||
if (find_entry(buffer, id, &tmp)) { |
||||
print_entry_value(&tmp); |
||||
printf("\n"); |
||||
return EXIT_SUCCESS; |
||||
} else { |
||||
fprintf(stderr, |
||||
"ERROR: no value found for name %s!\n", |
||||
name); |
||||
return EXIT_FAILURE; |
||||
} |
||||
} |
||||
} |
||||
|
||||
fprintf(stderr, "ERROR: Unknown key name %s!\n", name_filter); |
||||
return EXIT_FAILURE; |
||||
} |
||||
|
||||
static void usage(int status) |
||||
{ |
||||
FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; |
||||
|
||||
fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); |
||||
fprintf(stream, |
||||
"\n" |
||||
"Options:\n" |
||||
" -a list all key value pairs found in the TFFS file/device\n" |
||||
" -b swap bytes while parsing the TFFS file/device\n" |
||||
" -h show this screen\n" |
||||
" -i <file> inspect the given TFFS file/device <file>\n" |
||||
" -l list all supported keys\n" |
||||
" -n <key name> display the value of the given key\n" |
||||
" -s <size> the (max) size of the TFFS file/device <size>\n" |
||||
); |
||||
|
||||
exit(status); |
||||
} |
||||
|
||||
static int file_exist(char *filename) |
||||
{ |
||||
struct stat buffer; |
||||
|
||||
return stat(filename, &buffer) == 0; |
||||
} |
||||
|
||||
static void parse_options(int argc, char *argv[]) |
||||
{ |
||||
while (1) |
||||
{ |
||||
int c; |
||||
|
||||
c = getopt(argc, argv, "abhi:ln:s"); |
||||
if (c == -1) |
||||
break; |
||||
|
||||
switch (c) { |
||||
case 'a': |
||||
show_all = true; |
||||
name_filter = NULL; |
||||
print_all_key_names = false; |
||||
break; |
||||
case 'b': |
||||
swap_bytes = 1; |
||||
break; |
||||
case 'h': |
||||
usage(EXIT_SUCCESS); |
||||
break; |
||||
case 'i': |
||||
input_file = optarg; |
||||
break; |
||||
case 'l': |
||||
print_all_key_names = true; |
||||
show_all = false; |
||||
name_filter = NULL; |
||||
break; |
||||
case 'n': |
||||
name_filter = optarg; |
||||
show_all = false; |
||||
print_all_key_names = false; |
||||
break; |
||||
case 's': |
||||
tffs_size = strtoul(optarg, NULL, 0); |
||||
break; |
||||
default: |
||||
usage(EXIT_FAILURE); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (!input_file) { |
||||
fprintf(stderr, "ERROR: No input file (-i <file>) given!\n"); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
if (!file_exist(input_file)) { |
||||
fprintf(stderr, "ERROR: %s does not exist\n", input_file); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
if (!show_all && !name_filter && !print_all_key_names) { |
||||
fprintf(stderr, |
||||
"ERROR: either -l, -a or -n <key name> is required!\n"); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
int ret = EXIT_FAILURE; |
||||
uint8_t *buffer; |
||||
FILE *fp; |
||||
struct tffs_entry name_table; |
||||
struct tffs_key_name_table key_names; |
||||
|
||||
progname = basename(argv[0]); |
||||
|
||||
parse_options(argc, argv); |
||||
|
||||
fp = fopen(input_file, "r"); |
||||
|
||||
if (!fp) { |
||||
fprintf(stderr, "ERROR: Failed to open tffs input file %s\n", |
||||
input_file); |
||||
goto out; |
||||
} |
||||
|
||||
buffer = malloc(tffs_size); |
||||
|
||||
if (fread(buffer, 1, tffs_size, fp) != tffs_size) { |
||||
fprintf(stderr, "ERROR: Failed read tffs file %s\n", |
||||
input_file); |
||||
goto out_free; |
||||
} |
||||
|
||||
if (!find_entry(buffer, TFFS_ID_TABLE_NAME, &name_table)) { |
||||
fprintf(stderr,"ERROR: No name table found in tffs file %s\n", |
||||
input_file); |
||||
fprintf(stderr," Is byte-swapping (-b) required?\n"); |
||||
goto out_free; |
||||
} |
||||
|
||||
parse_key_names(&name_table, &key_names); |
||||
if (key_names.size < 1) { |
||||
fprintf(stderr, "ERROR: No name table found in tffs file %s\n", |
||||
input_file); |
||||
goto out_free_names; |
||||
} |
||||
|
||||
if (print_all_key_names) { |
||||
show_all_key_names(&key_names); |
||||
ret = EXIT_SUCCESS; |
||||
} else if (show_all) { |
||||
ret = show_all_key_value_pairs(buffer, &key_names); |
||||
} else { |
||||
ret = show_matching_key_value(buffer, &key_names); |
||||
} |
||||
|
||||
out_free_names: |
||||
free(key_names.entries); |
||||
out_free: |
||||
fclose(fp); |
||||
free(buffer); |
||||
out: |
||||
return ret; |
||||
} |
Loading…
Reference in new issue