source: trunk/kStuff/kRdr/kRdrFile.c@ 3545

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

Helpers and macros.

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