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

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

Made it compile on darwin.

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