VirtualBox

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

Last change on this file since 8123 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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