From 528e6598a17b11796c936c62c6aaca2cffb89cea Mon Sep 17 00:00:00 2001 From: Dennis Goodlett Date: Sun, 10 Jan 2021 11:34:38 -0500 Subject: [PATCH] Add arall:// and liball:// open_many plugins #io (#18196) --- libr/io/io.c | 2 + libr/io/p/io_ar.c | 88 ++++++++++++++++++++++----- shlr/ar/ar.c | 147 +++++++++++++++++++++++++++++++++++----------- shlr/ar/ar.h | 4 ++ 4 files changed, 191 insertions(+), 50 deletions(-) diff --git a/libr/io/io.c b/libr/io/io.c index 73c4015049..f843878d18 100644 --- a/libr/io/io.c +++ b/libr/io/io.c @@ -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; } diff --git a/libr/io/p/io_ar.c b/libr/io/p/io_ar.c index 6dbacdd1c0..00d609badb 100644 --- a/libr/io/p/io_ar.c +++ b/libr/io/p/io_ar.c @@ -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, diff --git a/shlr/ar/ar.c b/shlr/ar/ar.c index 6075e4834a..cc2f7ff25d 100644 --- a/shlr/ar/ar.c +++ b/shlr/ar/ar.c @@ -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); } diff --git a/shlr/ar/ar.h b/shlr/ar/ar.h index 10986752e4..3275051b98 100644 --- a/shlr/ar/ar.h +++ b/shlr/ar/ar.h @@ -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);