source: trunk/kStuff/kRdr/kRdrFile.cpp@ 3578

Last change on this file since 3578 was 3578, checked in by bird, 18 years ago

kLdrRdr cleanup.

  • Property svn:keywords set to Id
File size: 33.5 KB
Line 
1/* $Id: kRdrFile.cpp 3578 2007-09-02 20:55:51Z bird $ */
2/** @file
3 * kRdrFile - The Native File Provider
4 */
5
6/*
7 * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
8 *
9 * This file is part of kStuff.
10 *
11 * kStuff is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * kStuff is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with kStuff; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "kRdrInternal.h"
32#include <k/kHlpAlloc.h>
33#include <k/kHlpString.h>
34
35#if K_OS == K_OS_OS2
36# define INCL_ERRORS
37# define INCL_BASE
38# include <os2.h>
39
40#elif K_OS == K_OS_WINDOWS
41# define WIN32_NO_STATUS
42# include <Windows.h>
43# ifndef __WIN__
44# define __WIN__
45# endif
46# include <ntsecapi.h>
47# include <ntstatus.h>
48
49# ifdef __cplusplus
50 extern "C" {
51# endif
52
53 /** @todo find a non-conflicting header with NTSTATUS, NTAPI, ++ */
54 typedef LONG NTSTATUS;
55 #define NT_SUCCESS(x) ((x)>=0)
56
57 typedef struct _OBJECT_ATTRIBUTES
58 {
59 ULONG Length;
60 HANDLE RootDirectory;
61 PUNICODE_STRING ObjectName;
62 ULONG Attributes;
63 PVOID SecurityDescriptor;
64 PVOID SecurityQualityOfService;
65 } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
66
67 typedef enum _SECTION_INHERIT
68 {
69 ViewShare = 1,
70 ViewUnmap = 2
71 } SECTION_INHERIT;
72
73# define NTOSAPI __declspec(dllimport)
74# define NtCurrentProcess() GetCurrentProcess()
75
76# ifndef MEM_DOS_LIM
77# define MEM_DOS_LIM 0x40000000UL
78# endif
79
80 NTOSAPI
81 NTSTATUS
82 NTAPI
83 NtCreateSection(
84 OUT PHANDLE SectionHandle,
85 IN ACCESS_MASK DesiredAccess,
86 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
87 IN PLARGE_INTEGER SectionSize OPTIONAL,
88 IN ULONG Protect,
89 IN ULONG Attributes,
90 IN HANDLE FileHandle OPTIONAL
91 );
92
93 NTOSAPI
94 NTSTATUS
95 NTAPI
96 NtMapViewOfSection(
97 IN HANDLE SectionHandle,
98 IN HANDLE ProcessHandle,
99 IN OUT PVOID *BaseAddress,
100 IN ULONG ZeroBits,
101 IN ULONG CommitSize,
102 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
103 IN OUT PSIZE_T ViewSize,
104 IN SECTION_INHERIT InheritDisposition,
105 IN ULONG AllocationType,
106 IN ULONG Protect
107 );
108
109 NTOSAPI
110 NTSTATUS
111 NTAPI
112 NtUnmapViewOfSection(
113 IN HANDLE ProcessHandle,
114 IN PVOID BaseAddress
115 );
116
117 NTOSAPI
118 NTSTATUS
119 NTAPI
120 NtClose(
121 IN HANDLE Handle
122 );
123
124 NTOSAPI
125 NTSTATUS
126 NTAPI
127 ZwProtectVirtualMemory(
128 IN HANDLE ProcessHandle,
129 IN OUT PVOID *BaseAddress,
130 IN OUT PULONG ProtectSize,
131 IN ULONG NewProtect,
132 OUT PULONG OldProtect
133 );
134# define NtProtectVirtualMemory ZwProtectVirtualMemory
135
136 NTOSAPI
137 NTSTATUS
138 NTAPI
139 NtAllocateVirtualMemory(
140 IN HANDLE ProcessHandle,
141 IN OUT PVOID *BaseAddress,
142 IN ULONG ZeroBits,
143 IN OUT PULONG AllocationSize,
144 IN ULONG AllocationType,
145 IN ULONG Protect
146 );
147
148 NTOSAPI
149 NTSTATUS
150 NTAPI
151 NtFreeVirtualMemory(
152 IN HANDLE ProcessHandle,
153 IN OUT PVOID *BaseAddress,
154 IN OUT PULONG FreeSize,
155 IN ULONG FreeType
156 );
157
158# ifdef __cplusplus
159 }
160# endif
161
162#else
163# error "port me"
164#endif
165
166
167/*******************************************************************************
168* Structures and Typedefs *
169*******************************************************************************/
170/**
171 * Prepared stuff.
172 */
173typedef struct KRDRFILEPREP
174{
175 /** The address of the prepared region. */
176 void *pv;
177 /** The size of the prepared region. */
178 KSIZE cb;
179#if K_OS == K_OS_WINDOWS
180 /** Handle to the section created to map the file. */
181 HANDLE hSection;
182#endif
183} KRDRFILEPREP, *PKRDRFILEPREP;
184
185/**
186 * The file provier instance for native files.
187 */
188typedef struct KRDRFILE
189{
190 /** The file reader vtable. */
191 KRDR Core;
192 /** The file handle. */
193#if K_OS == K_OS_OS2
194 HFILE File;
195#elif K_OS == K_OS_WINDOWS
196 HANDLE File;
197#else
198# error "Port me!"
199#endif
200 /** The current file offset. */
201 KFOFF off;
202 /** The file size. */
203 KFOFF cb;
204 /** Array where we stuff the mapping area data. */
205 KRDRFILEPREP aPreps[4];
206 /** The number of current preps. */
207 KU32 cPreps;
208 /** Number of mapping references. */
209 KI32 cMappings;
210 /** The memory mapping. */
211 void *pvMapping;
212 /** The filename. */
213 char szFilename[1];
214} KRDRFILE, *PKRDRFILE;
215
216
217/*******************************************************************************
218* Internal Functions *
219*******************************************************************************/
220static void krdrFileDone(PKRDR pRdr);
221static int krdrFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
222static int krdrFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments);
223static int krdrFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
224static int krdrFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
225static int krdrFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
226static int krdrFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments);
227static int krdrFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
228static int krdrFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
229static KSIZE krdrFilePageSize(PKRDR pRdr);
230static const char *krdrFileName(PKRDR pRdr);
231static KIPTR krdrFileNativeFH(PKRDR pRdr);
232static KFOFF krdrFileTell(PKRDR pRdr);
233static KFOFF krdrFileSize(PKRDR pRdr);
234static int krdrFileAllUnmap(PKRDR pRdr, const void *pvBits);
235static int krdrFileAllMap(PKRDR pRdr, const void **ppvBits);
236static int krdrFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off);
237static int krdrFileDestroy(PKRDR pRdr);
238static int krdrFileCreate(PPKRDR ppRdr, const char *pszFilename);
239
240
241/*******************************************************************************
242* Global Variables *
243*******************************************************************************/
244/** Native file provider operations. */
245const KRDROPS g_kRdrFileOps =
246{
247 "native file",
248 NULL,
249 krdrFileCreate,
250 krdrFileDestroy,
251 krdrFileRead,
252 krdrFileAllMap,
253 krdrFileAllUnmap,
254 krdrFileSize,
255 krdrFileTell,
256 krdrFileName,
257 krdrFileNativeFH,
258 krdrFilePageSize,
259 krdrFileMap,
260 krdrFileRefresh,
261 krdrFileProtect,
262 krdrFileUnmap,
263 krdrFileDone,
264 42
265};
266
267
268#if K_OS == K_OS_WINDOWS
269/**
270 * Converts a kLdr segment protection to NT protection for a mapping.
271 *
272 * @returns Nt page protection.
273 * @param enmProt kLdr protection.
274 */
275static ULONG krdrFileGetNtMapProt(KPROT enmProt)
276{
277 switch (enmProt)
278 {
279 case KPROT_NOACCESS: return PAGE_NOACCESS;
280 case KPROT_READONLY: return PAGE_READONLY;
281 case KPROT_READWRITE: return PAGE_READWRITE;
282 case KPROT_WRITECOPY: return PAGE_WRITECOPY;
283 case KPROT_EXECUTE: return PAGE_EXECUTE;
284 case KPROT_EXECUTE_READ: return PAGE_EXECUTE_READ;
285 case KPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE;
286 case KPROT_EXECUTE_WRITECOPY: return PAGE_EXECUTE_WRITECOPY;
287 default: return ~(ULONG)0;
288 }
289}
290
291
292/**
293 * Converts a kLdr segment protection to NT protection for a allocation.
294 *
295 * @returns Nt page protection.
296 * @param enmProt kLdr protection.
297 */
298static ULONG krdrFileGetNtAllocProt(KPROT enmProt)
299{
300 switch (enmProt)
301 {
302 case KPROT_NOACCESS: return PAGE_NOACCESS;
303 case KPROT_READONLY: return PAGE_READONLY;
304 case KPROT_WRITECOPY:
305 case KPROT_READWRITE: return PAGE_READWRITE;
306 case KPROT_EXECUTE: return PAGE_EXECUTE;
307 case KPROT_EXECUTE_READ: return PAGE_EXECUTE_READ;
308 case KPROT_EXECUTE_WRITECOPY:
309 case KPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE;
310 default: return ~(ULONG)0;
311 }
312}
313#endif
314
315
316/** @copydoc KRDROPS::pfnDone */
317static void krdrFileDone(PKRDR pRdr)
318{
319}
320
321
322/**
323 * Finds a prepared mapping region.
324 *
325 * @returns Pointer to the aPrep entry.
326 * @param pFile The instance data.
327 * @param pv The base of the region.
328 */
329static PKRDRFILEPREP krdrFileFindPrepExact(PKRDRFILE pFile, void *pv)
330{
331 KI32 i = pFile->cPreps;
332 while (i-- > 0)
333 if (pFile->aPreps[i].pv == pv)
334 return &pFile->aPreps[i];
335 return NULL;
336}
337
338
339/** @copydoc KRDROPS::pfnUnmap */
340static int krdrFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
341{
342 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
343 PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase);
344 int rc;
345 if (!pPrep)
346 return KERR_INVALID_PARAMETER;
347
348#if K_OS == K_OS_WINDOWS
349 if (pPrep->hSection != NULL)
350 {
351 /** @todo implement me. */
352 return -1;
353 }
354#endif
355
356 rc = krdrFileGenericUnmap(pRdr, pPrep, cSegments, paSegments);
357
358 /* remove the mapping data on success. */
359 if (!rc)
360 {
361 pRdrFile->cPreps--;
362 if (pPrep != &pRdrFile->aPreps[pRdrFile->cPreps])
363 *pPrep = pRdrFile->aPreps[pRdrFile->cPreps];
364 }
365 return rc;
366}
367
368
369/** Generic implementation of krdrFileUnmap. */
370static int krdrFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments)
371{
372 krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
373 return kHlpPageFree(pPrep->pv, pPrep->cb);
374}
375
376
377/** @copydoc KRDROPS::pfnProtect */
378static int krdrFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
379{
380 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
381 PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase);
382 if (!pPrep)
383 return KERR_INVALID_PARAMETER;
384
385#if K_OS == K_OS_WINDOWS
386 if (pPrep->hSection != NULL)
387 {
388 /** @todo implement me. */
389 return -1;
390 }
391#endif
392
393 return krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, fUnprotectOrProtect);
394}
395
396
397/** Generic implementation of krdrFileProtect. */
398static int krdrFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
399{
400 KU32 i;
401
402 /*
403 * Iterate the segments and apply memory protection changes.
404 */
405 for (i = 0; i < cSegments; i++)
406 {
407 int rc;
408 void *pv;
409 KPROT enmProt;
410
411 if (paSegments[i].RVA == NIL_KLDRADDR)
412 continue;
413
414 /* calc new protection. */
415 enmProt = (KPROT)paSegments[i].enmProt; /** @todo drop cast */
416 if (fUnprotectOrProtect)
417 {
418 switch (enmProt)
419 {
420 case KPROT_NOACCESS:
421 case KPROT_READONLY:
422 case KPROT_READWRITE:
423 case KPROT_WRITECOPY:
424 enmProt = KPROT_READWRITE;
425 break;
426 case KPROT_EXECUTE:
427 case KPROT_EXECUTE_READ:
428 case KPROT_EXECUTE_READWRITE:
429 case KPROT_EXECUTE_WRITECOPY:
430 enmProt = KPROT_EXECUTE_READWRITE;
431 break;
432 default:
433 kRdrAssert(!"bad enmProt");
434 return -1;
435 }
436 }
437 else
438 {
439 /* copy on write -> normal write. */
440 if (enmProt == KPROT_EXECUTE_WRITECOPY)
441 enmProt = KPROT_EXECUTE_READWRITE;
442 else if (enmProt == KPROT_WRITECOPY)
443 enmProt = KPROT_READWRITE;
444 }
445
446 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
447
448 rc = kHlpPageProtect(pv, paSegments[i].cbMapped, enmProt);
449 if (rc)
450 break;
451 }
452
453 return 0;
454}
455
456
457/** @copydoc KRDROPS::pfnRefresh */
458static int krdrFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
459{
460 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
461 PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase);
462 if (!pPrep)
463 return KERR_INVALID_PARAMETER;
464
465#if K_OS == K_OS_WINDOWS
466 if (pPrep->hSection != NULL)
467 {
468 /** @todo implement me. */
469 return -1;
470 }
471#endif
472
473 return krdrFileGenericRefresh(pRdr, pPrep, cSegments, paSegments);
474}
475
476
477/** Generic implementation of krdrFileRefresh. */
478static int krdrFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments)
479{
480 int rc;
481 int rc2;
482 KU32 i;
483
484 /*
485 * Make everything writable again.
486 */
487 rc = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
488 if (rc)
489 {
490 krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
491 return rc;
492 }
493
494 /*
495 * Clear everything.
496 */
497 /** @todo only zero the areas not covered by raw file bits. */
498 kHlpMemSet(pPrep->pv, 0, pPrep->cb);
499
500 /*
501 * Reload all the segments.
502 * We could possibly skip some segments, but we currently have
503 * no generic way of figuring out which at the moment.
504 */
505 for (i = 0; i < cSegments; i++)
506 {
507 void *pv;
508
509 if ( paSegments[i].RVA == NIL_KLDRADDR
510 || paSegments[i].cbFile <= 0)
511 continue;
512
513 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
514 rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile);
515 if (rc)
516 break;
517 }
518
519 /*
520 * Protect the bits again.
521 */
522 rc2 = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
523 if (rc2 && rc)
524 rc = rc2;
525
526 return rc;
527}
528
529
530/** @copydoc KRDROPS::pfnMap */
531static int krdrFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
532{
533 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
534 PKRDRFILEPREP pPrep = &pRdrFile->aPreps[pRdrFile->cPreps];
535 KLDRSIZE cbTotal;
536 const KSIZE cbPage = pRdr->pOps->pfnPageSize(pRdr);
537 int rc;
538 KU32 i;
539
540 if (pRdrFile->cPreps >= K_ELEMENTS(pRdrFile->aPreps))
541 return KRDR_ERR_TOO_MANY_MAPPINGS;
542
543 /*
544 * Calc the total mapping space needed.
545 */
546 cbTotal = 0;
547 for (i = 0; i < cSegments; i++)
548 {
549 KLDRSIZE uRVASegmentEnd;
550 if (paSegments[i].RVA == NIL_KLDRADDR)
551 continue;
552 uRVASegmentEnd = paSegments[i].RVA + paSegments[i].cbMapped;
553 if (cbTotal < uRVASegmentEnd)
554 cbTotal = uRVASegmentEnd;
555 }
556 pPrep->cb = (KSIZE)cbTotal;
557 if (pPrep->cb != cbTotal)
558 return KLDR_ERR_ADDRESS_OVERFLOW;
559 pPrep->cb = (pPrep->cb + (cbPage - 1)) & ~(cbPage- 1);
560
561#if K_OS == K_OS_WINDOWS
562 /*
563 * The NT memory mapped file API sucks in a lot of ways. Unless you're actually
564 * trying to map a PE image and the kernel can parse the file for it self, the
565 * API just isn't up to scratch.
566 *
567 * Problems:
568 * 1. Reserving memory for the views is risky because you can't reserve and
569 * map into the reserved space. So, other threads might grab the memory
570 * before we get to it.
571 * 2. The page aligning of file offsets makes it impossible to map most
572 * executable images since these are commonly sector aligned.
573 * 3. When mapping a read+execute file, its not possible to create section
574 * larger than the file since the section size is bound to the data file
575 * size. This wouldn't have been such a problem if it was possible to
576 * map views beyond the section restriction, i.e. have a file size and
577 * view size.
578 * 4. Only x86 can map views at page granularity it seems, and that only
579 * using an undocument flag. The default granularity is 64KB.
580 * 5. There is more crappyness here...
581 *
582 * So, first we'll have to check if we can the file using the crappy NT APIs.
583 * Chances are we can't.
584 */
585 for (i = 0; i < cSegments; i++)
586 {
587 if (paSegments[i].RVA == NIL_KLDRADDR)
588 continue;
589
590 /* The file backing of the segments must be page aligned. */
591 if ( paSegments[i].cbFile > 0
592 && paSegments[i].offFile & (cbPage - 1))
593 break;
594
595 /* Only page alignment gaps between the file size and the mapping size. */
596 if ( paSegments[i].cbFile > 0
597 && (paSegments[i].cbFile & ~(cbPage - 1)) != (paSegments[i].cbMapped & ~(cbPage - 1)) )
598 break;
599
600 /* The mapping addresses of the segments must be page aligned.
601 * Non-x86 will probably require 64KB alignment here. */
602 if (paSegments[i].RVA & (cbPage - 1))
603 break;
604
605 /* If we do have to allocate the segment it's RVA must be 64KB aligned. */
606 if ( paSegments[i].cbFile > 0
607 && (paSegments[i].RVA & 0xffff))
608 break;
609 }
610 /** @todo if this is a PE image, we might just try a SEC_IMAGE mapping. It'll work if the host and image machines matches. */
611 if (i == cSegments)
612 {
613 /* WOW! it may work out! Incredible! */
614 SIZE_T ViewSize;
615 LARGE_INTEGER SectionOffset;
616 LARGE_INTEGER MaxiumSize;
617 NTSTATUS Status;
618 PVOID pv;
619
620 MaxiumSize.QuadPart = pRdr->pOps->pfnSize(pRdr);
621 if (MaxiumSize.QuadPart > (LONGLONG)cbTotal)
622 MaxiumSize.QuadPart = cbTotal;
623
624 Status = NtCreateSection(&pPrep->hSection,
625 SECTION_MAP_EXECUTE | SECTION_MAP_READ, /* desired access */
626 NULL, /* object attributes */
627 &MaxiumSize,
628 PAGE_EXECUTE_WRITECOPY, /* page attributes */
629 SEC_COMMIT, /* section attributes */
630 pRdrFile->File);
631 if (!NT_SUCCESS(Status))
632 return (int)Status;
633
634 /*
635 * Determin the base address.
636 */
637 if (fFixed)
638 pPrep->pv = *ppvBase;
639 else
640 {
641 pv = NULL;
642 ViewSize = (KSIZE)cbTotal;
643
644 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
645 &pv,
646 0, /* ZeroBits */
647 &ViewSize,
648 MEM_RESERVE,
649 PAGE_READONLY);
650 if (NT_SUCCESS(Status))
651 {
652 pPrep->pv = *ppvBase = pv;
653 ViewSize = 0;
654 Status = NtFreeVirtualMemory(NtCurrentProcess(), &pv, &ViewSize, MEM_RELEASE);
655 }
656 if (!NT_SUCCESS(Status))
657 {
658 NtClose(pPrep->hSection);
659 return Status;
660 }
661 }
662
663 /*
664 * Map the segments.
665 */
666 for (i = 0; i < cSegments; i++)
667 {
668 ULONG fPageProt;
669
670 if (paSegments[i].RVA == NIL_KLDRADDR)
671 continue;
672
673 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
674 if (paSegments[i].cbFile > 0)
675 {
676 SectionOffset.QuadPart = paSegments[i].offFile;
677 ViewSize = paSegments[i].cbFile;
678 fPageProt = krdrFileGetNtMapProt(paSegments[i].enmProt);
679 /* STATUS_MAPPED_ALIGNMENT
680 STATUS_CONFLICTING_ADDRESSES
681 STATUS_INVALID_VIEW_SIZE */
682 Status = NtMapViewOfSection(pPrep->hSection, NtCurrentProcess(),
683 &pv,
684 0, /* ZeroBits */
685 0, /* CommitSize */
686 &SectionOffset, /* SectionOffset */
687 &ViewSize,
688 ViewUnmap,
689 MEM_DOS_LIM, /* AllocationType */
690 fPageProt);
691 /* do we have to zero anything? */
692 if ( NT_SUCCESS(Status)
693 && 0/*later*/)
694 {
695 /*ULONG OldPageProt = 0;
696 NtProtectVirtualMemory(NtCurrentProcess(), &pv, &ViewSize, , */
697 }
698 }
699 else
700 {
701 ViewSize = paSegments[i].cbMapped;
702 fPageProt = krdrFileGetNtAllocProt(paSegments[i].enmProt);
703 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
704 &pv,
705 0, /* ZeroBits */
706 &ViewSize,
707 MEM_COMMIT,
708 fPageProt);
709 }
710 if (!NT_SUCCESS(Status))
711 break;
712 }
713
714 /*
715 * On success, commit the mapping and return.
716 */
717 if (NT_SUCCESS(Status))
718 {
719 pRdrFile->cPreps++;
720 return 0;
721 }
722
723 /* bail out and fall back on the generic code. */
724 while (i-- > 0)
725 {
726 PVOID pv;
727
728 if (paSegments[i].RVA == NIL_KLDRADDR)
729 continue;
730
731 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
732 if (paSegments[i].cbFile > 0)
733 NtUnmapViewOfSection(NtCurrentProcess(), pv);
734 else
735 NtFreeVirtualMemory(NtCurrentProcess(), &pv, NULL, MEM_RELEASE);
736 }
737 NtClose(pPrep->hSection);
738 }
739 /* else: fall back to the generic code */
740 pPrep->hSection = NULL;
741#endif
742
743 /*
744 * Use the generic map emulation.
745 */
746 pPrep->pv = fFixed ? *ppvBase : NULL;
747 rc = krdrFileGenericMap(pRdr, pPrep, cSegments, paSegments, fFixed);
748 if (!rc)
749 {
750 *ppvBase = pPrep->pv;
751 pRdrFile->cPreps++;
752 }
753
754 return rc;
755}
756
757
758/** Generic implementation of krdrFileMap. */
759static int krdrFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
760{
761 int rc;
762 KU32 i;
763
764 /*
765 * Generic mapping code using kHlpPageAlloc(), kHlpPageFree() and kHlpPageProtect().
766 */
767 rc = kHlpPageAlloc(&pPrep->pv, pPrep->cb, KPROT_EXECUTE_READWRITE, fFixed);
768 if (rc)
769 return rc;
770
771 /*
772 * Load the data.
773 */
774 for (i = 0; i < cSegments; i++)
775 {
776 void *pv;
777
778 if ( paSegments[i].RVA == NIL_KLDRADDR
779 || paSegments[i].cbFile <= 0)
780 continue;
781
782 pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
783 rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile);
784 if (rc)
785 break;
786 }
787
788 /*
789 * Set segment protection.
790 */
791 if (!rc)
792 {
793 rc = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
794 if (!rc)
795 return 0;
796 krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
797 }
798
799 /* bailout */
800 kHlpPageFree(pPrep->pv, pPrep->cb);
801 return rc;
802}
803
804
805/** @copydoc KRDROPS::pfnPageSize */
806static KSIZE krdrFilePageSize(PKRDR pRdr)
807{
808#if K_OS == K_OS_OS2
809 /* The page size on OS/2 wont change anytime soon. :-) */
810 return 0x1000;
811
812#elif K_OS == K_OS_WINDOWS
813 SYSTEM_INFO SysInfo;
814 GetSystemInfo(&SysInfo);
815 return SysInfo.dwPageSize;
816 /*return SysInfo.dwAllocationGranularity;*/
817#else
818# error "port me"
819#endif
820}
821
822
823/** @copydoc KRDROPS::pfnName */
824static const char *krdrFileName(PKRDR pRdr)
825{
826 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
827 return &pRdrFile->szFilename[0];
828}
829
830
831static KIPTR krdrFileNativeFH(PKRDR pRdr)
832{
833 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
834#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
835 return (KIPTR)pRdrFile->File;
836#else
837# error "port me"
838#endif
839}
840
841
842/** @copydoc KRDROPS::pfnTell */
843static KFOFF krdrFileTell(PKRDR pRdr)
844{
845 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
846
847 /*
848 * If the offset is undefined, try figure out what it is.
849 */
850 if (pRdrFile->off == -1)
851 {
852#if K_OS == K_OS_OS2
853 ULONG ulNew;
854 APIRET rc = DosSetFilePtr(pRdrFile->File, 0, FILE_CURRENT, &ulNew);
855 if (rc)
856 return -1;
857 pRdrFile->off = ulNew;
858
859#elif K_OS == K_OS_WINDOWS
860 LONG offHigh = 0;
861 LONG offLow;
862 int rc;
863
864 SetLastError(0);
865 offLow = SetFilePointer(pRdrFile->File, 0, &offHigh, FILE_CURRENT);
866 rc = GetLastError();
867 if (rc)
868 return -1;
869 pRdrFile->off = ((KFOFF)offHigh << 32) | offLow;
870
871#else
872# error "port me."
873#endif
874 }
875 return pRdrFile->off;
876}
877
878
879/** @copydoc KRDROPS::pfnSize */
880static KFOFF krdrFileSize(PKRDR pRdr)
881{
882 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
883 return pRdrFile->cb;
884}
885
886
887/** @copydoc KRDROPS::pfnAllUnmap */
888static int krdrFileAllUnmap(PKRDR pRdr, const void *pvBits)
889{
890 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
891
892 /* check for underflow */
893 if (pRdrFile->cMappings <= 0)
894#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
895 return ERROR_INVALID_PARAMETER;
896#else
897# error "port me"
898#endif
899
900 /* decrement usage counter, free mapping if no longer in use. */
901 if (!--pRdrFile->cMappings)
902 {
903 kHlpFree(pRdrFile->pvMapping);
904 pRdrFile->pvMapping = NULL;
905 }
906
907 return 0;
908}
909
910
911/** @copydoc KRDROPS::pfnAllMap */
912static int krdrFileAllMap(PKRDR pRdr, const void **ppvBits)
913{
914 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
915
916 /*
917 * Do we need to map it?
918 */
919 if (!pRdrFile->pvMapping)
920 {
921 int rc;
922 KFOFF cbFile = pRdrFile->Core.pOps->pfnSize(pRdr);
923 KSIZE cb = (KSIZE)cbFile;
924 if (cb != cbFile)
925 return ERROR_NOT_ENOUGH_MEMORY;
926
927 pRdrFile->pvMapping = kHlpAlloc(cb);
928 if (!pRdrFile->pvMapping)
929#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
930 return ERROR_NOT_ENOUGH_MEMORY;
931#else
932# error "port me"
933#endif
934 rc = pRdrFile->Core.pOps->pfnRead(pRdr, pRdrFile->pvMapping, cb, 0);
935 if (rc)
936 {
937 kHlpFree(pRdrFile->pvMapping);
938 pRdrFile->pvMapping = NULL;
939 return rc;
940 }
941 pRdrFile->cMappings = 0;
942 }
943
944 *ppvBits = pRdrFile->pvMapping;
945 pRdrFile->cMappings++;
946 return 0;
947}
948
949
950/** @copydoc KRDROPS::pfnRead */
951static int krdrFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off)
952{
953 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
954
955 /*
956 * Do a seek if needed.
957 */
958 if (pRdrFile->off != off)
959 {
960#if K_OS == K_OS_OS2
961 ULONG ulNew;
962 APIRET rc;
963
964 rc = DosSetFilePtr(pRdrFile->File, off, FILE_BEGIN, &ulNew);
965 if (rc)
966 {
967 pRdrFile->off = -1;
968 return rc;
969 }
970
971#elif K_OS == K_OS_WINDOWS
972 LONG offHigh;
973 LONG offLow;
974
975 offHigh = (LONG)(off >> 32);
976 offLow = SetFilePointer(pRdrFile->File, (LONG)off, &offHigh, FILE_BEGIN);
977 if ( offLow != (LONG)off
978 || offHigh != (LONG)(off >> 32))
979 {
980 int rc = GetLastError();
981 if (!rc)
982 rc = ERROR_GEN_FAILURE;
983 pRdrFile->off = -1;
984 return rc;
985 }
986
987#else
988# error "port me."
989#endif
990 }
991
992 /*
993 * Do the read.
994 */
995#if K_OS == K_OS_OS2
996 {
997 ULONG cbRead = 0;
998 APIRET rc = DosRead(pRdrFile->File, pvBuf, cb, &cbRead);
999 if (rc)
1000 {
1001 pRdrFile->off = -1;
1002 return rc;
1003 }
1004 if (cbRead != cb)
1005 {
1006 pRdrFile->off = -1;
1007 return ERROR_GEN_FAILURE;
1008 }
1009 }
1010
1011#elif K_OS == K_OS_WINDOWS
1012 {
1013 DWORD cbRead = 0;
1014 if (!ReadFile(pRdrFile->File, pvBuf, cb, &cbRead, NULL))
1015 {
1016 int rc = GetLastError();
1017 if (!rc)
1018 rc = ERROR_GEN_FAILURE;
1019 pRdrFile->off = -1;
1020 return rc;
1021 }
1022 if (cbRead != cb)
1023 {
1024 pRdrFile->off = -1;
1025 return ERROR_GEN_FAILURE;
1026 }
1027 }
1028
1029#else
1030# error "port me."
1031#endif
1032
1033 pRdrFile->off = off + cb;
1034 return 0;
1035}
1036
1037
1038/** @copydoc KRDROPS::pfnDestroy */
1039static int krdrFileDestroy(PKRDR pRdr)
1040{
1041 PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
1042 int rc;
1043#if K_OS == K_OS_OS2
1044 rc = DosClose(pRdrFile->File);
1045
1046#elif K_OS == K_OS_WINDOWS
1047 rc = 0;
1048 if (!CloseHandle(pRdrFile->File))
1049 rc = GetLastError();
1050
1051#else
1052# error "port me"
1053#endif
1054
1055 if (pRdrFile->pvMapping)
1056 {
1057 kHlpFree(pRdrFile->pvMapping);
1058 pRdrFile->pvMapping = NULL;
1059 }
1060
1061 kHlpFree(pRdr);
1062 return rc;
1063}
1064
1065
1066/** @copydoc KRDROPS::pfnCreate */
1067static int krdrFileCreate(PPKRDR ppRdr, const char *pszFilename)
1068{
1069 KSIZE cchFilename;
1070 PKRDRFILE pRdrFile;
1071
1072 /*
1073 * Open the file and determin its size.
1074 */
1075#if K_OS == K_OS_OS2
1076 ULONG ulAction = 0;
1077 FILESTATUS3 Info;
1078 APIRET rc;
1079 HFILE File = 0;
1080 KFOFF cb;
1081 char szFilename[CCHMAXPATH];
1082
1083 if ((uintptr_t)pszFilename >= 0x20000000)
1084 {
1085 char *psz;
1086 cchFilename = kHlpStrLen(szFilename);
1087 psz = (char *)kHlpAllocA(cchFilename + 1);
1088 kHlpMemCopy(psz, pszFilename, cchFilename + 1);
1089 pszFilename = psz;
1090 }
1091 rc = DosOpen((PCSZ)pszFilename, &File, &ulAction, 0, FILE_NORMAL,
1092 OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
1093 OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY | OPEN_FLAGS_RANDOMSEQUENTIAL,
1094 NULL);
1095 if (rc)
1096 return rc;
1097
1098 rc = DosQueryPathInfo((PCSZ)pszFilename, FIL_QUERYFULLNAME, szFilename, sizeof(szFilename));
1099 if (rc)
1100 {
1101 DosClose(File);
1102 return rc;
1103 }
1104
1105 rc = DosQueryFileInfo(File, FIL_STANDARD, &Info, sizeof(Info));
1106 if (rc)
1107 {
1108 DosClose(File);
1109 return rc;
1110 }
1111 cb = Info.cbFile;
1112
1113#elif K_OS == K_OS_WINDOWS
1114 SECURITY_ATTRIBUTES SecAttr;
1115 DWORD High;
1116 DWORD Low;
1117 int rc;
1118 HANDLE File;
1119 KFOFF cb;
1120 char szFilename[MAX_PATH];
1121
1122 SecAttr.bInheritHandle = FALSE;
1123 SecAttr.lpSecurityDescriptor = NULL;
1124 SecAttr.nLength = 0;
1125 File = CreateFile(pszFilename, GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, &SecAttr, OPEN_EXISTING, 0, NULL);
1126 if (File == INVALID_HANDLE_VALUE)
1127 return GetLastError();
1128
1129 if (!GetFullPathName(pszFilename, sizeof(szFilename), szFilename, NULL))
1130 {
1131 rc = GetLastError();
1132 CloseHandle(File);
1133 return rc;
1134 }
1135
1136 SetLastError(0);
1137 Low = GetFileSize(File, &High);
1138 rc = GetLastError();
1139 if (rc)
1140 {
1141 CloseHandle(File);
1142 return rc;
1143 }
1144 cb = ((KFOFF)High << 32) | Low;
1145
1146#else
1147# error "port me"
1148#endif
1149
1150
1151 /*
1152 * Allocate the reader instance.
1153 */
1154 cchFilename = kHlpStrLen(szFilename);
1155 pRdrFile = (PKRDRFILE)kHlpAlloc(sizeof(*pRdrFile) + cchFilename);
1156 if (!pRdrFile)
1157#if K_OS == K_OS_OS2
1158 {
1159 DosClose(File);
1160 return ERROR_NOT_ENOUGH_MEMORY;
1161 }
1162#elif K_OS == K_OS_WINDOWS
1163 {
1164 CloseHandle(File);
1165 return ERROR_NOT_ENOUGH_MEMORY;
1166 }
1167#else
1168# error "port me"
1169#endif
1170
1171 /*
1172 * Initialize it and return successfully.
1173 */
1174 pRdrFile->Core.u32Magic = KRDR_MAGIC;
1175 pRdrFile->Core.pOps = &g_kRdrFileOps;
1176 pRdrFile->File = File;
1177 pRdrFile->cb = cb;
1178 pRdrFile->off = 0;
1179 pRdrFile->cMappings = 0;
1180 pRdrFile->cPreps = 0;
1181 kHlpMemCopy(&pRdrFile->szFilename[0], szFilename, cchFilename + 1);
1182
1183 *ppRdr = &pRdrFile->Core;
1184 return 0;
1185}
1186
Note: See TracBrowser for help on using the repository browser.