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