2017-08-16 08:20:21 +00:00
|
|
|
/* radare - LGPLv3 - Copyright 2017 - xarkes */
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <r_util.h>
|
|
|
|
#include "ar.h"
|
|
|
|
|
2017-08-22 23:07:21 +00:00
|
|
|
#define BUF_SIZE 512
|
|
|
|
|
2017-08-16 08:20:21 +00:00
|
|
|
const char *AR_MAGIC_HEADER = "!<arch>\n";
|
|
|
|
const char *AR_FILE_HEADER_END = "`\n";
|
2017-08-24 11:37:20 +00:00
|
|
|
const int AR_FILENAME_LEN = 16;
|
2017-08-16 08:20:21 +00:00
|
|
|
|
2017-08-22 23:07:21 +00:00
|
|
|
/* Used to lookup filename table */
|
|
|
|
static int index_filename = -2;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open an ar/lib file. If filename is NULL, list archive files
|
|
|
|
*/
|
2017-08-16 08:20:21 +00:00
|
|
|
R_API RBuffer *ar_open_file(const char *arname, const char *filename) {
|
|
|
|
int r;
|
2017-08-23 10:55:10 +00:00
|
|
|
RList *files = NULL;
|
2019-05-15 13:34:06 +00:00
|
|
|
RBuffer *b = r_buf_new_file (arname, O_RDWR, 0);
|
2017-08-16 08:20:21 +00:00
|
|
|
if (!b) {
|
|
|
|
r_sys_perror (__FUNCTION__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read magic header */
|
2017-08-23 14:45:05 +00:00
|
|
|
char *buffer = calloc (1, BUF_SIZE + 1);
|
2017-08-22 23:07:21 +00:00
|
|
|
if (!buffer) {
|
|
|
|
return NULL;
|
2017-08-16 08:20:21 +00:00
|
|
|
}
|
2017-08-22 23:07:21 +00:00
|
|
|
r = ar_read_header (b, buffer);
|
|
|
|
if (!r) {
|
|
|
|
goto fail;
|
2017-08-16 08:20:21 +00:00
|
|
|
}
|
2017-08-23 10:55:10 +00:00
|
|
|
files = r_list_new ();
|
2017-08-22 23:07:21 +00:00
|
|
|
/* Parse archive */
|
2017-08-24 11:37:20 +00:00
|
|
|
ar_read_file (b, buffer, true, NULL, NULL);
|
2017-08-22 23:07:21 +00:00
|
|
|
ar_read_filename_table (b, buffer, files, filename);
|
|
|
|
|
|
|
|
/* If b->base is set, then we found the file root in the archive */
|
2019-05-15 13:34:06 +00:00
|
|
|
while (r) {
|
2017-08-24 11:37:20 +00:00
|
|
|
r = ar_read_file (b, buffer, false, files, filename);
|
2017-08-22 23:07:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!filename) {
|
|
|
|
RListIter *iter;
|
|
|
|
char *name;
|
|
|
|
r_list_foreach (files, iter, name) {
|
|
|
|
printf ("%s\n", name);
|
2017-08-16 08:20:21 +00:00
|
|
|
}
|
2017-08-22 23:07:21 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!r) {
|
|
|
|
goto fail;
|
2017-08-16 08:20:21 +00:00
|
|
|
}
|
2017-08-22 23:07:21 +00:00
|
|
|
|
2017-08-16 08:20:21 +00:00
|
|
|
free (buffer);
|
2017-08-22 23:07:21 +00:00
|
|
|
r_list_free (files);
|
2017-08-16 08:20:21 +00:00
|
|
|
return b;
|
2017-08-22 23:07:21 +00:00
|
|
|
fail:
|
|
|
|
r_list_free (files);
|
2017-08-16 08:20:21 +00:00
|
|
|
free (buffer);
|
|
|
|
ar_close (b);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
R_API int ar_close(RBuffer *b) {
|
|
|
|
r_buf_free (b);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
R_API int ar_read_at(RBuffer *b, ut64 off, void *buf, int count) {
|
2019-05-15 13:34:06 +00:00
|
|
|
return r_buf_read_at (b, off, buf, count);
|
2017-08-16 08:20:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
R_API int ar_write_at(RBuffer *b, ut64 off, void *buf, int count) {
|
2019-05-15 13:34:06 +00:00
|
|
|
return r_buf_write_at (b, off, buf, count);
|
2017-08-16 08:20:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int ar_read(RBuffer *b, void *dest, int len) {
|
2019-03-15 19:28:52 +00:00
|
|
|
int r = r_buf_read (b, dest, len);
|
2017-08-16 08:20:21 +00:00
|
|
|
if (!r) {
|
|
|
|
return 0;
|
|
|
|
}
|
2019-05-15 13:34:06 +00:00
|
|
|
r_buf_seek (b, r, R_BUF_CUR);
|
2017-08-16 08:20:21 +00:00
|
|
|
return r;
|
|
|
|
}
|
2017-08-22 23:07:21 +00:00
|
|
|
|
|
|
|
int ar_read_until_slash(RBuffer *b, char *buffer, int limit) {
|
|
|
|
ut32 i = 0;
|
|
|
|
ut32 lim = (limit && limit < BUF_SIZE)? limit: BUF_SIZE;
|
|
|
|
while (i < lim) {
|
|
|
|
ar_read (b, buffer + i, 1);
|
|
|
|
if (buffer[i] == '/') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
buffer[i] = '\0';
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ar_read_header(RBuffer *b, char *buffer) {
|
|
|
|
int r = ar_read (b, buffer, 8);
|
|
|
|
if (!r) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (strncmp (buffer, AR_MAGIC_HEADER, 8)) {
|
|
|
|
eprintf ("Wrong file type.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2017-08-24 11:37:20 +00:00
|
|
|
int ar_read_file(RBuffer *b, char *buffer, bool lookup, RList *files, const char *filename) {
|
2017-08-22 23:07:21 +00:00
|
|
|
ut64 filesize = 0;
|
|
|
|
char *tmp = NULL;
|
|
|
|
char *curfile = NULL;
|
|
|
|
ut32 index = -1;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
/* File identifier */
|
|
|
|
if (lookup) {
|
2017-08-24 11:37:20 +00:00
|
|
|
ar_read (b, buffer, AR_FILENAME_LEN);
|
2017-08-22 23:07:21 +00:00
|
|
|
} else {
|
2017-08-24 11:37:20 +00:00
|
|
|
ar_read (b, buffer, 2);
|
|
|
|
/* Fix padding issues */
|
|
|
|
if (*buffer == '\n') {
|
|
|
|
buffer[0] = buffer[1];
|
2019-05-15 13:34:06 +00:00
|
|
|
r_buf_seek (b, -1, R_BUF_CUR);
|
2017-08-24 11:37:20 +00:00
|
|
|
ar_read (b, buffer, 2);
|
|
|
|
}
|
|
|
|
ar_read (b, buffer + 2, AR_FILENAME_LEN - 2);
|
2017-08-22 23:07:21 +00:00
|
|
|
}
|
2017-08-24 11:37:20 +00:00
|
|
|
buffer[AR_FILENAME_LEN] = '\0';
|
2017-08-22 23:07:21 +00:00
|
|
|
/* Fix some padding issues */
|
2017-08-24 11:37:20 +00:00
|
|
|
if (buffer[AR_FILENAME_LEN - 1] != '/' && buffer[AR_FILENAME_LEN - 1] != ' ') {
|
|
|
|
// Find padding offset
|
2017-08-23 14:45:05 +00:00
|
|
|
tmp = (char *)r_str_lchr (buffer, ' ');
|
|
|
|
if (!tmp) {
|
2017-08-24 11:37:20 +00:00
|
|
|
goto fail;
|
2017-08-23 14:45:05 +00:00
|
|
|
}
|
2017-08-22 23:07:21 +00:00
|
|
|
int dif = (int) (tmp - buffer);
|
|
|
|
dif = 31 - dif;
|
2017-08-24 11:37:20 +00:00
|
|
|
// Re-read the whole filename
|
2019-05-15 13:34:06 +00:00
|
|
|
r_buf_seek (b, -dif, R_BUF_CUR);
|
2017-08-24 11:37:20 +00:00
|
|
|
r = ar_read (b, buffer, AR_FILENAME_LEN);
|
2018-12-13 23:17:50 +00:00
|
|
|
if (r != AR_FILENAME_LEN) {
|
|
|
|
goto fail;
|
|
|
|
}
|
2017-08-22 23:07:21 +00:00
|
|
|
}
|
|
|
|
free (curfile);
|
|
|
|
curfile = strdup (buffer);
|
|
|
|
if (!curfile) {
|
2017-08-24 11:37:20 +00:00
|
|
|
goto fail;
|
2017-08-22 23:07:21 +00:00
|
|
|
}
|
|
|
|
/* If /XX then refer to filename table later */
|
|
|
|
if (curfile[0] == '/' && curfile[1] >= '0' && curfile[1] <= '9') {
|
|
|
|
index = strtoul (buffer + 1, NULL, 10);
|
|
|
|
} else if (curfile[0] != '/') {
|
|
|
|
/* Read filename */
|
|
|
|
tmp = strchr (curfile, '/');
|
|
|
|
if (!tmp) {
|
2017-08-24 11:37:20 +00:00
|
|
|
goto fail;
|
2017-08-22 23:07:21 +00:00
|
|
|
}
|
|
|
|
*tmp = '\0';
|
|
|
|
if (files) {
|
|
|
|
r_list_append (files, strdup (curfile));
|
|
|
|
}
|
|
|
|
}
|
2017-08-24 11:37:20 +00:00
|
|
|
/* Timestamp 12
|
|
|
|
* Owner ID 6
|
|
|
|
* Group ID 6
|
|
|
|
* File Mode 8
|
|
|
|
* File Size 10
|
|
|
|
* Header end 2 */
|
|
|
|
r = ar_read (b, buffer, 44);
|
|
|
|
if (r != 44) {
|
|
|
|
goto fail;
|
2017-08-22 23:07:21 +00:00
|
|
|
}
|
2017-08-24 11:37:20 +00:00
|
|
|
|
|
|
|
filesize = strtoull (buffer + 32, &tmp, 10);
|
|
|
|
|
2017-08-22 23:07:21 +00:00
|
|
|
/* Header end */
|
2017-08-24 11:37:20 +00:00
|
|
|
if (strncmp (buffer + 42, AR_FILE_HEADER_END, 2)) {
|
|
|
|
goto fail;
|
2017-08-22 23:07:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* File content */
|
|
|
|
if (!lookup && filename) {
|
|
|
|
/* Check filename */
|
|
|
|
if (index == index_filename || !strcmp (curfile, filename)) {
|
2019-03-15 19:28:52 +00:00
|
|
|
r_buf_resize(b, filesize);
|
2017-08-23 14:45:05 +00:00
|
|
|
free (curfile);
|
2019-03-15 19:28:52 +00:00
|
|
|
return r_buf_size (b);
|
2017-08-22 23:07:21 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-06 16:00:08 +00:00
|
|
|
(void)ar_read (b, buffer, 1);
|
2017-08-22 23:07:21 +00:00
|
|
|
|
2019-05-15 13:34:06 +00:00
|
|
|
r_buf_seek (b, filesize - 1, R_BUF_CUR);
|
2017-08-23 14:45:05 +00:00
|
|
|
free (curfile);
|
2017-08-22 23:07:21 +00:00
|
|
|
return filesize;
|
2017-08-24 11:37:20 +00:00
|
|
|
fail:
|
|
|
|
free (curfile);
|
|
|
|
return 0;
|
2017-08-22 23:07:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int ar_read_filename_table(RBuffer *b, char *buffer, RList *files, const char *filename) {
|
2017-08-24 11:37:20 +00:00
|
|
|
int r = ar_read (b, buffer, AR_FILENAME_LEN);
|
|
|
|
if (r != AR_FILENAME_LEN) {
|
2017-08-22 23:07:21 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (strncmp (buffer, "//", 2)) {
|
2017-08-24 11:37:20 +00:00
|
|
|
// What we read was not a filename table, just go back
|
2019-05-15 13:34:06 +00:00
|
|
|
r_buf_seek (b, -AR_FILENAME_LEN, R_BUF_CUR);
|
2017-08-22 23:07:21 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read table size */
|
2019-05-15 13:34:06 +00:00
|
|
|
r_buf_seek (b, 32, R_BUF_CUR);
|
2017-08-22 23:07:21 +00:00
|
|
|
r = ar_read (b, buffer, 10);
|
2018-12-13 23:17:50 +00:00
|
|
|
if (r != 10) {
|
|
|
|
return 0;
|
|
|
|
}
|
2017-08-22 23:07:21 +00:00
|
|
|
ut64 tablesize = strtoull (buffer, NULL, 10);
|
|
|
|
|
|
|
|
/* Header end */
|
|
|
|
r = ar_read (b, buffer, 2);
|
|
|
|
if (strncmp (buffer, AR_FILE_HEADER_END, 2)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read table */
|
|
|
|
ut64 len = 0;
|
|
|
|
ut32 index = 0;
|
|
|
|
while (r && len < tablesize) {
|
|
|
|
r = ar_read_until_slash (b, buffer, tablesize - len);
|
|
|
|
if (filename && !strcmp (filename, (char *) buffer)) {
|
|
|
|
index_filename = index;
|
|
|
|
}
|
|
|
|
if (*(char *) buffer == '\n') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
r_list_append (files, strdup ((char *) buffer));
|
|
|
|
/* End slash plus separation character ("/\n") */
|
|
|
|
len += r + 2;
|
|
|
|
/* Separation character (not always '\n') */
|
2019-05-15 13:34:06 +00:00
|
|
|
r_buf_seek (b, 1, R_BUF_CUR);
|
2017-08-22 23:07:21 +00:00
|
|
|
index++;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|