source: trunk/src/helpers/dosh2.c@ 68

Last change on this file since 68 was 67, checked in by umoeller, 24 years ago

Misc changes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 144.8 KB
Line 
1
2/*
3 *@@sourcefile dosh2.c:
4 * dosh.c contains more Control Program helper functions.
5 *
6 * This file is new with V0.9.4 (2000-07-26) [umoeller].
7 *
8 * As opposed to the functions in dosh.c, these require
9 * linking against other helpers. As a result, these have
10 * been separated from dosh.c to allow linking against
11 * dosh.obj only.
12 *
13 * Function prefixes:
14 * -- dosh* Dos (Control Program) helper functions
15 *
16 * This has the same header as dosh.c, dosh.h.
17 *
18 * The partition functions in this file are based on
19 * code which has kindly been provided by Dmitry A. Steklenev.
20 * See doshGetPartitionsList for how to use these.
21 *
22 * Note: Version numbering in this file relates to XWorkplace version
23 * numbering.
24 *
25 *@@header "helpers\dosh.h"
26 *@@added V0.9.4 (2000-07-27) [umoeller]
27 */
28
29/*
30 * This file Copyright (C) 1997-2000 Ulrich M”ller,
31 * Dmitry A. Steklenev.
32 * This file is part of the "XWorkplace helpers" source package.
33 * This is free software; you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published
35 * by the Free Software Foundation, in version 2 as it comes in the
36 * "COPYING" file of the XWorkplace main distribution.
37 * This program is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
41 */
42
43#define OS2EMX_PLAIN_CHAR
44 // this is needed for "os2emx.h"; if this is defined,
45 // emx will define PSZ as _signed_ char, otherwise
46 // as unsigned char
47
48#define INCL_DOSMODULEMGR
49#define INCL_DOSPROCESS
50#define INCL_DOSSESMGR
51#define INCL_DOSQUEUES
52#define INCL_DOSMISC
53#define INCL_DOSDEVICES
54#define INCL_DOSDEVIOCTL
55#define INCL_DOSERRORS
56#include <os2.h>
57
58#include <stdlib.h>
59#include <string.h>
60#include <stdio.h>
61#include <ctype.h>
62
63#include "setup.h" // code generation and debugging options
64
65#include "helpers\dosh.h"
66#include "helpers\ensure.h"
67#include "helpers\standards.h"
68#include "helpers\stringh.h"
69
70#pragma hdrstop
71
72/*
73 *@@category: Helpers\Control program helpers\Miscellaneous
74 */
75
76/* ******************************************************************
77 *
78 * Miscellaneous
79 *
80 ********************************************************************/
81
82/*
83 *@@ doshIsValidFileName:
84 * this returns NO_ERROR only if pszFile is a valid file name.
85 * This may include a full path.
86 *
87 * If a drive letter is specified, this checks for whether
88 * that drive is a FAT drive and adjust the checks accordingly,
89 * i.e. 8+3 syntax (per path component).
90 *
91 * If no drive letter is specified, this check is performed
92 * for the current drive.
93 *
94 * This also checks if pszFileNames contains characters which
95 * are invalid for the current drive.
96 *
97 * Note: this performs syntactic checks only. This does not
98 * check for whether the specified path components exist.
99 * However, it _is_ checked for whether the given drive
100 * exists.
101 *
102 * This func is especially useful to check filenames that
103 * have been entered by the user in a "Save as" dialog.
104 *
105 * If an error is found, the corresponding DOS error code
106 * is returned:
107 * -- ERROR_INVALID_DRIVE
108 * -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
109 * -- ERROR_INVALID_NAME (invalid character)
110 * -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
111 *
112 *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
113 */
114
115APIRET doshIsValidFileName(const char* pcszFile,
116 BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
117{
118 APIRET arc = NO_ERROR;
119 CHAR szPath[CCHMAXPATH+4] = " :";
120 CHAR szComponent[CCHMAXPATH];
121 PSZ p1, p2;
122 BOOL fIsFAT = FALSE;
123 PSZ pszInvalid;
124
125 if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
126 {
127 if ( (*(pcszFile + 1) != ':')
128 || (*(pcszFile + 2) != '\\')
129 )
130 arc = ERROR_CURRENT_DIRECTORY;
131 }
132
133 // check drive first
134 if (*(pcszFile + 1) == ':')
135 {
136 CHAR cDrive = toupper(*pcszFile);
137 double d;
138 // drive specified:
139 strcpy(szPath, pcszFile);
140 szPath[0] = toupper(*pcszFile);
141 arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
142 }
143 else
144 {
145 // no drive specified: take current
146 ULONG ulDriveNum = 0,
147 ulDriveMap = 0;
148 arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
149 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
150 szPath[1] = ':';
151 strcpy(&szPath[2], pcszFile);
152 }
153
154 if (arc == NO_ERROR)
155 {
156 fIsFAT = doshIsFileOnFAT(szPath);
157
158 pszInvalid = (fIsFAT)
159 ? "<>|+=:;,\"/[] " // invalid characters in FAT
160 : "<>|:\"/"; // invalid characters in IFS's
161
162 // now separate path components
163 p1 = &szPath[2]; // advance past ':'
164
165 do {
166
167 if (*p1 == '\\')
168 p1++;
169
170 p2 = strchr(p1, '\\');
171 if (p2 == NULL)
172 p2 = p1 + strlen(p1);
173
174 if (p1 != p2)
175 {
176 LONG lDotOfs = -1,
177 lAfterDot = -1;
178 ULONG cbFile,
179 ul;
180 PSZ pSource = szComponent;
181
182 strncpy(szComponent, p1, p2-p1);
183 szComponent[p2-p1] = 0;
184 cbFile = strlen(szComponent);
185
186 // now check each path component
187 for (ul = 0; ul < cbFile; ul++)
188 {
189 if (fIsFAT)
190 {
191 // on FAT: only 8 characters allowed before dot
192 if (*pSource == '.')
193 {
194 lDotOfs = ul;
195 lAfterDot = 0;
196 if (ul > 7)
197 return (ERROR_FILENAME_EXCED_RANGE);
198 }
199 }
200 // and check for invalid characters
201 if (strchr(pszInvalid, *pSource) != NULL)
202 return (ERROR_INVALID_NAME);
203
204 pSource++;
205
206 // on FAT, allow only three chars after dot
207 if (fIsFAT)
208 if (lAfterDot != -1)
209 {
210 lAfterDot++;
211 if (lAfterDot > 3)
212 return (ERROR_FILENAME_EXCED_RANGE);
213 }
214 }
215
216 // we are still missing the case of a FAT file
217 // name without extension; if so, check whether
218 // the file stem is <= 8 chars
219 if (fIsFAT)
220 if (lDotOfs == -1) // dot not found:
221 if (cbFile > 8)
222 return (ERROR_FILENAME_EXCED_RANGE);
223 }
224
225 // go for next component
226 p1 = p2+1;
227 } while (*p2);
228 }
229
230 return (arc);
231}
232
233/*
234 *@@ doshMakeRealName:
235 * this copies pszSource to pszTarget, replacing
236 * all characters which are not supported by file
237 * systems with cReplace.
238 *
239 * pszTarget must be at least the same size as pszSource.
240 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
241 *
242 * Returns TRUE if characters were replaced.
243 *
244 *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
245 */
246
247BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
248 PSZ pszSource, // in: filename to translate
249 CHAR cReplace, // in: replacement char for invalid
250 // characters (e.g. '!')
251 BOOL fIsFAT) // in: make-FAT-compatible flag
252{
253 ULONG ul,
254 cbSource = strlen(pszSource);
255 LONG lDotOfs = -1,
256 lAfterDot = -1;
257 BOOL brc = FALSE;
258 PSZ pSource = pszSource,
259 pTarget = pszTarget;
260
261 const char *pcszInvalid = (fIsFAT)
262 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
263 : "*<>|:\"/\\"; // invalid characters in IFS's
264
265 for (ul = 0; ul < cbSource; ul++)
266 {
267 if (fIsFAT)
268 {
269 // on FAT: truncate filename if neccessary
270 if (*pSource == '.')
271 {
272 lDotOfs = ul;
273 lAfterDot = 0;
274 if (ul > 7) {
275 // only 8 characters allowed before dot,
276 // so set target ptr to dot pos
277 pTarget = pszTarget+8;
278 }
279 }
280 }
281 // and replace invalid characters
282 if (strchr(pcszInvalid, *pSource) == NULL)
283 *pTarget = *pSource;
284 else
285 {
286 *pTarget = cReplace;
287 brc = TRUE;
288 }
289 pTarget++;
290 pSource++;
291
292 // on FAT, allow only three chars after dot
293 if (fIsFAT)
294 if (lAfterDot != -1)
295 {
296 lAfterDot++;
297 if (lAfterDot > 3)
298 break;
299 }
300 }
301 *pTarget = '\0';
302
303 if (fIsFAT)
304 {
305 // we are still missing the case of a FAT file
306 // name without extension; if so, check whether
307 // the file stem is <= 8 chars
308 if (lDotOfs == -1) // dot not found:
309 if (cbSource > 8)
310 *(pszTarget+8) = 0; // truncate
311
312 // convert to upper case
313 strupr(pszTarget);
314 }
315
316 return (brc);
317}
318
319/*
320 *@@ doshSetCurrentDir:
321 * sets the current working directory
322 * to the given path.
323 *
324 * As opposed to DosSetCurrentDir, this
325 * one will change the current drive
326 * also, if one is specified.
327 *
328 *@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occured, fixed
329 */
330
331APIRET doshSetCurrentDir(const char *pcszDir)
332{
333 APIRET arc = NO_ERROR;
334 if (!pcszDir)
335 return (ERROR_INVALID_PARAMETER);
336 {
337 if (*pcszDir != 0)
338 if (*(pcszDir+1) == ':')
339 {
340 // drive given:
341 CHAR cDrive = toupper(*(pcszDir));
342 // change drive
343 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
344 // 1 = A:, 2 = B:, ...
345 }
346
347 arc = DosSetCurrentDir((PSZ)pcszDir);
348 }
349
350 return (arc); // V0.9.9 (2001-04-04) [umoeller]
351}
352
353/*
354 *@@category: Helpers\Control program helpers\Environment management
355 * helpers for managing those ugly environment string arrays
356 * that are used with DosStartSession and WinStartApp.
357 */
358
359/* ******************************************************************
360 *
361 * Environment helpers
362 *
363 ********************************************************************/
364
365/*
366 *@@ doshParseEnvironment:
367 * this takes one of those ugly environment strings
368 * as used by DosStartSession and WinStartApp (with
369 * lots of zero-terminated strings one after another
370 * and a duplicate zero byte as a terminator) as
371 * input and splits it into an array of separate
372 * strings in pEnv.
373 *
374 * The newly allocated strings are stored in in
375 * pEnv->papszVars. The array count is stored in
376 * pEnv->cVars.
377 *
378 * Each environment variable will be copied into
379 * one newly allocated string in the array. Use
380 * doshFreeEnvironment to free the memory allocated
381 * by this function.
382 *
383 * Use the following code to browse thru the array:
384 +
385 + DOSENVIRONMENT Env = {0};
386 + if (doshParseEnvironment(pszEnv,
387 + &Env)
388 + == NO_ERROR)
389 + {
390 + if (Env.papszVars)
391 + {
392 + PSZ *ppszThis = Env.papszVars;
393 + for (ul = 0;
394 + ul < Env.cVars;
395 + ul++)
396 + {
397 + PSZ pszThis = *ppszThis;
398 + // pszThis now has something like PATH=C:\TEMP
399 + // ...
400 + // next environment string
401 + ppszThis++;
402 + }
403 + }
404 + doshFreeEnvironment(&Env);
405 + }
406 *
407 *@@added V0.9.4 (2000-08-02) [umoeller]
408 */
409
410APIRET doshParseEnvironment(const char *pcszEnv,
411 PDOSENVIRONMENT pEnv)
412{
413 APIRET arc = NO_ERROR;
414 if (!pcszEnv)
415 arc = ERROR_INVALID_PARAMETER;
416 else
417 {
418 PSZ pszVarThis = (PSZ)pcszEnv;
419 ULONG cVars = 0;
420 // count strings
421 while (*pszVarThis)
422 {
423 cVars++;
424 pszVarThis += strlen(pszVarThis) + 1;
425 }
426
427 pEnv->cVars = cVars;
428 pEnv->papszVars = 0;
429
430 if (cVars)
431 {
432 PSZ *papsz = (PSZ*)malloc(sizeof(PSZ) * cVars);
433 if (!papsz)
434 arc = ERROR_NOT_ENOUGH_MEMORY;
435 else
436 {
437 PSZ *ppszTarget = papsz;
438 memset(papsz, 0, sizeof(PSZ) * cVars);
439 pszVarThis = (PSZ)pcszEnv;
440 while (*pszVarThis)
441 {
442 *ppszTarget = strdup(pszVarThis);
443 ppszTarget++;
444 pszVarThis += strlen(pszVarThis) + 1;
445 }
446
447 pEnv->papszVars = papsz;
448 }
449 }
450 }
451
452 return (arc);
453}
454
455/*
456 *@@ doshGetEnvironment:
457 * calls doshParseEnvironment for the current
458 * process environment, which is retrieved from
459 * the info blocks.
460 *
461 *@@added V0.9.4 (2000-07-19) [umoeller]
462 */
463
464APIRET doshGetEnvironment(PDOSENVIRONMENT pEnv)
465{
466 APIRET arc = NO_ERROR;
467 if (!pEnv)
468 arc = ERROR_INVALID_PARAMETER;
469 else
470 {
471 PTIB ptib = 0;
472 PPIB ppib = 0;
473 arc = DosGetInfoBlocks(&ptib, &ppib);
474 if (arc == NO_ERROR)
475 {
476 PSZ pszEnv = ppib->pib_pchenv;
477 if (pszEnv)
478 arc = doshParseEnvironment(pszEnv, pEnv);
479 else
480 arc = ERROR_BAD_ENVIRONMENT;
481 }
482 }
483
484 return (arc);
485}
486
487/*
488 *@@ doshFindEnvironmentVar:
489 * returns the PSZ* in the pEnv->papszVars array
490 * which specifies the environment variable in pszVarName.
491 *
492 * With pszVarName, you can either specify the variable
493 * name only ("VARNAME") or a full environment string
494 * ("VARNAME=BLAH"). In any case, only the variable name
495 * is compared.
496 *
497 * Returns NULL if no such variable name was found in
498 * the array.
499 *
500 *@@added V0.9.4 (2000-07-19) [umoeller]
501 */
502
503PSZ* doshFindEnvironmentVar(PDOSENVIRONMENT pEnv,
504 PSZ pszVarName)
505{
506 PSZ *ppszRet = 0;
507 if (pEnv)
508 {
509 if ((pEnv->papszVars) && (pszVarName))
510 {
511 PSZ *ppszThis = pEnv->papszVars;
512 PSZ pszThis;
513 ULONG ul = 0;
514 ULONG ulVarNameLen = 0;
515
516 PSZ pszSearch = NULL; // receives "VARNAME="
517 PSZ pFirstEqual = strchr(pszVarName, '=');
518 if (pFirstEqual)
519 pszSearch = strhSubstr(pszVarName, pFirstEqual + 1);
520 else
521 {
522 ulVarNameLen = strlen(pszVarName);
523 pszSearch = (PSZ)malloc(ulVarNameLen + 2);
524 memcpy(pszSearch, pszVarName, ulVarNameLen);
525 *(pszSearch + ulVarNameLen) = '=';
526 *(pszSearch + ulVarNameLen + 1) = 0;
527 }
528
529 ulVarNameLen = strlen(pszSearch);
530
531 for (ul = 0;
532 ul < pEnv->cVars;
533 ul++)
534 {
535 pszThis = *ppszThis;
536
537 if (strnicmp(*ppszThis, pszSearch, ulVarNameLen) == 0)
538 {
539 ppszRet = ppszThis;
540 break;
541 }
542
543 // next environment string
544 ppszThis++;
545 }
546 }
547 }
548
549 return (ppszRet);
550}
551
552/*
553 *@@ doshSetEnvironmentVar:
554 * sets an environment variable in the specified
555 * environment, which must have been initialized
556 * using doshGetEnvironment first.
557 *
558 * pszNewEnv must be a full environment string
559 * in the form "VARNAME=VALUE".
560 *
561 * If "VARNAME" has already been set to something
562 * in the string array in pEnv, that array item
563 * is replaced.
564 *
565 * OTOH, if "VARNAME" has not been set yet, a new
566 * item is added to the array, and pEnv->cVars is
567 * raised by one. In that case, fAddFirst determines
568 * whether the new array item is added to the front
569 * or the tail of the environment list.
570 *
571 *@@added V0.9.4 (2000-07-19) [umoeller]
572 *@@changed V0.9.7 (2000-12-17) [umoeller]: added fAddFirst
573 */
574
575APIRET doshSetEnvironmentVar(PDOSENVIRONMENT pEnv,
576 PSZ pszNewEnv,
577 BOOL fAddFirst)
578{
579 APIRET arc = NO_ERROR;
580 if (!pEnv)
581 arc = ERROR_INVALID_PARAMETER;
582 else
583 {
584 if ((!pEnv->papszVars) || (!pszNewEnv))
585 arc = ERROR_INVALID_PARAMETER;
586 else
587 {
588 PSZ *ppszEnvLine = doshFindEnvironmentVar(pEnv, pszNewEnv);
589 if (ppszEnvLine)
590 {
591 // was set already: replace
592 free(*ppszEnvLine);
593 *ppszEnvLine = strdup(pszNewEnv);
594 if (!(*ppszEnvLine))
595 arc = ERROR_NOT_ENOUGH_MEMORY;
596 }
597 else
598 {
599 PSZ *ppszNew = NULL;
600 PSZ *papszNew = NULL;
601 // not set already:
602 if (fAddFirst)
603 {
604 // add as first entry:
605 papszNew = (PSZ*)malloc(sizeof(PSZ) * (pEnv->cVars + 1));
606 // overwrite first entry
607 ppszNew = papszNew;
608 // copy old entries
609 memcpy(papszNew + 1, // second new entry
610 pEnv->papszVars, // first old entry
611 sizeof(PSZ) * pEnv->cVars);
612 }
613 else
614 {
615 // append at the tail:
616 // reallocate array and add new string
617 papszNew = (PSZ*)realloc(pEnv->papszVars,
618 sizeof(PSZ) * (pEnv->cVars + 1));
619 // overwrite last entry
620 ppszNew = papszNew + pEnv->cVars;
621 }
622
623 if (!papszNew)
624 arc = ERROR_NOT_ENOUGH_MEMORY;
625 else
626 {
627 pEnv->papszVars = papszNew;
628 pEnv->cVars++;
629 *ppszNew = strdup(pszNewEnv);
630 }
631 }
632 }
633 }
634
635 return (arc);
636}
637
638/*
639 *@@ doshConvertEnvironment:
640 * converts an environment initialized by doshGetEnvironment
641 * to the string format required by WinStartApp and DosExecPgm,
642 * that is, one memory block is allocated in *ppszEnv and all
643 * strings in pEnv->papszVars are copied to that block. Each
644 * string is terminated with a null character; the last string
645 * is terminated with two null characters.
646 *
647 * Use free() to free the memory block allocated by this
648 * function in *ppszEnv.
649 *
650 *@@added V0.9.4 (2000-07-19) [umoeller]
651 */
652
653APIRET doshConvertEnvironment(PDOSENVIRONMENT pEnv,
654 PSZ *ppszEnv, // out: environment string
655 PULONG pulSize) // out: size of block allocated in *ppszEnv; ptr can be NULL
656{
657 APIRET arc = NO_ERROR;
658 if (!pEnv)
659 arc = ERROR_INVALID_PARAMETER;
660 else
661 {
662 if (!pEnv->papszVars)
663 arc = ERROR_INVALID_PARAMETER;
664 else
665 {
666 // count memory needed for all strings
667 ULONG cbNeeded = 0,
668 ul = 0;
669 PSZ *ppszThis = pEnv->papszVars;
670
671 for (ul = 0;
672 ul < pEnv->cVars;
673 ul++)
674 {
675 cbNeeded += strlen(*ppszThis) + 1; // length of string plus null terminator
676
677 // next environment string
678 ppszThis++;
679 }
680
681 cbNeeded++; // for another null terminator
682
683 *ppszEnv = (PSZ)malloc(cbNeeded);
684 if (!(*ppszEnv))
685 arc = ERROR_NOT_ENOUGH_MEMORY;
686 else
687 {
688 PSZ pTarget = *ppszEnv;
689 if (pulSize)
690 *pulSize = cbNeeded;
691 ppszThis = pEnv->papszVars;
692
693 // now copy each string
694 for (ul = 0;
695 ul < pEnv->cVars;
696 ul++)
697 {
698 PSZ pSource = *ppszThis;
699
700 while ((*pTarget++ = *pSource++))
701 ;
702
703 // *pTarget++ = 0; // append null terminator per string
704
705 // next environment string
706 ppszThis++;
707 }
708
709 *pTarget++ = 0; // append second null terminator
710 }
711 }
712 }
713
714 return (arc);
715}
716
717/*
718 *@@ doshFreeEnvironment:
719 * frees memory allocated by doshGetEnvironment.
720 *
721 *@@added V0.9.4 (2000-07-19) [umoeller]
722 */
723
724APIRET doshFreeEnvironment(PDOSENVIRONMENT pEnv)
725{
726 APIRET arc = NO_ERROR;
727 if (!pEnv)
728 arc = ERROR_INVALID_PARAMETER;
729 else
730 {
731 if (!pEnv->papszVars)
732 arc = ERROR_INVALID_PARAMETER;
733 else
734 {
735 PSZ *ppszThis = pEnv->papszVars;
736 PSZ pszThis;
737 ULONG ul = 0;
738
739 for (ul = 0;
740 ul < pEnv->cVars;
741 ul++)
742 {
743 pszThis = *ppszThis;
744 free(pszThis);
745 // *ppszThis = NULL;
746 // next environment string
747 ppszThis++;
748 }
749
750 free(pEnv->papszVars);
751 pEnv->cVars = 0;
752 }
753 }
754
755 return (arc);
756}
757
758/*
759 *@@category: Helpers\Control program helpers\Executable info
760 * these functions can retrieve BLDLEVEL information,
761 * imported modules information, exported functions information,
762 * and resources information from any executable module. See
763 * doshExecOpen.
764 */
765
766/********************************************************************
767 *
768 * Executable functions
769 *
770 ********************************************************************/
771
772/*
773 *@@ doshExecOpen:
774 * this opens the specified executable file
775 * (which can be an .EXE, .COM, .DLL, or
776 * driver file) for use with the other
777 * doshExec* functions.
778 *
779 * If no error occurs, NO_ERROR is returned
780 * and a pointer to a new EXECUTABLE structure
781 * is stored in *ppExec. Consider this pointer a
782 * handle and pass it to doshExecClose to clean
783 * up.
784 *
785 * If NO_ERROR is returned, all the fields through
786 * ulOS are set in EXECUTABLE. The psz* fields
787 * which follow afterwards require an additional
788 * call to doshExecQueryBldLevel.
789 *
790 * NOTE: If NO_ERROR is returned, the executable
791 * file has been opened by this function. It will
792 * only be closed when you call doshExecClose.
793 *
794 * If errors occur, this function returns the
795 * following error codes:
796 *
797 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
798 *
799 * -- ERROR_INVALID_EXE_SIGNATURE: unknown
800 * executable type... the given file probably
801 * isn't even an executable.
802 *
803 * -- ERROR_INVALID_PARAMETER: ppExec is NULL.
804 *
805 * plus those of DosOpen, DosSetFilePtr, and
806 * DosRead.
807 *
808 * The following executable types are supported
809 * (see EXECUTABLE for details):
810 *
811 * -- Plain DOS 3.x executable without new header.
812 *
813 * -- New Executable (NE), used by Win16 and
814 * 16-bit OS/2 and still many of today's drivers.
815 *
816 * -- Linear Executable (LX), OS/2 2.x and above.
817 *
818 * -- Portable Executable (PE), used by Win32.
819 *
820 * V0.9.12 adds support for NOSTUB executables,
821 * which are new-style executables (NE or LX)
822 * without a leading DOS header. The executable
823 * then starts directly with the NE or LX header.
824 * I am not sure whether PE supports such things
825 * as well... if so, it should be supported too.
826 *
827 *@@added V0.9.0 [umoeller]
828 *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
829 *@@changed V0.9.7 (2000-12-20) [lafaix]: fixed ulNewHeaderOfs
830 *@@changed V0.9.10 (2001-04-08) [lafaix]: added PE support
831 *@@changed V0.9.10 (2001-04-08) [umoeller]: now setting ppExec only if NO_ERROR is returned
832 *@@changed V0.9.12 (2001-05-03) [umoeller]: added support for NOSTUB newstyle executables
833 */
834
835APIRET doshExecOpen(const char* pcszExecutable,
836 PEXECUTABLE* ppExec)
837{
838 APIRET arc = NO_ERROR;
839
840 ULONG ulAction = 0;
841 HFILE hFile;
842 PEXECUTABLE pExec = NULL;
843
844 if (!ppExec)
845 return (ERROR_INVALID_PARAMETER);
846
847 pExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE));
848 if (!(pExec))
849 return (ERROR_NOT_ENOUGH_MEMORY);
850
851 memset(pExec, 0, sizeof(EXECUTABLE));
852
853 if (!(arc = DosOpen((PSZ)pcszExecutable,
854 &hFile,
855 &ulAction, // out: action taken
856 0, // in: new file (ignored for read-mode)
857 0, // in: new file attribs (ignored)
858 // open-flags
859 OPEN_ACTION_FAIL_IF_NEW
860 | OPEN_ACTION_OPEN_IF_EXISTS,
861 // open-mode
862 OPEN_FLAGS_FAIL_ON_ERROR // report errors to caller
863 | OPEN_FLAGS_SEQUENTIAL
864 | OPEN_FLAGS_NOINHERIT
865 | OPEN_SHARE_DENYNONE
866 | OPEN_ACCESS_READONLY, // read-only mode
867 NULL))) // no EAs
868 {
869 // file opened successfully:
870
871 ULONG ulLocal = 0;
872
873 // read old DOS EXE header
874 pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
875 if (!(pExec->pDosExeHeader))
876 arc = ERROR_NOT_ENOUGH_MEMORY;
877 else
878 {
879 ULONG ulBytesRead = 0;
880
881 if ( (!(arc = DosSetFilePtr(hFile,
882 0L,
883 FILE_BEGIN,
884 &ulLocal))) // out: new offset
885 && (!(arc = DosRead(hFile,
886 pExec->pDosExeHeader,
887 sizeof(DOSEXEHEADER),
888 &(pExec->cbDosExeHeader))))
889 )
890 {
891 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
892 BOOL fLoadNewHeader = FALSE;
893
894 // now check if we really have a DOS header
895 if (pExec->pDosExeHeader->usDosExeID != 0x5a4d)
896 {
897 // arc = ERROR_INVALID_EXE_SIGNATURE;
898
899 // V0.9.12 (2001-05-03) [umoeller]
900 // try loading new header directly; there are
901 // drivers which were built with NOSTUB, and
902 // the exe image starts out with the NE or LX
903 // image directly
904 fLoadNewHeader = TRUE;
905 // ulNewHeaderOfs is 0 now
906
907 // remove the DOS header info, since we have none
908 // V0.9.12 (2001-05-03) [umoeller]
909 free (pExec->pDosExeHeader);
910 pExec->pDosExeHeader = 0;
911 }
912 else
913 {
914 // we have a DOS header:
915 if (pExec->pDosExeHeader->usRelocTableOfs < 0x40)
916 {
917 // neither LX nor PE nor NE:
918 pExec->ulOS = EXEOS_DOS3;
919 pExec->ulExeFormat = EXEFORMAT_OLDDOS;
920 }
921 else
922 {
923 // we have a new header offset:
924 fLoadNewHeader = TRUE;
925 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
926 }
927 }
928
929 if (fLoadNewHeader)
930 {
931 // either LX or PE or NE:
932 // read in new header...
933 // ulNewHeaderOfs is now either 0 (if no DOS header
934 // was found) or pDosExeHeader->ulNewHeaderOfs
935 // V0.9.12 (2001-05-03) [umoeller]
936 CHAR achNewHeaderType[2] = "";
937
938 if ( (!(arc = DosSetFilePtr(hFile,
939 ulNewHeaderOfs,
940 FILE_BEGIN,
941 &ulLocal)))
942 // read two chars to find out header type
943 && (!(arc = DosRead(hFile,
944 &achNewHeaderType,
945 sizeof(achNewHeaderType),
946 &ulBytesRead)))
947 )
948 {
949 PBYTE pbCheckOS = NULL;
950
951 // reset file ptr
952 DosSetFilePtr(hFile,
953 ulNewHeaderOfs,
954 FILE_BEGIN,
955 &ulLocal);
956
957 if (memcmp(achNewHeaderType, "NE", 2) == 0)
958 {
959 // New Executable:
960 pExec->ulExeFormat = EXEFORMAT_NE;
961 // allocate NE header
962 pExec->pNEHeader = (PNEHEADER)malloc(sizeof(NEHEADER));
963 if (!(pExec->pNEHeader))
964 arc = ERROR_NOT_ENOUGH_MEMORY;
965 else
966 // read in NE header
967 if (!(arc = DosRead(hFile,
968 pExec->pNEHeader,
969 sizeof(NEHEADER),
970 &(pExec->cbNEHeader))))
971 if (pExec->cbNEHeader == sizeof(NEHEADER))
972 pbCheckOS = &(pExec->pNEHeader->bTargetOS);
973 }
974 else if ( (memcmp(achNewHeaderType, "LX", 2) == 0)
975 || (memcmp(achNewHeaderType, "LE", 2) == 0)
976 // this is used by SMARTDRV.EXE
977 )
978 {
979 // OS/2 Linear Executable:
980 pExec->ulExeFormat = EXEFORMAT_LX;
981 // allocate LX header
982 pExec->pLXHeader = (PLXHEADER)malloc(sizeof(LXHEADER));
983 if (!(pExec->pLXHeader))
984 arc = ERROR_NOT_ENOUGH_MEMORY;
985 else
986 // read in LX header
987 if (!(arc = DosRead(hFile,
988 pExec->pLXHeader,
989 sizeof(LXHEADER),
990 &(pExec->cbLXHeader))))
991 if (pExec->cbLXHeader == sizeof(LXHEADER))
992 pbCheckOS = (PBYTE)(&(pExec->pLXHeader->usTargetOS));
993 }
994 else if (memcmp(achNewHeaderType, "PE", 2) == 0)
995 {
996 PEHEADER PEHeader = {0};
997
998 pExec->ulExeFormat = EXEFORMAT_PE;
999 pExec->ulOS = EXEOS_WIN32;
1000 pExec->f32Bits = TRUE;
1001
1002 // V0.9.10 (2001-04-08) [lafaix]
1003 // read in standard PE header
1004 if (!(arc = DosRead(hFile,
1005 &PEHeader,
1006 24,
1007 &ulBytesRead)))
1008 {
1009 // allocate PE header
1010 pExec->pPEHeader = (PPEHEADER)malloc(24 + PEHeader.usHeaderSize);
1011 if (!(pExec->pPEHeader))
1012 arc = ERROR_NOT_ENOUGH_MEMORY;
1013 else
1014 {
1015 // copy standard PE header
1016 memcpy(pExec->pPEHeader,
1017 &PEHeader,
1018 24);
1019
1020 // read in optional PE header
1021 if (!(arc = DosRead(hFile,
1022 &(pExec->pPEHeader->usReserved3),
1023 PEHeader.usHeaderSize,
1024 &(pExec->cbPEHeader))))
1025 pExec->cbPEHeader += 24;
1026 }
1027 }
1028 }
1029 else
1030 // strange type:
1031 arc = ERROR_INVALID_EXE_SIGNATURE;
1032
1033 if (pbCheckOS)
1034 // BYTE to check for operating system
1035 // (NE and LX):
1036 switch (*pbCheckOS)
1037 {
1038 case NEOS_OS2:
1039 pExec->ulOS = EXEOS_OS2;
1040 if (pExec->ulExeFormat == EXEFORMAT_LX)
1041 pExec->f32Bits = TRUE;
1042 break;
1043
1044 case NEOS_WIN16:
1045 pExec->ulOS = EXEOS_WIN16;
1046 break;
1047
1048 case NEOS_DOS4:
1049 pExec->ulOS = EXEOS_DOS4;
1050 break;
1051
1052 case NEOS_WIN386:
1053 pExec->ulOS = EXEOS_WIN386;
1054 pExec->f32Bits = TRUE;
1055 break;
1056 }
1057 } // end if (!(arc = DosSetFilePtr(hFile,
1058 }
1059 } // end if (!(arc = DosSetFilePtr(hFile,
1060 } // end if pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
1061
1062 // store exec's HFILE
1063 pExec->hfExe = hFile;
1064 } // end if (!(arc = DosOpen((PSZ)pcszExecutable,
1065
1066 if (arc != NO_ERROR)
1067 // error: clean up
1068 doshExecClose(pExec);
1069 else
1070 *ppExec = pExec;
1071
1072 return (arc);
1073}
1074
1075/*
1076 *@@ doshExecClose:
1077 * this closes an executable opened with doshExecOpen.
1078 * Always call this function if NO_ERROR was returned by
1079 * doshExecOpen.
1080 *
1081 *@@added V0.9.0 [umoeller]
1082 */
1083
1084APIRET doshExecClose(PEXECUTABLE pExec)
1085{
1086 APIRET arc = NO_ERROR;
1087 if (pExec)
1088 {
1089 if (pExec->pDosExeHeader)
1090 free(pExec->pDosExeHeader);
1091 if (pExec->pNEHeader)
1092 free(pExec->pNEHeader);
1093 if (pExec->pLXHeader)
1094 free(pExec->pLXHeader);
1095
1096 if (pExec->pszDescription)
1097 free(pExec->pszDescription);
1098 if (pExec->pszVendor)
1099 free(pExec->pszVendor);
1100 if (pExec->pszVersion)
1101 free(pExec->pszVersion);
1102 if (pExec->pszInfo)
1103 free(pExec->pszInfo);
1104
1105 if (pExec->hfExe)
1106 arc = DosClose(pExec->hfExe);
1107
1108 free(pExec);
1109 }
1110 else
1111 arc = ERROR_INVALID_PARAMETER;
1112
1113 return (arc);
1114}
1115
1116/*
1117 *@@ doshExecQueryBldLevel:
1118 * this retrieves buildlevel information for an
1119 * LX or NE executable previously opened with
1120 * doshExecOpen.
1121 *
1122 * BuildLevel information must be contained in the
1123 * DESCRIPTION field of an executable's module
1124 * definition (.DEF) file. In order to be readable
1125 * by BLDLEVEL.EXE (which ships with OS/2), this
1126 * string must have the following format:
1127 *
1128 + Description '@#AUTHOR:VERSION#@ DESCRIPTION'
1129 *
1130 * Example:
1131 *
1132 + Description '@#Ulrich M”ller:0.9.0#@ XWorkplace Sound Support Module'
1133 *
1134 * The "Description" entry always ends up as the
1135 * very first entry in the non-resident name table
1136 * in LX and NE executables. So this is what we retrieve
1137 * here.
1138 *
1139 * If the first entry in that table exists, NO_ERROR is
1140 * returned and at least the pszDescription field in
1141 * EXECUTABLE is set to that information.
1142 *
1143 * If that string is in IBM BLDLEVEL format, the string
1144 * is automatically parsed, and the pszVendor, pszVersion,
1145 * and pszInfo fields are also set. In the above examples,
1146 * this would return the following information:
1147 + pszVendor = "Ulrich M”ller"
1148 + pszVersion = "0.9.0"
1149 + pszInfo = "XWorkplace Sound Support Module"
1150 *
1151 * If that string is not in BLDLEVEL format, only pszDescription
1152 * will be set. The other fields remain NULL.
1153 *
1154 * This returns the following errors:
1155 *
1156 * -- ERROR_INVALID_PARAMETER: pExec invalid
1157 *
1158 * -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in LX or NE format
1159 *
1160 * -- ERROR_INVALID_DATA (13): non-resident name table not found.
1161 *
1162 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
1163 *
1164 * plus the error codes of DosSetFilePtr and DosRead.
1165 *
1166 *@@added V0.9.0 [umoeller]
1167 *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
1168 *@@changed V0.9.1 (99-12-06): fixed memory leak
1169 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
1170 */
1171
1172APIRET doshExecQueryBldLevel(PEXECUTABLE pExec)
1173{
1174 APIRET arc = NO_ERROR;
1175
1176 if (!pExec)
1177 arc = ERROR_INVALID_PARAMETER;
1178 else
1179 {
1180 ULONG ulNRNTOfs = 0;
1181
1182 if (pExec->ulExeFormat == EXEFORMAT_LX)
1183 {
1184 // OK, LX format:
1185 // check if we have a non-resident name table
1186 if (pExec->pLXHeader == NULL)
1187 arc = ERROR_INVALID_DATA;
1188 else if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
1189 arc = ERROR_INVALID_DATA;
1190 else
1191 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
1192 }
1193 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1194 {
1195 // OK, NE format:
1196 // check if we have a non-resident name table
1197 if (pExec->pNEHeader == NULL)
1198 arc = ERROR_INVALID_DATA;
1199 else if (pExec->pNEHeader->ulNonResdTblOfs == 0)
1200 arc = ERROR_INVALID_DATA;
1201 else
1202 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
1203 }
1204 else
1205 // neither LX nor NE: stop
1206 arc = ERROR_INVALID_EXE_SIGNATURE;
1207
1208 if ( (!arc)
1209 && (ulNRNTOfs)
1210 )
1211 {
1212 ULONG ulLocal = 0,
1213 ulBytesRead = 0;
1214
1215 // move EXE file pointer to offset of non-resident name table
1216 // (from LX header)
1217 if (!(arc = DosSetFilePtr(pExec->hfExe, // file is still open
1218 ulNRNTOfs, // ofs determined above
1219 FILE_BEGIN,
1220 &ulLocal)))
1221 {
1222 // allocate memory as necessary
1223 PSZ pszNameTable = (PSZ)malloc(2001); // should suffice, because each entry
1224 // may only be 255 bytes in length
1225 if (!pszNameTable)
1226 arc = ERROR_NOT_ENOUGH_MEMORY;
1227 else
1228 {
1229 if (!(arc = DosRead(pExec->hfExe,
1230 pszNameTable,
1231 2000,
1232 &ulBytesRead)))
1233 {
1234 if (*pszNameTable == 0)
1235 // first byte (length byte) is null:
1236 arc = ERROR_INVALID_DATA;
1237 else
1238 {
1239 // now copy the string, which is in Pascal format
1240 pExec->pszDescription = (PSZ)malloc((*pszNameTable) + 1); // addt'l null byte
1241 if (!pExec->pszDescription)
1242 arc = ERROR_NOT_ENOUGH_MEMORY;
1243 else
1244 {
1245 const char *pStartOfAuthor = 0,
1246 *pStartOfVendor = 0;
1247
1248 memcpy(pExec->pszDescription,
1249 pszNameTable + 1, // skip length byte
1250 *pszNameTable); // length byte
1251 // terminate string
1252 *(pExec->pszDescription + (*pszNameTable)) = 0;
1253
1254 // now parse the damn thing:
1255 // @#VENDOR:VERSION#@ DESCRIPTION
1256 // but skip the first byte, which has the string length
1257 pStartOfVendor = strstr(pExec->pszDescription,
1258 "@#");
1259 if (pStartOfVendor)
1260 {
1261 const char *pStartOfInfo = strstr(pStartOfVendor + 2,
1262 "#@");
1263 if (pStartOfInfo)
1264 {
1265 PSZ pEndOfVendor = strchr(pStartOfVendor + 2,
1266 ':');
1267 if (pEndOfVendor)
1268 {
1269 pExec->pszVendor = strhSubstr(pStartOfVendor + 2,
1270 pEndOfVendor);
1271 pExec->pszVersion = strhSubstr(pEndOfVendor + 1,
1272 pStartOfInfo);
1273 // skip "@#" in info string
1274 pStartOfInfo += 2;
1275 // skip leading spaces in info string
1276 while (*pStartOfInfo == ' ')
1277 pStartOfInfo++;
1278 if (*pStartOfInfo) // V0.9.9 (2001-04-04) [umoeller]
1279 // and copy until end of string
1280 pExec->pszInfo = strdup(pStartOfInfo);
1281 }
1282 }
1283 }
1284 }
1285 }
1286 }
1287
1288 free(pszNameTable);
1289 } // end if PSZ pszNameTable = (PSZ)malloc(2001);
1290 }
1291 }
1292 } // end if (!pExec)
1293
1294 return (arc);
1295}
1296
1297/*
1298 *@@ doshExecQueryImportedModules:
1299 * returns an array of FSYSMODULE structure describing all
1300 * imported modules.
1301 *
1302 * *pcModules receives the # of items in the array (not the
1303 * array size!). Use doshFreeImportedModules to clean up.
1304 *
1305 * This returns a standard OS/2 error code, which might be
1306 * any of the codes returned by DosSetFilePtr and DosRead.
1307 * In addition, this may return:
1308 *
1309 * -- ERROR_NOT_ENOUGH_MEMORY
1310 *
1311 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1312 * than LX or NE, which is not understood by this function.
1313 *
1314 * Even if NO_ERROR is returned, the array pointer might still
1315 * be NULL if the module contains no such data.
1316 *
1317 *@@added V0.9.9 (2001-03-11) [lafaix]
1318 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1319 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1320 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1321 *@@changed V0.9.10 (2001-04-13) [lafaix]: removed 127 characters limit
1322 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1323 */
1324
1325APIRET doshExecQueryImportedModules(PEXECUTABLE pExec,
1326 PFSYSMODULE *ppaModules, // out: modules array
1327 PULONG pcModules) // out: array item count
1328{
1329 if ( (pExec)
1330 && ( (pExec->ulOS == EXEOS_OS2)
1331 || (pExec->ulOS == EXEOS_WIN16)
1332 || (pExec->ulOS == EXEOS_WIN386)
1333 )
1334 )
1335 {
1336 ENSURE_BEGIN;
1337 ULONG cModules = 0;
1338 PFSYSMODULE paModules = NULL;
1339 int i;
1340
1341 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1342
1343 if (pExec->pDosExeHeader)
1344 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1345 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1346
1347 if (pExec->ulExeFormat == EXEFORMAT_LX)
1348 {
1349 // 32-bit OS/2 executable:
1350 cModules = pExec->pLXHeader->ulImportModTblCnt;
1351
1352 if (cModules)
1353 {
1354 ULONG cb = sizeof(FSYSMODULE) * cModules; // V0.9.9 (2001-04-03) [umoeller]
1355 ULONG ulDummy;
1356
1357 paModules = (PFSYSMODULE)malloc(cb);
1358 if (!paModules)
1359 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1360
1361 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1362
1363 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1364 pExec->pLXHeader->ulImportModTblOfs
1365 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1366 FILE_BEGIN,
1367 &ulDummy));
1368
1369 for (i = 0; i < cModules; i++)
1370 {
1371 BYTE bLen = 0;
1372
1373 // reading the length of the module name
1374 ENSURE_SAFE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1375
1376 // reading the module name
1377 ENSURE_SAFE(DosRead(pExec->hfExe,
1378 paModules[i].achModuleName,
1379 bLen,
1380 &ulDummy));
1381
1382 // module names are not null terminated, so we must
1383 // do it now
1384 paModules[i].achModuleName[bLen] = 0;
1385 } // end for
1386 }
1387 } // end LX
1388 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1389 {
1390 // 16-bit executable:
1391 cModules = pExec->pNEHeader->usModuleTblEntries;
1392
1393 if (cModules)
1394 {
1395 ULONG cb = sizeof(FSYSMODULE) * cModules;
1396
1397 paModules = (PFSYSMODULE)malloc(cb);
1398 if (!paModules)
1399 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1400
1401 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1402
1403 for (i = 0; i < cModules; i ++)
1404 {
1405 BYTE bLen;
1406 USHORT usOfs;
1407 ULONG ulDummy;
1408
1409 // the module reference table contains offsets
1410 // relative to the import table; we hence read
1411 // the offset in the module reference table, and
1412 // then we read the name in the import table
1413
1414 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1415 pExec->pNEHeader->usModRefTblOfs
1416 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
1417 + sizeof(usOfs) * i,
1418 FILE_BEGIN,
1419 &ulDummy));
1420
1421 ENSURE_SAFE(DosRead(pExec->hfExe, &usOfs, 2, &ulDummy));
1422
1423 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1424 pExec->pNEHeader->usImportTblOfs
1425 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
1426 + usOfs,
1427 FILE_BEGIN,
1428 &ulDummy));
1429
1430 ENSURE_SAFE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1431
1432 ENSURE_SAFE(DosRead(pExec->hfExe,
1433 paModules[i].achModuleName,
1434 bLen,
1435 &ulDummy));
1436
1437 paModules[i].achModuleName[bLen] = 0;
1438 } // end for
1439 }
1440 } // end NE
1441 else
1442 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1443
1444 // no error: output data
1445 *ppaModules = paModules;
1446 *pcModules = cModules;
1447
1448 ENSURE_FINALLY;
1449 // if we had an error above, clean up
1450 free(paModules);
1451 ENSURE_END;
1452 }
1453 else
1454 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1455
1456 ENSURE_OK;
1457}
1458
1459/*
1460 *@@ doshExecFreeImportedModules:
1461 * frees resources allocated by doshExecQueryImportedModules.
1462 *
1463 *@@added V0.9.9 (2001-03-11)
1464 */
1465
1466APIRET doshExecFreeImportedModules(PFSYSMODULE paModules)
1467{
1468 free(paModules);
1469 return (NO_ERROR);
1470}
1471
1472/*
1473 *@@ ScanLXEntryTable:
1474 * returns the number of exported entries in the entry table.
1475 *
1476 * If paFunctions is not NULL, then successive entries are
1477 * filled with the found type and ordinal values.
1478 *
1479 *@@added V0.9.9 (2001-03-30) [lafaix]
1480 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1481 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1482 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1483 */
1484
1485APIRET ScanLXEntryTable(PEXECUTABLE pExec,
1486 PFSYSFUNCTION paFunctions,
1487 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1488{
1489 ULONG ulDummy;
1490 USHORT usOrdinal = 1,
1491 usCurrent = 0;
1492 int i;
1493
1494 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1495
1496 if (pExec->pDosExeHeader)
1497 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1498 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1499
1500 ENSURE(DosSetFilePtr(pExec->hfExe,
1501 pExec->pLXHeader->ulEntryTblOfs
1502 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1503 FILE_BEGIN,
1504 &ulDummy));
1505
1506 while (TRUE)
1507 {
1508 BYTE bCnt,
1509 bType,
1510 bFlag;
1511
1512 ENSURE(DosRead(pExec->hfExe, &bCnt, 1, &ulDummy));
1513
1514 if (bCnt == 0)
1515 // end of the entry table
1516 break;
1517
1518 ENSURE(DosRead(pExec->hfExe, &bType, 1, &ulDummy));
1519
1520 switch (bType & 0x7F)
1521 {
1522 /*
1523 * unused entries
1524 *
1525 */
1526
1527 case 0:
1528 usOrdinal += bCnt;
1529 break;
1530
1531 /*
1532 * 16-bit entries
1533 *
1534 * the bundle type is followed by the object number
1535 * and by bCnt bFlag+usOffset entries
1536 *
1537 */
1538
1539 case 1:
1540 ENSURE(DosSetFilePtr(pExec->hfExe,
1541 sizeof(USHORT),
1542 FILE_CURRENT,
1543 &ulDummy));
1544
1545 for (i = 0; i < bCnt; i ++)
1546 {
1547 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1548
1549 if (bFlag & 0x01)
1550 {
1551 if (paFunctions)
1552 {
1553 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1554 paFunctions[usCurrent].ulType = 1;
1555 paFunctions[usCurrent].achFunctionName[0] = 0;
1556 }
1557 usCurrent++;
1558 }
1559
1560 usOrdinal++;
1561
1562 ENSURE(DosSetFilePtr(pExec->hfExe,
1563 sizeof(USHORT),
1564 FILE_CURRENT,
1565 &ulDummy));
1566
1567 } // end for
1568 break;
1569
1570 /*
1571 * 286 call gate entries
1572 *
1573 * the bundle type is followed by the object number
1574 * and by bCnt bFlag+usOffset+usCallGate entries
1575 *
1576 */
1577
1578 case 2:
1579 ENSURE(DosSetFilePtr(pExec->hfExe,
1580 sizeof(USHORT),
1581 FILE_CURRENT,
1582 &ulDummy));
1583
1584 for (i = 0; i < bCnt; i ++)
1585 {
1586 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1587
1588 if (bFlag & 0x01)
1589 {
1590 if (paFunctions)
1591 {
1592 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1593 paFunctions[usCurrent].ulType = 2;
1594 paFunctions[usCurrent].achFunctionName[0] = 0;
1595 }
1596 usCurrent++;
1597 }
1598
1599 usOrdinal++;
1600
1601 ENSURE(DosSetFilePtr(pExec->hfExe,
1602 sizeof(USHORT) + sizeof(USHORT),
1603 FILE_CURRENT,
1604 &ulDummy));
1605
1606 } // end for
1607 break;
1608
1609 /*
1610 * 32-bit entries
1611 *
1612 * the bundle type is followed by the object number
1613 * and by bCnt bFlag+ulOffset entries
1614 *
1615 */
1616
1617 case 3:
1618 ENSURE(DosSetFilePtr(pExec->hfExe,
1619 sizeof(USHORT),
1620 FILE_CURRENT,
1621 &ulDummy));
1622
1623 for (i = 0; i < bCnt; i ++)
1624 {
1625 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1626
1627 if (bFlag & 0x01)
1628 {
1629 if (paFunctions)
1630 {
1631 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1632 paFunctions[usCurrent].ulType = 3;
1633 paFunctions[usCurrent].achFunctionName[0] = 0;
1634 }
1635 usCurrent++;
1636 }
1637
1638 usOrdinal++;
1639
1640 ENSURE(DosSetFilePtr(pExec->hfExe,
1641 sizeof(ULONG),
1642 FILE_CURRENT,
1643 &ulDummy));
1644 } // end for
1645 break;
1646
1647 /*
1648 * forwarder entries
1649 *
1650 * the bundle type is followed by a reserved word
1651 * and by bCnt bFlag+usModOrd+ulOffsOrdNum entries
1652 *
1653 */
1654
1655 case 4:
1656 ENSURE(DosSetFilePtr(pExec->hfExe,
1657 sizeof(USHORT),
1658 FILE_CURRENT,
1659 &ulDummy));
1660
1661 for (i = 0; i < bCnt; i ++)
1662 {
1663 ENSURE(DosSetFilePtr(pExec->hfExe,
1664 sizeof(BYTE) + sizeof(USHORT) + sizeof(ULONG),
1665 FILE_CURRENT,
1666 &ulDummy));
1667
1668 if (paFunctions)
1669 {
1670 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1671 paFunctions[usCurrent].ulType = 4;
1672 paFunctions[usCurrent].achFunctionName[0] = 0;
1673 }
1674 usCurrent++;
1675
1676 usOrdinal++;
1677 } // end for
1678 break;
1679
1680 /*
1681 * unknown bundle type
1682 *
1683 * we don't know how to handle this bundle, so we must
1684 * stop parsing the entry table here (as we don't know the
1685 * bundle size); if paFunctions is not null, we fill it with
1686 * informative data
1687 */
1688
1689 default:
1690 if (paFunctions)
1691 {
1692 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1693 paFunctions[usCurrent].ulType = bType;
1694 sprintf(paFunctions[usCurrent].achFunctionName,
1695 "Unknown bundle type encountered (%d). Aborting entry table scan.",
1696 bType);
1697
1698 usCurrent++;
1699 }
1700 ENSURE_FAIL(ERROR_INVALID_LIST_FORMAT);
1701 // whatever
1702 // V0.9.9 (2001-04-03) [umoeller]
1703 } // end switch (bType & 0x7F)
1704 } // end while (TRUE)
1705
1706 if (pcEntries)
1707 *pcEntries = usCurrent;
1708
1709 ENSURE_OK;
1710}
1711
1712/*
1713 *@@ ScanNEEntryTable:
1714 * returns the number of exported entries in the entry table.
1715 *
1716 * if paFunctions is not NULL, then successive entries are
1717 * filled with the found type and ordinal values.
1718 *
1719 *@@added V0.9.9 (2001-03-30) [lafaix]
1720 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1721 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1722 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1723 */
1724
1725APIRET ScanNEEntryTable(PEXECUTABLE pExec,
1726 PFSYSFUNCTION paFunctions,
1727 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1728{
1729 ULONG ulDummy;
1730 USHORT usOrdinal = 1,
1731 usCurrent = 0;
1732 int i;
1733
1734 ULONG ulNewHeaderOfs = 0;
1735
1736 if (pExec->pDosExeHeader)
1737 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1738 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1739
1740 ENSURE(DosSetFilePtr(pExec->hfExe,
1741 pExec->pNEHeader->usEntryTblOfs
1742 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1743 FILE_BEGIN,
1744 &ulDummy));
1745
1746 while (TRUE)
1747 {
1748 BYTE bCnt,
1749 bType,
1750 bFlag;
1751
1752 ENSURE(DosRead(pExec->hfExe, &bCnt, 1, &ulDummy));
1753
1754 if (bCnt == 0)
1755 // end of the entry table
1756 break;
1757
1758 ENSURE(DosRead(pExec->hfExe, &bType, 1, &ulDummy));
1759
1760 if (bType)
1761 {
1762 for (i = 0; i < bCnt; i++)
1763 {
1764 ENSURE(DosRead(pExec->hfExe,
1765 &bFlag,
1766 1,
1767 &ulDummy));
1768
1769 if (bFlag & 0x01)
1770 {
1771 if (paFunctions)
1772 {
1773 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1774 paFunctions[usCurrent].ulType = 1; // 16-bit entry
1775 paFunctions[usCurrent].achFunctionName[0] = 0;
1776 }
1777 usCurrent++;
1778 }
1779
1780 usOrdinal++;
1781
1782 if (bType == 0xFF)
1783 {
1784 // moveable segment
1785 ENSURE(DosSetFilePtr(pExec->hfExe,
1786 5,
1787 FILE_CURRENT,
1788 &ulDummy));
1789 }
1790 else
1791 {
1792 // fixed segment or constant (0xFE)
1793 ENSURE(DosSetFilePtr(pExec->hfExe,
1794 2,
1795 FILE_CURRENT,
1796 &ulDummy));
1797 }
1798
1799 } // end for
1800 }
1801 else
1802 usOrdinal += bCnt;
1803 } // end while (TRUE)
1804
1805 if (pcEntries)
1806 *pcEntries = usCurrent;
1807
1808 ENSURE_OK;
1809}
1810
1811/*
1812 *@@ Compare:
1813 * binary search helper
1814 *
1815 *@@added V0.9.9 (2001-04-01) [lafaix]
1816 *@@changed V0.9.9 (2001-04-07) [umoeller]: added _Optlink, or this won't compile as C++
1817 */
1818
1819int _Optlink Compare(const void *key,
1820 const void *element)
1821{
1822 USHORT usOrdinal = *((PUSHORT) key);
1823 PFSYSFUNCTION pFunction = (PFSYSFUNCTION)element;
1824
1825 if (usOrdinal > pFunction->ulOrdinal)
1826 return (1);
1827 else if (usOrdinal < pFunction->ulOrdinal)
1828 return (-1);
1829 else
1830 return (0);
1831}
1832
1833/*
1834 *@@ ScanNameTable:
1835 * scans a resident or non-resident name table, and fills the
1836 * appropriate paFunctions entries when it encounters exported
1837 * entries names.
1838 *
1839 * This functions works for both NE and LX executables.
1840 *
1841 *@@added V0.9.9 (2001-03-30) [lafaix]
1842 *@@changed V0.9.9 (2001-04-02) [lafaix]: the first entry is special
1843 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1844 *@@changed V0.9.9 (2001-04-05) [lafaix]: removed the 127 char limit
1845 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1846 */
1847
1848APIRET ScanNameTable(PEXECUTABLE pExec,
1849 ULONG cFunctions,
1850 PFSYSFUNCTION paFunctions)
1851{
1852 ULONG ulDummy;
1853
1854 USHORT usOrdinal;
1855 PFSYSFUNCTION pFunction;
1856
1857 while (TRUE)
1858 {
1859 BYTE bLen;
1860 CHAR achName[256];
1861 int i;
1862
1863 ENSURE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1864
1865 if (bLen == 0)
1866 // end of the name table
1867 break;
1868
1869 ENSURE(DosRead(pExec->hfExe, &achName, bLen, &ulDummy));
1870 achName[bLen] = 0;
1871
1872 ENSURE(DosRead(pExec->hfExe, &usOrdinal, sizeof(USHORT), &ulDummy));
1873
1874 if ((pFunction = (PFSYSFUNCTION)bsearch(&usOrdinal,
1875 paFunctions,
1876 cFunctions,
1877 sizeof(FSYSFUNCTION),
1878 Compare)))
1879 {
1880 memcpy(pFunction->achFunctionName,
1881 achName,
1882 bLen+1);
1883 }
1884 }
1885
1886 ENSURE_OK;
1887}
1888
1889/*
1890 *@@ doshExecQueryExportedFunctions:
1891 * returns an array of FSYSFUNCTION structure describing all
1892 * exported functions.
1893 *
1894 * *pcFunctions receives the # of items in the array (not the
1895 * array size!). Use doshFreeExportedFunctions to clean up.
1896 *
1897 * Note that the returned array only contains entry for exported
1898 * functions. Empty export entries are _not_ included.
1899 *
1900 * This returns a standard OS/2 error code, which might be
1901 * any of the codes returned by DosSetFilePtr and DosRead.
1902 * In addition, this may return:
1903 *
1904 * -- ERROR_NOT_ENOUGH_MEMORY
1905 *
1906 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1907 * than LX or NE, which is not understood by this function.
1908 *
1909 * -- If ERROR_INVALID_LIST_FORMAT is returned, the format of an
1910 * export entry wasn't understood here.
1911 *
1912 * Even if NO_ERROR is returned, the array pointer might still
1913 * be NULL if the module contains no such data.
1914 *
1915 *@@added V0.9.9 (2001-03-11) [lafaix]
1916 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1917 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1918 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1919 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1920 */
1921
1922APIRET doshExecQueryExportedFunctions(PEXECUTABLE pExec,
1923 PFSYSFUNCTION *ppaFunctions, // out: functions array
1924 PULONG pcFunctions) // out: array item count
1925{
1926 if ( (pExec)
1927 && ( (pExec->ulOS == EXEOS_OS2)
1928 || (pExec->ulOS == EXEOS_WIN16)
1929 || (pExec->ulOS == EXEOS_WIN386)
1930 )
1931 )
1932 {
1933 ENSURE_BEGIN;
1934 ULONG cFunctions = 0;
1935 PFSYSFUNCTION paFunctions = NULL;
1936
1937 ULONG ulDummy;
1938
1939 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1940
1941 if (pExec->pDosExeHeader)
1942 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1943 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1944
1945 if (pExec->ulExeFormat == EXEFORMAT_LX)
1946 {
1947 // It's a 32bit OS/2 executable
1948
1949 // the number of exported entry points is not stored
1950 // in the executable header; we have to count them in
1951 // the entry table
1952
1953 ENSURE(ScanLXEntryTable(pExec, NULL, &cFunctions));
1954
1955 // we now have the number of exported entries; let us
1956 // build them
1957
1958 if (cFunctions)
1959 {
1960 ULONG cb = sizeof(FSYSFUNCTION) * cFunctions;
1961
1962 paFunctions = (PFSYSFUNCTION)malloc(cb);
1963 if (!paFunctions)
1964 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1965
1966 // we rescan the entry table (the cost is not as bad
1967 // as it may seem, due to disk caching)
1968
1969 ENSURE_SAFE(ScanLXEntryTable(pExec, paFunctions, NULL));
1970
1971 // we now scan the resident name table entries
1972
1973 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1974 pExec->pLXHeader->ulResdNameTblOfs
1975 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1976 FILE_BEGIN,
1977 &ulDummy));
1978
1979 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1980
1981 // we now scan the non-resident name table entries,
1982 // whose offset is _from the begining of the file_
1983
1984 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1985 pExec->pLXHeader->ulNonResdNameTblOfs,
1986 FILE_BEGIN,
1987 &ulDummy));
1988
1989 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1990 } // end if (cFunctions)
1991 }
1992 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1993 {
1994 // it's a "new" segmented 16bit executable
1995
1996 // here too the number of exported entry points
1997 // is not stored in the executable header; we
1998 // have to count them in the entry table
1999
2000 ENSURE(ScanNEEntryTable(pExec, NULL, &cFunctions));
2001
2002 // we now have the number of exported entries; let us
2003 // build them
2004
2005 if (cFunctions)
2006 {
2007 USHORT usOrdinal = 1,
2008 usCurrent = 0;
2009
2010 paFunctions = (PFSYSFUNCTION)malloc(sizeof(FSYSFUNCTION) * cFunctions);
2011 if (!paFunctions)
2012 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2013
2014 // we rescan the entry table (the cost is not as bad
2015 // as it may seem, due to disk caching)
2016
2017 ENSURE_SAFE(ScanNEEntryTable(pExec, paFunctions, NULL));
2018
2019 // we now scan the resident name table entries
2020
2021 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2022 pExec->pNEHeader->usResdNameTblOfs
2023 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2024 FILE_BEGIN,
2025 &ulDummy));
2026
2027 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
2028
2029 // we now scan the non-resident name table entries,
2030 // whose offset is _from the begining of the file_
2031
2032 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2033 pExec->pNEHeader->ulNonResdTblOfs,
2034 FILE_BEGIN,
2035 &ulDummy));
2036
2037 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
2038 }
2039 }
2040 else
2041 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2042
2043 // no error: output data
2044 *ppaFunctions = paFunctions;
2045 *pcFunctions = cFunctions;
2046
2047 ENSURE_FINALLY;
2048 // if we had an error above, clean up
2049 free(paFunctions);
2050 ENSURE_END;
2051 }
2052 else
2053 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2054
2055 ENSURE_OK;
2056}
2057
2058/*
2059 *@@ doshExecFreeExportedFunctions:
2060 * frees resources allocated by doshExecQueryExportedFunctions.
2061 *
2062 *@@added V0.9.9 (2001-03-11)
2063 */
2064
2065APIRET doshExecFreeExportedFunctions(PFSYSFUNCTION paFunctions)
2066{
2067 free(paFunctions);
2068
2069 return (NO_ERROR);
2070}
2071
2072/*
2073 *@@ doshExecQueryResources:
2074 * returns an array of FSYSRESOURCE structures describing all
2075 * available resources in the module.
2076 *
2077 * *pcResources receives the no. of items in the array
2078 * (not the array size!). Use doshExecFreeResources to clean up.
2079 *
2080 * This returns a standard OS/2 error code, which might be
2081 * any of the codes returned by DosSetFilePtr and DosRead.
2082 * In addition, this may return:
2083 *
2084 * -- ERROR_NOT_ENOUGH_MEMORY
2085 *
2086 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
2087 * than LX or NE, which is not understood by this function.
2088 *
2089 * Even if NO_ERROR is returned, the array pointer might still
2090 * be NULL if the module contains no such data.
2091 *
2092 *@@added V0.9.7 (2000-12-18) [lafaix]
2093 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
2094 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
2095 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
2096 */
2097
2098APIRET doshExecQueryResources(PEXECUTABLE pExec, // in: executable from doshExecOpen
2099 PFSYSRESOURCE *ppaResources, // out: res's array
2100 PULONG pcResources) // out: array item count
2101{
2102 if ( (pExec)
2103 && ( (pExec->ulOS == EXEOS_OS2)
2104 || (pExec->ulOS == EXEOS_WIN16)
2105 || (pExec->ulOS == EXEOS_WIN386)
2106 )
2107 )
2108 {
2109 ENSURE_BEGIN;
2110 ULONG cResources = 0;
2111 PFSYSRESOURCE paResources = NULL;
2112
2113 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
2114
2115 if (pExec->pDosExeHeader)
2116 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
2117 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
2118
2119 if (pExec->ulExeFormat == EXEFORMAT_LX)
2120 {
2121 // 32-bit OS/2 executable:
2122 cResources = pExec->pLXHeader->ulResTblCnt;
2123
2124 if (cResources)
2125 {
2126 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2127 struct rsrc32 /* Resource Table Entry */
2128 {
2129 unsigned short type; /* Resource type */
2130 unsigned short name; /* Resource name */
2131 unsigned long cb; /* Resource size */
2132 unsigned short obj; /* Object number */
2133 unsigned long offset; /* Offset within object */
2134 } rs;
2135
2136 struct o32_obj /* Flat .EXE object table entry */
2137 {
2138 unsigned long o32_size; /* Object virtual size */
2139 unsigned long o32_base; /* Object base virtual address */
2140 unsigned long o32_flags; /* Attribute flags */
2141 unsigned long o32_pagemap; /* Object page map index */
2142 unsigned long o32_mapsize; /* Number of entries in object page map */
2143 unsigned long o32_reserved; /* Reserved */
2144 } ot;
2145 #pragma pack() // V0.9.9 (2001-04-03) [umoeller]
2146
2147 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2148 ULONG ulDummy;
2149 int i;
2150
2151 paResources = (PFSYSRESOURCE)malloc(cb);
2152 if (!paResources)
2153 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2154
2155 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2156
2157 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2158 pExec->pLXHeader->ulResTblOfs
2159 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2160 FILE_BEGIN,
2161 &ulDummy));
2162
2163 for (i = 0; i < cResources; i++)
2164 {
2165 ENSURE_SAFE(DosRead(pExec->hfExe, &rs, 14, &ulDummy));
2166
2167 paResources[i].ulID = rs.name;
2168 paResources[i].ulType = rs.type;
2169 paResources[i].ulSize = rs.cb;
2170 paResources[i].ulFlag = rs.obj; // Temp storage for Object
2171 // number. Will be filled
2172 // with resource flag
2173 // later.
2174 }
2175
2176 for (i = 0; i < cResources; i++)
2177 {
2178 ULONG ulOfsThis = pExec->pLXHeader->ulObjTblOfs
2179 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
2180 + ( sizeof(ot)
2181 * (paResources[i].ulFlag - 1));
2182
2183 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2184 ulOfsThis,
2185 FILE_BEGIN,
2186 &ulDummy));
2187
2188 ENSURE_SAFE(DosRead(pExec->hfExe, &ot, sizeof(ot), &ulDummy));
2189
2190 paResources[i].ulFlag = ((ot.o32_flags & OBJWRITE)
2191 ? 0
2192 : RNPURE);
2193 paResources[i].ulFlag |= ((ot.o32_flags & OBJDISCARD)
2194 ? 4096
2195 : 0);
2196 paResources[i].ulFlag |= ((ot.o32_flags & OBJSHARED)
2197 ? RNMOVE
2198 : 0);
2199 paResources[i].ulFlag |= ((ot.o32_flags & OBJPRELOAD)
2200 ? RNPRELOAD
2201 : 0);
2202 } // end for
2203 } // end if (cResources)
2204 } // end if (pExec->ulExeFormat == EXEFORMAT_LX)
2205 else if (pExec->ulExeFormat == EXEFORMAT_NE)
2206 {
2207 if (pExec->ulOS == EXEOS_OS2)
2208 {
2209 // 16-bit OS/2 executable:
2210 cResources = pExec->pNEHeader->usResSegmCount;
2211
2212 if (cResources)
2213 {
2214 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2215 struct {unsigned short type; unsigned short name;} rti;
2216 struct new_seg /* New .EXE segment table entry */
2217 {
2218 unsigned short ns_sector; /* File sector of start of segment */
2219 unsigned short ns_cbseg; /* Number of bytes in file */
2220 unsigned short ns_flags; /* Attribute flags */
2221 unsigned short ns_minalloc; /* Minimum allocation in bytes */
2222 } ns;
2223 #pragma pack()
2224
2225 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2226 ULONG ulDummy;
2227 int i;
2228
2229 paResources = (PFSYSRESOURCE)malloc(cb);
2230 if (!paResources)
2231 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2232
2233 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2234
2235 // we first read the resources IDs and types
2236
2237 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2238 pExec->pNEHeader->usResTblOfs
2239 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2240 FILE_BEGIN,
2241 &ulDummy));
2242
2243 for (i = 0; i < cResources; i++)
2244 {
2245 ENSURE_SAFE(DosRead(pExec->hfExe, &rti, sizeof(rti), &ulDummy));
2246
2247 paResources[i].ulID = rti.name;
2248 paResources[i].ulType = rti.type;
2249 }
2250
2251 // we then read their sizes and flags
2252
2253 for (i = 0; i < cResources; i++)
2254 {
2255 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2256 ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
2257 + pExec->pNEHeader->usSegTblOfs
2258 + (sizeof(ns)
2259 * ( pExec->pNEHeader->usSegTblEntries
2260 - pExec->pNEHeader->usResSegmCount
2261 + i)),
2262 FILE_BEGIN,
2263 &ulDummy));
2264
2265 ENSURE_SAFE(DosRead(pExec->hfExe, &ns, sizeof(ns), &ulDummy));
2266
2267 paResources[i].ulSize = ns.ns_cbseg;
2268
2269 paResources[i].ulFlag = (ns.ns_flags & OBJPRELOAD) ? RNPRELOAD : 0;
2270 paResources[i].ulFlag |= (ns.ns_flags & OBJSHARED) ? RNPURE : 0;
2271 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? RNMOVE : 0;
2272 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? 4096 : 0;
2273 }
2274 } // end if (cResources)
2275 }
2276 else
2277 {
2278 // 16-bit Windows executable
2279 USHORT usAlignShift;
2280 ULONG ulDummy;
2281
2282 ENSURE(DosSetFilePtr(pExec->hfExe,
2283 pExec->pNEHeader->usResTblOfs
2284 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2285 FILE_BEGIN,
2286 &ulDummy));
2287
2288 ENSURE(DosRead(pExec->hfExe,
2289 &usAlignShift,
2290 sizeof(usAlignShift),
2291 &ulDummy));
2292
2293 while (TRUE)
2294 {
2295 USHORT usTypeID;
2296 USHORT usCount;
2297
2298 ENSURE(DosRead(pExec->hfExe,
2299 &usTypeID,
2300 sizeof(usTypeID),
2301 &ulDummy));
2302
2303 if (usTypeID == 0)
2304 break;
2305
2306 ENSURE(DosRead(pExec->hfExe,
2307 &usCount,
2308 sizeof(usCount),
2309 &ulDummy));
2310
2311 ENSURE(DosSetFilePtr(pExec->hfExe,
2312 sizeof(ULONG),
2313 FILE_CURRENT,
2314 &ulDummy));
2315
2316 cResources += usCount;
2317
2318 // first pass, skip NAMEINFO table
2319 ENSURE(DosSetFilePtr(pExec->hfExe,
2320 usCount*6*sizeof(USHORT),
2321 FILE_CURRENT,
2322 &ulDummy));
2323 }
2324
2325 if (cResources)
2326 {
2327 USHORT usCurrent = 0;
2328 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2329
2330 paResources = (PFSYSRESOURCE)malloc(cb);
2331 if (!paResources)
2332 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2333
2334 memset(paResources, 0, cb);
2335
2336 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2337 pExec->pNEHeader->usResTblOfs
2338 + ulNewHeaderOfs,
2339 FILE_BEGIN,
2340 &ulDummy));
2341
2342 ENSURE_SAFE(DosRead(pExec->hfExe,
2343 &usAlignShift,
2344 sizeof(usAlignShift),
2345 &ulDummy));
2346
2347 while (TRUE)
2348 {
2349 USHORT usTypeID;
2350 USHORT usCount;
2351 int i;
2352
2353 ENSURE_SAFE(DosRead(pExec->hfExe,
2354 &usTypeID,
2355 sizeof(usTypeID),
2356 &ulDummy));
2357
2358 if (usTypeID == 0)
2359 break;
2360
2361 ENSURE_SAFE(DosRead(pExec->hfExe,
2362 &usCount,
2363 sizeof(usCount),
2364 &ulDummy));
2365
2366 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2367 sizeof(ULONG),
2368 FILE_CURRENT,
2369 &ulDummy));
2370
2371 // second pass, read NAMEINFO table
2372 for (i = 0; i < usCount; i++)
2373 {
2374 USHORT usLength,
2375 usFlags,
2376 usID;
2377
2378 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2379 sizeof(USHORT),
2380 FILE_CURRENT,
2381 &ulDummy));
2382
2383 ENSURE_SAFE(DosRead(pExec->hfExe,
2384 &usLength,
2385 sizeof(USHORT),
2386 &ulDummy));
2387 ENSURE_SAFE(DosRead(pExec->hfExe,
2388 &usFlags,
2389 sizeof(USHORT),
2390 &ulDummy));
2391 ENSURE_SAFE(DosRead(pExec->hfExe,
2392 &usID,
2393 sizeof(USHORT),
2394 &ulDummy));
2395
2396 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2397 2*sizeof(USHORT),
2398 FILE_CURRENT,
2399 &ulDummy));
2400
2401 // !!! strings ids and types not handled yet
2402 // !!! 15th bit is used to denotes strings
2403 // !!! offsets [lafaix]
2404 paResources[usCurrent].ulType = usTypeID ^ 0x8000;
2405 paResources[usCurrent].ulID = usID ^ 0x8000;
2406 paResources[usCurrent].ulSize = usLength << usAlignShift;
2407 paResources[usCurrent].ulFlag = usFlags & 0x70;
2408
2409 usCurrent++;
2410 }
2411 }
2412 }
2413 }
2414 } // end else if (pExec->ulExeFormat == EXEFORMAT_NE)
2415 else
2416 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2417
2418 *ppaResources = paResources;
2419 *pcResources = cResources;
2420
2421 ENSURE_FINALLY;
2422 // if we had an error above, clean up
2423 free(paResources);
2424 ENSURE_END;
2425 }
2426 else
2427 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2428
2429 ENSURE_OK;
2430}
2431
2432/*
2433 *@@ doshExecFreeResources:
2434 * frees resources allocated by doshExecQueryResources.
2435 *
2436 *@@added V0.9.7 (2000-12-18) [lafaix]
2437 */
2438
2439APIRET doshExecFreeResources(PFSYSRESOURCE paResources)
2440{
2441 free(paResources);
2442 return (NO_ERROR);
2443}
2444
2445/*
2446 * FindFile:
2447 * helper for doshFindExecutable.
2448 *
2449 *added V0.9.11 (2001-04-25) [umoeller]
2450 */
2451
2452APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
2453 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
2454 ULONG cbExecutable) // in: sizeof (*pszExecutable)
2455{
2456 APIRET arc = NO_ERROR;
2457 FILESTATUS3 fs3;
2458
2459 if ( (strchr(pcszCommand, '\\'))
2460 || (strchr(pcszCommand, ':'))
2461 )
2462 {
2463 // looks like this is qualified:
2464 arc = DosQueryPathInfo((PSZ)pcszCommand,
2465 FIL_STANDARD,
2466 &fs3,
2467 sizeof(fs3));
2468 if (!arc)
2469 if (!(fs3.attrFile & FILE_DIRECTORY))
2470 strhncpy0(pszExecutable,
2471 pcszCommand,
2472 cbExecutable);
2473 else
2474 // directory:
2475 arc = ERROR_INVALID_EXE_SIGNATURE;
2476 }
2477 else
2478 // non-qualified:
2479 arc = DosSearchPath(SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY,
2480 "PATH",
2481 (PSZ)pcszCommand,
2482 pszExecutable,
2483 cbExecutable);
2484
2485 return (arc);
2486}
2487
2488/*
2489 *@@ doshFindExecutable:
2490 * this attempts to find an executable by doing the
2491 * following:
2492 *
2493 * 1) If pcszCommand appears to be qualified (i.e. contains
2494 * a backslash), this checks for whether the file exists.
2495 *
2496 * 2) If pcszCommand contains no backslash, this calls
2497 * DosSearchPath in order to find the full path of the
2498 * executable.
2499 *
2500 * papcszExtensions determines if additional searches are to be
2501 * performed if the file doesn't exist (case 1) or DosSearchPath
2502 * returned ERROR_FILE_NOT_FOUND (case 2).
2503 * This must point to an array of strings specifying the extra
2504 * extensions to search for.
2505 *
2506 * If both papcszExtensions and cExtensions are null, no
2507 * extra searches are performed.
2508 *
2509 * If this returns NO_ERROR, pszExecutable receives
2510 * the full path of the executable found by DosSearchPath.
2511 * Otherwise ERROR_FILE_NOT_FOUND is returned.
2512 *
2513 * Example:
2514 *
2515 + const char *aExtensions[] = { "EXE",
2516 + "COM",
2517 + "CMD"
2518 + };
2519 + CHAR szExecutable[CCHMAXPATH];
2520 + APIRET arc = doshFindExecutable("lvm",
2521 + szExecutable,
2522 + sizeof(szExecutable),
2523 + aExtensions,
2524 + 3);
2525 *
2526 *@@added V0.9.9 (2001-03-07) [umoeller]
2527 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
2528 */
2529
2530APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
2531 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
2532 ULONG cbExecutable, // in: sizeof (*pszExecutable)
2533 const char **papcszExtensions, // in: array of extensions (without dots)
2534 ULONG cExtensions) // in: array item count
2535{
2536 APIRET arc = FindFile(pcszCommand,
2537 pszExecutable,
2538 cbExecutable);
2539
2540 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
2541 && (cExtensions) // any extra searches wanted?
2542 )
2543 {
2544 // try additional things then
2545 PSZ psz2 = (PSZ)malloc(strlen(pcszCommand) + 20);
2546 if (psz2)
2547 {
2548 ULONG ul;
2549 for (ul = 0;
2550 ul < cExtensions;
2551 ul++)
2552 {
2553 const char *pcszExtThis = papcszExtensions[ul];
2554 sprintf(psz2, "%s.%s", pcszCommand, pcszExtThis);
2555 arc = FindFile(psz2,
2556 pszExecutable,
2557 cbExecutable);
2558 if (arc != ERROR_FILE_NOT_FOUND)
2559 break;
2560 }
2561
2562 free(psz2);
2563 }
2564 else
2565 arc = ERROR_NOT_ENOUGH_MEMORY;
2566 }
2567
2568 return (arc);
2569}
2570
2571/*
2572 *@@category: Helpers\Control program helpers\Partitions info
2573 * functions for retrieving partition information directly
2574 * from the partition tables on the disk. See doshGetPartitionsList.
2575 */
2576
2577/********************************************************************
2578 *
2579 * Partition functions
2580 *
2581 ********************************************************************/
2582
2583/*
2584 *@@ doshQueryDiskCount:
2585 * returns the no. of physical disks installed
2586 * on the system.
2587 *
2588 * Based on code (C) Dmitry A. Steklenev.
2589 *
2590 *@@added V0.9.0 [umoeller]
2591 */
2592
2593UINT doshQueryDiskCount(VOID)
2594{
2595 USHORT count = 0;
2596
2597 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &count, 2, 0, 0);
2598 return (count);
2599}
2600
2601/*
2602 *@@ doshReadSector:
2603 * reads a physical disk sector.
2604 *
2605 * If NO_ERROR is returned, the sector contents
2606 * have been stored in *buff.
2607 *
2608 * Based on code (C) Dmitry A. Steklenev.
2609 *
2610 *@@added V0.9.0 [umoeller]
2611 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
2612 */
2613
2614APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
2615 void *buff,
2616 USHORT head,
2617 USHORT cylinder,
2618 USHORT sector)
2619{
2620 APIRET arc;
2621 HFILE dh = 0;
2622 char dn[256];
2623
2624 sprintf(dn, "%u:", disk);
2625 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
2626 {
2627 TRACKLAYOUT DiskIOParm;
2628 ULONG IOCtlDataLength = sizeof(DiskIOParm);
2629 ULONG IOCtlParmLength = 512;
2630
2631 DiskIOParm.bCommand = 0;
2632 DiskIOParm.usHead = head;
2633 DiskIOParm.usCylinder = cylinder;
2634 DiskIOParm.usFirstSector = 0;
2635 DiskIOParm.cSectors = 1;
2636 DiskIOParm.TrackTable[0].usSectorNumber = sector;
2637 DiskIOParm.TrackTable[0].usSectorSize = 512;
2638
2639 arc = DosDevIOCtl(dh,
2640 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
2641 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
2642 buff , IOCtlDataLength, &IOCtlDataLength);
2643
2644 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
2645 }
2646
2647 return (arc);
2648}
2649
2650/*
2651 *@@ doshType2FSName:
2652 * this returns a static, zero-terminated string
2653 * for the given FS type. This is always 7 bytes
2654 * in length.
2655 *
2656 * Values for operating system indicator:
2657 * -- 00h empty
2658 * -- 01h DOS 12-bit FAT
2659 * -- 02h XENIX root file system
2660 * -- 03h XENIX /usr file system (obsolete)
2661 * -- 04h DOS 16-bit FAT (up to 32M)
2662 * -- 05h DOS 3.3+ extended partition
2663 * -- 06h DOS 3.31+ Large File System (16-bit FAT, over 32M)
2664 * -- 07h QNX
2665 * -- 07h OS/2 HPFS
2666 * -- 07h Windows NT NTFS
2667 * -- 07h Advanced Unix
2668 * -- 08h OS/2 (v1.0-1.3 only)
2669 * -- 08h AIX bootable partition, SplitDrive
2670 * -- 08h Commodore DOS
2671 * -- 08h DELL partition spanning multiple drives
2672 * -- 09h AIX data partition
2673 * -- 09h Coherent filesystem
2674 * -- 0Ah OS/2 Boot Manager
2675 * -- 0Ah OPUS
2676 * -- 0Ah Coherent swap partition
2677 * -- 0Bh Windows95 with 32-bit FAT
2678 * -- 0Ch Windows95 with 32-bit FAT (using LBA-mode INT 13 extensions)
2679 * -- 0Eh logical-block-addressable VFAT (same as 06h but using LBA-mode INT 13)
2680 * -- 0Fh logical-block-addressable VFAT (same as 05h but using LBA-mode INT 13)
2681 * -- 10h OPUS
2682 * -- 11h OS/2 Boot Manager hidden 12-bit FAT partition
2683 * -- 12h Compaq Diagnostics partition
2684 * -- 14h (resulted from using Novell DOS 7.0 FDISK to delete Linux Native part)
2685 * -- 14h OS/2 Boot Manager hidden sub-32M 16-bit FAT partition
2686 * -- 16h OS/2 Boot Manager hidden over-32M 16-bit FAT partition
2687 * -- 17h OS/2 Boot Manager hidden HPFS partition
2688 * -- 18h AST special Windows swap file ("Zero-Volt Suspend" partition)
2689 * -- 21h officially listed as reserved
2690 * -- 23h officially listed as reserved
2691 * -- 24h NEC MS-DOS 3.x
2692 * -- 26h officially listed as reserved
2693 * -- 31h officially listed as reserved
2694 * -- 33h officially listed as reserved
2695 * -- 34h officially listed as reserved
2696 * -- 36h officially listed as reserved
2697 * -- 38h Theos
2698 * -- 3Ch PowerQuest PartitionMagic recovery partition
2699 * -- 40h VENIX 80286
2700 * -- 41h Personal RISC Boot
2701 * -- 42h SFS (Secure File System) by Peter Gutmann
2702 * -- 50h OnTrack Disk Manager, read-only partition
2703 * -- 51h OnTrack Disk Manager, read/write partition
2704 * -- 51h NOVEL
2705 * -- 52h CP/M
2706 * -- 52h Microport System V/386
2707 * -- 53h OnTrack Disk Manager, write-only partition???
2708 * -- 54h OnTrack Disk Manager (DDO)
2709 * -- 56h GoldenBow VFeature
2710 * -- 61h SpeedStor
2711 * -- 63h Unix SysV/386, 386/ix
2712 * -- 63h Mach, MtXinu BSD 4.3 on Mach
2713 * -- 63h GNU HURD
2714 * -- 64h Novell NetWare 286
2715 * -- 65h Novell NetWare (3.11)
2716 * -- 67h Novell
2717 * -- 68h Novell
2718 * -- 69h Novell
2719 * -- 70h DiskSecure Multi-Boot
2720 * -- 71h officially listed as reserved
2721 * -- 73h officially listed as reserved
2722 * -- 74h officially listed as reserved
2723 * -- 75h PC/IX
2724 * -- 76h officially listed as reserved
2725 * -- 80h Minix v1.1 - 1.4a
2726 * -- 81h Minix v1.4b+
2727 * -- 81h Linux
2728 * -- 81h Mitac Advanced Disk Manager
2729 * -- 82h Linux Swap partition
2730 * -- 82h Prime
2731 * -- 83h Linux native file system (ext2fs/xiafs)
2732 * -- 84h OS/2-renumbered type 04h partition (related to hiding DOS C: drive)
2733 * -- 86h FAT16 volume/stripe set (Windows NT)
2734 * -- 87h HPFS Fault-Tolerant mirrored partition
2735 * -- 87h NTFS volume/stripe set
2736 * -- 93h Amoeba file system
2737 * -- 94h Amoeba bad block table
2738 * -- A0h Phoenix NoteBIOS Power Management "Save-to-Disk" partition
2739 * -- A1h officially listed as reserved
2740 * -- A3h officially listed as reserved
2741 * -- A4h officially listed as reserved
2742 * -- A5h FreeBSD, BSD/386
2743 * -- A6h officially listed as reserved
2744 * -- B1h officially listed as reserved
2745 * -- B3h officially listed as reserved
2746 * -- B4h officially listed as reserved
2747 * -- B6h officially listed as reserved
2748 * -- B7h BSDI file system (secondarily swap)
2749 * -- B8h BSDI swap partition (secondarily file system)
2750 * -- C1h DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT partition
2751 * -- C4h DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT partition
2752 * -- C6h DR DOS 6.0 LOGIN.EXE-secured Huge partition
2753 * -- C6h corrupted FAT16 volume/stripe set (Windows NT)
2754 * -- C7h Syrinx Boot
2755 * -- C7h corrupted NTFS volume/stripe set
2756 * -- D8h CP/M-86
2757 * -- DBh CP/M, Concurrent CP/M, Concurrent DOS
2758 * -- DBh CTOS (Convergent Technologies OS)
2759 * -- E1h SpeedStor 12-bit FAT extended partition
2760 * -- E3h DOS read-only
2761 * -- E3h Storage Dimensions
2762 * -- E4h SpeedStor 16-bit FAT extended partition
2763 * -- E5h officially listed as reserved
2764 * -- E6h officially listed as reserved
2765 * -- F1h Storage Dimensions
2766 * -- F2h DOS 3.3+ secondary partition
2767 * -- F3h officially listed as reserved
2768 * -- F4h SpeedStor
2769 * -- F4h Storage Dimensions
2770 * -- F6h officially listed as reserved
2771 * -- FEh LANstep
2772 * -- FEh IBM PS/2 IML
2773 * -- FFh Xenix bad block table
2774 *
2775 * Note: for partition type 07h, one should inspect the partition boot record
2776 * for the actual file system type
2777 *
2778 * Based on code (C) Dmitry A. Steklenev.
2779 *
2780 *@@added V0.9.0 [umoeller]
2781 */
2782
2783const char* doshType2FSName(unsigned char bFSType) // in: FS type
2784{
2785 PSZ zFSName = NULL;
2786
2787 switch (bFSType)
2788 {
2789 case PAR_UNUSED:
2790 zFSName = "UNUSED ";
2791 break;
2792 case PAR_FAT12SMALL:
2793 zFSName = "FAT-12 ";
2794 break;
2795 case PAR_XENIXROOT:
2796 zFSName = "XENIX ";
2797 break;
2798 case PAR_XENIXUSER:
2799 zFSName = "XENIX ";
2800 break;
2801 case PAR_FAT16SMALL:
2802 zFSName = "FAT-16 ";
2803 break;
2804 case PAR_EXTENDED:
2805 zFSName = "EXTEND ";
2806 break;
2807 case PAR_FAT16BIG:
2808 zFSName = "BIGDOS ";
2809 break;
2810 case PAR_HPFS:
2811 zFSName = "HPFS ";
2812 break;
2813 case PAR_AIXBOOT:
2814 zFSName = "AIX ";
2815 break;
2816 case PAR_AIXDATA:
2817 zFSName = "AIX ";
2818 break;
2819 case PAR_BOOTMANAGER:
2820 zFSName = "BOOTMNG";
2821 break;
2822 case PAR_WINDOWS95:
2823 zFSName = "WIN95 ";
2824 break;
2825 case PAR_WINDOWS95LB:
2826 zFSName = "WIN95 ";
2827 break;
2828 case PAR_VFAT16BIG:
2829 zFSName = "VFAT ";
2830 break;
2831 case PAR_VFAT16EXT:
2832 zFSName = "VFAT ";
2833 break;
2834 case PAR_OPUS:
2835 zFSName = "OPUS ";
2836 break;
2837 case PAR_HID12SMALL:
2838 zFSName = "FAT-12*";
2839 break;
2840 case PAR_COMPAQDIAG:
2841 zFSName = "COMPAQ ";
2842 break;
2843 case PAR_HID16SMALL:
2844 zFSName = "FAT-16*";
2845 break;
2846 case PAR_HID16BIG:
2847 zFSName = "BIGDOS*";
2848 break;
2849 case PAR_HIDHPFS:
2850 zFSName = "HPFS* ";
2851 break;
2852 case PAR_WINDOWSSWP:
2853 zFSName = "WINSWAP";
2854 break;
2855 case PAR_NECDOS:
2856 zFSName = "NECDOS ";
2857 break;
2858 case PAR_THEOS:
2859 zFSName = "THEOS ";
2860 break;
2861 case PAR_VENIX:
2862 zFSName = "VENIX ";
2863 break;
2864 case PAR_RISCBOOT:
2865 zFSName = "RISC ";
2866 break;
2867 case PAR_SFS:
2868 zFSName = "SFS ";
2869 break;
2870 case PAR_ONTRACK:
2871 zFSName = "ONTRACK";
2872 break;
2873 case PAR_ONTRACKEXT:
2874 zFSName = "ONTRACK";
2875 break;
2876 case PAR_CPM:
2877 zFSName = "CP/M ";
2878 break;
2879 case PAR_UNIXSYSV:
2880 zFSName = "UNIX ";
2881 break;
2882 case PAR_NOVELL_64:
2883 zFSName = "NOVELL ";
2884 break;
2885 case PAR_NOVELL_65:
2886 zFSName = "NOVELL ";
2887 break;
2888 case PAR_NOVELL_67:
2889 zFSName = "NOVELL ";
2890 break;
2891 case PAR_NOVELL_68:
2892 zFSName = "NOVELL ";
2893 break;
2894 case PAR_NOVELL_69:
2895 zFSName = "NOVELL ";
2896 break;
2897 case PAR_PCIX:
2898 zFSName = "PCIX ";
2899 break;
2900 case PAR_MINIX:
2901 zFSName = "MINIX ";
2902 break;
2903 case PAR_LINUX:
2904 zFSName = "LINUX ";
2905 break;
2906 case PAR_LINUXSWAP:
2907 zFSName = "LNXSWP ";
2908 break;
2909 case PAR_LINUXFILE:
2910 zFSName = "LINUX ";
2911 break;
2912 case PAR_FREEBSD:
2913 zFSName = "FREEBSD";
2914 break;
2915 case PAR_BBT:
2916 zFSName = "BBT ";
2917 break;
2918
2919 default:
2920 zFSName = " ";
2921 break;
2922 }
2923 return zFSName;
2924}
2925
2926/*
2927 * AppendPartition:
2928 * this appends the given partition information to
2929 * the given partition list. To do this, a new
2930 * PARTITIONINFO structure is created and appended
2931 * in a list (managed thru the PARTITIONINFO.pNext
2932 * items).
2933 *
2934 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
2935 * With each call of this function, this pointer is advanced
2936 * to point to the newly created PARTITIONINFO, so before
2937 * calling this function for the first time,
2938 *
2939 * Based on code (C) Dmitry A. Steklenev.
2940 *
2941 *@@added V0.9.0 [umoeller]
2942 */
2943
2944APIRET AppendPartition(PARTITIONINFO **pppiFirst,
2945 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
2946 PUSHORT posCount, // in/out: partition count
2947 BYTE bDisk, // in: disk of partition
2948 const char *pszBootName, // in: boot partition name
2949 CHAR cLetter, // in/out: drive letter
2950 BYTE bFsType, // in: file system type
2951 BOOL fPrimary, // in: primary?
2952 BOOL fBootable,
2953 ULONG ulSectors) // in: no. of sectors
2954{
2955 APIRET arc = NO_ERROR;
2956 PPARTITIONINFO ppiNew = NEW(PARTITIONINFO);
2957 if (ppiNew)
2958 {
2959 ZERO(ppiNew);
2960
2961 // store data
2962 ppiNew->bDisk = bDisk;
2963 if ((fBootable) && (pszBootName) )
2964 {
2965 memcpy(ppiNew->szBootName, pszBootName, 8);
2966 ppiNew->szBootName[8] = 0;
2967 }
2968 else
2969 ppiNew->szBootName[0] = 0;
2970 ppiNew->cLetter = cLetter;
2971 ppiNew->bFSType = bFsType;
2972 strcpy(ppiNew->szFSType,
2973 doshType2FSName(bFsType));
2974 ppiNew->fPrimary = fPrimary;
2975 ppiNew->fBootable = fBootable;
2976 ppiNew->ulSize = ulSectors / 2048;
2977
2978 ppiNew->pNext = NULL;
2979
2980 (*posCount)++;
2981
2982 if (*pppiFirst == (PPARTITIONINFO)NULL)
2983 {
2984 // first call:
2985 *pppiFirst = ppiNew;
2986 *pppiThis = ppiNew;
2987 }
2988 else
2989 {
2990 // append to list
2991 (**pppiThis).pNext = ppiNew;
2992 *pppiThis = ppiNew;
2993 }
2994 }
2995 else
2996 arc = ERROR_NOT_ENOUGH_MEMORY;
2997
2998 return (arc);
2999}
3000
3001// Sector and Cylinder values are actually 6 bits and 10 bits:
3002//
3003// 1 1 1 1 1 1
3004// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
3005// ³c c c c c c c c C c S s s s s s³
3006// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
3007//
3008// The high two bits of the second byte are used as the high bits
3009// of a 10-bit value. This allows for as many as 1024 cylinders
3010// and 64 sectors per cylinder.
3011
3012/*
3013 * GetCyl:
3014 * get cylinder number.
3015 *
3016 * Based on code (C) Dmitry A. Steklenev.
3017 *
3018 *@@added V0.9.0 [umoeller]
3019 */
3020
3021static USHORT GetCyl(USHORT rBeginSecCyl)
3022{
3023 return ( (rBeginSecCyl & 0x00C0) << 2)
3024 + ((rBeginSecCyl & 0xFF00) >> 8);
3025}
3026
3027/*
3028 * GetSec:
3029 * get sector number.
3030 *
3031 * Based on code (C) Dmitry A. Steklenev.
3032 *
3033 *@@added V0.9.0 [umoeller]
3034 */
3035
3036static USHORT GetSec(USHORT rBeginSecCyl)
3037{
3038 return rBeginSecCyl & 0x003F;
3039}
3040
3041/*
3042 *@@ doshGetBootManager:
3043 * this goes thru the master boot records on all
3044 * disks to find the boot manager partition.
3045 *
3046 * Returns:
3047 *
3048 * -- NO_ERROR: boot manager found; in that case,
3049 * information about the boot manager
3050 * is written into *pusDisk, *pusPart,
3051 * *BmInfo. Any of these pointers can
3052 * be NULL if you're not interested.
3053 *
3054 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
3055 *
3056 * Based on code (C) Dmitry A. Steklenev.
3057 *
3058 *@@added V0.9.0 [umoeller]
3059 */
3060
3061APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
3062 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
3063 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
3064{
3065 APIRET arc = NO_ERROR;
3066 USHORT count = doshQueryDiskCount(); // Physical disk number
3067 MBR_INFO MBoot; // Master Boot
3068 USHORT usDisk;
3069
3070 if (count > 8) // Not above 8 disks
3071 count = 8;
3072
3073 for (usDisk = 1; usDisk <= count; usDisk++)
3074 {
3075 USHORT usPrim = 0;
3076
3077 // for each disk, read the MBR, which has the
3078 // primary partitions
3079 if ((arc = doshReadSector(usDisk,
3080 &MBoot,
3081 0, // head
3082 0, // cylinder
3083 1))) // sector
3084 return (arc);
3085
3086 // scan primary partitions for whether
3087 // BootManager partition exists
3088 for (usPrim = 0; usPrim < 4; usPrim++)
3089 {
3090 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
3091 {
3092 // this is boot manager:
3093 if (pBmInfo)
3094 *pBmInfo = MBoot.sPrtnInfo[usPrim];
3095 if (pusPart)
3096 *pusPart = usPrim;
3097 if (pusDisk)
3098 *pusDisk = usDisk;
3099 // stop scanning
3100 return (NO_ERROR);
3101 }
3102 }
3103 }
3104
3105 return (ERROR_NOT_SUPPORTED);
3106}
3107
3108/*
3109 * GetPrimaryPartitions:
3110 * this returns the primary partitions.
3111 *
3112 * This gets called from doshGetPartitionsList.
3113 *
3114 * Returns:
3115 *
3116 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
3117 *
3118 * Based on code (C) Dmitry A. Steklenev.
3119 *
3120 *@@added V0.9.0 [umoeller]
3121 */
3122
3123APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
3124 PARTITIONINFO **pppiThis,
3125 PUSHORT posCount, // in/out: partition count
3126 PCHAR pcLetter, // in/out: drive letter counter
3127 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
3128 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
3129 UINT iDisk) // in: system's physical disk count
3130{
3131 APIRET arc = NO_ERROR;
3132
3133 if (!pBmInfo)
3134 arc = ERROR_INVALID_PARAMETER;
3135 else
3136 {
3137 SYS_INFO MName[32]; // Name Space from Boot Manager
3138 memset(&MName, 0, sizeof(MName));
3139
3140 // read boot manager name table;
3141 // this is in the boot manager primary partition
3142 // at sector offset 3 (?!?)
3143 if (!(arc = doshReadSector(BmDisk,
3144 &MName,
3145 // head, cylinder, sector of bmgr primary partition:
3146 pBmInfo->bBeginHead,
3147 GetCyl(pBmInfo->rBeginSecCyl),
3148 GetSec(pBmInfo->rBeginSecCyl) + 3)))
3149 {
3150 // got bmgr name table:
3151 MBR_INFO MBoot; // Master Boot
3152 USHORT i;
3153
3154 // read master boot record of this disk
3155 if (!(arc = doshReadSector(iDisk,
3156 &MBoot,
3157 0, // head
3158 0, // cylinder
3159 1))) // sector
3160 {
3161 for (i = 0;
3162 i < 4; // there can be only four primary partitions
3163 i++)
3164 {
3165 // skip unused partition, BootManager or Extended partition
3166 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
3167 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER) // skip boot manager
3168 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_EXTENDED) // skip extended
3169 )
3170 {
3171 BOOL fBootable = ( (pBmInfo)
3172 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
3173 );
3174 // store this partition
3175 if ((arc = AppendPartition(pppiFirst,
3176 pppiThis,
3177 posCount,
3178 iDisk,
3179 (fBootable)
3180 ? (char*)&MName[(iDisk - 1) * 4 + i].name
3181 : "",
3182 *pcLetter,
3183 MBoot.sPrtnInfo[i].bFileSysCode,
3184 TRUE, // primary
3185 fBootable,
3186 MBoot.sPrtnInfo[i].lTotalSects)))
3187 return (arc);
3188 }
3189 }
3190 }
3191 }
3192 }
3193
3194 return (arc);
3195}
3196
3197/*
3198 * GetLogicalDrives:
3199 * this returns info for the logical drives
3200 * in the extended partition. This gets called
3201 * from GetExtendedPartition.
3202 *
3203 * This gets called from GetExtendedPartition.
3204 *
3205 * Based on code (C) Dmitry A. Steklenev.
3206 *
3207 *@@added V0.9.0 [umoeller]
3208 */
3209
3210APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
3211 PARTITIONINFO **pppiThis,
3212 PUSHORT posCount,
3213 PCHAR pcLetter,
3214 PAR_INFO* PrInfo, // in: MBR entry of extended partition
3215 UINT PrDisk,
3216 PAR_INFO* BmInfo)
3217{
3218 APIRET arc = NO_ERROR;
3219 EXT_INFO MBoot; // Master Boot
3220 USHORT i;
3221
3222 if ((arc = doshReadSector(PrDisk,
3223 &MBoot,
3224 PrInfo->bBeginHead,
3225 GetCyl(PrInfo->rBeginSecCyl),
3226 GetSec(PrInfo->rBeginSecCyl))))
3227 return (arc);
3228
3229 for (i = 0; i < 4; i++)
3230 {
3231 // skip unused partition or BootManager partition
3232 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
3233 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER)
3234 )
3235 {
3236 BOOL fBootable = FALSE;
3237 BOOL fAssignLetter = FALSE;
3238
3239 // special work around extended partition
3240 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
3241 {
3242 if ((arc = GetLogicalDrives(pppiFirst,
3243 pppiThis,
3244 posCount,
3245 pcLetter,
3246 &MBoot.sPrtnInfo[i],
3247 PrDisk,
3248 BmInfo)))
3249 return (arc);
3250
3251 continue;
3252 }
3253
3254 // raise driver letter if OS/2 would recognize this drive
3255 if ( (MBoot.sPrtnInfo[i].bFileSysCode < PAR_PCIX)
3256 )
3257 fAssignLetter = TRUE;
3258
3259 if (fAssignLetter)
3260 (*pcLetter)++;
3261
3262 fBootable = ( (BmInfo)
3263 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
3264 );
3265
3266 if ((arc = AppendPartition(pppiFirst,
3267 pppiThis,
3268 posCount,
3269 PrDisk,
3270 (fBootable)
3271 ? (char*)&MBoot.sBmNames[i].name
3272 : "",
3273 (fAssignLetter)
3274 ? *pcLetter
3275 : ' ',
3276 MBoot.sPrtnInfo[i].bFileSysCode,
3277 FALSE, // primary
3278 fBootable, // bootable
3279 MBoot.sPrtnInfo[i].lTotalSects)))
3280 return (arc);
3281 }
3282 }
3283
3284 return (NO_ERROR);
3285}
3286
3287/*
3288 * GetExtendedPartition:
3289 * this finds the extended partition on the given
3290 * drive and calls GetLogicalDrives in turn.
3291 *
3292 * This gets called from doshGetPartitionsList.
3293 *
3294 * Based on code (C) Dmitry A. Steklenev.
3295 *
3296 *@@added V0.9.0 [umoeller]
3297 */
3298
3299APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
3300 PARTITIONINFO **pppiThis,
3301 PUSHORT posCount,
3302 PCHAR pcLetter,
3303 PAR_INFO* BmInfo,
3304 UINT iDisk) // in: disk to query
3305{
3306 APIRET arc = NO_ERROR;
3307 MBR_INFO MBoot; // Master Boot
3308 USHORT i;
3309
3310 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
3311 return (arc);
3312
3313 // go thru MBR entries to find extended partition
3314 for (i = 0;
3315 i < 4;
3316 i++)
3317 {
3318 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
3319 {
3320 if ((arc = GetLogicalDrives(pppiFirst,
3321 pppiThis,
3322 posCount,
3323 pcLetter,
3324 &MBoot.sPrtnInfo[i],
3325 iDisk,
3326 BmInfo)))
3327 return (arc);
3328 }
3329 }
3330
3331 return (NO_ERROR);
3332}
3333
3334/*
3335 *@@ CleanPartitionInfos:
3336 *
3337 *@@added V0.9.9 (2001-04-07) [umoeller]
3338 */
3339
3340VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
3341{
3342 while (ppiThis)
3343 {
3344 PPARTITIONINFO ppiNext = ppiThis->pNext;
3345 free(ppiThis);
3346 ppiThis = ppiNext;
3347 }
3348}
3349
3350/*
3351 *@@ doshGetPartitionsList:
3352 * this returns lots of information about the
3353 * partitions on all physical disks, which is
3354 * read directly from the MBRs and partition
3355 * tables.
3356 *
3357 * If NO_ERROR is returned by this function,
3358 * *ppPartitionInfo points to a linked list of
3359 * PARTITIONINFO structures, which has
3360 * *pusPartitionCount items.
3361 *
3362 * In that case, use doshFreePartitionsList to
3363 * free the resources allocated by this function.
3364 *
3365 * What this function returns depends on whether
3366 * LVM is installed.
3367 *
3368 * -- If LVM.DLL is found on the LIBPATH, this opens
3369 * the LVM engine and returns the info from the
3370 * LVM engine in the PARTITIONINFO structures.
3371 * The partitions are then sorted by disk in
3372 * ascending order.
3373 *
3374 * -- Otherwise, we parse the partition tables
3375 * manually. The linked list then starts out with
3376 * all the primary partitions, followed by the
3377 * logical drives in the extended partitions.
3378 * This function attempts to guess the correct drive
3379 * letters and stores these with the PARTITIONINFO
3380 * items, but there's no guarantee that this is
3381 * correct. We correctly ignore Linux partitions here
3382 * and give all primary partitions the C: letter, but
3383 * I have no idea what happens with NTFS partitions,
3384 * since I have none.
3385 *
3386 * If an error != NO_ERROR is returned, *pusContext
3387 * will be set to one of the following:
3388 *
3389 * -- 1: boot manager not found
3390 *
3391 * -- 2: primary partitions error
3392 *
3393 * -- 3: secondary partitions error
3394 *
3395 * -- 0: something else.
3396 *
3397 * Based on code (C) Dmitry A. Steklenev.
3398 *
3399 *@@added V0.9.0 [umoeller]
3400 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
3401 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
3402 */
3403
3404APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
3405 PUSHORT pusContext) // out: error context
3406{
3407 APIRET arc = NO_ERROR;
3408
3409 PLVMINFO pLVMInfo = NULL;
3410
3411 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3412 *ppiTemp = NULL;
3413 USHORT cPartitions = 0; // bootable partition count
3414
3415 if (!ppList)
3416 return (ERROR_INVALID_PARAMETER);
3417
3418 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
3419 {
3420 // LVM installed:
3421 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
3422 &pPartitionInfos, // out: partitions array
3423 &cPartitions); // out: partitions count
3424 // copied to output below
3425
3426 if (arc)
3427 {
3428 // error: start over
3429 doshFreeLVMInfo(pLVMInfo);
3430 CleanPartitionInfos(pPartitionInfos);
3431 pPartitionInfos = NULL;
3432 cPartitions = 0;
3433 }
3434 }
3435
3436 if (arc)
3437 {
3438 // LVM not installed, or failed:
3439 // parse partitions manually
3440 PAR_INFO BmInfo; // BootManager partition
3441 USHORT usBmDisk; // BootManager disk
3442 USHORT cDisks = doshQueryDiskCount(); // physical disks count
3443 USHORT i;
3444
3445 CHAR cLetter = 'C'; // first drive letter
3446
3447 // start over
3448 arc = NO_ERROR;
3449
3450 if (cDisks > 8) // Not above 8 disks
3451 cDisks = 8;
3452
3453 // get boot manager disk and info
3454 if ((arc = doshGetBootManager(&usBmDisk,
3455 NULL,
3456 &BmInfo)) != NO_ERROR)
3457 {
3458 *pusContext = 1;
3459 }
3460 else
3461 {
3462 // on each disk, read primary partitions
3463 for (i = 1; i <= cDisks; i++)
3464 {
3465 if ((arc = GetPrimaryPartitions(&pPartitionInfos,
3466 &ppiTemp,
3467 &cPartitions,
3468 &cLetter,
3469 usBmDisk,
3470 usBmDisk ? &BmInfo : 0,
3471 i)))
3472 {
3473 *pusContext = 2;
3474 }
3475 }
3476
3477 if (!arc && usBmDisk)
3478 {
3479 // boot manager found:
3480 // on each disk, read extended partition
3481 // with logical drives
3482 for (i = 1; i <= cDisks; i++)
3483 {
3484 if ((arc = GetExtendedPartition(&pPartitionInfos,
3485 &ppiTemp,
3486 &cPartitions,
3487 &cLetter,
3488 &BmInfo,
3489 i)))
3490 {
3491 *pusContext = 3;
3492 }
3493 }
3494 }
3495 } // end else if ((arc = doshGetBootManager(&usBmDisk,
3496 } // end else if (!doshQueryLVMInfo(&pLVMInfo))
3497
3498 if (!arc)
3499 {
3500 // no error so far:
3501 *pusContext = 0;
3502
3503 *ppList = NEW(PARTITIONSLIST);
3504 if (!(*ppList))
3505 arc = ERROR_NOT_ENOUGH_MEMORY;
3506 else
3507 {
3508 ZERO(*ppList);
3509
3510 (*ppList)->pPartitionInfo = pPartitionInfos;
3511 (*ppList)->cPartitions = cPartitions;
3512
3513 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
3514 }
3515 }
3516
3517 if (arc)
3518 CleanPartitionInfos(pPartitionInfos);
3519
3520 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
3521
3522 return (arc);
3523}
3524
3525/*
3526 *@@ doshFreePartitionsList:
3527 * this frees the resources allocated by
3528 * doshGetPartitionsList.
3529 *
3530 *@@added V0.9.0 [umoeller]
3531 */
3532
3533APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
3534{
3535 if (!ppList)
3536 return (ERROR_INVALID_PARAMETER);
3537 else
3538 {
3539 CleanPartitionInfos(ppList->pPartitionInfo);
3540 doshFreeLVMInfo(ppList->pLVMInfo);
3541 free(ppList);
3542 }
3543
3544 return (NO_ERROR);
3545}
3546
3547/********************************************************************
3548 *
3549 * LVM declarations
3550 *
3551 ********************************************************************/
3552
3553/*
3554 *@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
3555 * functions for transparently interfacing LVM.DLL.
3556 */
3557
3558typedef unsigned char BOOLEAN;
3559typedef unsigned short int CARDINAL16;
3560typedef unsigned long CARDINAL32;
3561typedef unsigned int CARDINAL;
3562typedef unsigned long DoubleWord;
3563
3564#ifdef ADDRESS
3565#undef ADDRESS
3566#endif
3567
3568typedef void* ADDRESS;
3569
3570#pragma pack(1)
3571
3572#define DISK_NAME_SIZE 20
3573#define FILESYSTEM_NAME_SIZE 20
3574#define PARTITION_NAME_SIZE 20
3575#define VOLUME_NAME_SIZE 20
3576
3577/*
3578 *@@ Drive_Control_Record:
3579 * invariant for a disk drive.
3580 *
3581 *@@added V0.9.9 (2001-04-07) [umoeller]
3582 */
3583
3584typedef struct _Drive_Control_Record
3585{
3586 CARDINAL32 Drive_Number; // OS/2 Drive Number for this drive.
3587 CARDINAL32 Drive_Size; // The total number of sectors on the drive.
3588 DoubleWord Drive_Serial_Number; // The serial number assigned to this drive. For info. purposes only.
3589 ADDRESS Drive_Handle; // Handle used for operations on the disk that this record corresponds to.
3590 CARDINAL32 Cylinder_Count; // The number of cylinders on the drive.
3591 CARDINAL32 Heads_Per_Cylinder; // The number of heads per cylinder for this drive.
3592 CARDINAL32 Sectors_Per_Track; // The number of sectors per track for this drive.
3593 BOOLEAN Drive_Is_PRM; // Set to TRUE if this drive is a PRM.
3594 BYTE Reserved[3]; // Alignment.
3595} Drive_Control_Record;
3596
3597/*
3598 *@@ Drive_Control_Array:
3599 * returned by the Get_Drive_Control_Data function
3600 *
3601 *@@added V0.9.9 (2001-04-07) [umoeller]
3602 */
3603
3604typedef struct _Drive_Control_Array
3605{
3606 Drive_Control_Record * Drive_Control_Data; // An array of drive control records.
3607 CARDINAL32 Count; // The number of entries in the array of drive control records.
3608} Drive_Control_Array;
3609
3610/*
3611 *@@ Drive_Information_Record:
3612 * defines the information that can be changed for a specific disk drive.
3613 *
3614 *@@added V0.9.9 (2001-04-07) [umoeller]
3615 */
3616
3617typedef struct _Drive_Information_Record
3618{
3619 CARDINAL32 Total_Available_Sectors; // The number of sectors on the disk which are not currently assigned to a partition.
3620 CARDINAL32 Largest_Free_Block_Of_Sectors; // The number of sectors in the largest contiguous block of available sectors.
3621 BOOLEAN Corrupt_Partition_Table; // If TRUE, then the partitioning information found on the drive is incorrect!
3622 BOOLEAN Unusable; // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
3623 BOOLEAN IO_Error; // If TRUE, then the last I/O operation on this drive failed!
3624 BOOLEAN Is_Big_Floppy; // If TRUE, then the drive is a PRM formatted as a big floppy (i.e. the old style removable media support).
3625 char Drive_Name[DISK_NAME_SIZE]; // User assigned name for this disk drive.
3626} Drive_Information_Record;
3627
3628/*
3629 *@@ Partition_Information_Record:
3630 *
3631 *@@added V0.9.9 (2001-04-07) [umoeller]
3632 */
3633
3634typedef struct _Partition_Information_Record
3635{
3636 ADDRESS Partition_Handle;
3637 // The handle used to perform operations on this partition.
3638 ADDRESS Volume_Handle;
3639 // If this partition is part of a volume, this will be the handle of
3640 // the volume. If this partition is NOT part of a volume, then this
3641 // handle will be 0.
3642 ADDRESS Drive_Handle;
3643 // The handle for the drive this partition resides on.
3644 DoubleWord Partition_Serial_Number;
3645 // The serial number assigned to this partition.
3646 CARDINAL32 Partition_Start;
3647 // The LBA of the first sector of the partition.
3648 CARDINAL32 True_Partition_Size;
3649 // The total number of sectors comprising the partition.
3650 CARDINAL32 Usable_Partition_Size;
3651 // The size of the partition as reported to the IFSM. This is the
3652 // size of the partition less any LVM overhead.
3653 CARDINAL32 Boot_Limit;
3654 // The maximum number of sectors from this block of free space that
3655 // can be used to create a bootable partition if you allocate from the
3656 // beginning of the block of free space.
3657 BOOLEAN Spanned_Volume;
3658 // TRUE if this partition is part of a multi-partition volume.
3659 BOOLEAN Primary_Partition;
3660 // True or False. Any non-zero value here indicates that this partition
3661 // is a primary partition. Zero here indicates that this partition is
3662 // a "logical drive" - i.e. it resides inside of an extended partition.
3663 BYTE Active_Flag;
3664 // 80 = Partition is marked as being active.
3665 // 0 = Partition is not active.
3666 BYTE OS_Flag;
3667 // This field is from the partition table. It is known as the OS flag,
3668 // the Partition Type Field, Filesystem Type, and various other names.
3669 // Values of interest
3670 // If this field is: (values are in hex)
3671 // 07 = The partition is a compatibility partition formatted for use
3672 // with an installable filesystem, such as HPFS or JFS.
3673 // 00 = Unformatted partition
3674 // 01 = FAT12 filesystem is in use on this partition.
3675 // 04 = FAT16 filesystem is in use on this partition.
3676 // 0A = OS/2 Boot Manager Partition
3677 // 35 = LVM partition
3678 // 84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
3679 BYTE Partition_Type;
3680 // 0 = Free Space
3681 // 1 = LVM Partition (Part of an LVM Volume.)
3682 // 2 = Compatibility Partition
3683 // All other values are reserved for future use.
3684 BYTE Partition_Status;
3685 // 0 = Free Space
3686 // 1 = In Use - i.e. already assigned to a volume.
3687 // 2 = Available - i.e. not currently assigned to a volume.
3688 BOOLEAN On_Boot_Manager_Menu;
3689 // Set to TRUE if this partition is not part of a Volume yet is on the
3690 // Boot Manager Menu.
3691 BYTE Reserved;
3692 // Alignment.
3693 char Volume_Drive_Letter;
3694 // The drive letter assigned to the volume that this partition is a part of.
3695 char Drive_Name[DISK_NAME_SIZE];
3696 // User assigned name for this disk drive.
3697 char File_System_Name[FILESYSTEM_NAME_SIZE];
3698 // The name of the filesystem in use on this partition, if it is known.
3699 char Partition_Name[PARTITION_NAME_SIZE];
3700 // The user assigned name for this partition.
3701 char Volume_Name[VOLUME_NAME_SIZE];
3702 // If this partition is part of a volume, then this will be the
3703 // name of the volume that this partition is a part of. If this
3704 // record represents free space, then the Volume_Name will be
3705 // "FREE SPACE xx", where xx is a unique numeric ID generated by
3706 // LVM.DLL. Otherwise it will be an empty string.
3707} Partition_Information_Record;
3708
3709// The following defines are for use with the Partition_Type field in the
3710// Partition_Information_Record.
3711#define FREE_SPACE_PARTITION 0
3712#define LVM_PARTITION 1
3713#define COMPATIBILITY_PARTITION 2
3714
3715// The following defines are for use with the Partition_Status field in the
3716// Partition_Information_Record.
3717#define PARTITION_IS_IN_USE 1
3718#define PARTITION_IS_AVAILABLE 2
3719#define PARTITION_IS_FREE_SPACE 0
3720
3721/*
3722 *@@ Partition_Information_Array:
3723 * returned by various functions in the LVM Engine.
3724 *
3725 *@@added V0.9.9 (2001-04-07) [umoeller]
3726 */
3727
3728typedef struct _Partition_Information_Array
3729{
3730 Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
3731 CARDINAL32 Count; // The number of entries in the Partition_Array.
3732} Partition_Information_Array;
3733
3734/*
3735 *@@ Volume_Information_Record:
3736 * variable information for a volume.
3737 *
3738 *@@added V0.9.9 (2001-04-07) [umoeller]
3739 */
3740
3741typedef struct _Volume_Information_Record
3742{
3743 CARDINAL32 Volume_Size;
3744 // The number of sectors comprising the volume.
3745 CARDINAL32 Partition_Count;
3746 // The number of partitions which comprise this volume.
3747 CARDINAL32 Drive_Letter_Conflict;
3748 // 0 indicates that the drive letter preference for this volume is unique.
3749 // 1 indicates that the drive letter preference for this volume
3750 // is not unique, but this volume got its preferred drive letter anyway.
3751 // 2 indicates that the drive letter preference for this volume
3752 // is not unique, and this volume did NOT get its preferred drive letter.
3753 // 4 indicates that this volume is currently "hidden" - i.e. it has
3754 // no drive letter preference at the current time.
3755 BOOLEAN Compatibility_Volume;
3756 // TRUE if this is for a compatibility volume, FALSE otherwise.
3757 BOOLEAN Bootable;
3758 // Set to TRUE if this volume appears on the Boot Manager menu, or if it is
3759 // a compatibility volume and its corresponding partition is the first active
3760 // primary partition on the first drive.
3761 char Drive_Letter_Preference;
3762 // The drive letter that this volume desires to be.
3763 char Current_Drive_Letter;
3764 // The drive letter currently used to access this volume.
3765 // May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
3766 // is already in use by another volume ).
3767 char Initial_Drive_Letter;
3768 // The drive letter assigned to this volume by the operating system
3769 // when LVM was started. This may be different from the
3770 // Drive_Letter_Preference if there were conflicts, and
3771 // may be different from the Current_Drive_Letter. This
3772 // will be 0x0 if the Volume did not exist when the LVM Engine
3773 // was opened (i.e. it was created during this LVM session).
3774 BOOLEAN New_Volume;
3775 // Set to FALSE if this volume existed before the LVM Engine was
3776 // opened. Set to TRUE if this volume was created after the LVM
3777 // Engine was opened.
3778 BYTE Status;
3779 // 0 = None.
3780 // 1 = Bootable
3781 // 2 = Startable
3782 // 3 = Installable.
3783 BYTE Reserved_1;
3784 char Volume_Name[VOLUME_NAME_SIZE];
3785 // The user assigned name for this volume.
3786 char File_System_Name[FILESYSTEM_NAME_SIZE];
3787 // The name of the filesystem in use on this partition, if it
3788 // is known.
3789} Volume_Information_Record;
3790
3791#pragma pack()
3792
3793/********************************************************************
3794 *
3795 * Quick LVM Interface API
3796 *
3797 ********************************************************************/
3798
3799/*
3800 *@@ LVMINFOPRIVATE:
3801 * private structure used by doshQueryLVMInfo.
3802 * This is what the LVMINFO pointer really
3803 * points to.
3804 *
3805 *@@added V0.9.9 (2001-04-07) [umoeller]
3806 */
3807
3808typedef struct _LVMINFOPRIVATE
3809{
3810 LVMINFO LVMInfo; // public structure (dosh.h)
3811
3812 // function pointers resolved from LVM.DLL
3813
3814 void (* _System Open_LVM_Engine)(BOOLEAN Ignore_CHS,
3815 CARDINAL32 *Error_Code);
3816
3817 void (* _System Free_Engine_Memory)(ADDRESS Object);
3818
3819 void (* _System Close_LVM_Engine)(void);
3820
3821 Drive_Control_Array (* _System
3822 Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
3823
3824 Drive_Information_Record (* _System
3825 Get_Drive_Status)(ADDRESS Drive_Handle,
3826 CARDINAL32 *Error_Code);
3827
3828 Partition_Information_Array (* _System
3829 Get_Partitions)(ADDRESS Handle,
3830 CARDINAL32 *Error_Code);
3831
3832 Volume_Information_Record (*_System
3833 Get_Volume_Information)(ADDRESS Volume_Handle,
3834 CARDINAL32 *Error_Code);
3835
3836} LVMINFOPRIVATE, *PLVMINFOPRIVATE;
3837
3838#define LVM_ERROR_FIRST 20000
3839
3840/*
3841 *@@ doshQueryLVMInfo:
3842 * creates an LVMINFO structure if LVM is installed.
3843 * Returns that structure (which the caller must free
3844 * using doshFreeLVMInfo) or NULL if LVM.DLL was not
3845 * found along the LIBPATH.
3846 *
3847 *@@added V0.9.9 (2001-04-07) [umoeller]
3848 */
3849
3850APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
3851{
3852 APIRET arc = NO_ERROR;
3853 CHAR szError[100];
3854 PLVMINFOPRIVATE pLVMInfo = NULL;
3855 HMODULE hmodLVM = NULLHANDLE;
3856
3857 if (!(arc = DosLoadModule(szError,
3858 sizeof(szError),
3859 "LVM",
3860 &hmodLVM)))
3861 {
3862 // got LVM.DLL:
3863 pLVMInfo = NEW(LVMINFOPRIVATE);
3864 if (!pLVMInfo)
3865 arc = ERROR_NOT_ENOUGH_MEMORY;
3866 else
3867 {
3868 // array of function pointers to be resolved from LVM.DLL
3869 RESOLVEFUNCTION aFunctions[] =
3870 {
3871 "Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
3872 "Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
3873 "Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
3874 "Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
3875 "Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
3876 "Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
3877 "Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
3878 };
3879 ULONG ul;
3880
3881 ZERO(pLVMInfo);
3882
3883 pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
3884
3885 // now resolve function pointers
3886 for (ul = 0;
3887 ul < ARRAYITEMCOUNT(aFunctions);
3888 ul++)
3889 {
3890 PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
3891 arc = DosQueryProcAddr(hmodLVM,
3892 0, // ordinal, ignored
3893 (PSZ)pFuncThis->pcszFunctionName,
3894 pFuncThis->ppFuncAddress);
3895 if (!pFuncThis->ppFuncAddress)
3896 arc = ERROR_INVALID_NAME;
3897
3898 if (arc)
3899 break;
3900 }
3901 }
3902 }
3903
3904 if (arc)
3905 doshFreeLVMInfo((PLVMINFO)pLVMInfo);
3906 else
3907 *ppLVMInfo = (PLVMINFO)pLVMInfo;
3908
3909 return (arc);
3910}
3911
3912/*
3913 *@@ doshReadLVMPartitions:
3914 * using the LVMINFO parameter from doshQueryLVMInfo,
3915 * builds an array of PARTITIONINFO structures with
3916 * the data returned from LVM.DLL.
3917 *
3918 *@@added V0.9.9 (2001-04-07) [umoeller]
3919 */
3920
3921APIRET doshReadLVMPartitions(PLVMINFO pInfo, // in: LVM info
3922 PPARTITIONINFO *ppPartitionInfo, // out: partitions array
3923 PUSHORT pcPartitions) // out: partitions count
3924{
3925 APIRET arc = NO_ERROR;
3926 CARDINAL32 Error = 0;
3927
3928 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3929 *ppiTemp = NULL;
3930 USHORT cPartitions = 0; // bootable partition count
3931 PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
3932
3933 _Pmpf((__FUNCTION__ ": entering"));
3934
3935 if (!pLVMInfo)
3936 return (ERROR_INVALID_PARAMETER);
3937
3938 // initialize LVM engine
3939 pLVMInfo->Open_LVM_Engine(TRUE,
3940 &Error);
3941
3942 _Pmpf((" Open_LVM_Engine Error: %d"));
3943
3944 if (!Error)
3945 {
3946 Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
3947 // member records to be freed
3948
3949 _Pmpf((" Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
3950
3951 if ( (!Error)
3952 && (DCA.Count)
3953 )
3954 {
3955 // DCA.Drive_Control_Data now contains drive information records;
3956 // this must be freed
3957 ULONG ulDisk;
3958
3959 for (ulDisk = 0;
3960 ulDisk < DCA.Count;
3961 ulDisk++)
3962 {
3963 Drive_Control_Record *pDriveControlRecord
3964 = &DCA.Drive_Control_Data[ulDisk];
3965 ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
3966
3967 Drive_Information_Record pDriveInfoRecord
3968 = pLVMInfo->Get_Drive_Status(hDrive,
3969 &Error);
3970
3971 _Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
3972
3973 if (!Error)
3974 {
3975 Partition_Information_Array PIA
3976 = pLVMInfo->Get_Partitions(hDrive,
3977 &Error);
3978
3979 _Pmpf((" Get_Partitions Error: %d", Error));
3980
3981 if (!Error)
3982 {
3983 // PIA.Partition_Array now contains
3984 // Partition_Information_Record; must be freed
3985
3986 // now go thru partitions of this drive
3987 ULONG ulPart;
3988 for (ulPart = 0;
3989 ulPart < PIA.Count;
3990 ulPart++)
3991 {
3992 Partition_Information_Record *pPartition
3993 = &PIA.Partition_Array[ulPart];
3994 Volume_Information_Record VolumeInfo;
3995
3996 const char *pcszBootName = NULL; // for now
3997 BOOL fBootable = FALSE;
3998
3999 if (pPartition->Volume_Handle)
4000 {
4001 // this partition is part of a volume:
4002 // only then can it be bootable...
4003 // get the volume info
4004 VolumeInfo
4005 = pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
4006 &Error);
4007 pcszBootName = VolumeInfo.Volume_Name;
4008
4009 fBootable = (VolumeInfo.Status == 1);
4010 }
4011
4012
4013 if (arc = AppendPartition(&pPartitionInfos,
4014 &ppiTemp,
4015 &cPartitions,
4016 ulDisk + 1,
4017 pcszBootName,
4018 pPartition->Volume_Drive_Letter,
4019 pPartition->OS_Flag, // FS type
4020 pPartition->Primary_Partition,
4021 fBootable,
4022 pPartition->True_Partition_Size))
4023 break;
4024 }
4025
4026 // clean up partitions
4027 pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
4028 }
4029 }
4030 else
4031 // error:
4032 break;
4033 }
4034
4035 // clean up drive data
4036 pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
4037 }
4038 }
4039
4040 // close LVM
4041 pLVMInfo->Close_LVM_Engine();
4042
4043 if (Error)
4044 {
4045 // if we got an error, return it with the
4046 // LVM error offset
4047 arc = LVM_ERROR_FIRST + Error;
4048
4049 CleanPartitionInfos(pPartitionInfos);
4050 }
4051
4052 if (!arc)
4053 {
4054 *ppPartitionInfo = pPartitionInfos;
4055 *pcPartitions = cPartitions;
4056 }
4057
4058 _Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
4059
4060 return (arc);
4061}
4062
4063/*
4064 *@@ doshFreeLVMInfo:
4065 *
4066 *@@added V0.9.9 (2001-04-07) [umoeller]
4067 */
4068
4069VOID doshFreeLVMInfo(PLVMINFO pInfo)
4070{
4071 if (pInfo)
4072 {
4073 if (pInfo->hmodLVM)
4074 DosFreeModule(pInfo->hmodLVM);
4075
4076 free(pInfo);
4077 }
4078}
Note: See TracBrowser for help on using the repository browser.