/* radare - LGPL - Copyright 2008-2016 - pancake */ #include "r_io.h" #include "r_util.h" #include R_LIB_VERSION (r_io); /* allocate 128 MB */ #define R_IO_MAX_ALLOC (1024 * 1024 * 128) // XXX: this is buggy. must use seek+read #define USE_CACHE 1 // the new io is buggy //liar #define USE_NEW_IO 0 #define DO_THE_IO_DBG 0 #define IO_IFDBG if (DO_THE_IO_DBG == 1) static ut8 *r_io_desc_read(RIO *io, RIODesc *desc, ut64 *out_sz); static RIO *r_io_bind_get_io(RIOBind *bnd); R_API RIO *r_io_new() { RIO *io = R_NEW0 (RIO); if (!io) { return NULL; } io->buffer = r_cache_new (); // RCache is a list of ranged buffers. maybe rename? io->write_mask_fd = -1; io->cb_printf = (void *)printf; io->bits = (sizeof (void *) == 8)? 64: 32; io->ff = true; io->Oxff = 0xff; io->aslr = 0; io->pava = false; io->raised = -1; io->autofd = true; r_io_map_init (io); r_io_desc_init (io); r_io_undo_init (io); r_io_cache_init (io); r_io_plugin_init (io); r_io_section_init (io); { char *env = r_sys_getenv ("R_IO_MAX_ALLOC"); if (env) { io->maxalloc = r_num_get (NULL, env); free (env); } } return io; } R_API void r_io_raise(RIO *io, int fd) { io->raised = fd; } R_API int r_io_is_listener(RIO *io) { if (io && io->plugin && io->plugin->listener) { return io->plugin->listener (io->desc); } return false; } R_API RBuffer *r_io_read_buf(RIO *io, ut64 addr, int len) { RBuffer *b = R_NEW0 (RBuffer); if (!b) return NULL; b->buf = malloc (len); if (!b->buf) { free (b); return NULL; } len = r_io_read_at (io, addr, b->buf, len); b->length = (len < 0)? 0: len; return b; } R_API int r_io_write_buf(RIO *io, struct r_buf_t *b) { return r_io_write_at (io, b->base, b->buf, b->length); } R_API RIO *r_io_free(RIO *io) { if (!io) { return NULL; } R_FREE (io->plugin_default); r_list_free (io->plugins); r_list_free (io->sections); r_list_free (io->maps); r_list_free (io->undo.w_list); r_cache_free (io->buffer); r_list_free (io->cache); r_io_desc_fini (io); free (io); return NULL; } /* used by uri handler plugins */ R_API int r_io_redirect(RIO *io, const char *file) { free (io->redirect); io->redirect = file? strdup (file): NULL; return 0; } R_API RIODesc *r_io_open_as(RIO *io, const char *urihandler, const char *file, int flags, int mode) { RIODesc *ret; char *uri; int urilen, hlen = strlen (urihandler); urilen = hlen + strlen (file) + 5; uri = malloc (urilen); if (!uri) return NULL; if (hlen > 0) snprintf (uri, urilen, "%s://%s", urihandler, file); else strncpy (uri, file, urilen); ret = r_io_open_nomap (io, uri, flags, mode); free (uri); return ret; } static inline RIODesc *__getioplugin(RIO *io, const char *_uri, int flags, int mode) { RIOPlugin *plugin; RIODesc *desc = NULL; char *uri = strdup (_uri? _uri: ""); char *redir = NULL; for (;;) { plugin = r_io_plugin_resolve (io, uri, 0); if (plugin && plugin->open) { desc = plugin->open (io, uri, flags, mode); if (io->redirect) { redir = uri; uri = strdup (io->redirect); r_io_redirect (io, NULL); continue; } if (desc) { desc->uri = uri; desc->referer = redir; io->plugin = plugin; } } break; } if (!desc) { plugin = r_io_plugin_get_default (io, uri, 0); desc = (plugin && plugin->open) ? plugin->open (io, uri, flags, mode) : NULL; if (desc) { desc->uri = uri; io->plugin = plugin; } } if (!desc) { free (uri); io->plugin = NULL; } return desc; } static inline RList *__getioplugin_many(RIO *io, const char *_uri, int flags, int mode) { RIOPlugin *plugin, *iop = NULL; RList *list_fds = NULL; RListIter *iter; RIODesc *desc; char *uri = strdup (_uri); for (;;) { // resolve plugin = r_io_plugin_resolve (io, uri, 1); if (plugin && plugin->open_many) { // open list_fds = plugin->open_many (io, uri, flags, mode); if (io->redirect) { free (uri); uri = strdup (io->redirect); r_io_redirect (io, NULL); continue; } } break; } if (!list_fds) { free (uri); return NULL; } r_list_foreach (list_fds, iter, desc) { desc->uri = strdup (uri); } io->plugin = iop; free (uri); return list_fds; } R_API RIODesc *r_io_open_nomap(RIO *io, const char *file, int flags, int mode) { RIODesc *desc; if (!io || !file || io->redirect) { return NULL; } desc = __getioplugin (io, file, flags, mode); if (desc) { r_io_desc_add (io, desc); if (io->autofd || !io->desc) r_io_use_desc (io, desc); } // else eprintf ("r_io_open_nomap: Unable to open file: %s\n", file); return desc; } R_API RIODesc *r_io_open_at(RIO *io, const char *file, int flags, int mode, ut64 maddr) { RIODesc *desc; ut64 size; if (!io || !file || io->redirect) { return NULL; } desc = __getioplugin (io, file, flags, mode); if (desc) { r_io_desc_add (io, desc); size = r_io_desc_size (io, desc); if (io->autofd || !io->desc) { r_io_use_desc (io, desc); } r_io_map_new (io, desc->fd, mode, 0, maddr, size); } else { eprintf ("r_io_open_at: Unable to open file: %s\n", file); } return desc; } R_API RIODesc *r_io_open(RIO *io, const char *file, int flags, int mode) { return r_io_open_at (io, file, flags, mode, 0LL); } R_API RList *r_io_open_many(RIO *io, const char *file, int flags, int mode) { RIODesc *desc; RListIter *desc_iter = NULL; int fd; RList *list_fds; if (!io || !file || io->redirect) return NULL; list_fds = __getioplugin_many (io, file, flags, mode); if (!list_fds) return NULL; r_list_foreach (list_fds, desc_iter, desc) { fd = -1; if (desc) fd = desc->fd; if (fd >= 0) r_io_desc_add (io, desc); } return list_fds; } R_API int r_io_reopen(RIO *io, RIODesc *desc, int flags, int mode) { RIODesc *n = NULL; RListIter *iter; RIOMap *map; if (desc && desc->uri && io && io->files && (desc == r_io_desc_get (io, desc->fd))) { n = __getioplugin (io, desc->uri, flags, mode); if (!n) { return false; } r_io_section_rm_all (io, desc->fd); if (io->maps) { r_list_foreach (io->maps, iter, map) { if (map->fd == desc->fd) { map->fd = n->fd; map->flags &= n->flags; } } } if (desc->plugin && desc->plugin->close) desc->plugin->close (desc); //free desc->data free (desc->name); free (desc->uri); *desc = *n; free (n); return true; } return false; } R_API int r_io_use_desc(RIO *io, RIODesc *d) { if (io && d && d->plugin) { io->desc = d; io->plugin = d->plugin; return true; } return false; } R_API RIODesc *r_io_use_fd(RIO *io, int fd) { RIODesc *desc = r_io_desc_get (io, fd); if (!desc) return NULL; io->desc = desc; io->plugin = desc->plugin; return desc; } static bool readcache = false; R_API int r_io_read_internal(RIO *io, ut8 *buf, int len) { int bytes_read = 0; const char *source = NULL; if (io->desc && io->desc->plugin && io->desc->plugin->read) { source = io->desc->plugin->name; bytes_read = io->desc->plugin->read (io, io->desc, buf, len); #if 1 if (readcache) { if (bytes_read > 0) { readcache = false; bytes_read = r_io_cache_write (io, io->off, buf, len); readcache = true; } } #endif } else if (!io->desc) { if (io->files && r_list_length (io->files) != 0) { eprintf ("Something really bad has happened, and r2 is going to die soon. sorry! :-(\n"); } source = "FAILED"; bytes_read = 0; } else { source = "File"; bytes_read = read (io->desc->fd, buf, len); } IO_IFDBG { if (io->desc) eprintf ("Data source: %s\n", io->desc->name); eprintf ("Asked for %d bytes, provided %d from %s\n", len, bytes_read, source); } return bytes_read; } R_API int r_io_read(RIO *io, ut8 *buf, int len) { int ret; if (!io || !io->desc || !buf || io->off == UT64_MAX) return -1; /* IGNORE check section permissions */ if (io->enforce_rwx & R_IO_READ) { if (!(r_io_section_get_rwx (io, io->off) & R_IO_READ)) { return -1; } } /* io->off is in maddr, but r_io_read_at works in vaddr * FIXME: in some cases, r_io_seek sets io->off in vaddr */ ut64 vaddr = r_io_section_maddr_to_vaddr(io, io->off); vaddr = (vaddr == UT64_MAX) ? io->off : vaddr; ret = r_io_read_at (io, vaddr, buf, len); if (ret > 0) io->off += ret; return ret; } int r_io_read_cr (RIO *io, ut64 addr, ut8 *buf, int len) { RList *maps; RListIter *iter; RIOMap *map; if (!io) return R_FAIL; if (io->ff) memset (buf, io->Oxff, len); if (io->raw) { r_io_seek (io, addr, R_IO_SEEK_SET); return r_io_read_internal (io, buf, len); } if (io->va) { r_io_vread (io, addr, buf, len); //must check return-stat if (io->cached) r_io_cache_read (io, addr, buf, len); return len; } maps = r_io_map_get_maps_in_range (io, addr, addr + len); r_list_foreach (maps, iter, map) { r_io_mread (io, map->fd, addr, buf, len); //must check return-stat } r_io_mread (io, io->desc->fd, addr, buf, len); //must check return-stat if (io->cached) r_io_cache_read (io, addr, buf, len); r_list_free (maps); return len; } R_API int r_io_read_at(RIO *io, ut64 addr, ut8 *buf, int len) { ut64 paddr, last, last2; int ms, ret, l = 0, olen = len, w = 0; if (!io || !buf || len < 0) { return 0; } if (io->vio) { return r_io_read_cr (io, addr, buf, len); } if (io->sectonly && !r_list_empty (io->sections)) { if (!r_io_section_exists_for_vaddr (io, addr, 0)) { // find next sec memset (buf, io->Oxff, len); ut64 next = r_io_section_next (io, addr); if (next < (addr + len)) { int delta = (next - addr); addr = next; len -= delta; buf += delta; } else { next = 0; } if (!next) { return 0; } } } if (io->raw) { if (r_io_seek (io, addr, R_IO_SEEK_SET) == UT64_MAX) { memset (buf, io->Oxff, len); } return r_io_read_internal (io, buf, len); } io->off = addr; memset (buf, io->Oxff, len); // probably unnecessary if (io->buffer_enabled) { return r_io_buffer_read (io, addr, buf, len); } while (len > 0) { if ((addr + w) < ((addr + w) + len)) { // this code assumes that the IO backend knows // 1) the size of a loaded file and its offset into the r2 data space // 2) the sections with physical (offsets) and virtual addresses in r2 data space // Currently debuggers may not support registering these data spaces in r2 and this // may prevent "raw" access to locations in the data space for entities like debuggers. // Until that issue is resolved this code will be disabled. // step one does a section exist for the offset int exists = r_io_section_exists_for_paddr (io, addr + w, 0) || r_io_section_exists_for_vaddr (io, addr + w, 0) || r_io_map_exists_for_offset (io, addr + w); // XXX this is a break b/c external IO caller do not need to create // an IO Map (yet.), so the "checking existence of" only works if r_core_file // APIs are used to load files. if (!exists && r_io_map_count (io) > 0) { // XXX this will break if there is actually data at this location // or within UT64_MAX - len ut64 next_map_addr = UT64_MAX; ut64 next_sec_addr = UT64_MAX; RIOMap *next_map = NULL; RIOSection *next_sec = NULL; // is there a map somewhere within the next range for // us to read from next_sec = r_io_section_get_first_in_vaddr_range (io, addr + w, addr + len + w); next_sec_addr = next_sec? next_sec->paddr: UT64_MAX; if (!next_sec) { next_map = r_io_map_get_first_map_in_range (io, addr + w, addr + len + w); next_map_addr = next_map? next_map->from: UT64_MAX; if (len <= next_map_addr - addr) next_map_addr = UT64_MAX; else l = next_map_addr - addr; } else if (len <= next_map_addr - addr) { next_sec_addr = UT64_MAX; } else { if (addr > next_sec_addr) { /* avoid negative deltas */ return olen; } l = next_sec_addr - addr; } if (!next_sec && !next_map) { // done return olen; } // want to capture monotonicity even when maps are 0 in length if (l == 0) l++; w += l; len -= l; continue; } last = r_io_section_next (io, addr + w); last2 = r_io_map_next (io, addr + w); // XXX: must use physical address if (last == (addr + w)) last = last2; //else if (last2 (last - addr + w))? (last - addr + w): len; } else { // overflow // l = UT64_MAX - addr + 1; } if (l < 1) l = len; if (addr != UT64_MAX) { paddr = w? r_io_section_vaddr_to_maddr_try (io, addr + w): addr; } else { paddr = 0; } //if (!paddr || paddr==UT64_MAX) if (paddr == UT64_MAX) { paddr = r_io_map_select (io, addr); // XXX } if (paddr == UT64_MAX) { w += l; len -= l; continue; } r_io_map_select (io, addr); // XXX if (len > 0 && l > len) l = len; addr = paddr - w; if (r_io_seek (io, paddr, R_IO_SEEK_SET) == UT64_MAX) { memset (buf + w, io->Oxff, l); } // XXX is this necessary? ms = r_io_map_select (io, addr + w); if (readcache) { if (r_io_cache_read (io, io->off, buf + w, l) == l) { eprintf ("CACHED\n"); w += l; len -= l; continue; } } ret = r_io_read_internal (io, buf + w, l); if (ret < 1) { memset (buf + w, io->Oxff, l); // reading out of file ret = l; } else if (ret < l) { l = ret; } if (readcache) { r_io_cache_write (io, io->off, buf + w, len); } #if USE_CACHE if (io->cached) { r_io_cache_read (io, addr + w, buf + w, len); //-w); #if 0 int cov = r_io_cache_write (io, addr+w, buf+w, len); //-w); if (cov != len) { } #endif } else if (r_list_length (io->maps) > 1) { if (!io->debug && ms > 0) { //eprintf ("FAIL MS=%d l=%d d=%d\n", ms, l, d); /* check if address is vaddred in sections */ ut64 o = r_io_section_maddr_to_vaddr (io, addr + w); if (o == UT64_MAX) { ut64 o = r_io_section_vaddr_to_maddr_try (io, addr + w); if (o == UT64_MAX) memset (buf + w, io->Oxff, l); } break; } // } else { //eprintf ("ONT USING CACH\n"); } #endif w += l; len -= l; /* Fix famous io/f bug */ #if 0 this is not a real fix, because it just avoids reading again , even if the seek returns error. bear in mind that we need to fix that loop and honor lseek sections and sio maps fine #endif if (len > 0) { memset (buf + w, io->Oxff, len); } //break; } return olen; } R_API ut64 r_io_read_i(RIO *io, ut64 addr, int sz) { ut64 ret = 0LL; ut8 buf[8]; sz = R_DIM (sz, 1, 8); if (sz != r_io_read_at (io, addr, buf, sz)) return UT64_MAX; memcpy ((ut8 *)&ret, buf, sz); return ret; } /* Same as r_io_read_at, but not consume bytes */ R_API int r_io_peek_at(RIO *io, const ut64 addr, ut8 *buf, const int sz) { int ret = -1, tmp_ret = -1; ret = r_io_seek (io, addr, R_IO_SEEK_SET); if (ret != -1) { ret = r_io_read (io, buf, sz); } if (ret != -1) { tmp_ret = r_io_seek (io, addr, R_IO_SEEK_SET); } if (tmp_ret == -1) ret = tmp_ret; return ret; } // TODO. this is a physical resize R_API bool r_io_resize(RIO *io, ut64 newsize) { if (io->plugin) { if (io->plugin->resize && io->desc) { bool res = io->plugin->resize (io, io->desc, newsize); if (res) { r_io_map_truncate_update (io, io->desc->fd, newsize); } return res; } return false; } return true; } R_API int r_io_extend(RIO *io, ut64 size) { ut64 curr_off = io->off; ut64 cur_size = r_io_size (io), tmp_size = cur_size - size; ut8 *buffer = NULL; if (!size) { return false; } if (io->plugin && io->plugin->extend) { return io->plugin->extend (io, io->desc, size); } if (!UT64_ADD_OVFCHK (size, cur_size)) { if (!r_io_resize (io, size + cur_size)) { return false; } } else { return false; } if (cur_size < size) { tmp_size = size - cur_size; } buffer = malloc (tmp_size); // shift the bytes over by size r_io_seek (io, curr_off, R_IO_SEEK_SET); r_io_read (io, buffer, tmp_size); // move/write the bytes r_io_seek (io, curr_off + size, R_IO_SEEK_SET); r_io_write (io, buffer, tmp_size); // zero out new bytes if (cur_size < size) { free (buffer); buffer = malloc (size); } memset (buffer, 0, size); r_io_seek (io, curr_off, R_IO_SEEK_SET); r_io_write (io, buffer, size); // reset the cursor r_io_seek (io, curr_off, R_IO_SEEK_SET); free (buffer); return true; } R_API int r_io_extend_at(RIO *io, ut64 addr, ut64 size) { if (!size) return false; r_io_seek (io, addr, R_IO_SEEK_SET); return r_io_extend (io, size); } R_API int r_io_set_write_mask(RIO *io, const ut8 *buf, int len) { if (len > 0) { io->write_mask_fd = io->desc->fd; io->write_mask_buf = (ut8 *)malloc (len); if (io->write_mask_buf) { memcpy (io->write_mask_buf, buf, len); io->write_mask_len = len; return true; } } io->write_mask_fd = -1; return false; } R_API int r_io_write(RIO *io, const ut8 *buf, int len) { ut64 maddr = io->off; int i, ret = -1, orig_len = 0; ut8 *data = NULL, *orig_bytes = NULL; /* check section permissions */ if (io->enforce_rwx & R_IO_WRITE) { if (!(r_io_section_get_rwx (io, io->off) & R_IO_WRITE)) { ret = -1; goto cleanup; } } orig_bytes = malloc (len); if (!orig_bytes) { eprintf ("Cannot allocate %d bytes", len); ret = -1; goto cleanup; } orig_len = r_io_peek_at (io, io->off, orig_bytes, len); if (io->cached) { ret = r_io_cache_write (io, io->off, buf, len); if (ret == len) { if (orig_len > 0 && io->cb_core_post_write) { io->cb_core_post_write (io->user, maddr, orig_bytes, orig_len); } goto cleanup; } if (ret > 0) { len -= ret; buf += ret; } } /* TODO: implement IO cache here. to avoid dupping work on vm for example */ /* apply write binary mask */ if (io->write_mask_fd != -1) { data = (len > 0)? malloc (len): NULL; if (!data) { eprintf ("malloc failed in write_mask_fd"); ret = -1; goto cleanup; } // memset (data, io->Oxff, len); r_io_peek_at (io, io->off, data, len); for (i = 0; i < len; i++) { data[i] = buf[i] & io->write_mask_buf[i % io->write_mask_len]; } buf = data; } // this makes a double sub, so we restore the io->off { ut64 addr = io->off; r_io_map_select (io, io->off); io->off = addr; } { RIOMap *map = r_io_map_get (io, io->off); if (map) { io->off -= map->from; } } if (io->plugin) { if (io->plugin->write) { ret = io->plugin->write (io, io->desc, buf, len); } else { eprintf ("r_io_write: io handler with no write callback\n"); ret = -1; } } else { ret = io->desc? write (io->desc->fd, buf, len): -1; } if (ret == -1) { if (io->cached != 2) { eprintf ("r_io_write: cannot write %d bytes " "at 0x%" PFMT64x " (file=%s, fd=%d)\n", len, io->off, io->desc? io->desc->uri: "unknown", io->desc? io->desc->fd: -1); eprintf ("hint: try oo+ or e io.cache=true\n"); r_io_cache_invalidate (io, io->off, io->off + 1); } } else { if (readcache) { //r_io_cache_invalidate (io, io->off, io->off + len); r_io_cache_write (io, io->off, buf, len); } if (io->desc) { r_io_map_write_update (io, io->desc->fd, io->off, ret); io->off += ret; } } if (ret > 0 && orig_len > 0 && io->cb_core_post_write) { io->cb_core_post_write (io->user, maddr, orig_bytes, orig_len); } cleanup: free (data); free (orig_bytes); return ret; } R_API int r_io_write_at(RIO *io, ut64 addr, const ut8 *buf, int len) { if (io->cached) { return r_io_cache_write (io, addr, buf, len); } (void)r_io_seek (io, addr, R_IO_SEEK_SET); // errors on seek are checked and ignored here // return r_io_write (io, buf, len); } R_API ut64 r_io_seek(RIO *io, ut64 offset, int whence) { // TODO: review the offset/vaddr/paddr/maddr thing here // now, io-seek always works with vaddr, because it depends on read/write ops that use it int posix_whence = SEEK_SET; ut64 ret = UT64_MAX; if (!io) { return ret; } if (io->buffer_enabled) { io->off = offset; return offset; } switch (whence) { case R_IO_SEEK_SET: posix_whence = SEEK_SET; break; case R_IO_SEEK_CUR: // offset += io->off; posix_whence = SEEK_CUR; break; case R_IO_SEEK_END: //offset = UT64_MAX; // XXX: depending on io bits? posix_whence = SEEK_END; break; } // XXX: list_empty trick must be done in r_io_set_va(); //eprintf ("-(seek)-> 0x%08llx\n", offset); //if (!io->debug && io->va && !r_list_empty (io->sections)) { if (!io->debug || !io->raw) { // if (io->va && !r_list_empty (io->sections)) { ut64 o = r_io_section_vaddr_to_maddr_try (io, offset); if (o != UT64_MAX) { offset = o; } // eprintf ("-(vadd)-> 0x%08llx\n", offset); } } // if resolution fails... just return as invalid address if (offset == UT64_MAX || !io->desc) { return UT64_MAX; } if (io->plugin && io->plugin->lseek) { ret = io->plugin->lseek (io, io->desc, offset, whence); } else { ret = (ut64)lseek (io->desc->fd, offset, posix_whence); } if (whence == R_IO_SEEK_SET) { io->off = offset; } #if 0 // XXX can be problematic on w32..so no 64 bit offset? if (ret != UT64_MAX) { io->off = (whence == R_IO_SEEK_SET) ? offset // HACKY FIX linux-arm-32-bs at 0x10000 : ret; io->off = offset; ret = (!io->debug && io->va && !r_list_empty (io->sections)) ? r_io_section_maddr_to_vaddr (io, io->off) : io->off; } #endif return ret; } R_API ut64 r_io_fd_size(RIO *io, int fd) { RIODesc *desc = r_io_desc_get (io, fd); return r_io_desc_size (io, desc); } R_API bool r_io_is_blockdevice(RIO *io) { #if __UNIX__ if (io && io->desc && io->desc->fd) { struct stat buf; if (io->desc->obsz) { return 1; } if (fstat (io->desc->fd, &buf) == -1) return 0; if (io->plugin == &r_io_plugin_default) { // TODO: optimal blocksize = 2048 for disk, 4096 for files // usually is 128K // eprintf ("OPtimal blocksize: %d\n", buf.st_blksize); if ((buf.st_mode & S_IFCHR) == S_IFCHR) { io->desc->obsz = buf.st_blksize; return true; } return ((buf.st_mode & S_IFBLK) == S_IFBLK); } } #endif return false; } R_API ut64 r_io_size(RIO *io) { int oldva; ut64 size, here; if (!io) return 0LL; oldva = io->va; if (r_io_is_listener (io)) { return UT64_MAX; } io->va = false; here = r_io_seek (io, 0, R_IO_SEEK_CUR); size = r_io_seek (io, 0, R_IO_SEEK_END); if (r_io_seek (io, here, R_IO_SEEK_SET) != here) { eprintf ("Failed to reset the file position\n"); } io->va = oldva; if (r_io_is_blockdevice (io)) { io->va = 0; size = UT64_MAX; } return size; } R_API int r_io_system(RIO *io, const char *cmd) { int ret = -1; if (io->plugin && io->plugin->system) { ret = io->plugin->system (io, io->desc, cmd); } return ret; } R_API int r_io_plugin_close(RIO *io, RIODesc *desc) { if (io->plugin && io->plugin->close) { int ret = io->plugin->close (desc); if (desc == io->desc) { io->desc = NULL; } return ret; } return -1; } R_API int r_io_close(RIO *io, RIODesc *d) { RIODesc *cur = NULL; if (!io || !d) { return -1; } if (d != io->desc) { cur = io->desc; } if (r_io_use_desc (io, d)) { int nfd = d->fd; RIODesc *desc = r_io_desc_get (io, nfd); if (desc) { if (desc == io->desc) { cur = NULL; } r_io_map_del (io, nfd); r_io_section_rm_all (io, nfd); r_io_plugin_close (io, io->desc); //r_io_desc_del (io, desc->fd); } if (nfd == io->raised) { io->raised = -1; } } io->desc = cur; return false; } R_API int r_io_close_all(RIO *io) { // LOT OF MEMLEAKS HERE if (!io) { return 0; } r_cache_free (io->buffer); io->buffer = r_cache_new (); // RCache is a list of ranged buffers. maybe rename? io->write_mask_fd = -1; io->ff = 1; io->raised = -1; io->autofd = true; r_io_map_del (io, -1); r_io_desc_del (io, -1); r_io_section_rm_all (io, -1); r_io_undo_init (io); r_io_cache_reset (io, 0); // r_io_plugin_init (io); return 1; } R_API int r_io_bind(RIO *io, RIOBind *bnd) { bnd->io = io; bnd->init = true; bnd->get_io = r_io_bind_get_io; bnd->read_at = r_io_read_at; bnd->write_at = r_io_write_at; bnd->size = r_io_size; bnd->seek = r_io_seek; bnd->system = r_io_system; bnd->is_valid_offset = r_io_is_valid_offset; bnd->desc_open = r_io_open_nomap; bnd->desc_open_at = r_io_open_at; bnd->desc_close = r_io_close; bnd->desc_read = r_io_desc_read; bnd->desc_size = r_io_desc_size; bnd->desc_seek = r_io_desc_seek; bnd->desc_get_by_fd = r_io_desc_get; bnd->section_add = r_io_section_add; bnd->section_vget = r_io_section_vget; bnd->section_set_arch = r_io_section_set_archbits; bnd->section_set_arch_bin_id = r_io_section_set_archbits_bin_id; return true; } R_API int r_io_accept(RIO *io, int fd) { if (r_io_is_listener (io) && io->plugin && io->plugin->accept) { return io->plugin->accept (io, io->desc, fd); } return false; } /* moves bytes up (+) or down (-) within the specified range */ R_API int r_io_shift(RIO *io, ut64 start, ut64 end, st64 move) { ut8 *buf; ut64 chunksize = 0x10000; ut64 rest, src, shiftsize = r_num_abs (move); if (!shiftsize || (end - start) <= shiftsize) return false; rest = (end - start) - shiftsize; if (!(buf = malloc (chunksize))) { return false; } if (move > 0) { src = end - shiftsize; } else { src = start + shiftsize; } while (rest > 0) { if (chunksize > rest) { chunksize = rest; } if (move > 0) { src -= chunksize; } r_io_read_at (io, src, buf, chunksize); r_io_write_at (io, src + move, buf, chunksize); if (move < 0) { src += chunksize; } rest -= chunksize; } free (buf); return true; } R_API int r_io_create(RIO *io, const char *file, int mode, int type) { if (io->plugin && io->plugin->create) { return io->plugin->create (io, file, mode, type); } if (type == 'd' || type == 1) { return r_sys_mkdir (file); } return r_sandbox_creat (file, mode); } R_API void r_io_sort_maps(RIO *io) { r_list_sort (io->maps, (RListComparator)r_io_map_sort); } // THIS IS pread.. a weird one static ut8 *r_io_desc_read(RIO *io, RIODesc *desc, ut64 *out_sz) { ut8 *buf = NULL; ut64 off = 0; if (!io || !out_sz) { return NULL; } if (!desc) { desc = io->desc; } if (*out_sz == UT64_MAX) { *out_sz = r_io_desc_size (io, desc); } if (*out_sz == 0x8000000) { *out_sz = 1024 * 1024 * 1; // 2MB } off = io->off; if (*out_sz == UT64_MAX) { return NULL; } if (io->maxalloc && *out_sz > io->maxalloc) { eprintf ("WARNING: File is greater than 0x%"PFMT64x" bytes.\n" "Allocating R_IO_MAX_ALLOC set as the environment variable.\n", io->maxalloc); *out_sz = io->maxalloc; } buf = malloc (*out_sz + 1); buf[*out_sz] = 0; if (!buf) { if (*out_sz > R_IO_MAX_ALLOC) { char *num_unit = r_num_units (NULL, *out_sz); eprintf ("Failed to allocate %s bytes.\n" "Allocating %"PFMT64u" bytes.\n", num_unit, (ut64)R_IO_MAX_ALLOC); free (num_unit); *out_sz = R_IO_MAX_ALLOC; buf = malloc (*out_sz + 1); buf[*out_sz] = 0; } if (!buf) { char *num_unit = r_num_units (NULL, *out_sz); eprintf ("Failed to allocate %s bytes.\n", num_unit); free (num_unit); return NULL; } } if (buf && desc->plugin && desc->plugin->read) { if (!buf || !desc->plugin->read (io, desc, buf, *out_sz)) { free (buf); io->off = off; return NULL; } } io->off = off; return buf; } static RIO *r_io_bind_get_io(RIOBind *bnd) { return bnd? bnd->io: NULL; } R_API void r_io_set_raw(RIO *io, int raw) { io->raw = raw? 1: 0; } // check if reading at offset or writting to offset is reasonable R_API int r_io_is_valid_offset(RIO *io, ut64 offset, int hasperm) { if (!io) { eprintf ("r_io_is_valid_offset: io is NULL\n"); r_sys_backtrace (); return R_FAIL; } bool io_sectonly = io->sectonly; bool io_va = io->va; if (!io->files) { eprintf ("r_io_is_valid_offset: io->files is NULL\n"); r_sys_backtrace (); return R_FAIL; } if (r_list_empty (io->files)) { return false; } if (!io->desc) { eprintf ("r_io_is_valid_offset: io->desc is NULL\n"); r_sys_backtrace (); return R_FAIL; } #if 0 if (hasperm) { int ret = (r_io_map_exists_for_offset (io, offset) || r_io_section_exists_for_vaddr (io, offset, hasperm)); } #endif if (r_list_empty (io->sections)) { if ((r_io_map_exists_for_offset (io, offset))) { return true; } } if (!io_va) { if ((r_io_map_exists_for_offset (io, offset))) { return true; } return (offset < r_io_size (io)); } if (io->debug) { // TODO check debug maps here return true; } else { if (io_sectonly) { if (r_list_empty (io->sections)) { return true; } return (r_io_map_exists_for_offset (io, offset) || r_io_section_exists_for_vaddr (io, offset, hasperm)); } else { if (!io_va) { if (!io_va && r_io_map_exists_for_offset (io, offset)) { return true; } } return r_io_section_exists_for_vaddr (io, offset, hasperm); //return (offset < r_io_size (io)); } } eprintf ("r_io_is_valid_offset: io->va is %i\n", io->va); r_sys_backtrace (); return R_FAIL; }