51static const char *remove_subpath(
const char *path,
const char *path_end,
const char *subpath) {
53 const char *text = path;
55 while((uintptr_t)text < (uintptr_t)path_end &&
78static const char* path_from_pax_header(
const char *buf,
size_t size,
const char *templ) {
80 size_t line_start,i = 0, j, digi = 0;
82 int templ_len = strlen(templ);
93 while (j <
size && buf[j] !=
' ')
95 if (buf[j] <
'0' || buf[j] >
'9')
97 len = len * 10 + (buf[j] -
'0');
102 if (j >=
size || buf[j] !=
' ')
106 if (line_start >=
size)
109 if (line_start + len >
size)
114 line = buf + line_start;
117 if (len >= templ_len && memcmp(line, templ, templ_len) == 0)
120 bool good_line =
false;
121 for (
int i=templ_len; i < (len - digi - 1); i++)
122 if (line[i] == 0 || line[i] ==
'\r' || line[i] ==
'\n') {
128 log(
"PAX header is\r\n");
130 return good_line ? line + templ_len : NULL;
134 i = line_start + len - digi - 1;
147static int inode_compare(
const struct tarfs_inode *a,
const struct tarfs_inode *b) {
149 if (a->in_hash < b->in_hash)
return -1;
150 if (a->in_hash > b->in_hash)
return 1;
158static inline void inode_exchange(
struct tarfs_inode **a,
struct tarfs_inode **b) {
160 struct tarfs_inode *t = *a;
170static void inode_siftdown(
struct tarfs_inode **v,
size_t root,
size_t end)
173 size_t child = root * 2 + 1;
178 if (child + 1 < end && inode_compare(v[child], v[child + 1]) < 0)
181 if (inode_compare(v[root], v[child]) >= 0)
184 inode_exchange(&v[root], &v[child]);
194 struct tarfs_inode **v = (
struct tarfs_inode **)iarr;
199 for (
size_t i = count / 2; i-- > 0;)
200 inode_siftdown(v, i, count);
202 for (
size_t end = count; end > 1; end--) {
204 inode_exchange(&v[0], &v[end - 1]);
205 inode_siftdown(v, 0, end - 1);
221static struct tarfs_inode *merge(
struct tarfs_inode *a,
struct tarfs_inode *b) {
223 struct tarfs_inode *head = NULL;
224 struct tarfs_inode **tail = &head;
228 if (
tar_strcmp((
char const *)a->in_path, NULL,(
char const *)b->in_path) <= 0) {
236 tail = &(*tail)->in_next;
250static struct tarfs_inode *merge_sort(
struct tarfs_inode *head) {
252 if (!head || !head->in_next)
256 struct tarfs_inode *slow = head;
257 struct tarfs_inode *fast = head->in_next;
259 while (fast && fast->in_next) {
260 slow = slow->in_next;
261 fast = fast->in_next->in_next;
264 struct tarfs_inode *right = slow->in_next;
265 slow->in_next = NULL;
267 struct tarfs_inode *left = merge_sort(head);
268 right = merge_sort(right);
270 return merge(left, right);
281 if (count == 0 || array == NULL)
285 for (
size_t i = 0; i + 1 < count; i++)
286 array[i].in_next = &array[i + 1];
289 array[count - 1].in_next = NULL;
292 return merge_sort(array);
306 struct tarfs_inode *nodes, **index;
307 size_t index_size, nodes_size;
315 index_size = count *
sizeof(
struct tarfs_inode *);
316 nodes_size = count *
sizeof(
struct tarfs_inode);
319 if (NULL == (ptr =
tarfs_calloc(1, index_size + nodes_size)))
323 index = (
struct tarfs_inode **)ptr;
324 nodes = (
struct tarfs_inode *)(ptr + index_size);
329 for (
size_t i = 0; i < count; i++)
330 index[i] = &nodes[i];
332 log(
"Allocated %u inodes, %u bytes (%u + %u)\r\n",(
unsigned int)count,(
unsigned int)(index_size + nodes_size),(
unsigned int)index_size, (
unsigned int)nodes_size);
342void inode_free(
struct tarfs_inode **index,
size_t count, uintptr_t tar_start,
size_t tar_length) {
346 for (
int i = 0; i < count; i++) {
347 if (index[i]->in_path != 0) {
349 if (index[i]->in_path < tar_start || index[i]->in_path >= (tar_start + tar_length)) {
364static bool inode_pathcmp(
const struct tarfs_inode *inode,
const char *src) {
366 if (inode == NULL || src == NULL)
370 const char *in_path = (
const char *)inode->in_path;
375 while(*in_path == *src) {
384 return (*src == 0 && (*in_path ==
'\r' || *in_path ==
'\n' || *in_path ==
'\0'));
399int inode_lookup(
struct tarfs_inode
const *
const *index,
size_t num_inodes,
const char *path) {
406 if (index == NULL || path == NULL)
416 while (left < right) {
418 size_t mid = left + ((right - left) >> 1);
419 uint32_t mid_hash = index[mid]->in_hash;
423 else if (hash > mid_hash)
433 while (first > 0 && index[first - 1]->in_hash == mid_hash)
438 for (
int i = first; i < num_inodes && index[i]->in_hash == hash; i++) {
440 if (inode_pathcmp(index[i], path)) {
441 log(
"found inode#%u, hash=<e8bb5ed2> path='%s'\r\n", i, path);
446 log(
"unresolved collision, hash=%08x\r\n", (
unsigned int)hash);
450 log(
"hash=<%08x> path='%s' not found\r\n",(
unsigned int)hash, path);
469 struct tarfs_inode const *ino = index[idx];
472 if (NULL != (hdr = (
struct tarhdr const *)ino->in_dvaddr)) {
495 if (NULL != (hdr = (
struct tarhdr const *)ino->in_dvaddr))
511 if (NULL != (hdr = (
struct tarhdr const *)ino->in_vaddr))
526 struct tarhdr const *hdr = (
struct tarhdr const *)ino->in_vaddr;
527 return hdr->
type == TART_SYMLINK || hdr->
type == TART_HARDLINK;
537 if (fs != NULL && idx >= 0 && idx < fs->fs_nino) {
539 struct tarfs_inode const *ino;
541 if (NULL != (ino = fs->
fs_ino[idx])) {
545 if (NULL != (hdr = (
struct tarhdr const *)ino->in_dvaddr)) {
563 int floating = 0, resolved = 0, attempted = 0;
566 for (
int i = 0; i < count; i++ ) {
568 char *
link_name = (
char *)index[i]->in_next;
574 index[i]->in_dvaddr = 0;
595 log(
"failed to resolve '%s' in two attempts\r\n",
link_name);
600 if (
type == TART_BAD) {
601 log(
"can not get info on inode %d\r\n", dest);
605 if (
type != TART_HARDLINK &&
type != TART_SYMLINK) {
608 tar_print((
const char *)index[i]->in_path, NULL);
610 tar_print((
const char *)index[dest]->in_path, NULL);
614 index[i]->in_dvaddr = index[dest]->in_dvaddr;
619 link_name = (
char *)index[dest]->in_next;
620 log(
"link to a link, continuing to resolve..\r\n");
622 }
while(--depth > 0);
624 if (index[i]->in_dvaddr == 0) {
625 log(
"inode #%d is a floating link\r\n",i);
632 log(
"memory cleanup, release unneded memory chunks\r\n");
634 for (
int i = 0; i < count; i++ ) {
636 const char *
link_name = (
const char *)index[i]->in_next;
638 index[i]->in_next = NULL;
643 log(
"%u of %u links were resolved, floating inodes: %u\r\n",resolved, attempted, floating);
647static char const * s_bad_path =
"<bad path>";
657 const uint8_t *tar_start,
659 const char *link_rebase,
660 const char *root_folder,
665 uint32_t total_data_size = 0;
666 uint32_t total_headers_size = 0;
667 uint32_t overhead = 0;
669 int files = 0, dirs = 0, links = 0, pax_headers = 0, idx = 0, bad_path = 0;
672 unsigned int hdr_no = 0;
673 unsigned int bad = 0, total_bad = 0, total_badcrc = 0;
674 const char *pax_entry_path = NULL, *pax_entry_link = NULL, *pax_entry_end;
675 uintptr_t tar_end = (uintptr_t )((
const uint8_t *)tar_start + tar_length);
682 overhead =
sizeof(
struct tarfs_fs) + nino * (
sizeof(
struct tarfs_inode *) +
sizeof(
struct tarfs_inode));
684 while (off +
sizeof(tarhdr_t) <= tar_length) {
686 const tarhdr_t *hdr = (
const tarhdr_t *)(tar_start + off);
690 pax_entry_path = NULL;
691 pax_entry_link = NULL;
694 log(
"Header #%u is ignored (or NUL-header)\r\n", hdr_no);
696 log(
"Scanning from offset %u..\r\n", (
unsigned int)off);
701 off +=
sizeof(tarhdr_t);
706 log(
"Resuming at offset %u; %u blocks were lost \n", (
unsigned int)off, bad);
715 if (((uintptr_t)(hdr + 1)) +
size >= tar_end) {
716 log(
"Invalid entry size, sector marked as bad\r\n");
720 total_headers_size += 512;
722#if CONFIG_TARFS_INTEGRITY
723 bool bad_crc =
false;
753 total_headers_size +=
size;
757 goto skip_header_and_data;
760 total_data_size +=
size;
770 if (pax_entry_path) {
771 pax_entry_path = remove_subpath(pax_entry_path, pax_entry_end, root_folder);
774 inodes[idx].in_path = (uintptr_t)pax_entry_path;
776 pax_entry_path = NULL;
784 if (hdr->prefix[0]) {
785 char tmp[
sizeof(hdr->prefix) +
sizeof(hdr->name) + 1 + 1];
787 int nlen =
tar_strlen(hdr->name, &hdr->name[0] +
sizeof(hdr->name));
788 int plen =
tar_strlen(hdr->prefix, &hdr->prefix[0] +
sizeof(hdr->prefix));
790 memcpy(tmp,hdr->prefix,plen);
792 memcpy(tmp + plen + 1,hdr->name,nlen);
793 tmp[plen+nlen+1] =
'\0';
795 const char *t = remove_subpath(tmp, tmp+
sizeof(tmp), root_folder);
798 overhead += strlen(t);
805 const char *reb = remove_subpath(hdr->name, &hdr->name[0] +
sizeof(hdr->name), root_folder);
807 if (hdr->name[
sizeof(hdr->name) - 1] != 0) {
809 inodes[idx].in_path = (uintptr_t )
tar_strdup1(reb , &hdr->name[0] +
sizeof(hdr->name));
810 overhead +=
tar_strlen(reb , &hdr->name[0] +
sizeof(hdr->name));
813 inodes[idx].in_path = (uintptr_t)reb;
826 if (hdr->type == TART_SYMLINK || hdr->type == TART_HARDLINK) {
830 if (pax_entry_link) {
832 if (pax_entry_link[0] ==
'/')
833 pax_entry_link = remove_subpath(pax_entry_link, pax_entry_end, link_rebase);
835 pax_entry_link = remove_subpath(pax_entry_link, pax_entry_end, root_folder);
840 inodes[idx].in_next = (
void *)
tar_strdup1(pax_entry_link, pax_entry_end);
842 pax_entry_link = NULL;
845 const char *t = remove_subpath(hdr->link_name, (
char *)(hdr->link_name) +
sizeof(hdr->link_name), hdr->type == TART_SYMLINK ? link_rebase : root_folder);
847 inodes[idx].in_next = (
void *)
tar_strdup1(t, (
char *)(hdr->link_name) +
sizeof(hdr->link_name));
854 if (inodes[idx].in_path == 0) {
855 inodes[idx].in_path = (uintptr_t )s_bad_path;
856 inodes[idx].in_hash = 0 ;
863 int path_len =
tar_strlen((
const char *)inodes[idx].in_path, NULL);
864 inodes[idx].in_hash =
hash32(
HASH32_IV, (uint8_t
const *)inodes[idx].in_path, path_len);
867 inodes[idx].in_vaddr = (uintptr_t)hdr;
868#if CONFIG_TARFS_INTEGRITY
871 inodes[idx].in_dvaddr = 0;
872 log(
"Inode %d, dropped, hash sum mismatch\r\n", idx);
876 inodes[idx].in_dvaddr = (uintptr_t)hdr;
885 if (hdr->type == TART_PAX) {
887 pax_entry_path = path_from_pax_header((
const char *)(hdr + 1),
size,
"path=");
888 pax_entry_link = path_from_pax_header((
const char *)(hdr + 1),
size,
"linkpath=");
889 pax_entry_end = ((
const char *)(hdr + 1)) +
size;
894 off +=
sizeof(tarhdr_t) + (((
size_t)
size + 511) & ~511u);
903 log(
"end of file reached\r\n");
910#if CONFIG_TARFS_INTEGRITY
911 st->badcrc = total_badcrc;
914 return total_data_size;
923 if (fs != NULL && fs->
fs_ino != NULL) {
924 log(
"free inodes\r\n");
946 log(
"PASS1, analyzing..\n");
949 log(
"%u inodes, expected RAM usage: %u bytes of RAM\n",nino, (
unsigned int)(
sizeof(
struct tarfs_fs) + nino * (
sizeof(
struct tarfs_inode) +
sizeof(
struct tarfs_inode *))));
953 log(
"filesystem prefix '%s' \n", base_dir);
956 struct tarfs_inode *inodes = (
struct tarfs_inode *)(index + nino);
961 log(
"PASS2, populating inodes..\n");
968 log(
"building binary search index..\n");
977 log(
"symlinks and hardlinks resolution..\n");
985 log(
"lexigraphical sorting..\n");
990 fs->
fs_ino = (
struct tarfs_inode const *
const *)index;
992 fs->
fs_root = (
struct tarfs_inode const * )root;
1001 log(
"WARN: root directory hash differs from expected %08x != 0x2a0c975e\r\n", (
unsigned int )root->in_hash);
1006 struct tarhdr const *hdr = (
struct tarhdr const *)root->in_vaddr;
1011 log(
"mtime is taken from the root entry\r\n");
1014 log(
"CRITICAL: root->in_vaddr is NULL, this must not happen!\r\n");
1021 log(
"WARN: no root inode after alphasort, opendir() is disabled\r\n");
1035 puts(
"-- HASH SORTED INODES --");
1037 for (
size_t i = 0; i < count; i++) {
1038 struct tarfs_inode const *inode = (
struct tarfs_inode const *)index[i];
1040 printf(
"<%08x> %c %s path=", (
unsigned int)inode->in_hash,
1042 inode->in_vaddr != inode->in_dvaddr ?
"*" :
" ");
1044 tar_print((
char const *)inode->in_path, NULL);
1049 puts(
"Enable CONFIG_TARFS_LOG to use inode_dumphash_sorted()");
1060 puts(
"-- ALPHA SORTED INODES --");
1062 puts(
"< HASH > *X Absolute path:");
1063 for (
size_t i = 0; root != NULL; i++) {
1065 printf(
"<%08x> %s%s path=",
1066 (
unsigned int)root->in_hash,
1067 root->in_vaddr != root->in_dvaddr ?
"*" :
" ",
1068 root->in_dvaddr == 0 ?
"X" :
" ");
1070 tar_print((
char const *)root->in_path, NULL);
1074 root = root->in_next;
1077 puts(
"\r\nLegend: * - Symlink or Hardlink");
1078 puts(
" X - Bad (unresolved) hardlink or symlink");
1080 puts(
"Enable CONFIG_TARFS_LOG to use inode_dumppath_sorted()");
void * tarfs_calloc(size_t count, size_t size)
calloc() based on a memory backend; Memory backend must set errno if there were errors
char * tarfs_strdup(char const *str)
int tarfs_integrity(int en)
Enables or disables CRC64 integrity verification for TARFS archives.
uint32_t hash32(uint32_t prev_hash, const uint8_t *data, size_t len)
Compute FNV-1a hash over a byte buffer.
#define HASH32_IV
1) CRC64/ECMA182 algorithm implementation (no tables version, slow but memory-efficient 2) Optimized ...
size_t inode_populate(struct tarfs_inode *inodes, size_t nino, const uint8_t *tar_start, size_t tar_length, const char *link_rebase, const char *root_folder, struct tarfs_stats *st)
Populate inodes.
struct tarfs_inode * inode_alphasort(struct tarfs_inode *array, size_t count)
Sort inodes alphabetically: we do not move inodes.
int inode_resolve(struct tarfs_inode **index, size_t count)
void inode_dumphash_sorted(struct tarfs_inode const *const *index, size_t count)
Displays inodes sorted by hash.
bool inode_islink(struct tarfs_inode const *ino)
void inode_dumppath_sorted(struct tarfs_inode const *root)
Displays inodes sorted by path.
void inode_sort(struct tarfs_inode **iarr, size_t count)
Sort inode indicies.
void inode_free(struct tarfs_inode **index, size_t count, uintptr_t tar_start, size_t tar_length)
Free inodes.
tart_t inode_getinfo(struct tarfs_inode const *const *index, int idx, size_t *size, time_t *mtime)
inode_getinfo() : get inode's Type, Size and Mtime These are not precached and must be calculated eve...
void inode_unmount(struct tarfs_fs *fs, const void *tar_start, size_t tar_size)
Unmount a TAR image.
tart_t inode_rawtype(struct tarfs_inode const *ino)
Return raw inode type: TART_HARDLINK, TART_SYMLINK, TART_DIR, TART_FILE or TART_BAD.
int inode_mount(struct tarfs_fs *fs, const unsigned char *buf, size_t size, const char *rebase_link, const char *base_dir)
Build an inode index for a TAR image.
struct tarfs_inode ** inode_alloc(size_t count)
Returns an array of pointers to tarfs_inode structures.
int inode_lookup(struct tarfs_inode const *const *index, size_t num_inodes, const char *path)
Find an inode that corresponds to given path name.
time_t inode_mtime(struct tarfs_fs *fs, int idx, size_t *size)
tart_t inode_type(struct tarfs_inode const *ino)
void tarfs_os_free(void *buffer)
This descriptor holds all file descriptors opened.
tarfs_inode_t const * fs_root
struct tarfs_stats fs_stats
tarfs_inode_t const *const * fs_ino
For TAR files with modified PADDING field (see tarsum.c TARFS Checksum Utility): The type and meaning...
char * tar_strdup1(const char *s1, const char *s1_end)
Duplicate a TAR string as a regular NUL-terminated C string.
uint32_t tar_octal(const char *p, size_t max_len)
bool tar_badhdr(tarhdr_t const *hdr)
Validate a TAR header.
int tar_getnino(const uint8_t *tar_start, size_t tar_length)
Quick run through the tarfile to count number of inodes we have to create.
int tar_strcmp(const char *s1, const char *s1_end, const char *s2)
Compare an UTS/CTS to a CTS.
int tar_strlen(const char *s1, const char *s1_end)
Return the length of a TAR string.
bool tar_baddata(struct tarhdr const *hdr, size_t size)
Verify CRC64 checksum stored in a TAR archive, if present.
const char link_name[100]