source: trunk/src/win32k/ldr/myldrOpen.cpp@ 5120

Last change on this file since 5120 was 5119, checked in by bird, 25 years ago

Added force preload.

File size: 42.9 KB
Line 
1/* $Id: myldrOpen.cpp,v 1.15 2001-02-11 23:43:51 bird Exp $
2 *
3 * myldrOpen - ldrOpen.
4 *
5 * Copyright (c) 1998-2001 knut st. osmundsen <knut.stange.osmundsen@mynd.no>
6 *
7 * Project Odin Software License can be found in LICENSE.TXT
8 *
9 */
10
11
12/*******************************************************************************
13* Defined Constants And Macros *
14*******************************************************************************/
15#define INCL_DOSERRORS
16#define INCL_NOPMAPI
17
18#define INCL_OS2KRNL_IO
19#define INCL_OS2KRNL_TCB
20#define INCL_OS2KRNL_SEM
21#define INCL_OS2KRNL_SEC
22#define INCL_OS2KRNL_LDR
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#include <os2.h>
28
29#include "devSegDf.h" /* Win32k segment definitions. */
30#include "rmalloc.h"
31#include "malloc.h"
32#include <memory.h>
33#include <stdlib.h>
34#include <string.h>
35#include <stdarg.h>
36
37#include "log.h"
38#include "avl.h"
39#include "options.h"
40#include <peexe.h>
41#include <exe386.h>
42#include "elf.h"
43#include "OS2Krnl.h"
44#include "dev32.h"
45#include "ldr.h"
46#include "ModuleBase.h"
47#include "pe2lx.h"
48#include "myExecPgm.h"
49#include "env.h"
50#include "vprintf.h" /* Make 100% sure we have va_start. */
51
52
53
54/*******************************************************************************
55* Internal Functions *
56*******************************************************************************/
57/* static */ APIRET AddArgsToFront(int cArgs, ...);
58/* static */ APIRET SetExecName(const char *pszExeName);
59/* static */ APIRET OpenPATH(PSFN phFile, char *pszFilename, PULONG pfl);
60
61
62/**
63 * ldrOpen override.
64 * @returns Return code.
65 * @param phFile Pointer to file handler. Holds filehandle on output.
66 * @param pszFilename Pointer to filename.
67 * @parma pfl Pointer to some flags.
68 */
69ULONG LDRCALL myldrOpen(PSFN phFile, PSZ pszFilename, PULONG pfl)
70{
71 static int cNesting = 0; /* This is an variable which hold the nesting */
72 /* level of this function. This is useful */
73 /* when we call it recurcively. */
74 /* The maximum nesting level is currently 3. */
75 /* When the maximum depth has been reached */
76 /* we'll not intercept loading any longer! */
77 ULONG rc; /* Return value. */
78
79 /** @sketch
80 * Try open the file (that's why this function is called)
81 * Apply Extention fix if this is requested.
82 */
83 if (fldrOpenExtentionFix)
84 {
85 int cchFilename = strlen(pszFilename);
86 pszFilename[cchFilename - 4] = '\0';
87 rc = ldrOpen(phFile, pszFilename, pfl);
88 if (rc != NO_ERROR)
89 {
90 pszFilename[cchFilename - 4] = '.';
91 rc = ldrOpen(phFile, pszFilename, pfl);
92 }
93 }
94 else
95 rc = ldrOpen(phFile, pszFilename, pfl);
96
97 if (rc == NO_ERROR)
98 kprintf(("myldrOpen-%d: phFile=%#.4x, flags=%#.8x, pszFn=%s\n", cNesting, *phFile, pfl, pszFilename));
99
100
101 /** @sketch
102 * Are we to intercept the loading?
103 * - If open were successful.
104 * - And Not too deep nesting.
105 * - And that this isn't an unsupported load.
106 * - And one of the loaders are enabled.
107 */
108 if (rc == NO_ERROR
109 && cNesting < 3
110 && !isLdrStateLoadingUnsupported()
111 && isAnyLoaderEnabled()
112 )
113 {
114 union _u_ReadBufferPointers /* Read buffer pointer(s). */
115 {
116 char *pach; /* Pointer to the buffer as char. */
117 unsigned long *pul; /* Pointer to the buffer as unsigned long. */
118 PIMAGE_DOS_HEADER pMzHdr; /* Use the buffer as a dosheader. */
119 PIMAGE_NT_HEADERS pNtHdrs; /* Use the buffer as a NT header. */
120 } u1;
121 unsigned cbFile; /* Filesize (0xffffffff if call to SftFileSize failed - should _never_ happen though) */
122 unsigned cbRead; /* Amount of the buffer which contains valid data. */
123 char * psz; /* Multipurpose string pointer no.1. */
124 char * psz2; /* Multipurpose string pointer no.2. */
125 char * psz3; /* Multipurpose string pointer no.3. */
126
127 /** @sketch
128 * Allocate read buffer from resident heap.
129 * IF this fails THEN we'll simply return NO_ERROR.
130 */
131 u1.pach = (char*)rmalloc(640);
132 if (u1.pach == NULL)
133 {
134 kprintf(("myldrOpen-%d: rmalloc(640) failed\n", cNesting));
135 goto ret;
136 }
137
138
139 /** @sketch
140 * Increment nesting level.
141 */
142 cNesting++;
143
144
145 /** @sketch
146 * Get the filesize. On failure filesize is set to ~0.
147 */
148 rc = SftFileSize(*phFile, (PULONG)SSToDS(&cbFile));
149 if (rc != NO_ERROR)
150 {
151 kprintf(("myldrOpen-%d: SftFileSize failed with rc=%d\n", cNesting, rc));
152 cbFile = (unsigned)~0;
153 }
154
155
156 /** @sketch
157 * Read the size of a DOS (ie. MZ) header.
158 * IF successful and more stuff in file THEN
159 * See if this is an recognizable module binary format:
160 */
161 cbRead = min(sizeof(IMAGE_DOS_HEADER), cbFile);
162 rc = ldrRead(*phFile, 0UL, u1.pMzHdr, 0UL, cbRead, NULL);
163 if (rc == NO_ERROR && cbRead < cbFile)
164 {
165 /** @sketch
166 * If LX header just give up at once.
167 */
168 if (u1.pMzHdr->e_magic == E32MAGIC)
169 goto cleanup;
170
171 /** @sketch
172 * IF PE or MZ header THEN
173 */
174 if (u1.pMzHdr->e_magic == IMAGE_DOS_SIGNATURE
175 || u1.pNtHdrs->Signature == IMAGE_NT_SIGNATURE)
176 {
177 ULONG offPe; /* Offset to PE header. */
178
179 /** @sketch
180 * ---
181 * We now known that this is file has a MZ or a PE header. If it's
182 * a MZ header, we might end up with no "New" header or the "New"
183 * header might turn out to be a NE, LE, or LX header. I any of
184 * these non PE headers occur OS/2 will take care of it, we'll do nothing.
185 * ---
186 * IF PE loading is disable or MZ header and e_lfanew is invalid THEN
187 * return (successfully) to the caller.
188 * ENDIF
189 * (Find the offset of the PE header while testing (offPe).)
190 */
191 if (isPELoaderDisabled())
192 goto cleanup;
193 if (u1.pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
194 {
195 offPe = u1.pMzHdr->e_lfanew;
196 if (offPe < sizeof(IMAGE_DOS_HEADER) || offPe > 0x04000000UL)
197 goto cleanup;
198 }
199 else
200 offPe = 0;
201
202
203 /** @sketch
204 * Read the PE header.
205 * If the read failes or not PE signature, there isn't anything for us to do.
206 */
207 rc = ldrRead(*phFile, offPe, u1.pach, 0UL, sizeof(IMAGE_NT_HEADERS), NULL);
208 if (rc != NO_ERROR || u1.pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
209 goto cleanup_noerror;
210
211
212 /** @sketch
213 * PE signature found!
214 */
215 kprintf(("myldrOpen-%d: PE executable...\n", cNesting));
216
217
218 /** @sketch
219 * Use Pe2Lx?
220 * - When Pe2Lx flag is set
221 * - When the MIXED flag is set and the image isn't an executable
222 * above the first 64MB private limit without relocations
223 */
224 if (isPe2LxLoaderEnabled()
225 || (isMixedPeLoaderEnabled()
226 && ((u1.pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL)
227 || !(u1.pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
228 || u1.pNtHdrs->OptionalHeader.ImageBase < 0x04000000UL /* 64MB */
229 )
230 )
231 )
232 { /** @sketch
233 * Pe2Lx (Ring0 of course)
234 * - Create a Pe2Lx class,
235 * - initiate it
236 * - Add the module to the module tree so we may find it later...
237 * - Set the (file)handle state to 'our'.
238 * - Set pExeModule to module pointer and loaderstate to our exe.
239 */
240 Pe2Lx * pPe2Lx = new Pe2Lx(*phFile);
241 if (pPe2Lx != NULL)
242 {
243 rc = pPe2Lx->init(pszFilename);
244 if (rc == NO_ERROR)
245 {
246 kprintf(("myldrOpen-%d: Successfully init of Pe2Lx object.\n", cNesting));
247 rc = addModule(*phFile, NULL, MOD_TYPE_PE2LX, pPe2Lx);
248 if (rc == NO_ERROR)
249 {
250 #pragma info(notrd)
251 SetState(*phFile, HSTATE_OUR);
252 #pragma info(restore)
253 if (pPe2Lx->isExe())
254 {
255 setLdrStateLoadingOurEXE();
256 pExeModule = getModuleBySFN(*phFile);
257 #ifdef DEBUG
258 if (pExeModule == NULL)
259 kprintf(("myldrOpen-%d: getModuleBySFN failed when setting pExeModule! FATAL!\n", cNesting));
260 #endif
261 }
262 }
263 else
264 kprintf(("myldrOpen-%d: Failed to add the module. rc=%d\n", cNesting));
265 }
266 else
267 kprintf(("myldrOpen-%d: Failed to init Pe2Lx object. rc=%d\n", cNesting));
268 if (rc != NO_ERROR)
269 delete pPe2Lx;
270 }
271 else
272 {
273 kprintf(("myldrOpen-%d: Failed to allocate Pe2Lx object.\n", cNesting));
274 rc = ERROR_NOT_ENOUGH_MEMORY;
275 }
276
277 goto cleanup;
278 }
279
280
281 /** @sketch
282 * Using PE.EXE to start EXE?
283 * - When the file is an EXE file and PE.EXE is enabled.
284 */
285 if ((u1.pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0UL
286 && (options.fPE == FLAGS_PE_PE || options.fPE == FLAGS_PE_MIXED)
287 && (isLdrStateExecPgm() || isLdrStateQAppType())
288 )
289 {
290 /** @sketch
291 * PE.EXE:
292 * Find pe.exe - look in current directory and thru the PATH.
293 * Note! We use the read buffer (u1.p*) as a storage for the
294 * pe.exe filename and path.
295 */
296 kprintf(("myldrOpen-%d: pe.exe - opening\n", cNesting));
297 ldrClose(*phFile);
298 strcpy(u1.pach, "PE.EXE");
299 rc = ldrOpen(phFile, u1.pach, pfl); /* This isn't recusive! */
300 if (rc != NO_ERROR)
301 rc = OpenPATH(phFile, u1.pach, pfl);
302 if (rc == NO_ERROR)
303 {
304 /** @sketch
305 * If we're in tkExecPgm state we'll have to shuffle the parameters
306 * and executable filename tkExecPgm were called with.
307 * If not tkExecPgm we can't do anything about parameters (and there is
308 * probably nothing to do either).
309 * We'll always enclose the PE executable name in quotes.
310 */
311 kprintf(("myldrOpen-%d: pe.exe - %s\n", cNesting, u1.pach));
312 if (isLdrStateExecPgm() && fTkExecPgm)
313 {
314 u1.pach[0] = '"';
315 strcpy(&u1.pach[1], achTkExecPgmFilename);
316 u1.pach[strlen(u1.pach)] = '\0';
317 rc = AddArgsToFront(2, ldrpFileNameBuf, u1.pach);
318 if (rc == NO_ERROR)
319 {
320 rc = SetExecName(ldrpFileNameBuf);
321 if (rc != NO_ERROR)
322 kprintf(("myldrOpen-%d: pe.exe - failed to set pe.exe as execname. rc=%d\n", cNesting));
323 }
324 else
325 kprintf(("myldrOpen-%d: pe.exe - failed to add programname as argument. rc=%d\n", cNesting, rc));
326 goto cleanup_noerror;
327 }
328 }
329 else
330 kprintf(("myldrOpen-%d: pe.exe - couldn't find/open pe.exe\n", cNesting));
331 }
332 goto cleanup;
333 }
334 /** @sketch End of PE Loading. */
335
336
337 /** @sketch
338 * ELF image?
339 */
340 if (*u1.pul == ELFMAGICLSB)
341 {
342 if (isELFDisabled())
343 goto cleanup_noerror;
344
345 /*
346 * ELF signature found.
347 */
348 kprintf(("myldrOpen-%d: ELF image! - not implemented yet!\n", cNesting));
349
350 /*
351 * Do nothing more yet. NEED AN ELF LOADER!!!
352 */
353 goto cleanup;
354 }
355
356
357 /** @sketch
358 * Java image?
359 */
360 if (*u1.pul == 0xBEBAFECAUL) //CAh FEh BAh BEh
361 {
362 char *pszName = NULL;
363 int cchName;
364
365 if (isJAVADisabled())
366 goto cleanup_noerror;
367
368 /** @sketch
369 * Java signature found.
370 * Copy the name to a temporary buffer. (only if necessary)
371 * Remove the extention (.class) and insert a space between the name and the path.
372 * (This is the needed processing of the class filename to make it a classpath
373 * entry (path) and a class name (filename).)
374 * Try find the java executor in current dir or PATH: java.exe
375 */
376 kprintf(("myldrOpen-%d: Jave image!\n", cNesting));
377
378 if (isLdrStateExecPgm() && fTkExecPgm)
379 {
380 /* Ooops we had to get the file name from the MFT. ldrpFileNameBuf is allways uppercased... */
381 /* MFT seems to hold uppercased filenames! ARG! But (by pure luck?) achTkExecPgmArguments is
382 * not uppercased (yet). Nothing could be simpler!
383 */
384 #if 1
385 psz3 = achTkExecPgmArguments;
386 #elif 0
387 psz3 = SecPathFromSFN(*phFile);
388 if (psz3 == NULL)
389 psz3 = ldrpFileNameBuf;
390 #else
391 psz3 = ldrpFileNameBuf;
392 #endif
393 cchName = strlen(psz3);
394 pszName = (char*)rmalloc(cchName + 2);
395 if (pszName == NULL)
396 {
397 rc = ERROR_NOT_ENOUGH_MEMORY;
398 goto cleanup;
399 }
400 memcpy(pszName, psz3, cchName+1);
401
402 psz = pszName + strlen(pszName) - 1;
403 while (psz > pszName && *psz != '.' && *psz != '\\' && *psz != '/')
404 psz--;
405 if (*psz == '.')
406 {
407 cchName = psz - pszName;
408 *psz-- = '\0';
409 while (psz > pszName && *psz != '\\' && *psz != '/')
410 psz--;
411
412 /* check for root and evt. make room for an extra slash. */
413 if (psz - pszName == 2)
414 {
415 memmove(psz + 1, psz, cchName - 1);
416 *psz++ = '\\';
417 }
418 }
419 /* check if no path */
420 if (psz == pszName)
421 memmove(pszName + 1, pszName, cchName + 1);
422 *psz = ' ';
423 }
424
425 ldrClose(*phFile);
426 rc = ldrOpen(phFile, ".\\JAVA.EXE", pfl);
427 if (rc != NO_ERROR)
428 rc = OpenPATH(phFile, "JAVA.EXE", pfl);
429 if (rc == NO_ERROR)
430 {
431 kprintf(("myldrOpen-%d: java - %s\n", cNesting, ldrpFileNameBuf));
432
433 /** @sketch
434 * To be able to execute any given class name we'll have to pass in the
435 * directory as -classpath. But -classpath seems to override the default
436 * and environmental CLASSPATHs. So, we'll have to pass in the value of
437 * the CLASSPATH env.var. or generate the default class path (what ever that is).
438 *
439 * TODO: spaces in class path.
440 */
441 if (isLdrStateExecPgm() && fTkExecPgm)
442 {
443 psz = u1.pach;
444
445 /*
446 * Get classpath and add it as a parameter
447 */
448 strcpy(u1.pach, "-classpath ");
449 psz = u1.pach + strlen(u1.pach);
450
451 psz3 = (char*)ScanEnv(GetEnv(TRUE), "CLASSPATH");
452 if (psz3 != NULL)
453 { /* environment variable set */
454 if (strlen(psz3) > 640 - 11 - 1 - cchName) //check for overflow
455 { // TODO? should reallocate...
456 memcpy(psz, psz3, 640 - 11 - 1 - cchName);
457 psz[640 - 11 - 1 - cchName] = '\0';
458 }
459 else
460 strcpy(psz, psz3);
461 psz += strlen(psz);
462 }
463 else
464 {
465 /* Make default classpath by taking the java.exe path + '..\lib\classes.zip' */
466 strcpy(psz, ldrpFileNameBuf);
467 psz3 = psz + strlen(psz) - 1;
468 while (psz3 > psz && *psz3 != '\\' && *psz3 != '/')
469 psz3--;
470 strcpy(++psz3, "..\\lib\\classes.zip");
471 psz = psz3 + strlen(psz3);
472 }
473
474 /*
475 * Add the class directory (as the last classpath entry) and the class name.
476 * (Note. I may happen that there is no directory, but that don't matter
477 * a space is allways preceding the class name.)
478 */
479 *psz++ = ';';
480 strcpy(psz, pszName);
481 if (pszName != NULL)
482 rfree(pszName);
483
484 /*
485 * Setup JAVA.EXE as executable with the parameters we've build.
486 */
487 rc = AddArgsToFront(2, ldrpFileNameBuf, u1.pach);
488 kprintf(("myldrOpen-%d: java - Exe: %s Args: %s\n", cNesting, ldrpFileNameBuf, u1.pach));
489 if (rc == NO_ERROR)
490 {
491 rc = SetExecName(ldrpFileNameBuf);
492 if (rc != NO_ERROR)
493 kprintf(("myldrOpen-%d: java - failed to set java.exe as execname. rc=%d\n", cNesting, rc));
494 }
495 else
496 kprintf(("myldrOpen-%d: java - failed to setup the parameters. rc=%d\n", cNesting, rc));
497
498 goto cleanup_noerror;
499 }
500 }
501 else
502 kprintf(("myldrOpen-%d: java - couldn't find/open java.exe\n", cNesting));
503
504
505 /** @sketch
506 * End of Java loading. (return)
507 */
508 if (pszName != NULL)
509 rfree(pszName);
510 goto cleanup;
511 }
512
513 }
514 else
515 {
516 /** @sketch
517 * ELSE - the reading size of a DOS header failed or file is smaller than the dos header.
518 * IF read failed or filesize is less than 4 bytes THEN
519 * return no_error to the caller.
520 * ENDIF
521 */
522 #ifdef DEBUG
523 if (rc != NO_ERROR)
524 {
525 kprintf(("myldrOpen-%d: ldrRead failed cbRead=%d, cbFile=%d, rc=%d\n", cNesting, cbRead, cbFile, rc));
526 goto cleanup_noerror;
527 }
528 if (cbRead < 4)
529 {
530 kprintf(("myldrOpen-%d: File too small! cbFile=%d\n", cNesting, cbFile));
531 goto cleanup_noerror;
532 }
533 #else
534 if (rc != NO_ERROR || cbRead < 4) //just forget files less than 4 bytes!
535 goto cleanup_noerror;
536 #endif
537 }
538 /** @sketch ENDIF (dos header read) */
539
540
541
542 /*
543 * Only unreconized files passes this point!
544 *
545 * * Fileformats with lower priority should reside here. *
546 *
547 */
548
549 /** @sketch
550 * UNIX styled script?
551 * - Starts with a hash (#)
552 * - And we're loading an EXE
553 * - And we're either in QAppType or ExecPgm state.
554 * - And that a bang (!) is the first char after the hash (ignoring blanks).
555 *
556 * FIXME: spaces script name.
557 */
558 if (*u1.pach == '#'
559 && isLdrStateLoadingEXE()
560 && (isLdrStateQAppType() || isLdrStateExecPgm())
561 )
562 {
563 if (isUNIXScriptDisabled())
564 goto cleanup_noerror;
565 /*
566 * Look for a bang (!). Tabs and spaces are skipped, anything else result in error.
567 */
568 psz = u1.pach + 1;
569 while ((*psz == ' ' || *psz == '\t') && psz - u1.pach < cbRead)
570 psz++;
571 if (*psz == '!')
572 {
573 /** @sketch Found UNIX styled script! */
574
575 /** @sketch
576 * Read more of the script if necessary. (max is 256 chars (- Linux max is 127))
577 * Terminate the string read from the file to make sure with stop somewhere!
578 */
579 if (cbRead < cbFile /*&& cbRead != 256*/)
580 {
581 cbRead = min(256, cbFile);
582 rc = ldrRead(*phFile, 0UL, u1.pach, 0UL, cbRead, NULL);
583 }
584 u1.pach[cbRead] = '\0';
585
586 if (rc == NO_ERROR)
587 {
588 /** @sketch
589 * Parse out filename and optional arguments (if any).
590 * The result of the parsing is that:
591 * psz will point at the executable name.
592 * psz2 will point at the arguments.
593 * Both strings are trimmed.
594 */
595 psz++; /* psz points to the bang, skip it. */
596 while (*psz == ' ' || *psz == '\t') /* skip blanks after bang */
597 psz++;
598 if (*psz == '\r' || *psz == '\n' || *psz == '\0') /* End-of-line? */
599 {
600 kprintf(("myldrOpen-%d: script no executable name.\n", cNesting));
601 goto cleanup_noerror; /* other error code? */
602 }
603 psz2 = psz + 1; /* Not end-of-line, so add 1 before searching for args. */
604 while (*psz2 != '\0' && *psz2 != '\n' && *psz2 != '\r' /* skip executable name. */
605 && *psz2 != ' ' && *psz2 != '\t')
606 psz2++;
607 while (*psz2 == ' ' || *psz2 == '\t') /* skip blanks after executable - pad them with '\0'! */
608 *psz2++ = '\0';
609
610 psz3 = psz2;
611 while (*psz3 != '\n' && *psz3 != '\r' && *psz3 != '\0') /* find end of parameters and terminate the string. */
612 psz3++;
613 *psz3 = '\0';
614 while (psz3 >= psz2 && (*psz3 == '\0' || *psz3 == ' ' || *psz3 == '\t')) /* trim args */
615 *psz3-- = '\0';
616
617
618 /** @sketch
619 * IF tkExecPgm THEN
620 * Correct parameters - ie. add exec name (as argv[0]),
621 * arguments (psz2) as argv[1+], old exec name, and finally
622 * the existing parameters (current argv[1+]).
623 * Set the executable name.
624 * ENDIF
625 * Open the new executable file recursively. (psz)
626 */
627 if (isLdrStateExecPgm())
628 {
629 if (*psz2)
630 rc = AddArgsToFront(3, psz, psz2, achTkExecPgmFilename);
631 else
632 rc = AddArgsToFront(2, psz, achTkExecPgmFilename);
633 if (rc != NO_ERROR)
634 {
635 kprintf(("myldrOpen-%d: AddArgsToFront failed with rc=%d\n", cNesting));
636 goto cleanup_noerror;
637 }
638 rc = SetExecName(psz);
639 if (rc != NO_ERROR)
640 kprintf(("myldrOpen-%d: SetExecName failed with rc=%d\n", cNesting));
641 }
642 ldrClose(*phFile);
643 rc = myldrOpen(phFile, psz, pfl);
644 if (rc != NO_ERROR)
645 {
646 psz2 = psz + strlen(psz);
647 if (psz + 4 >= psz2 || strcmp(psz2 - 4, ".EXE") != 0)
648 {
649 strcpy(psz2, ".EXE");
650 rc = myldrOpen(phFile, psz, pfl);
651 *psz2 = '\0';
652 }
653 else
654 psz2 = NULL;
655
656 //should we search the PATH??? For a starting, we'll do it.
657 if (rc != NO_ERROR
658 && (rc = OpenPATH(phFile, psz, pfl)) != NO_ERROR
659 && psz2 != NULL)
660 {
661 *psz2 = '.';
662 rc = OpenPATH(phFile, psz, pfl);
663 }
664 }
665 }
666 else
667 {
668 kprintf(("myldrOpen-%d: script - failed to read more of the script!, rc=%d cbRead=%d cbFile=%d.\n",
669 cNesting, rc, cbRead, cbFile));
670 }
671
672 goto cleanup;
673 }
674 else
675 {
676 kprintf(("myldrOpen-%d: script - hash found but no bang (!).\n", cNesting));
677 }
678 } /**@sketch ENDIF - UNIX styled script. */
679
680
681
682 /** @sketch
683 * REXX script?
684 * - Starts with a REXX start comment ('/','*')
685 * - And we're loading an EXE
686 * - And we're either in QAppType or ExecPgm state.
687 * - Extention:
688 * .RX and .REX are known to be pure REXX scripts.
689 * While .CMD has to invoked used the commandline OS2_SHELL or COMSPEC variable.
690 *
691 * FIXME: spaces script name.
692 */
693 psz2 = pszFilename + strlen(pszFilename) - 1;
694 while (psz2 > pszFilename && *psz2 != '.')
695 psz2--;
696 if (*psz2 == '.'
697 && *u1.pach == '/' && u1.pach[1] == '*'
698 && isLdrStateLoadingEXE()
699 && (isLdrStateQAppType() || isLdrStateExecPgm())
700 && (stricmp(psz2, ".RX") == 0 || stricmp(psz2, ".REX") == 0)
701 )
702 {
703 if (isREXXScriptDisabled())
704 goto cleanup_noerror;
705
706 /** @sketch
707 * Found REXX styled script!
708 * Find the REXX interpreter. We'll use kRx.exe to execute the REXX scripts.
709 * (This interpreter could be embedded as a child of ModuleBase as it turned out
710 * to be quite small about 700 bytes.)
711 */
712 kprintf(("myldrOpen-%d: Found REXX script\n", cNesting));
713 ldrClose(*phFile);
714 psz = "KRX.EXE";
715 rc = ldrOpen(phFile, psz, pfl);
716 if (rc != NO_ERROR)
717 rc = OpenPATH(phFile, psz, pfl);
718
719 /** @sketch
720 * IF tkExecPgm THEN
721 * Correct parameters - ie. add exec name (as argv[0]), old exec name,
722 * and finally the existing parameters (current argv[1+]).
723 * Set the executable name.
724 * ENDIF
725 */
726 if (rc == NO_ERROR && isLdrStateExecPgm())
727 {
728 rc = AddArgsToFront(2, ldrpFileNameBuf, achTkExecPgmFilename);
729 if (rc != NO_ERROR)
730 {
731 kprintf(("myldrOpen-%d: AddArgsToFront failed with rc=%d\n", cNesting));
732 goto cleanup_noerror;
733 }
734 rc = SetExecName(ldrpFileNameBuf);
735 if (rc != NO_ERROR)
736 kprintf(("myldrOpen-%d: SetExecName failed with rc=%d\n", cNesting));
737
738 goto cleanup_noerror;
739 }
740 goto cleanup;
741 } /**@sketch ENDIF - REXX styled script. */
742
743
744 /*
745 * Cleanup with rc set to NO_ERROR.
746 */
747 cleanup_noerror:
748 rc = NO_ERROR;
749
750 /*
751 * Cleanup without having rc set to NO_ERROR.
752 * Decrement the nesting count.
753 */
754 cleanup:
755 rfree(u1.pach);
756 cNesting--;
757 }
758 #ifdef DEBUG
759 else if (cNesting >= 3)
760 kprintf(("myldrOpen-%d: cNesting = %d, which is too deep!\n", cNesting, cNesting));
761 #endif
762
763ret:
764 /** @sketch
765 * If successful and force preload enabled and media flag pointer valid Then
766 * Set removable media.
767 * Return rc.
768 */
769 if (rc == NO_ERROR && isForcePreloadEnabled() && pfl)
770 *pfl &= ~0x1000UL; /* 0x1000 is the fixed media flag. */
771
772 return rc;
773}
774
775
776/**
777 * Adds new arguments to the front of the startup arguments for the program about to be
778 * executed.
779 *
780 * @returns OS/2 return code.
781 * @param cArgs Count of arguments to add. At least 1!!!
782 * @param ... Pointers to the arguments to add.
783 * The first argument have to be the executable name. This have to the
784 * the only argument in the first string.
785 * The other arguements are space separated, so you could add a bunch
786 * of arguments in a single string!
787 * The last argument should be the old first parameter if this is to be
788 * preserved. The old first parameter is overwritten since it's
789 * normally the executable name.
790 *
791 * @status completly implemented.
792 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
793 * @remark Implementation note:
794 * The arguments convention is as follows:
795 * First argument, which should be the executable name, is terminated with a '\0'.
796 * It starts at offset 0 into the argument buffer, of course.
797 * All other arguemnts are separated by a space and follows the immediately after the
798 * first argument.
799 * The arguments are terminated by a double nulltermination: '\0\0'.
800 */
801APIRET AddArgsToFront(int cArgs, ...)
802{
803 va_list vaarg; /* Variable length argument list. */
804 int cchOldArgs; /* Length of the old arguments (including the first argument). */
805 /* cchOldArgs = 1 means no arguments. It don't include the very last '\0' */
806 /* (remember argumets are terminated with two '\0's). */
807 int iSecondArg; /* Index of the second argument. (Used to skip the first argument.) */
808 /* Used first on the original arguments and them when adding the first */
809 /* new argument. */
810 int cchNewArgs; /* Length of the new arguments to be inserted. */
811 int i; /* Loop variable. Current function argument. */
812 char * psz; /* General string pointer. */
813
814
815 /** @sketch
816 * Assert that we're in the right state.
817 * Calc the length of the existing parameters.
818 * Calc the length of the new arguments to determin.
819 * Assert that the new arguments have length > 0.
820 */
821 #ifdef DEBUG
822 if (!isLdrStateExecPgm())
823 {
824 kprintf(("AddArgsToFront: not in tkExecPgm state.\n"));
825 return ERROR_INVALID_PARAMETER;
826 }
827 #endif
828 if (!fTkExecPgm)
829 {
830 kprintf(("AddArgsToFront: called when not in tkExecPgm data is invalid!\n"));
831 return ERROR_INVALID_PARAMETER;
832 }
833
834 iSecondArg = strlen(&achTkExecPgmArguments[0]) + 1;
835 psz = &achTkExecPgmArguments[iSecondArg];
836 while (*psz != '\0')
837 psz += strlen(psz) + 1;
838 cchOldArgs = psz - &achTkExecPgmArguments[iSecondArg];
839
840 va_start(vaarg, cArgs);
841 for (cchNewArgs = i = 0; i < cArgs; i++)
842 cchNewArgs += strlen(va_arg(vaarg, char *)) + 1; /* 1 is for space or '\0'. */
843 va_end(vaarg);
844 #ifdef DEBUG
845 if (cchNewArgs == 0)
846 {
847 kprintf(("AddArgsToFront: the size of the arguments to add is zero!\n"));
848 return ERROR_INVALID_PARAMETER;
849 }
850 #endif
851
852
853 /** @sketch
854 * Check if we have enough room for the new arguments. Fail if not enough.
855 * Move the existing arguments to make room for the new ones.
856 * !IMPORTANT! The first existing arguments (executable name) is skipped !IMPORTANT!
857 * !IMPORTANT! in this move as this have to be re-added in this call! !IMPORTANT!
858 */
859 if (cchOldArgs + cchNewArgs + 1 > CCHARGUMENTS)
860 {
861 kprintf(("AddArgsToFront: argument buffer is too small to hold the arguments to add, cchOldArgs=%d, cchNewArgs=%d\n",
862 cchOldArgs, cchNewArgs));
863 return ERROR_BAD_ARGUMENTS;
864 }
865
866 if (cchOldArgs > 0)
867 {
868 memmove(&achTkExecPgmArguments[cchNewArgs], &achTkExecPgmArguments[iSecondArg],
869 cchOldArgs + 1);
870 }
871 else
872 achTkExecPgmArguments[cchNewArgs] = '\0';
873
874
875 /** @sketch
876 * Copy new arguments.
877 * Since the first argument is special case we'll do it separately. (Uses '\0' as separator.)
878 * We assume that the entire first argument passed into this function should be the first argument!
879 * (This don't have to be true for the other arguments since these are space separated. You could
880 * pass in more than argument in a single string.)
881 * Loop thru the rest of the new arguments and add them with space as separator.
882 */
883 va_start(vaarg, cArgs);
884 psz = va_arg(vaarg, char *);
885 memcpy(&achTkExecPgmArguments[0], psz, (i = strlen(psz) + 1));
886 psz = &achTkExecPgmArguments[i];
887
888 for (i = 1; i < cArgs; i++)
889 {
890 if (i > 1) *psz++ = ' '; //Add space if not second argument.
891 strcpy(psz, va_arg(vaarg, char *));
892 psz += strlen(psz);
893 }
894 va_end(vaarg);
895 if (cchOldArgs > 0) *psz++ = ' '; //Add space if old arguments
896
897 #ifdef DEBUG /* assertion */
898 if (psz != &achTkExecPgmArguments[cchNewArgs])
899 {
900 kprintf(("AddArgsToFront: !Assertion failed! psz didn't end up where it should! (psz -> %d should be %d)\n",
901 psz - &achTkExecPgmArguments[0], cchNewArgs));
902 if (cchOldArgs <= 1)
903 psz[0] = psz[1] = '\0';
904 }
905 #endif
906
907 return NO_ERROR;
908}
909
910
911/**
912 * Sets the executable name of the module.
913 * This function is normally invoked after a different executable than the one requested was
914 * opened. It does _NOT_ set the new executable name as the first argument, since it is more
915 * convenient to this while calling AddArgsToFront to add other arguments.
916 *
917 * @returns OS/2 return code.
918 * @param pszExecName Pointer to new executable name.
919 * @status completly implemented.
920 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
921 * @remark .
922 */
923APIRET SetExecName(const char *pszExecName)
924{
925 #ifdef DEBUG
926 int cch;
927 cch = strlen(pszExecName);
928 if (cch > CCHMAXPATH)
929 {
930 kprintf(("ChangeExecName: filename is too long! cch=%d. name=%s\n", cch, pszExecName));
931 return ERROR_FILENAME_EXCED_RANGE;
932 }
933 if (!isLdrStateExecPgm())
934 {
935 kprintf(("ChangeExecName: called when not in tkExecPgm state!!! FATAL ERROR!\n"));
936 return ERROR_INVALID_PARAMETER;
937 }
938 #endif
939 if (!fTkExecPgm)
940 {
941 kprintf(("ChangeExecName: called when not in tkExecPgm data is invalid!!! FATAL ERROR!\n"));
942 return ERROR_INVALID_PARAMETER;
943 }
944
945 strcpy(achTkExecPgmFilename, pszExecName);
946
947 return 0;
948}
949
950
951/**
952 * Opens a file using the PATH environment variable of the current process.
953 * @returns OS2 return code.
954 * @param phFile Pointer to filehandle. The filehandle is set to the SFN for the opened
955 * file on successful return.
956 * The filehandle is 0 on failure.
957 * @param pszFilename Pointer to filename buffer. This will hold the filename on input.
958 * On successful return it holds the filepath found.
959 * On failiure it's undefined.
960 * @param pfl Some flags set by ldrOpen.
961 * @sketch stub
962 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
963 * @remark
964 */
965APIRET OpenPATH(PSFN phFile, char *pszFilename, PULONG pfl)
966{
967 APIRET rc;
968 USHORT TCBFailErr_save;
969 int cchFile; /* Filename length + 1. */
970 const char *pszFile; /* Pointer to filename portion. */
971 const char *pszPath = ScanEnv(GetEnv(FALSE), "PATH"); /* Current Process environment? */
972
973 /**@sketch
974 * No PATH environment.
975 */
976 if (pszPath == NULL)
977 return ERROR_FILE_NOT_FOUND;
978
979 /**@sketch
980 * Skip any paths in the filename.
981 */
982 pszFile = pszFilename + (cchFile = strlen(pszFilename));
983 while (pszFile >= pszFilename && *pszFile != '\\' && *pszFile != '/')
984 pszFile--;
985 cchFile -= pszFile - pszFilename;
986 pszFile++;
987
988 /**@sketch
989 * We'll have to save the TCBFailErr since we don't want to cause
990 * Hard Errors while searching invalid paths, etc. (ldrOpenPath does this!)
991 */
992 TCBFailErr_save = tcbGetTCBFailErr(tcbGetCur());
993
994 /**@ sketch
995 * Loop thru the PATH trying to open the specified file in each
996 * directory.
997 */
998 while (*pszPath != '\0')
999 {
1000 const char * pszNext;
1001 int cchPath;
1002 char chEnd;
1003 register char ch;
1004
1005 /*
1006 * Find end of this path.
1007 */
1008 while (*pszPath == ' ') pszPath++; //skip leading spaces.
1009 if (*pszPath == '"')
1010 {
1011 chEnd = '"';
1012 pszPath++;
1013 }
1014 else
1015 chEnd = ';';
1016 pszNext = pszPath;
1017 while ((ch = *pszNext) != chEnd && ch != '\0')
1018 pszNext++;
1019
1020 cchPath = pszNext - pszPath;
1021 if (chEnd == '"')
1022 {
1023 /* Skip anything between the " and the ; or string end. */
1024 while ((ch = *pszNext) != ';' && ch != '\0')
1025 pszNext++;
1026 }
1027 else
1028 {
1029 /* Trim the string. */
1030 while (cchPath > 0 && pszPath[cchPath-1] == ' ') //??
1031 cchPath--;
1032 }
1033
1034 /*
1035 * No length? No Path! Or path'\'filename too long? => Next
1036 */
1037 if (cchPath > 0 && cchPath + cchFile + 1 < CCHMAXPATH)
1038 {
1039 static char achFilename[CCHMAXPATH];
1040 /*
1041 * Build filename
1042 */
1043 memcpy(achFilename, pszPath, cchPath);
1044 if ((ch = achFilename[cchPath - 1]) == '\\' || ch == '/')
1045 cchPath--;
1046 else
1047 achFilename[cchPath] = '\\';
1048 memcpy(&achFilename[cchPath + 1], pszFile, cchFile); /* cchFile = length + 1; hence we copy the terminator too. */
1049
1050 /*
1051 * Try open the file.
1052 */
1053 rc = myldrOpen(phFile, achFilename, pfl);
1054 switch (rc)
1055 {
1056 case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_ACCESS_DENIED: case ERROR_INVALID_ACCESS:
1057 case ERROR_INVALID_DRIVE: case ERROR_NOT_DOS_DISK: case ERROR_REM_NOT_LIST: case ERROR_BAD_NETPATH:
1058 case ERROR_NETWORK_BUSY: case ERROR_DEV_NOT_EXIST: case ERROR_TOO_MANY_CMDS: case ERROR_ADAP_HDW_ERR:
1059 case ERROR_UNEXP_NET_ERR: case ERROR_BAD_REM_ADAP: case ERROR_NETNAME_DELETED: case ERROR_BAD_DEV_TYPE:
1060 case ERROR_NETWORK_ACCESS_DENIED: case ERROR_BAD_NET_NAME: case ERROR_TOO_MANY_SESS: case ERROR_REQ_NOT_ACCEP:
1061 case ERROR_INVALID_PASSWORD: case ERROR_OPEN_FAILED: case ERROR_INVALID_NAME: case ERROR_FILENAME_EXCED_RANGE:
1062 case ERROR_VC_DISCONNECTED:
1063 break;
1064
1065 case NO_ERROR:
1066 strcpy(pszFilename, achFilename);
1067 default:
1068 tcbSetTCBFailErr(tcbGetCur(), TCBFailErr_save);
1069 return rc;
1070 }
1071 }
1072 #ifdef DEBUG
1073 else if (cchPath > 0) kprintf(("OpenPATH: Path component is too long\n"));
1074 #endif
1075
1076 /*
1077 * Next
1078 */
1079 if (*pszNext == '\0')
1080 break;
1081 pszPath = pszNext + 1;
1082 }
1083
1084
1085 /*
1086 * File is not found.
1087 */
1088 *phFile = 0;
1089 tcbSetTCBFailErr(tcbGetCur(), TCBFailErr_save);
1090 return ERROR_FILE_NOT_FOUND;
1091}
Note: See TracBrowser for help on using the repository browser.