VirtualBox

source: vbox/trunk/src/libs/kStuff/iprt/kRdrFile-iprt.cpp@ 5881

Last change on this file since 5881 was 5775, checked in by vboxsync, 17 years ago

Corrected the copyright header.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.7 KB
Line 
1/* $Id: kRdrFile-iprt.cpp 5775 2007-11-16 15:47:20Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - kRdr Backend.
4 */
5
6/*
7 * Copyright (C) 2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 */
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <k/kRdr.h>
23#include <k/kRdrAll.h>
24#include <k/kErrors.h>
25#include <k/kMagics.h>
26
27#include <iprt/file.h>
28#include <iprt/assert.h>
29#include <iprt/string.h>
30#include <iprt/path.h>
31#include <iprt/param.h>
32#include <iprt/mem.h>
33#include <iprt/err.h>
34
35
36/*******************************************************************************
37* Structures and Typedefs *
38*******************************************************************************/
39/**
40 * Prepared stuff.
41 */
42typedef struct KRDRFILEPREP
43{
44 /** The address of the prepared region. */
45 void *pv;
46 /** The size of the prepared region. */
47 KSIZE cb;
48} KRDRFILEPREP, *PKRDRFILEPREP;
49
50/**
51 * The file provier instance for native files.
52 */
53typedef struct KRDRFILE
54{
55 /** The file reader vtable. */
56 KRDR Core;
57 /** The file handle. */
58 RTFILE File;
59 /** The current file offset. */
60 KFOFF off;
61 /** The file size. */
62 KFOFF cb;
63 /** Array where we stuff the mapping area data. */
64 KRDRFILEPREP aPreps[4];
65 /** The number of current preps. */
66 KU32 cPreps;
67 /** Number of mapping references. */
68 KI32 cMappings;
69 /** The memory mapping. */
70 void *pvMapping;
71 /** The filename. */
72 char szFilename[1];
73} KRDRFILE, *PKRDRFILE;
74
75
76/*******************************************************************************
77* Internal Functions *
78*******************************************************************************/
79static void krdrRTFileDone(PKRDR pRdr);
80static int krdrRTFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
81static int krdrRTFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments);
82static int krdrRTFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
83static int krdrRTFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
84static int krdrRTFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
85static int krdrRTFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments);
86static int krdrRTFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
87static int krdrRTFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
88static KSIZE krdrRTFilePageSize(PKRDR pRdr);
89static const char *krdrRTFileName(PKRDR pRdr);
90static KIPTR krdrRTFileNativeFH(PKRDR pRdr);
91static KFOFF krdrRTFileTell(PKRDR pRdr);
92static KFOFF krdrRTFileSize(PKRDR pRdr);
93static int krdrRTFileAllUnmap(PKRDR pRdr, const void *pvBits);
94static int krdrRTFileAllMap(PKRDR pRdr, const void **ppvBits);
95static int krdrRTFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off);
96static int krdrRTFileDestroy(PKRDR pRdr);
97static int krdrRTFileCreate(PPKRDR ppRdr, const char *pszFilename);
98
99
100/*******************************************************************************
101* Global Variables *
102*******************************************************************************/
103extern "C" const KRDROPS g_kRdrFileOps;
104
105/** Native file provider operations. */
106const KRDROPS g_kRdrFileOps =
107{
108 "IPRT file",
109 NULL,
110 krdrRTFileCreate,
111 krdrRTFileDestroy,
112 krdrRTFileRead,
113 krdrRTFileAllMap,
114 krdrRTFileAllUnmap,
115 krdrRTFileSize,
116 krdrRTFileTell,
117 krdrRTFileName,
118 krdrRTFileNativeFH,
119 krdrRTFilePageSize,
120 krdrRTFileMap,
121 krdrRTFileRefresh,
122 krdrRTFileProtect,
123 krdrRTFileUnmap,
124 krdrRTFileDone,
125 42
126};
127
128
129/** @copydoc KRDROPS::pfnDone */
130static void krdrRTFileDone(PKRDR pRdr)
131{
132}
133
134
135/**
136 * Finds a prepared mapping region.
137 *
138 * @returns Pointer to the aPrep entry.
139 * @param pFile The instance data.
140 * @param pv The base of the region.
141 */
142static PKRDRFILEPREP krdrRTFileFindPrepExact(PKRDRFILE pFile, void *pv)
143{
144 KI32 i = pFile->cPreps;
145 while (i-- > 0)
146 if (pFile->aPreps[i].pv == pv)
147 return &pFile->aPreps[i];
148 return NULL;
149}
150
151
152/** @copydoc KRDROPS::pfnUnmap */
153static int krdrRTFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
154{
155 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
156 PKRDRFILEPREP pPrep = krdrRTFileFindPrepExact(pRdrFile, pvBase);
157 int rc;
158 if (!pPrep)
159 return KERR_INVALID_PARAMETER;
160
161 rc = krdrRTFileGenericUnmap(pRdr, pPrep, cSegments, paSegments);
162
163 /* remove the mapping data on success. */
164 if (!rc)
165 {
166 pRdrFile->cPreps--;
167 if (pPrep != &pRdrFile->aPreps[pRdrFile->cPreps])
168 *pPrep = pRdrFile->aPreps[pRdrFile->cPreps];
169 }
170 return rc;
171}
172
173
174/** Generic implementation of krdrRTFileUnmap. */
175static int krdrRTFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments)
176{
177 krdrRTFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
178 RTMemPageFree(pPrep->pv);
179 return 0;
180}
181
182
183/** @copydoc KRDROPS::pfnProtect */
184static int krdrRTFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
185{
186 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
187 PKRDRFILEPREP pPrep = krdrRTFileFindPrepExact(pRdrFile, pvBase);
188 if (!pPrep)
189 return KERR_INVALID_PARAMETER;
190
191 return krdrRTFileGenericProtect(pRdr, pPrep, cSegments, paSegments, fUnprotectOrProtect);
192}
193
194
195static unsigned krdrRTFileConvertProt(KPROT enmProt)
196{
197 switch (enmProt)
198 {
199 case KPROT_NOACCESS: return RTMEM_PROT_NONE;
200 case KPROT_READONLY: return RTMEM_PROT_READ;
201 case KPROT_READWRITE: return RTMEM_PROT_READ | RTMEM_PROT_WRITE;
202 case KPROT_WRITECOPY: return RTMEM_PROT_READ | RTMEM_PROT_WRITE;
203 case KPROT_EXECUTE: return RTMEM_PROT_EXEC;
204 case KPROT_EXECUTE_READ: return RTMEM_PROT_EXEC | RTMEM_PROT_READ;
205 case KPROT_EXECUTE_READWRITE: return RTMEM_PROT_EXEC | RTMEM_PROT_READ | RTMEM_PROT_WRITE;
206 case KPROT_EXECUTE_WRITECOPY: return RTMEM_PROT_EXEC | RTMEM_PROT_READ | RTMEM_PROT_WRITE;
207 default:
208 AssertFailed();
209 return ~0U;
210 }
211}
212
213
214/** Generic implementation of krdrRTFileProtect. */
215static int krdrRTFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
216{
217 KU32 i;
218
219 /*
220 * Iterate the segments and apply memory protection changes.
221 */
222 for (i = 0; i < cSegments; i++)
223 {
224 int rc;
225 void *pv;
226 KPROT enmProt;
227
228 if (paSegments[i].RVA == NIL_KLDRADDR)
229 continue;
230
231 /* calc new protection. */
232 enmProt = (KPROT)paSegments[i].enmProt; /** @todo drop cast */
233 if (fUnprotectOrProtect)
234 {
235 switch (enmProt)
236 {
237 case KPROT_NOACCESS:
238 case KPROT_READONLY:
239 case KPROT_READWRITE:
240 case KPROT_WRITECOPY:
241 enmProt = KPROT_READWRITE;
242 break;
243 case KPROT_EXECUTE:
244 case KPROT_EXECUTE_READ:
245 case KPROT_EXECUTE_READWRITE:
246 case KPROT_EXECUTE_WRITECOPY:
247 enmProt = KPROT_EXECUTE_READWRITE;
248 break;
249 default:
250 AssertFailed();
251 return -1;
252 }
253 }
254 else
255 {
256 /* copy on write -> normal write. */
257 if (enmProt == KPROT_EXECUTE_WRITECOPY)
258 enmProt = KPROT_EXECUTE_READWRITE;
259 else if (enmProt == KPROT_WRITECOPY)
260 enmProt = KPROT_READWRITE;
261 }
262
263 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
264 rc = RTMemProtect(pv, paSegments[i].cbMapped, krdrRTFileConvertProt(enmProt));
265 if (rc)
266 break;
267 }
268
269 return 0;
270}
271
272
273/** @copydoc KRDROPS::pfnRefresh */
274static int krdrRTFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
275{
276 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
277 PKRDRFILEPREP pPrep = krdrRTFileFindPrepExact(pRdrFile, pvBase);
278 if (!pPrep)
279 return KERR_INVALID_PARAMETER;
280 return krdrRTFileGenericRefresh(pRdr, pPrep, cSegments, paSegments);
281}
282
283
284/** Generic implementation of krdrRTFileRefresh. */
285static int krdrRTFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments)
286{
287 int rc;
288 int rc2;
289 KU32 i;
290
291 /*
292 * Make everything writable again.
293 */
294 rc = krdrRTFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
295 if (rc)
296 {
297 krdrRTFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
298 return rc;
299 }
300
301 /*
302 * Clear everything.
303 */
304 /** @todo only zero the areas not covered by raw file bits. */
305 memset(pPrep->pv, 0, pPrep->cb);
306
307 /*
308 * Reload all the segments.
309 * We could possibly skip some segments, but we currently have
310 * no generic way of figuring out which at the moment.
311 */
312 for (i = 0; i < cSegments; i++)
313 {
314 void *pv;
315
316 if ( paSegments[i].RVA == NIL_KLDRADDR
317 || paSegments[i].cbFile <= 0)
318 continue;
319
320 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
321 rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile);
322 if (rc)
323 break;
324 }
325
326 /*
327 * Protect the bits again.
328 */
329 rc2 = krdrRTFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
330 if (rc2 && rc)
331 rc = rc2;
332
333 return rc;
334}
335
336
337/** @copydoc KRDROPS::pfnMap */
338static int krdrRTFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
339{
340 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
341 PKRDRFILEPREP pPrep = &pRdrFile->aPreps[pRdrFile->cPreps];
342 KLDRSIZE cbTotal;
343 const KSIZE cbPage = pRdr->pOps->pfnPageSize(pRdr);
344 int rc;
345 KU32 i;
346
347 if (pRdrFile->cPreps >= K_ELEMENTS(pRdrFile->aPreps))
348 return KRDR_ERR_TOO_MANY_MAPPINGS;
349
350 /*
351 * Calc the total mapping space needed.
352 */
353 cbTotal = 0;
354 for (i = 0; i < cSegments; i++)
355 {
356 KLDRSIZE uRVASegmentEnd;
357 if (paSegments[i].RVA == NIL_KLDRADDR)
358 continue;
359 uRVASegmentEnd = paSegments[i].RVA + paSegments[i].cbMapped;
360 if (cbTotal < uRVASegmentEnd)
361 cbTotal = uRVASegmentEnd;
362 }
363 pPrep->cb = (KSIZE)cbTotal;
364 if (pPrep->cb != cbTotal)
365 return KLDR_ERR_ADDRESS_OVERFLOW;
366 pPrep->cb = (pPrep->cb + (cbPage - 1)) & ~(cbPage- 1);
367
368 /*
369 * Use the generic map emulation.
370 */
371 pPrep->pv = fFixed ? *ppvBase : NULL;
372 rc = krdrRTFileGenericMap(pRdr, pPrep, cSegments, paSegments, fFixed);
373 if (!rc)
374 {
375 *ppvBase = pPrep->pv;
376 pRdrFile->cPreps++;
377 }
378
379 return rc;
380}
381
382
383/** Generic implementation of krdrRTFileMap. */
384static int krdrRTFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
385{
386 int rc = 0;
387 KU32 i;
388
389 /*
390 * Generic mapping code using kHlpPageAlloc(), kHlpPageFree() and kHlpPageProtect().
391 */
392 pPrep->pv = RTMemPageAlloc(pPrep->cb);
393 if (!pPrep->pv)
394 return KERR_NO_MEMORY;
395
396 /*
397 * Load the data.
398 */
399 for (i = 0; i < cSegments; i++)
400 {
401 void *pv;
402
403 if ( paSegments[i].RVA == NIL_KLDRADDR
404 || paSegments[i].cbFile <= 0)
405 continue;
406
407 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
408 rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile);
409 if (rc)
410 break;
411 }
412
413 /*
414 * Set segment protection.
415 */
416 if (!rc)
417 {
418 rc = krdrRTFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
419 if (!rc)
420 return 0;
421 krdrRTFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
422 }
423
424 /* bailout */
425 RTMemPageFree(pPrep->pv);
426 return rc;
427}
428
429
430/** @copydoc KRDROPS::pfnPageSize */
431static KSIZE krdrRTFilePageSize(PKRDR pRdr)
432{
433 return PAGE_SIZE;
434}
435
436
437/** @copydoc KRDROPS::pfnName */
438static const char *krdrRTFileName(PKRDR pRdr)
439{
440 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
441 return &pRdrFile->szFilename[0];
442}
443
444
445static KIPTR krdrRTFileNativeFH(PKRDR pRdr)
446{
447 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
448 return (KIPTR)pRdrFile->File;
449}
450
451
452/** @copydoc KRDROPS::pfnTell */
453static KFOFF krdrRTFileTell(PKRDR pRdr)
454{
455 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
456
457 /*
458 * If the offset is undefined, try figure out what it is.
459 */
460 if (pRdrFile->off == -1)
461 {
462 pRdrFile->off = RTFileTell(pRdrFile->File);
463 if (pRdrFile->off < 0)
464 pRdrFile->off = -1;
465 }
466 return pRdrFile->off;
467}
468
469
470/** @copydoc KRDROPS::pfnSize */
471static KFOFF krdrRTFileSize(PKRDR pRdr)
472{
473 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
474 return pRdrFile->cb;
475}
476
477
478/** @copydoc KRDROPS::pfnAllUnmap */
479static int krdrRTFileAllUnmap(PKRDR pRdr, const void *pvBits)
480{
481 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
482
483 /* check for underflow */
484 if (pRdrFile->cMappings <= 0)
485 return KERR_INVALID_PARAMETER;
486
487 /* decrement usage counter, free mapping if no longer in use. */
488 if (!--pRdrFile->cMappings)
489 {
490 RTMemFree(pRdrFile->pvMapping);
491 pRdrFile->pvMapping = NULL;
492 }
493
494 return 0;
495}
496
497
498/** @copydoc KRDROPS::pfnAllMap */
499static int krdrRTFileAllMap(PKRDR pRdr, const void **ppvBits)
500{
501 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
502
503 /*
504 * Do we need to map it?
505 */
506 if (!pRdrFile->pvMapping)
507 {
508 int rc;
509 KFOFF cbFile = pRdrFile->Core.pOps->pfnSize(pRdr);
510 KSIZE cb = (KSIZE)cbFile;
511 if ((KFOFF)cb != cbFile)
512 return KERR_NO_MEMORY;
513
514 pRdrFile->pvMapping = RTMemAlloc(cb);
515 if (!pRdrFile->pvMapping)
516 return KERR_NO_MEMORY;
517 rc = pRdrFile->Core.pOps->pfnRead(pRdr, pRdrFile->pvMapping, cb, 0);
518 if (rc)
519 {
520 RTMemFree(pRdrFile->pvMapping);
521 pRdrFile->pvMapping = NULL;
522 return rc;
523 }
524 pRdrFile->cMappings = 0;
525 }
526
527 *ppvBits = pRdrFile->pvMapping;
528 pRdrFile->cMappings++;
529 return 0;
530}
531
532
533/** @copydoc KRDROPS::pfnRead */
534static int krdrRTFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off)
535{
536 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
537 int rc;
538
539 /*
540 * Do a seek if needed.
541 */
542 if (pRdrFile->off != off)
543 {
544 rc = RTFileSeek(pRdrFile->File, off, RTFILE_SEEK_BEGIN, NULL);
545 if (RT_FAILURE(rc))
546 {
547 pRdrFile->off = -1;
548 return rc;
549 }
550 }
551
552 /*
553 * Do the read.
554 */
555 rc = RTFileRead(pRdrFile->File, pvBuf, cb, NULL);
556 if (RT_FAILURE(rc))
557 {
558 pRdrFile->off = -1;
559 return rc;
560 }
561
562 pRdrFile->off = off + cb;
563 return 0;
564}
565
566
567/** @copydoc KRDROPS::pfnDestroy */
568static int krdrRTFileDestroy(PKRDR pRdr)
569{
570 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
571 int rc;
572
573 rc = RTFileClose(pRdrFile->File);
574
575 if (pRdrFile->pvMapping)
576 {
577 RTMemFree(pRdrFile->pvMapping);
578 pRdrFile->pvMapping = NULL;
579 }
580
581 RTMemFree(pRdr);
582 return rc;
583}
584
585
586/** @copydoc KRDROPS::pfnCreate */
587static int krdrRTFileCreate(PPKRDR ppRdr, const char *pszFilename)
588{
589 KSIZE cchFilename;
590 PKRDRFILE pRdrFile;
591 RTFILE File;
592 uint64_t cb;
593 int rc;
594 char szFilename[RTPATH_MAX];
595
596 /*
597 * Open the file, determin its size and correct filename.
598 */
599 rc = RTFileOpen(&File, pszFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
600 if (RT_FAILURE(rc))
601 return rc;
602
603 rc = RTFileGetSize(File, &cb);
604 if (RT_SUCCESS(rc))
605 {
606 rc = RTPathReal(pszFilename, szFilename, sizeof(szFilename));
607 if (RT_SUCCESS(rc))
608 {
609 /*
610 * Allocate the reader instance.
611 */
612 cchFilename = strlen(szFilename);
613 pRdrFile = (PKRDRFILE)RTMemAlloc(sizeof(*pRdrFile) + cchFilename);
614 if (pRdrFile)
615 {
616 /*
617 * Initialize it and return successfully.
618 */
619 pRdrFile->Core.u32Magic = KRDR_MAGIC;
620 pRdrFile->Core.pOps = &g_kRdrFileOps;
621 pRdrFile->File = File;
622 pRdrFile->cb = cb;
623 pRdrFile->off = 0;
624 pRdrFile->cMappings = 0;
625 pRdrFile->cPreps = 0;
626 memcpy(&pRdrFile->szFilename[0], szFilename, cchFilename + 1);
627
628 *ppRdr = &pRdrFile->Core;
629 return 0;
630 }
631
632 rc = KERR_NO_MEMORY;
633 }
634 }
635
636 RTFileClose(File);
637 return rc;
638}
639
640
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette