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

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

use .cpp. some string fixes.

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