Add arall:// and liball:// open_many plugins #io (#18196)

This commit is contained in:
Dennis Goodlett 2021-01-10 11:34:38 -05:00 committed by GitHub
parent 32652cbbc8
commit 528e6598a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 191 additions and 50 deletions

View File

@ -205,6 +205,8 @@ R_API RList* r_io_open_many(RIO* io, const char* uri, int perm, int mode) {
}
}
}
// ensure no double free with r_list_close and r_io_free
desc_list->free = NULL;
return desc_list;
}

View File

@ -6,14 +6,34 @@
#include "ar.h"
static const char *r_io_get_individual_schema(const char *file) {
if (r_str_startswith (file, "arall://")) {
return "ar://";
}
if (r_str_startswith (file, "liball://")) {
return "lib://";
}
return NULL;
}
static bool r_io_ar_plugin_open(RIO *io, const char *file, bool many) {
r_return_val_if_fail (io && file, NULL);
if (many) {
return (r_io_get_individual_schema (file) != NULL);
}
return !strncmp ("ar://", file, 5) || !strncmp ("lib://", file, 6);
}
static int r_io_ar_close(RIODesc *fd) {
if (!fd || !fd->data) {
return -1;
}
return ar_close ((RArFp *)fd->data);
}
static RIODesc *r_io_ar_open(RIO *io, const char *file, int rw, int mode) {
RIODesc *res = NULL;
char *url = strdup (file);
char *arname = strstr (url, "://") + 3;
r_return_val_if_fail (r_io_ar_plugin_open (io, file, false), NULL);
const char *arname = strstr (file, "://") + 3;
char *filename = strstr (arname, "//");
if (filename) {
*filename = 0;
@ -21,16 +41,61 @@ static RIODesc *r_io_ar_open(RIO *io, const char *file, int rw, int mode) {
}
RArFp *arf = ar_open_file (arname, filename);
RIODesc *res = NULL;
if (arf) {
res = r_io_desc_new (io, &r_io_plugin_ar, filename, rw, mode, arf);
res = r_io_desc_new (io, &r_io_plugin_ar, file, rw, mode, arf);
if (res) {
res->name = strdup (filename);
}
}
free (url);
return res;
}
typedef struct ar_many_data {
const char *schema;
const char *arname;
RIO *io;
bool rw;
int mode;
RList *list;
} ar_many_data;
static int __io_ar_list(RArFp *arf, void *user) {
ar_many_data *data = (ar_many_data *)user;
char *uri = r_str_newf ("%s%s//%s", data->schema, data->arname, arf->name);
RIODesc *des = r_io_desc_new (data->io, &r_io_plugin_ar, uri, data->rw, data->mode, arf);
free (uri);
if (!des) {
ar_close (arf);
return -1; // stop error
}
des->name = strdup (arf->name);
if (!r_list_append (data->list, des)) {
r_io_ar_close (des);
return -1; // stop error
}
return 0; // continue
}
static RList *r_io_ar_open_many(RIO *io, const char *file, int rw, int mode) {
eprintf ("Not implemented\n");
return NULL;
r_return_val_if_fail (io && file, NULL);
ar_many_data data;
if ((data.schema = r_io_get_individual_schema (file)) == NULL) {
r_warn_if_reached ();
return NULL;
}
data.io = io;
data.rw = rw;
data.mode = mode;
data.arname = strstr (file, "://") + 3;
data.list = r_list_newf ((RListFree)r_io_ar_close);
if (data.list && ar_open_all_cb (data.arname, (RArOpenManyCB)__io_ar_list, (void *)&data) < 0) {
r_list_free (data.list);
return NULL;
}
return data.list;
}
static ut64 r_io_ar_lseek(RIO *io, RIODesc *fd, ut64 offset, int whence) {
@ -69,18 +134,11 @@ static int r_io_ar_write(RIO *io, RIODesc *fd, const ut8 *buf, int count) {
return ar_write_at ((RArFp *) fd->data, io->off, (void *) buf, count);
}
static int r_io_ar_close(RIODesc *fd) {
if (!fd || !fd->data) {
return -1;
}
return ar_close ((RArFp *) fd->data);
}
RIOPlugin r_io_plugin_ar = {
.name = "ar",
.desc = "Open ar/lib files",
.license = "LGPL3",
.uris = "ar://,lib://",
.uris = "ar://,lib://,arall://,liball://",
.open = r_io_ar_open,
.open_many = r_io_ar_open_many,
.write = r_io_ar_write,

View File

@ -75,7 +75,7 @@ static char *name_from_table(ut64 off, filetable *tbl) {
return -1; \
}
/* -1 error, 0 end, 1 contnue */
/* -1 error, 0 continue, 1 finished */
static int ar_parse_header(RArFp *arf, filetable *tbl, ut64 arsize) {
r_return_val_if_fail (arf && arf->buf && tbl, -1);
RBuffer *b = arf->buf;
@ -103,12 +103,12 @@ static int ar_parse_header(RArFp *arf, filetable *tbl, ut64 arsize) {
int r = r_buf_read (b, (ut8 *)&h, sizeof (h));
if (r != sizeof (h)) {
if (r == 0) {
return 0; // no more file
return 1; // no more file
}
if (r < 0) {
eprintf ("io_ar: io error\n");
eprintf ("AR read io error\n");
} else {
eprintf ("io_ar: Invalid file length\n");
eprintf ("Malformed AR: Invalid length while parsing header at 0x%" PFMT64x "\n", h_off);
}
return -1;
}
@ -207,12 +207,29 @@ static int ar_parse_header(RArFp *arf, filetable *tbl, ut64 arsize) {
return -1;
}
return 1;
return 0;
}
#undef VERIFY_AR_NUM_FIELD
typedef struct single_file_data {
const char *name;
RArFp **ret;
} single_file_data;
static int __ar_open_file_cb(RArFp *arf, void *user) {
single_file_data *data = user;
if (!data->name) {
printf ("%s\n", arf->name);
} else if (!strcmp (data->name, arf->name)) {
*data->ret = arf;
return 1; // stop success
}
ar_close (arf);
return 0; // continue
}
/**
* \brief Open specific file withen a ar/lib file.
* \brief Open specific file within a ar/lib file.
* \param arname the name of the .a file
* \param filename the name of file in the .a file that you wish to open
* \return a handle of the internal filename or NULL
@ -221,10 +238,64 @@ static int ar_parse_header(RArFp *arf, filetable *tbl, ut64 arsize) {
* listed.
*/
R_API RArFp *ar_open_file(const char *arname, const char *filename) {
RArFp *ret = NULL;
single_file_data data;
data.name = filename;
data.ret = &ret;
if (ar_open_all_cb (arname, (RArOpenManyCB)__ar_open_file_cb, (void *)&data) < 0) {
ar_close (ret);
return NULL;
}
return ret;
}
static int __ar_open_list_cb(RArFp *arf, void *user) {
RList *l = user;
if (!r_list_append (l, arf)) {
ar_close (arf);
return -1; // stop error
}
return 0; // continue
}
/**
* \brief Get a RList* of handles to every file in ar
* \param arname the name of the .a file
* \return RList* containg a RArFp* per file
*
* Open an ar/lib file by name. If filename is NULL, then archive files will be
* listed.
*/
R_API RList *ar_open_all(const char *arname) {
RList *list = r_list_newf ((RListFree)ar_close);
if (!list) {
return NULL;
}
if (ar_open_all_cb (arname, (RArOpenManyCB)__ar_open_list_cb, (void *)list) > 0) {
return list;
}
r_list_free (list);
return NULL;
}
/**
* \brief Send all files in AR file to callback
* \param arname the name of the .a file
* \param cb callback function
* \param user (void *) data to be passed to callback
* \return bool success or error
*
* Each file in AR will be parsed into a RArFp* and sent to the callback. The
* callback is trusted to close all the RArFp*'s it receives. Callback should
* return an int <0 on error, 0 if the callback wants to receive more files and
* >0 if it's done.
*/
R_API int ar_open_all_cb(const char *arname, RArOpenManyCB cb, void *user) {
r_return_val_if_fail (arname, -1);
RBuffer *b = r_buf_new_file (arname, O_RDWR, 0);
if (!b) {
r_sys_perror (__FUNCTION__);
return NULL;
return -1;
}
r_buf_seek (b, 0, R_BUF_END);
@ -233,53 +304,59 @@ R_API RArFp *ar_open_file(const char *arname, const char *filename) {
if (!ar_check_magic (b)) {
r_buf_free (b);
return NULL;
return -1;
}
RArFp *arf = arfp_new (b, NULL);
if (!arf) {
filetable tbl = { NULL, 0, 0 };
ut32 *refc = R_NEW (ut32);
if (!refc) {
r_buf_free (b);
return NULL;
free (refc);
return -1;
}
/* The refcount is artificially inflated here. This allows the callback to
* use ar_close without fear of free'ing the RBuffer. The refcounter must
* be decremented later before returning.
*/
*refc = 1;
filetable tbl = {NULL, 0, 0};
int r;
while ((r = ar_parse_header (arf, &tbl, arsize)) > 0) {
if (filename) {
if (!strcmp (filename, arf->name)) {
// found the right file
break;
}
} else {
printf ("%s\n", arf->name);
int r = 0;
while (!r) {
RArFp *arf = arfp_new (b, refc);
if (!arf) {
r = -1;
break;
}
r = ar_parse_header (arf, &tbl, arsize);
if (!r) {
r = cb (arf, user);
} else {
ar_close (arf);
}
// clean RArFp for next loop
arf_clean_name (arf);
}
free (tbl.data);
if (r <= 0) {
if (r == 0 && filename) {
eprintf ("Cound not find file '%s' in archive '%s'\n", filename, arname);
}
ar_close (arf); // results in buf being free'd
return NULL;
if (*refc == 1) {
// the cb closed all the RArFp's, so we free these resources
free (refc);
r_buf_free (b);
} else {
// return recf to true value
(*refc)--;
}
return arf;
return r;
}
R_API int ar_close(RArFp *f) {
if (f) {
free (f->name);
if (f->refcount) {
(*f->refcount)--;
}
(*f->refcount)--;
// no more files open, clean underlying buffer
if (!f->refcount || f->refcount == 0) {
if (*f->refcount == 0) {
free (f->refcount);
r_buf_free (f->buf);
}

View File

@ -10,9 +10,13 @@ typedef struct RARFP {
ut32 *refcount;
} RArFp;
typedef int (*RArOpenManyCB) (RArFp *arf, void *user);
/* Offset passed is always the real io->off of the inspected file,
* the functions automatically translate it to relative offset within the archive */
R_API RArFp *ar_open_file(const char *arname, const char *filename);
R_API RList *ar_open_all(const char *arname);
R_API int ar_open_all_cb(const char *arname, RArOpenManyCB cb, void *user);
R_API int ar_close(RArFp *f);
R_API int ar_read_at(RArFp *f, ut64 off, void *buf, int count);
R_API int ar_write_at(RArFp *f, ut64 off, void *buf, int count);