1 /* radare2 - LGPL - Copyright 2017-2018 - condret, alvaro */
2
3 #include <r_io.h>
4 #include <r_types.h>
5 #include <string.h>
6
7 const ut64 cleanup_masks[] = {
8 0x0000000000000001,
9 0x0000000000000003,
10 0x0000000000000007,
11 0x000000000000000f,
12 0x000000000000001f,
13 0x000000000000003f,
14 0x000000000000007f,
15 0x00000000000000ff,
16 0x00000000000001ff,
17 0x00000000000003ff,
18 0x00000000000007ff,
19 0x0000000000000fff,
20 0x0000000000001fff,
21 0x0000000000003fff,
22 0x0000000000007fff,
23 0x000000000000ffff,
24 0x000000000001ffff,
25 0x000000000003ffff,
26 0x000000000007ffff,
27 0x00000000000fffff,
28 0x00000000001fffff,
29 0x00000000003fffff,
30 0x00000000007fffff,
31 0x0000000000ffffff,
32 0x0000000001ffffff,
33 0x0000000003ffffff,
34 0x0000000007ffffff,
35 0x000000000fffffff,
36 0x000000001fffffff,
37 0x000000003fffffff,
38 0x000000007fffffff,
39 0x00000000ffffffff,
40 0x00000001ffffffff,
41 0x00000003ffffffff,
42 0x00000007ffffffff,
43 0x0000000fffffffff,
44 0x0000001fffffffff,
45 0x0000003fffffffff,
46 0x0000007fffffffff,
47 0x000000ffffffffff,
48 0x000001ffffffffff,
49 0x000003ffffffffff,
50 0x000007ffffffffff,
51 0x00000fffffffffff,
52 0x00001fffffffffff,
53 0x00003fffffffffff,
54 0x00007fffffffffff,
55 0x0000ffffffffffff,
56 0x0001ffffffffffff,
57 0x0003ffffffffffff,
58 0x0007ffffffffffff,
59 0x000fffffffffffff,
60 0x001fffffffffffff,
61 0x003fffffffffffff,
62 0x007fffffffffffff,
63 0x00ffffffffffffff,
64 0x01ffffffffffffff,
65 0x03ffffffffffffff,
66 0x07ffffffffffffff,
67 0x0fffffffffffffff,
68 0x1fffffffffffffff,
69 0x3fffffffffffffff,
70 0x7fffffffffffffff
71 };
72
pcache_kv_free(HtUPKv * kv)73 static void pcache_kv_free(HtUPKv *kv) {
74 free (kv->value);
75 }
76
r_io_desc_cache_init(RIODesc * desc)77 R_API bool r_io_desc_cache_init(RIODesc *desc) {
78 if (!desc || desc->cache) {
79 return false;
80 }
81 return (desc->cache = ht_up_new (NULL, pcache_kv_free, NULL)) ? true : false;
82 }
83
r_io_desc_cache_write(RIODesc * desc,ut64 paddr,const ut8 * buf,int len)84 R_API int r_io_desc_cache_write(RIODesc *desc, ut64 paddr, const ut8 *buf, int len) {
85 RIODescCache *cache;
86 ut64 caddr, desc_sz = r_io_desc_size (desc);
87 int cbaddr, written = 0;
88 if ((len < 1) || !desc || (desc_sz <= paddr) ||
89 !desc->io || (!desc->cache && !r_io_desc_cache_init (desc))) {
90 return 0;
91 }
92 if (len > desc_sz) {
93 len = (int)desc_sz;
94 }
95 if (paddr > (desc_sz - len)) {
96 len = (int)(desc_sz - paddr);
97 }
98 caddr = paddr / R_IO_DESC_CACHE_SIZE;
99 cbaddr = paddr % R_IO_DESC_CACHE_SIZE;
100 while (written < len) {
101 //get an existing desc-cache, if it exists
102 if (!(cache = (RIODescCache *)ht_up_find (desc->cache, caddr, NULL))) {
103 //create new desc-cache
104 cache = R_NEW0 (RIODescCache);
105 if (!cache) {
106 return 0;
107 }
108 //feed ht with the new desc-cache
109 ht_up_insert (desc->cache, caddr, cache);
110 }
111 //check if the remaining data fits into the cache
112 if ((len - written) > (R_IO_DESC_CACHE_SIZE - cbaddr)) {
113 written += (R_IO_DESC_CACHE_SIZE - cbaddr);
114 //this can be optimized
115 for (; cbaddr < R_IO_DESC_CACHE_SIZE; cbaddr++) {
116 //write to cache
117 cache->cdata[cbaddr] = *buf;
118 //save, that its cached
119 cache->cached |= (0x1ULL << cbaddr);
120 buf++;
121 }
122 } else {
123 //XXX this looks like very suspicious
124 do {
125 cache->cdata[cbaddr] = *buf;
126 cache->cached |= (0x1ULL << cbaddr);
127 buf++;
128 written++;
129 cbaddr++;
130 } while (len > written);
131 }
132 caddr++;
133 cbaddr = 0;
134 }
135 REventIOWrite iow = { paddr, buf, len };
136 r_event_send (desc->io->event, R_EVENT_IO_WRITE, &iow);
137 return written;
138 }
139
r_io_desc_cache_read(RIODesc * desc,ut64 paddr,ut8 * buf,int len)140 R_API int r_io_desc_cache_read(RIODesc *desc, ut64 paddr, ut8 *buf, int len) {
141 RIODescCache *cache;
142 ut8 *ptr = buf;
143 ut64 caddr, desc_sz = r_io_desc_size (desc);
144 int cbaddr, amount = 0;
145 if ((len < 1) || !desc || (desc_sz <= paddr) || !desc->io || !desc->cache) {
146 return 0;
147 }
148 if (len > desc_sz) {
149 len = (int)desc_sz;
150 }
151 if (paddr > (desc_sz - len)) {
152 len = (int)(desc_sz - paddr);
153 }
154 caddr = paddr / R_IO_DESC_CACHE_SIZE;
155 cbaddr = paddr % R_IO_DESC_CACHE_SIZE;
156 while (amount < len) {
157 // get an existing desc-cache, if it exists
158 if (!(cache = (RIODescCache *)ht_up_find (desc->cache, caddr, NULL))) {
159 amount += (R_IO_DESC_CACHE_SIZE - cbaddr);
160 ptr += (R_IO_DESC_CACHE_SIZE - cbaddr);
161 goto beach;
162 }
163 if ((len - amount) > (R_IO_DESC_CACHE_SIZE - cbaddr)) {
164 amount += (R_IO_DESC_CACHE_SIZE - cbaddr);
165 for (; cbaddr < R_IO_DESC_CACHE_SIZE; cbaddr++) {
166 if (cache->cached & (0x1ULL << cbaddr)) {
167 *ptr = cache->cdata[cbaddr];
168 }
169 ptr++;
170 }
171 } else {
172 do {
173 if (cache->cached & (0x1ULL << cbaddr)) {
174 *ptr = cache->cdata[cbaddr];
175 }
176 ptr++;
177 amount++;
178 cbaddr++;
179 } while (len > amount);
180 }
181 beach:
182 caddr++;
183 cbaddr = 0;
184 }
185 return amount;
186 }
187
__riocache_free(void * user)188 static void __riocache_free(void *user) {
189 RIOCache *cache = (RIOCache *) user;
190 if (cache) {
191 free (cache->data);
192 free (cache->odata);
193 }
194 free (cache);
195 }
196
__desc_cache_list_cb(void * user,const ut64 k,const void * v)197 static bool __desc_cache_list_cb(void *user, const ut64 k, const void *v) {
198 RList *writes = (RList *)user;
199 RIOCache *cache = NULL;
200 ut64 blockaddr;
201 int byteaddr, i;
202 if (!writes) {
203 return false;
204 }
205 const RIODescCache *dcache = v;
206 blockaddr = k * R_IO_DESC_CACHE_SIZE;
207 for (i = byteaddr = 0; byteaddr < R_IO_DESC_CACHE_SIZE; byteaddr++) {
208 if (dcache->cached & (0x1LL << byteaddr)) {
209 if (!cache) {
210 cache = R_NEW0 (RIOCache);
211 if (!cache) {
212 return false;
213 }
214 cache->data = malloc (R_IO_DESC_CACHE_SIZE - byteaddr);
215 if (!cache->data) {
216 free (cache);
217 return false;
218 }
219 cache->itv.addr = blockaddr + byteaddr;
220 }
221 cache->data[i] = dcache->cdata[byteaddr];
222 i++;
223 } else if (cache) {
224 ut8 *data = realloc (cache->data, i);
225 if (!data) {
226 __riocache_free ((void *) cache);
227 return false;
228 }
229 cache->data = data;
230 cache->itv.size = i;
231 i = 0;
232 r_list_push (writes, cache);
233 cache = NULL;
234 }
235 }
236 if (cache) {
237 #if 0
238 cache->size = i;
239 cache->to = blockaddr + R_IO_DESC_CACHE_SIZE;
240 #endif
241 cache->itv.size = i;
242 r_list_push (writes, cache);
243 }
244 return true;
245 }
246
r_io_desc_cache_list(RIODesc * desc)247 R_API RList *r_io_desc_cache_list(RIODesc *desc) {
248 if (!desc || !desc->io || !desc->io->desc || !desc->io->p_cache || !desc->cache) {
249 return NULL;
250 }
251 RList *writes = r_list_newf ((RListFree)__riocache_free);
252 if (!writes) {
253 return NULL;
254 }
255 ht_up_foreach (desc->cache, __desc_cache_list_cb, writes);
256 RIODesc *current = desc->io->desc;
257 desc->io->desc = desc;
258 desc->io->p_cache = false;
259
260 RIOCache *c;
261 RListIter *iter;
262 r_list_foreach (writes, iter, c) {
263 const ut64 itvSize = r_itv_size (c->itv);
264 c->odata = calloc (1, itvSize);
265 if (!c->odata) {
266 r_list_free (writes);
267 return NULL;
268 }
269 r_io_pread_at (desc->io, r_itv_begin (c->itv), c->odata, itvSize);
270 }
271 desc->io->p_cache = true;
272 desc->io->desc = current;
273 return writes;
274 }
275
__desc_cache_commit_cb(void * user,const ut64 k,const void * v)276 static bool __desc_cache_commit_cb(void *user, const ut64 k, const void *v) {
277 RIODesc *desc = (RIODesc *)user;
278 int byteaddr, i;
279 ut8 buf[R_IO_DESC_CACHE_SIZE] = {0};
280 if (!desc || !desc->io) {
281 return false;
282 }
283 const RIODescCache *dcache = v;
284 ut64 blockaddr = R_IO_DESC_CACHE_SIZE * k;
285 for (i = byteaddr = 0; byteaddr < R_IO_DESC_CACHE_SIZE; byteaddr++) {
286 if (dcache->cached & (0x1LL << byteaddr)) {
287 buf[i] = dcache->cdata[byteaddr];
288 i++;
289 } else if (i > 0) {
290 r_io_pwrite_at (desc->io, blockaddr + byteaddr - i, buf, i);
291 i = 0;
292 }
293 }
294 if (i > 0) {
295 r_io_pwrite_at (desc->io, blockaddr + R_IO_DESC_CACHE_SIZE - i, buf, i);
296 }
297 return true;
298 }
299
r_io_desc_cache_commit(RIODesc * desc)300 R_API bool r_io_desc_cache_commit(RIODesc *desc) {
301 RIODesc *current;
302 if (!desc || !(desc->perm & R_PERM_W) || !desc->io || !desc->io->files || !desc->io->p_cache) {
303 return false;
304 }
305 if (!desc->cache) {
306 return true;
307 }
308 current = desc->io->desc;
309 desc->io->desc = desc;
310 desc->io->p_cache = false;
311 ht_up_foreach (desc->cache, __desc_cache_commit_cb, desc);
312 ht_up_free (desc->cache);
313 desc->cache = NULL;
314 desc->io->p_cache = true;
315 desc->io->desc = current;
316 return true;
317 }
318
__desc_cache_cleanup_cb(void * user,const ut64 k,const void * v)319 static bool __desc_cache_cleanup_cb(void *user, const ut64 k, const void *v) {
320 RIODesc *desc = (RIODesc *)user;
321 ut64 size, blockaddr;
322 int byteaddr;
323 if (!desc || !desc->cache) {
324 return false;
325 }
326 RIODescCache *cache = (RIODescCache *)v;
327 blockaddr = R_IO_DESC_CACHE_SIZE * k;
328 size = r_io_desc_size (desc);
329 if (size <= blockaddr) {
330 ht_up_delete (desc->cache, k);
331 return true;
332 }
333 if (size <= (blockaddr + R_IO_DESC_CACHE_SIZE - 1)) {
334 //this looks scary, but it isn't
335 byteaddr = (int)(size - blockaddr) - 1;
336 cache->cached &= cleanup_masks[byteaddr];
337 }
338 return true;
339 }
340
r_io_desc_cache_cleanup(RIODesc * desc)341 R_API void r_io_desc_cache_cleanup(RIODesc *desc) {
342 if (desc && desc->cache) {
343 ht_up_foreach (desc->cache, __desc_cache_cleanup_cb, desc);
344 }
345 }
346
__desc_fini_cb(void * user,void * data,ut32 id)347 static bool __desc_fini_cb (void *user, void *data, ut32 id) {
348 RIODesc *desc = (RIODesc *)data;
349 if (desc->cache) {
350 ht_up_free (desc->cache);
351 desc->cache = NULL;
352 }
353 return true;
354 }
355
r_io_desc_cache_fini(RIODesc * desc)356 R_API void r_io_desc_cache_fini(RIODesc *desc) {
357 __desc_fini_cb (NULL, (void *) desc, 0);
358 }
359
r_io_desc_cache_fini_all(RIO * io)360 R_API void r_io_desc_cache_fini_all(RIO *io) {
361 if (io && io->files) {
362 r_id_storage_foreach (io->files, __desc_fini_cb, NULL);
363 }
364 }
365