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

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

Made build on VCC80 / amd64.

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