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

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

Sources for V0.9.9, plus a couple of fixes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 128.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: specified file
800 * has no DOS EXE header, or it does, but
801 * the extended header is neither LX nor
802 * NE nor PE.
803 *
804 * -- ERROR_INVALID_PARAMETER: ppExec is NULL.
805 *
806 * plus those of DosOpen, DosSetFilePtr, and
807 * DosRead.
808 *
809 *@@added V0.9.0 [umoeller]
810 *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
811 *@@changed V0.9.7 (2000-12-20) [lafaix]: fixed ulNewHeaderOfs
812 *@@changed V0.9.10 (2001-04-08) [umoeller]: now setting ppExec only if NO_ERROR is returned
813 */
814
815APIRET doshExecOpen(const char* pcszExecutable,
816 PEXECUTABLE* ppExec)
817{
818 APIRET arc = NO_ERROR;
819
820 ULONG ulAction = 0;
821 HFILE hFile;
822 PEXECUTABLE pExec = NULL;
823
824 if (!ppExec)
825 return (ERROR_INVALID_PARAMETER);
826
827 pExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE));
828 if (!(pExec))
829 return (ERROR_NOT_ENOUGH_MEMORY);
830
831 memset(pExec, 0, sizeof(EXECUTABLE));
832
833 if (!(arc = DosOpen((PSZ)pcszExecutable,
834 &hFile,
835 &ulAction, // out: action taken
836 0, // in: new file (ignored for read-mode)
837 0, // in: new file attribs (ignored)
838 // open-flags
839 OPEN_ACTION_FAIL_IF_NEW
840 | OPEN_ACTION_OPEN_IF_EXISTS,
841 // open-mode
842 OPEN_FLAGS_FAIL_ON_ERROR // report errors to caller
843 | OPEN_FLAGS_SEQUENTIAL
844 | OPEN_FLAGS_NOINHERIT
845 | OPEN_SHARE_DENYNONE
846 | OPEN_ACCESS_READONLY, // read-only mode
847 NULL))) // no EAs
848 {
849 // file opened successfully:
850
851 ULONG ulLocal = 0;
852
853 // read old DOS EXE header
854 pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
855 if (!(pExec->pDosExeHeader))
856 arc = ERROR_NOT_ENOUGH_MEMORY;
857 else
858 {
859 ULONG ulBytesRead = 0;
860
861 if ( (!(arc = DosSetFilePtr(hFile,
862 0L,
863 FILE_BEGIN,
864 &ulLocal))) // out: new offset
865 && (!(arc = DosRead(hFile,
866 pExec->pDosExeHeader,
867 sizeof(DOSEXEHEADER),
868 &(pExec->cbDosExeHeader))))
869 )
870 {
871 // now check if we really have a DOS header
872 if (pExec->pDosExeHeader->usDosExeID != 0x5a4d)
873 arc = ERROR_INVALID_EXE_SIGNATURE;
874 else
875 {
876 // we have a DOS header:
877 if (pExec->pDosExeHeader->usRelocTableOfs < 0x40)
878 {
879 // neither LX nor PE nor NE:
880 pExec->ulOS = EXEOS_DOS3;
881 pExec->ulExeFormat = EXEFORMAT_OLDDOS;
882 }
883 else
884 {
885 // either LX or PE or NE:
886 // read more bytes from position specified in header
887 CHAR achNewHeaderType[2] = "";
888
889 if ( (!(arc = DosSetFilePtr(hFile,
890 pExec->pDosExeHeader->ulNewHeaderOfs,
891 FILE_BEGIN,
892 &ulLocal)))
893 // read two chars to find out header type
894 && (!(arc = DosRead(hFile,
895 &achNewHeaderType,
896 sizeof(achNewHeaderType),
897 &ulBytesRead)))
898 )
899 {
900 PBYTE pbCheckOS = NULL;
901
902 // reset file ptr
903 DosSetFilePtr(hFile,
904 pExec->pDosExeHeader->ulNewHeaderOfs,
905 FILE_BEGIN,
906 &ulLocal);
907
908 if (memcmp(achNewHeaderType, "NE", 2) == 0)
909 {
910 // New Executable:
911 pExec->ulExeFormat = EXEFORMAT_NE;
912 // allocate NE header
913 pExec->pNEHeader = (PNEHEADER)malloc(sizeof(NEHEADER));
914 if (!(pExec->pNEHeader))
915 arc = ERROR_NOT_ENOUGH_MEMORY;
916 else
917 // read in NE header
918 if (!(arc = DosRead(hFile,
919 pExec->pNEHeader,
920 sizeof(NEHEADER),
921 &(pExec->cbNEHeader))))
922 if (pExec->cbNEHeader == sizeof(NEHEADER))
923 pbCheckOS = &(pExec->pNEHeader->bTargetOS);
924 }
925 else if ( (memcmp(achNewHeaderType, "LX", 2) == 0)
926 || (memcmp(achNewHeaderType, "LE", 2) == 0)
927 // this is used by SMARTDRV.EXE
928 )
929 {
930 // OS/2 Linear Executable:
931 pExec->ulExeFormat = EXEFORMAT_LX;
932 // allocate LX header
933 pExec->pLXHeader = (PLXHEADER)malloc(sizeof(LXHEADER));
934 if (!(pExec->pLXHeader))
935 arc = ERROR_NOT_ENOUGH_MEMORY;
936 else
937 // read in LX header
938 if (!(arc = DosRead(hFile,
939 pExec->pLXHeader,
940 sizeof(LXHEADER),
941 &(pExec->cbLXHeader))))
942 if (pExec->cbLXHeader == sizeof(LXHEADER))
943 pbCheckOS = (PBYTE)(&(pExec->pLXHeader->usTargetOS));
944 }
945 else if (memcmp(achNewHeaderType, "PE", 2) == 0)
946 {
947 pExec->ulExeFormat = EXEFORMAT_PE;
948 pExec->ulOS = EXEOS_WIN32;
949 pExec->f32Bits = TRUE;
950
951 // can't parse this yet
952 }
953 else
954 // strange type:
955 arc = ERROR_INVALID_EXE_SIGNATURE;
956
957 if (pbCheckOS)
958 // BYTE to check for operating system
959 // (NE and LX):
960 switch (*pbCheckOS)
961 {
962 case NEOS_OS2:
963 pExec->ulOS = EXEOS_OS2;
964 if (pExec->ulExeFormat == EXEFORMAT_LX)
965 pExec->f32Bits = TRUE;
966 break;
967
968 case NEOS_WIN16:
969 pExec->ulOS = EXEOS_WIN16;
970 break;
971
972 case NEOS_DOS4:
973 pExec->ulOS = EXEOS_DOS4;
974 break;
975
976 case NEOS_WIN386:
977 pExec->ulOS = EXEOS_WIN386;
978 pExec->f32Bits = TRUE;
979 break;
980 }
981 } // end if (!(arc = DosSetFilePtr(hFile,
982 }
983 }
984 } // end if (!(arc = DosSetFilePtr(hFile,
985 } // end if pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
986
987 // store exec's HFILE
988 pExec->hfExe = hFile;
989 } // end if (!(arc = DosOpen((PSZ)pcszExecutable,
990
991 if (arc != NO_ERROR)
992 // error: clean up
993 doshExecClose(pExec);
994 else
995 *ppExec = pExec;
996
997 return (arc);
998}
999
1000/*
1001 *@@ doshExecClose:
1002 * this closes an executable opened with doshExecOpen.
1003 * Always call this function if NO_ERROR was returned by
1004 * doshExecOpen.
1005 *
1006 *@@added V0.9.0 [umoeller]
1007 */
1008
1009APIRET doshExecClose(PEXECUTABLE pExec)
1010{
1011 APIRET arc = NO_ERROR;
1012 if (pExec)
1013 {
1014 if (pExec->pDosExeHeader)
1015 free(pExec->pDosExeHeader);
1016 if (pExec->pNEHeader)
1017 free(pExec->pNEHeader);
1018 if (pExec->pLXHeader)
1019 free(pExec->pLXHeader);
1020
1021 if (pExec->pszDescription)
1022 free(pExec->pszDescription);
1023 if (pExec->pszVendor)
1024 free(pExec->pszVendor);
1025 if (pExec->pszVersion)
1026 free(pExec->pszVersion);
1027 if (pExec->pszInfo)
1028 free(pExec->pszInfo);
1029
1030 if (pExec->hfExe)
1031 arc = DosClose(pExec->hfExe);
1032
1033 free(pExec);
1034 }
1035 else
1036 arc = ERROR_INVALID_PARAMETER;
1037
1038 return (arc);
1039}
1040
1041/*
1042 *@@ doshExecQueryBldLevel:
1043 * this retrieves buildlevel information for an
1044 * LX or NE executable previously opened with
1045 * doshExecOpen.
1046 *
1047 * BuildLevel information must be contained in the
1048 * DESCRIPTION field of an executable's module
1049 * definition (.DEF) file. In order to be readable
1050 * by BLDLEVEL.EXE (which ships with OS/2), this
1051 * string must have the following format:
1052 *
1053 + Description '@#AUTHOR:VERSION#@ DESCRIPTION'
1054 *
1055 * Example:
1056 *
1057 + Description '@#Ulrich M”ller:0.9.0#@ XWorkplace Sound Support Module'
1058 *
1059 * The "Description" entry always ends up as the
1060 * very first entry in the non-resident name table
1061 * in LX and NE executables. So this is what we retrieve
1062 * here.
1063 *
1064 * If the first entry in that table exists, NO_ERROR is
1065 * returned and at least the pszDescription field in
1066 * EXECUTABLE is set to that information.
1067 *
1068 * If that string is in IBM BLDLEVEL format, the string
1069 * is automatically parsed, and the pszVendor, pszVersion,
1070 * and pszInfo fields are also set. In the above examples,
1071 * this would return the following information:
1072 + pszVendor = "Ulrich M”ller"
1073 + pszVersion = "0.9.0"
1074 + pszInfo = "XWorkplace Sound Support Module"
1075 *
1076 * If that string is not in BLDLEVEL format, only pszDescription
1077 * will be set. The other fields remain NULL.
1078 *
1079 * This returns the following errors:
1080 *
1081 * -- ERROR_INVALID_PARAMETER: pExec invalid
1082 *
1083 * -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in LX or NE format
1084 *
1085 * -- ERROR_INVALID_DATA (13): non-resident name table not found.
1086 *
1087 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
1088 *
1089 * plus the error codes of DosSetFilePtr and DosRead.
1090 *
1091 *@@added V0.9.0 [umoeller]
1092 *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
1093 *@@changed V0.9.1 (99-12-06): fixed memory leak
1094 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
1095 */
1096
1097APIRET doshExecQueryBldLevel(PEXECUTABLE pExec)
1098{
1099 APIRET arc = NO_ERROR;
1100
1101 if (!pExec)
1102 arc = ERROR_INVALID_PARAMETER;
1103 else
1104 {
1105 ULONG ulNRNTOfs = 0;
1106
1107 if (pExec->ulExeFormat == EXEFORMAT_LX)
1108 {
1109 // OK, LX format:
1110 // check if we have a non-resident name table
1111 if (pExec->pLXHeader == NULL)
1112 arc = ERROR_INVALID_DATA;
1113 else if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
1114 arc = ERROR_INVALID_DATA;
1115 else
1116 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
1117 }
1118 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1119 {
1120 // OK, NE format:
1121 // check if we have a non-resident name table
1122 if (pExec->pNEHeader == NULL)
1123 arc = ERROR_INVALID_DATA;
1124 else if (pExec->pNEHeader->ulNonResdTblOfs == 0)
1125 arc = ERROR_INVALID_DATA;
1126 else
1127 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
1128 }
1129 else
1130 // neither LX nor NE: stop
1131 arc = ERROR_INVALID_EXE_SIGNATURE;
1132
1133 if ( (!arc)
1134 && (ulNRNTOfs)
1135 )
1136 {
1137 ULONG ulLocal = 0,
1138 ulBytesRead = 0;
1139
1140 // move EXE file pointer to offset of non-resident name table
1141 // (from LX header)
1142 if (!(arc = DosSetFilePtr(pExec->hfExe, // file is still open
1143 ulNRNTOfs, // ofs determined above
1144 FILE_BEGIN,
1145 &ulLocal)))
1146 {
1147 // allocate memory as necessary
1148 PSZ pszNameTable = (PSZ)malloc(2001); // should suffice, because each entry
1149 // may only be 255 bytes in length
1150 if (!pszNameTable)
1151 arc = ERROR_NOT_ENOUGH_MEMORY;
1152 else
1153 {
1154 if (!(arc = DosRead(pExec->hfExe,
1155 pszNameTable,
1156 2000,
1157 &ulBytesRead)))
1158 {
1159 if (*pszNameTable == 0)
1160 // first byte (length byte) is null:
1161 arc = ERROR_INVALID_DATA;
1162 else
1163 {
1164 // now copy the string, which is in Pascal format
1165 pExec->pszDescription = (PSZ)malloc((*pszNameTable) + 1); // addt'l null byte
1166 if (!pExec->pszDescription)
1167 arc = ERROR_NOT_ENOUGH_MEMORY;
1168 else
1169 {
1170 const char *pStartOfAuthor = 0,
1171 *pStartOfVendor = 0;
1172
1173 memcpy(pExec->pszDescription,
1174 pszNameTable + 1, // skip length byte
1175 *pszNameTable); // length byte
1176 // terminate string
1177 *(pExec->pszDescription + (*pszNameTable)) = 0;
1178
1179 // now parse the damn thing:
1180 // @#VENDOR:VERSION#@ DESCRIPTION
1181 // but skip the first byte, which has the string length
1182 pStartOfVendor = strstr(pExec->pszDescription,
1183 "@#");
1184 if (pStartOfVendor)
1185 {
1186 const char *pStartOfInfo = strstr(pStartOfVendor + 2,
1187 "#@");
1188 if (pStartOfInfo)
1189 {
1190 PSZ pEndOfVendor = strchr(pStartOfVendor + 2,
1191 ':');
1192 if (pEndOfVendor)
1193 {
1194 pExec->pszVendor = strhSubstr(pStartOfVendor + 2,
1195 pEndOfVendor);
1196 pExec->pszVersion = strhSubstr(pEndOfVendor + 1,
1197 pStartOfInfo);
1198 // skip "@#" in info string
1199 pStartOfInfo += 2;
1200 // skip leading spaces in info string
1201 while (*pStartOfInfo == ' ')
1202 pStartOfInfo++;
1203 if (*pStartOfInfo) // V0.9.9 (2001-04-04) [umoeller]
1204 // and copy until end of string
1205 pExec->pszInfo = strdup(pStartOfInfo);
1206 }
1207 }
1208 }
1209 }
1210 }
1211 }
1212
1213 free(pszNameTable);
1214 } // end if PSZ pszNameTable = (PSZ)malloc(2001);
1215 }
1216 }
1217 } // end if (!pExec)
1218
1219 return (arc);
1220}
1221
1222/*
1223 *@@ doshExecQueryImportedModules:
1224 * returns an array of FSYSMODULE structure describing all
1225 * imported modules.
1226 *
1227 * *pcModules receives the # of items in the array (not the
1228 * array size!). Use doshFreeImportedModules to clean up.
1229 *
1230 * This returns a standard OS/2 error code, which might be
1231 * any of the codes returned by DosSetFilePtr and DosRead.
1232 * In addition, this may return:
1233 *
1234 * -- ERROR_NOT_ENOUGH_MEMORY
1235 *
1236 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1237 * than LX or NE, which is not understood by this function.
1238 *
1239 * Even if NO_ERROR is returned, the array pointer might still
1240 * be NULL if the module contains no such data.
1241 *
1242 *@@added V0.9.9 (2001-03-11) [lafaix]
1243 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1244 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1245 */
1246
1247APIRET doshExecQueryImportedModules(PEXECUTABLE pExec,
1248 PFSYSMODULE *ppaModules, // out: modules array
1249 PULONG pcModules) // out: array item count
1250{
1251 if ( (pExec)
1252 && (pExec->ulOS == EXEOS_OS2)
1253 )
1254 {
1255 ENSURE_BEGIN;
1256 ULONG cModules = 0;
1257 PFSYSMODULE paModules = NULL;
1258 int i;
1259
1260 if (pExec->ulExeFormat == EXEFORMAT_LX)
1261 {
1262 // 32-bit OS/2 executable:
1263 cModules = pExec->pLXHeader->ulImportModTblCnt;
1264
1265 if (cModules)
1266 {
1267 ULONG cb = sizeof(FSYSMODULE) * cModules; // V0.9.9 (2001-04-03) [umoeller]
1268 ULONG ulDummy;
1269
1270 paModules = (PFSYSMODULE)malloc(cb);
1271 if (!paModules)
1272 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1273
1274 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1275
1276 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1277 pExec->pLXHeader->ulImportModTblOfs
1278 + pExec->pDosExeHeader->ulNewHeaderOfs,
1279 FILE_BEGIN,
1280 &ulDummy));
1281
1282 for (i = 0; i < cModules; i++)
1283 {
1284 BYTE bLen = 0;
1285
1286 // reading the length of the module name
1287 ENSURE_SAFE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1288
1289 // At most 127 bytes
1290 bLen &= 0x7F;
1291
1292 // reading the module name
1293 ENSURE_SAFE(DosRead(pExec->hfExe,
1294 paModules[i].achModuleName,
1295 bLen,
1296 &ulDummy));
1297
1298 // module names are not null terminated, so we must
1299 // do it now
1300 paModules[i].achModuleName[bLen] = 0;
1301 } // end for
1302 }
1303 } // end LX
1304 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1305 {
1306 // 16-bit OS/2 executable:
1307 cModules = pExec->pNEHeader->usModuleTblEntries;
1308
1309 if (cModules)
1310 {
1311 ULONG cb = sizeof(FSYSMODULE) * cModules;
1312
1313 paModules = (PFSYSMODULE)malloc(cb);
1314 if (!paModules)
1315 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1316
1317 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1318
1319 for (i = 0; i < cModules; i ++)
1320 {
1321 BYTE bLen;
1322 USHORT usOfs;
1323 ULONG ulDummy;
1324
1325 // the module reference table contains offsets
1326 // relative to the import table; we hence read
1327 // the offset in the module reference table, and
1328 // then we read the name in the import table
1329
1330 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1331 pExec->pNEHeader->usModRefTblOfs
1332 + pExec->pDosExeHeader->ulNewHeaderOfs
1333 + sizeof(usOfs) * i,
1334 FILE_BEGIN,
1335 &ulDummy));
1336
1337 ENSURE_SAFE(DosRead(pExec->hfExe, &usOfs, 2, &ulDummy));
1338
1339 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1340 pExec->pNEHeader->usImportTblOfs
1341 + pExec->pDosExeHeader->ulNewHeaderOfs
1342 + usOfs,
1343 FILE_BEGIN,
1344 &ulDummy));
1345
1346 ENSURE_SAFE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1347
1348 bLen &= 0x7F;
1349
1350 ENSURE_SAFE(DosRead(pExec->hfExe,
1351 paModules[i].achModuleName,
1352 bLen,
1353 &ulDummy));
1354
1355 paModules[i].achModuleName[bLen] = 0;
1356 } // end for
1357 }
1358 }
1359 else
1360 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1361
1362 // no error: output data
1363 *ppaModules = paModules;
1364 *pcModules = cModules;
1365
1366 ENSURE_FINALLY;
1367 // if we had an error above, clean up
1368 free(paModules);
1369 ENSURE_END;
1370 }
1371 else
1372 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1373
1374 ENSURE_OK;
1375}
1376
1377/*
1378 *@@ doshExecFreeImportedModules:
1379 * frees resources allocated by doshExecQueryImportedModules.
1380 *
1381 *@@added V0.9.9 (2001-03-11)
1382 */
1383
1384APIRET doshExecFreeImportedModules(PFSYSMODULE paModules)
1385{
1386 free(paModules);
1387 return (NO_ERROR);
1388}
1389
1390/*
1391 *@@ ScanLXEntryTable:
1392 * returns the number of exported entries in the entry table.
1393 *
1394 * If paFunctions is not NULL, then successive entries are
1395 * filled with the found type and ordinal values.
1396 *
1397 *@@added V0.9.9 (2001-03-30) [lafaix]
1398 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1399 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1400 */
1401
1402APIRET ScanLXEntryTable(PEXECUTABLE pExec,
1403 PFSYSFUNCTION paFunctions,
1404 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1405{
1406 ULONG ulDummy;
1407 USHORT usOrdinal = 1,
1408 usCurrent = 0;
1409 int i;
1410
1411 ENSURE(DosSetFilePtr(pExec->hfExe,
1412 pExec->pLXHeader->ulEntryTblOfs
1413 + pExec->pDosExeHeader->ulNewHeaderOfs,
1414 FILE_BEGIN,
1415 &ulDummy));
1416
1417 while (TRUE)
1418 {
1419 BYTE bCnt,
1420 bType,
1421 bFlag;
1422
1423 ENSURE(DosRead(pExec->hfExe, &bCnt, 1, &ulDummy));
1424
1425 if (bCnt == 0)
1426 // end of the entry table
1427 break;
1428
1429 ENSURE(DosRead(pExec->hfExe, &bType, 1, &ulDummy));
1430
1431 switch (bType & 0x7F)
1432 {
1433 /*
1434 * unused entries
1435 *
1436 */
1437
1438 case 0:
1439 usOrdinal += bCnt;
1440 break;
1441
1442 /*
1443 * 16-bit entries
1444 *
1445 * the bundle type is followed by the object number
1446 * and by bCnt bFlag+usOffset entries
1447 *
1448 */
1449
1450 case 1:
1451 ENSURE(DosSetFilePtr(pExec->hfExe,
1452 sizeof(USHORT),
1453 FILE_CURRENT,
1454 &ulDummy));
1455
1456 for (i = 0; i < bCnt; i ++)
1457 {
1458 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1459
1460 if (bFlag & 0x01)
1461 {
1462 if (paFunctions)
1463 {
1464 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1465 paFunctions[usCurrent].ulType = 1;
1466 paFunctions[usCurrent].achFunctionName[0] = 0;
1467 }
1468 usCurrent++;
1469 }
1470
1471 usOrdinal++;
1472
1473 ENSURE(DosSetFilePtr(pExec->hfExe,
1474 sizeof(USHORT),
1475 FILE_CURRENT,
1476 &ulDummy));
1477
1478 } // end for
1479 break;
1480
1481 /*
1482 * 286 call gate entries
1483 *
1484 * the bundle type is followed by the object number
1485 * and by bCnt bFlag+usOffset+usCallGate entries
1486 *
1487 */
1488
1489 case 2:
1490 ENSURE(DosSetFilePtr(pExec->hfExe,
1491 sizeof(USHORT),
1492 FILE_CURRENT,
1493 &ulDummy));
1494
1495 for (i = 0; i < bCnt; i ++)
1496 {
1497 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1498
1499 if (bFlag & 0x01)
1500 {
1501 if (paFunctions)
1502 {
1503 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1504 paFunctions[usCurrent].ulType = 2;
1505 paFunctions[usCurrent].achFunctionName[0] = 0;
1506 }
1507 usCurrent++;
1508 }
1509
1510 usOrdinal++;
1511
1512 ENSURE(DosSetFilePtr(pExec->hfExe,
1513 sizeof(USHORT) + sizeof(USHORT),
1514 FILE_CURRENT,
1515 &ulDummy));
1516
1517 } // end for
1518 break;
1519
1520 /*
1521 * 32-bit entries
1522 *
1523 * the bundle type is followed by the object number
1524 * and by bCnt bFlag+ulOffset entries
1525 *
1526 */
1527
1528 case 3:
1529 ENSURE(DosSetFilePtr(pExec->hfExe,
1530 sizeof(USHORT),
1531 FILE_CURRENT,
1532 &ulDummy));
1533
1534 for (i = 0; i < bCnt; i ++)
1535 {
1536 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1537
1538 if (bFlag & 0x01)
1539 {
1540 if (paFunctions)
1541 {
1542 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1543 paFunctions[usCurrent].ulType = 3;
1544 paFunctions[usCurrent].achFunctionName[0] = 0;
1545 }
1546 usCurrent++;
1547 }
1548
1549 usOrdinal++;
1550
1551 ENSURE(DosSetFilePtr(pExec->hfExe,
1552 sizeof(ULONG),
1553 FILE_CURRENT,
1554 &ulDummy));
1555 } // end for
1556 break;
1557
1558 /*
1559 * forwarder entries
1560 *
1561 * the bundle type is followed by a reserved word
1562 * and by bCnt bFlag+usModOrd+ulOffsOrdNum entries
1563 *
1564 */
1565
1566 case 4:
1567 ENSURE(DosSetFilePtr(pExec->hfExe,
1568 sizeof(USHORT),
1569 FILE_CURRENT,
1570 &ulDummy));
1571
1572 for (i = 0; i < bCnt; i ++)
1573 {
1574 ENSURE(DosSetFilePtr(pExec->hfExe,
1575 sizeof(BYTE) + sizeof(USHORT) + sizeof(ULONG),
1576 FILE_CURRENT,
1577 &ulDummy));
1578
1579 if (paFunctions)
1580 {
1581 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1582 paFunctions[usCurrent].ulType = 4;
1583 paFunctions[usCurrent].achFunctionName[0] = 0;
1584 }
1585 usCurrent++;
1586
1587 usOrdinal++;
1588 } // end for
1589 break;
1590
1591 /*
1592 * unknown bundle type
1593 *
1594 * we don't know how to handle this bundle, so we must
1595 * stop parsing the entry table here (as we don't know the
1596 * bundle size); if paFunctions is not null, we fill it with
1597 * informative data
1598 */
1599
1600 default:
1601 if (paFunctions)
1602 {
1603 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1604 paFunctions[usCurrent].ulType = bType;
1605 sprintf(paFunctions[usCurrent].achFunctionName,
1606 "Unknown bundle type encountered (%d). Aborting entry table scan.",
1607 bType);
1608
1609 usCurrent++;
1610 }
1611 ENSURE_FAIL(ERROR_INVALID_LIST_FORMAT);
1612 // whatever
1613 // V0.9.9 (2001-04-03) [umoeller]
1614 } // end switch (bType & 0x7F)
1615 } // end while (TRUE)
1616
1617 if (pcEntries)
1618 *pcEntries = usCurrent;
1619
1620 ENSURE_OK;
1621}
1622
1623/*
1624 *@@ ScanNEEntryTable:
1625 * returns the number of exported entries in the entry table.
1626 *
1627 * if paFunctions is not NULL, then successive entries are
1628 * filled with the found type and ordinal values.
1629 *
1630 *@@added V0.9.9 (2001-03-30) [lafaix]
1631 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1632 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1633 */
1634
1635APIRET ScanNEEntryTable(PEXECUTABLE pExec,
1636 PFSYSFUNCTION paFunctions,
1637 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1638{
1639 ULONG ulDummy;
1640 USHORT usOrdinal = 1,
1641 usCurrent = 0;
1642 int i;
1643
1644 ENSURE(DosSetFilePtr(pExec->hfExe,
1645 pExec->pNEHeader->usEntryTblOfs
1646 + pExec->pDosExeHeader->ulNewHeaderOfs,
1647 FILE_BEGIN,
1648 &ulDummy));
1649
1650 while (TRUE)
1651 {
1652 BYTE bCnt,
1653 bType,
1654 bFlag;
1655
1656 ENSURE(DosRead(pExec->hfExe, &bCnt, 1, &ulDummy));
1657
1658 if (bCnt == 0)
1659 // end of the entry table
1660 break;
1661
1662 ENSURE(DosRead(pExec->hfExe, &bType, 1, &ulDummy));
1663
1664 for (i = 0; i < bCnt; i++)
1665 {
1666 ENSURE(DosRead(pExec->hfExe,
1667 &bFlag,
1668 1,
1669 &ulDummy));
1670
1671 if (bFlag & 0x01)
1672 {
1673 if (paFunctions)
1674 {
1675 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1676 paFunctions[usCurrent].ulType = 1; // 16-bit entry
1677 paFunctions[usCurrent].achFunctionName[0] = 0;
1678 }
1679 usCurrent++;
1680 }
1681
1682 usOrdinal++;
1683
1684 if (bType == 0xFF)
1685 {
1686 // moveable segment
1687 ENSURE(DosSetFilePtr(pExec->hfExe,
1688 5,
1689 FILE_CURRENT,
1690 &ulDummy));
1691 }
1692 else
1693 {
1694 // fixed segment
1695 ENSURE(DosSetFilePtr(pExec->hfExe,
1696 2,
1697 FILE_CURRENT,
1698 &ulDummy));
1699 }
1700
1701 } // end for
1702 } // end while (TRUE)
1703
1704 if (pcEntries)
1705 *pcEntries = usCurrent;
1706
1707 ENSURE_OK;
1708}
1709
1710/*
1711 *@@ Compare:
1712 * binary search helper
1713 *
1714 *@@added V0.9.9 (2001-04-01) [lafaix]
1715 *@@changed V0.9.9 (2001-04-07) [umoeller]: added _Optlink, or this won't compile as C++
1716 */
1717
1718int _Optlink Compare(const void *key,
1719 const void *element)
1720{
1721 USHORT usOrdinal = *((PUSHORT) key);
1722 PFSYSFUNCTION pFunction = (PFSYSFUNCTION)element;
1723
1724 if (usOrdinal > pFunction->ulOrdinal)
1725 return (1);
1726 else if (usOrdinal < pFunction->ulOrdinal)
1727 return (-1);
1728 else
1729 return (0);
1730}
1731
1732/*
1733 *@@ ScanNameTable:
1734 * scans a resident or non-resident name table, and fills the
1735 * appropriate paFunctions entries when it encounters exported
1736 * entries names.
1737 *
1738 * This functions works for both NE and LX executables.
1739 *
1740 *@@added V0.9.9 (2001-03-30) [lafaix]
1741 *@@changed V0.9.9 (2001-04-02) [lafaix]: the first entry is special
1742 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1743 *@@changed V0.9.9 (2001-04-05) [lafaix]: removed the 127 char limit
1744 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1745 */
1746
1747APIRET ScanNameTable(PEXECUTABLE pExec,
1748 ULONG cFunctions,
1749 PFSYSFUNCTION paFunctions)
1750{
1751 ULONG ulDummy;
1752
1753 USHORT usOrdinal;
1754 PFSYSFUNCTION pFunction;
1755
1756 while (TRUE)
1757 {
1758 BYTE bLen;
1759 CHAR achName[256];
1760 int i;
1761
1762 ENSURE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1763
1764 if (bLen == 0)
1765 // end of the name table
1766 break;
1767
1768 ENSURE(DosRead(pExec->hfExe, &achName, bLen, &ulDummy));
1769 achName[bLen] = 0;
1770
1771 ENSURE(DosRead(pExec->hfExe, &usOrdinal, sizeof(USHORT), &ulDummy));
1772
1773 if ((pFunction = (PFSYSFUNCTION)bsearch(&usOrdinal,
1774 paFunctions,
1775 cFunctions,
1776 sizeof(FSYSFUNCTION),
1777 Compare)))
1778 {
1779 memcpy(pFunction->achFunctionName,
1780 achName,
1781 bLen+1);
1782 }
1783 }
1784
1785 ENSURE_OK;
1786}
1787
1788/*
1789 *@@ doshExecQueryExportedFunctions:
1790 * returns an array of FSYSFUNCTION structure describing all
1791 * exported functions.
1792 *
1793 * *pcFunctions receives the # of items in the array (not the
1794 * array size!). Use doshFreeExportedFunctions to clean up.
1795 *
1796 * Note that the returned array only contains entry for exported
1797 * functions. Empty export entries are _not_ included.
1798 *
1799 * This returns a standard OS/2 error code, which might be
1800 * any of the codes returned by DosSetFilePtr and DosRead.
1801 * In addition, this may return:
1802 *
1803 * -- ERROR_NOT_ENOUGH_MEMORY
1804 *
1805 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1806 * than LX or NE, which is not understood by this function.
1807 *
1808 * -- If ERROR_INVALID_LIST_FORMAT is returned, the format of an
1809 * export entry wasn't understood here.
1810 *
1811 * Even if NO_ERROR is returned, the array pointer might still
1812 * be NULL if the module contains no such data.
1813 *
1814 *@@added V0.9.9 (2001-03-11) [lafaix]
1815 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1816 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1817 */
1818
1819APIRET doshExecQueryExportedFunctions(PEXECUTABLE pExec,
1820 PFSYSFUNCTION *ppaFunctions, // out: functions array
1821 PULONG pcFunctions) // out: array item count
1822{
1823 if ( (pExec)
1824 && (pExec->ulOS == EXEOS_OS2)
1825 )
1826 {
1827 ENSURE_BEGIN;
1828 ULONG cFunctions = 0;
1829 PFSYSFUNCTION paFunctions = NULL;
1830
1831 ULONG ulDummy;
1832
1833 if (pExec->ulExeFormat == EXEFORMAT_LX)
1834 {
1835 // It's a 32bit OS/2 executable
1836
1837 // the number of exported entry points is not stored
1838 // in the executable header; we have to count them in
1839 // the entry table
1840
1841 ENSURE(ScanLXEntryTable(pExec, NULL, &cFunctions));
1842
1843 // we now have the number of exported entries; let us
1844 // build them
1845
1846 if (cFunctions)
1847 {
1848 ULONG cb = sizeof(FSYSFUNCTION) * cFunctions;
1849
1850 paFunctions = (PFSYSFUNCTION)malloc(cb);
1851 if (!paFunctions)
1852 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1853
1854 // we rescan the entry table (the cost is not as bad
1855 // as it may seem, due to disk caching)
1856
1857 ENSURE_SAFE(ScanLXEntryTable(pExec, paFunctions, NULL));
1858
1859 // we now scan the resident name table entries
1860
1861 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1862 pExec->pLXHeader->ulResdNameTblOfs
1863 + pExec->pDosExeHeader->ulNewHeaderOfs,
1864 FILE_BEGIN,
1865 &ulDummy));
1866
1867 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1868
1869 // we now scan the non-resident name table entries,
1870 // whose offset is _from the begining of the file_
1871
1872 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1873 pExec->pLXHeader->ulNonResdNameTblOfs,
1874 FILE_BEGIN,
1875 &ulDummy));
1876
1877 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1878 } // end if (cFunctions)
1879 }
1880 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1881 {
1882 // It's a 16bit OS/2 executable
1883
1884 // here too the number of exported entry points
1885 // is not stored in the executable header; we
1886 // have to count them in the entry table
1887
1888 ENSURE(ScanNEEntryTable(pExec, NULL, &cFunctions));
1889
1890 // we now have the number of exported entries; let us
1891 // build them
1892
1893 if (cFunctions)
1894 {
1895 USHORT usOrdinal = 1,
1896 usCurrent = 0;
1897
1898 paFunctions = (PFSYSFUNCTION)malloc(sizeof(FSYSFUNCTION) * cFunctions);
1899 if (!paFunctions)
1900 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1901
1902 // we rescan the entry table (the cost is not as bad
1903 // as it may seem, due to disk caching)
1904
1905 ENSURE_SAFE(ScanNEEntryTable(pExec, paFunctions, NULL));
1906
1907 // we now scan the resident name table entries
1908
1909 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1910 pExec->pNEHeader->usResdNameTblOfs
1911 + pExec->pDosExeHeader->ulNewHeaderOfs,
1912 FILE_BEGIN,
1913 &ulDummy));
1914
1915 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1916
1917 // we now scan the non-resident name table entries,
1918 // whose offset is _from the begining of the file_
1919
1920 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1921 pExec->pNEHeader->ulNonResdTblOfs,
1922 FILE_BEGIN,
1923 &ulDummy));
1924
1925 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1926 }
1927 }
1928 else
1929 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1930
1931 // no error: output data
1932 *ppaFunctions = paFunctions;
1933 *pcFunctions = cFunctions;
1934
1935 ENSURE_FINALLY;
1936 // if we had an error above, clean up
1937 free(paFunctions);
1938 ENSURE_END;
1939 }
1940 else
1941 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1942
1943 ENSURE_OK;
1944}
1945
1946/*
1947 *@@ doshExecFreeExportedFunctions:
1948 * frees resources allocated by doshExecQueryExportedFunctions.
1949 *
1950 *@@added V0.9.9 (2001-03-11)
1951 */
1952
1953APIRET doshExecFreeExportedFunctions(PFSYSFUNCTION paFunctions)
1954{
1955 free(paFunctions);
1956
1957 return (NO_ERROR);
1958}
1959
1960/*
1961 *@@ doshExecQueryResources:
1962 * returns an array of FSYSRESOURCE structures describing all
1963 * available resources in the module.
1964 *
1965 * *pcResources receives the no. of items in the array
1966 * (not the array size!). Use doshExecFreeResources to clean up.
1967 *
1968 * This returns a standard OS/2 error code, which might be
1969 * any of the codes returned by DosSetFilePtr and DosRead.
1970 * In addition, this may return:
1971 *
1972 * -- ERROR_NOT_ENOUGH_MEMORY
1973 *
1974 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1975 * than LX or NE, which is not understood by this function.
1976 *
1977 * Even if NO_ERROR is returned, the array pointer might still
1978 * be NULL if the module contains no such data.
1979 *
1980 *@@added V0.9.7 (2000-12-18) [lafaix]
1981 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1982 */
1983
1984APIRET doshExecQueryResources(PEXECUTABLE pExec, // in: executable from doshExecOpen
1985 PFSYSRESOURCE *ppaResources, // out: res's array
1986 PULONG pcResources) // out: array item count
1987{
1988 if ( (pExec)
1989 && (pExec->ulOS == EXEOS_OS2)
1990 )
1991 {
1992 ENSURE_BEGIN;
1993 ULONG cResources = 0;
1994 PFSYSRESOURCE paResources = NULL;
1995
1996 if (pExec->ulExeFormat == EXEFORMAT_LX)
1997 {
1998 // 32-bit OS/2 executable:
1999 cResources = pExec->pLXHeader->ulResTblCnt;
2000
2001 if (cResources)
2002 {
2003 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2004 struct rsrc32 /* Resource Table Entry */
2005 {
2006 unsigned short type; /* Resource type */
2007 unsigned short name; /* Resource name */
2008 unsigned long cb; /* Resource size */
2009 unsigned short obj; /* Object number */
2010 unsigned long offset; /* Offset within object */
2011 } rs;
2012
2013 struct o32_obj /* Flat .EXE object table entry */
2014 {
2015 unsigned long o32_size; /* Object virtual size */
2016 unsigned long o32_base; /* Object base virtual address */
2017 unsigned long o32_flags; /* Attribute flags */
2018 unsigned long o32_pagemap; /* Object page map index */
2019 unsigned long o32_mapsize; /* Number of entries in object page map */
2020 unsigned long o32_reserved; /* Reserved */
2021 } ot;
2022 #pragma pack() // V0.9.9 (2001-04-03) [umoeller]
2023
2024 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2025 ULONG ulDummy;
2026 int i;
2027
2028 paResources = (PFSYSRESOURCE)malloc(cb);
2029 if (!paResources)
2030 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2031
2032 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2033
2034 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2035 pExec->pLXHeader->ulResTblOfs
2036 + pExec->pDosExeHeader->ulNewHeaderOfs,
2037 FILE_BEGIN,
2038 &ulDummy));
2039
2040 for (i = 0; i < cResources; i++)
2041 {
2042 ENSURE_SAFE(DosRead(pExec->hfExe, &rs, 14, &ulDummy));
2043
2044 paResources[i].ulID = rs.name;
2045 paResources[i].ulType = rs.type;
2046 paResources[i].ulSize = rs.cb;
2047 paResources[i].ulFlag = rs.obj; // Temp storage for Object
2048 // number. Will be filled
2049 // with resource flag
2050 // later.
2051 }
2052
2053 for (i = 0; i < cResources; i++)
2054 {
2055 ULONG ulOfsThis = pExec->pLXHeader->ulObjTblOfs
2056 + pExec->pDosExeHeader->ulNewHeaderOfs
2057 + ( sizeof(ot)
2058 * (paResources[i].ulFlag - 1));
2059
2060 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2061 ulOfsThis,
2062 FILE_BEGIN,
2063 &ulDummy));
2064
2065 ENSURE_SAFE(DosRead(pExec->hfExe, &ot, sizeof(ot), &ulDummy));
2066
2067 paResources[i].ulFlag = ((ot.o32_flags & OBJWRITE)
2068 ? 0
2069 : RNPURE);
2070 paResources[i].ulFlag |= ((ot.o32_flags & OBJDISCARD)
2071 ? 4096
2072 : 0);
2073 paResources[i].ulFlag |= ((ot.o32_flags & OBJSHARED)
2074 ? RNMOVE
2075 : 0);
2076 paResources[i].ulFlag |= ((ot.o32_flags & OBJPRELOAD)
2077 ? RNPRELOAD
2078 : 0);
2079 } // end for
2080 } // end if (cResources)
2081 } // end if (pExec->ulExeFormat == EXEFORMAT_LX)
2082 else if (pExec->ulExeFormat == EXEFORMAT_NE)
2083 {
2084 // 16-bit OS/2 executable:
2085 cResources = pExec->pNEHeader->usResSegmCount;
2086
2087 if (cResources)
2088 {
2089 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2090 struct {unsigned short type; unsigned short name;} rti;
2091 struct new_seg /* New .EXE segment table entry */
2092 {
2093 unsigned short ns_sector; /* File sector of start of segment */
2094 unsigned short ns_cbseg; /* Number of bytes in file */
2095 unsigned short ns_flags; /* Attribute flags */
2096 unsigned short ns_minalloc; /* Minimum allocation in bytes */
2097 } ns;
2098 #pragma pack()
2099
2100 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2101 ULONG ulDummy;
2102 int i;
2103
2104 paResources = (PFSYSRESOURCE)malloc(cb);
2105 if (!paResources)
2106 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2107
2108 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2109
2110 // we first read the resources IDs and types
2111
2112 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2113 pExec->pNEHeader->usResTblOfs
2114 + pExec->pDosExeHeader->ulNewHeaderOfs,
2115 FILE_BEGIN,
2116 &ulDummy));
2117
2118 for (i = 0; i < cResources; i++)
2119 {
2120 ENSURE_SAFE(DosRead(pExec->hfExe, &rti, sizeof(rti), &ulDummy));
2121
2122 paResources[i].ulID = rti.name;
2123 paResources[i].ulType = rti.type;
2124 }
2125
2126 // we then read their sizes and flags
2127
2128 for (i = 0; i < cResources; i++)
2129 {
2130 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2131 pExec->pDosExeHeader->ulNewHeaderOfs
2132 + pExec->pNEHeader->usSegTblOfs
2133 + (sizeof(ns)
2134 * ( pExec->pNEHeader->usSegTblEntries
2135 - pExec->pNEHeader->usResSegmCount
2136 + i)),
2137 FILE_BEGIN,
2138 &ulDummy));
2139
2140 ENSURE_SAFE(DosRead(pExec->hfExe, &ns, sizeof(ns), &ulDummy));
2141
2142 paResources[i].ulSize = ns.ns_cbseg;
2143
2144 paResources[i].ulFlag = (ns.ns_flags & OBJPRELOAD) ? RNPRELOAD : 0;
2145 paResources[i].ulFlag |= (ns.ns_flags & OBJSHARED) ? RNPURE : 0;
2146 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? RNMOVE : 0;
2147 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? 4096 : 0;
2148 }
2149 } // end if (cResources)
2150 } // end else if (pExec->ulExeFormat == EXEFORMAT_NE)
2151 else
2152 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2153
2154 *ppaResources = paResources;
2155 *pcResources = cResources;
2156
2157 ENSURE_FINALLY;
2158 // if we had an error above, clean up
2159 free(paResources);
2160 ENSURE_END;
2161 }
2162 else
2163 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2164
2165 ENSURE_OK;
2166}
2167
2168/*
2169 *@@ doshExecFreeResources:
2170 * frees resources allocated by doshExecQueryResources.
2171 *
2172 *@@added V0.9.7 (2000-12-18) [lafaix]
2173 */
2174
2175APIRET doshExecFreeResources(PFSYSRESOURCE paResources)
2176{
2177 free(paResources);
2178 return (NO_ERROR);
2179}
2180
2181/*
2182 *@@category: Helpers\Control program helpers\Partitions info
2183 * functions for retrieving partition information directly
2184 * from the partition tables on the disk. See doshGetPartitionsList.
2185 */
2186
2187/********************************************************************
2188 *
2189 * Partition functions
2190 *
2191 ********************************************************************/
2192
2193/*
2194 *@@ doshQueryDiskCount:
2195 * returns the no. of physical disks installed
2196 * on the system.
2197 *
2198 * Based on code (C) Dmitry A. Steklenev.
2199 *
2200 *@@added V0.9.0 [umoeller]
2201 */
2202
2203UINT doshQueryDiskCount(VOID)
2204{
2205 USHORT count = 0;
2206
2207 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &count, 2, 0, 0);
2208 return (count);
2209}
2210
2211/*
2212 *@@ doshReadSector:
2213 * reads a physical disk sector.
2214 *
2215 * If NO_ERROR is returned, the sector contents
2216 * have been stored in *buff.
2217 *
2218 * Based on code (C) Dmitry A. Steklenev.
2219 *
2220 *@@added V0.9.0 [umoeller]
2221 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
2222 */
2223
2224APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
2225 void *buff,
2226 USHORT head,
2227 USHORT cylinder,
2228 USHORT sector)
2229{
2230 APIRET arc;
2231 HFILE dh = 0;
2232 char dn[256];
2233
2234 sprintf(dn, "%u:", disk);
2235 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
2236 {
2237 TRACKLAYOUT DiskIOParm;
2238 ULONG IOCtlDataLength = sizeof(DiskIOParm);
2239 ULONG IOCtlParmLength = 512;
2240
2241 DiskIOParm.bCommand = 0;
2242 DiskIOParm.usHead = head;
2243 DiskIOParm.usCylinder = cylinder;
2244 DiskIOParm.usFirstSector = 0;
2245 DiskIOParm.cSectors = 1;
2246 DiskIOParm.TrackTable[0].usSectorNumber = sector;
2247 DiskIOParm.TrackTable[0].usSectorSize = 512;
2248
2249 arc = DosDevIOCtl(dh,
2250 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
2251 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
2252 buff , IOCtlDataLength, &IOCtlDataLength);
2253
2254 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
2255 }
2256
2257 return (arc);
2258}
2259
2260/*
2261 *@@ doshType2FSName:
2262 * this returns a static, zero-terminated string
2263 * for the given FS type. This is always 7 bytes
2264 * in length.
2265 *
2266 * Values for operating system indicator:
2267 * -- 00h empty
2268 * -- 01h DOS 12-bit FAT
2269 * -- 02h XENIX root file system
2270 * -- 03h XENIX /usr file system (obsolete)
2271 * -- 04h DOS 16-bit FAT (up to 32M)
2272 * -- 05h DOS 3.3+ extended partition
2273 * -- 06h DOS 3.31+ Large File System (16-bit FAT, over 32M)
2274 * -- 07h QNX
2275 * -- 07h OS/2 HPFS
2276 * -- 07h Windows NT NTFS
2277 * -- 07h Advanced Unix
2278 * -- 08h OS/2 (v1.0-1.3 only)
2279 * -- 08h AIX bootable partition, SplitDrive
2280 * -- 08h Commodore DOS
2281 * -- 08h DELL partition spanning multiple drives
2282 * -- 09h AIX data partition
2283 * -- 09h Coherent filesystem
2284 * -- 0Ah OS/2 Boot Manager
2285 * -- 0Ah OPUS
2286 * -- 0Ah Coherent swap partition
2287 * -- 0Bh Windows95 with 32-bit FAT
2288 * -- 0Ch Windows95 with 32-bit FAT (using LBA-mode INT 13 extensions)
2289 * -- 0Eh logical-block-addressable VFAT (same as 06h but using LBA-mode INT 13)
2290 * -- 0Fh logical-block-addressable VFAT (same as 05h but using LBA-mode INT 13)
2291 * -- 10h OPUS
2292 * -- 11h OS/2 Boot Manager hidden 12-bit FAT partition
2293 * -- 12h Compaq Diagnostics partition
2294 * -- 14h (resulted from using Novell DOS 7.0 FDISK to delete Linux Native part)
2295 * -- 14h OS/2 Boot Manager hidden sub-32M 16-bit FAT partition
2296 * -- 16h OS/2 Boot Manager hidden over-32M 16-bit FAT partition
2297 * -- 17h OS/2 Boot Manager hidden HPFS partition
2298 * -- 18h AST special Windows swap file ("Zero-Volt Suspend" partition)
2299 * -- 21h officially listed as reserved
2300 * -- 23h officially listed as reserved
2301 * -- 24h NEC MS-DOS 3.x
2302 * -- 26h officially listed as reserved
2303 * -- 31h officially listed as reserved
2304 * -- 33h officially listed as reserved
2305 * -- 34h officially listed as reserved
2306 * -- 36h officially listed as reserved
2307 * -- 38h Theos
2308 * -- 3Ch PowerQuest PartitionMagic recovery partition
2309 * -- 40h VENIX 80286
2310 * -- 41h Personal RISC Boot
2311 * -- 42h SFS (Secure File System) by Peter Gutmann
2312 * -- 50h OnTrack Disk Manager, read-only partition
2313 * -- 51h OnTrack Disk Manager, read/write partition
2314 * -- 51h NOVEL
2315 * -- 52h CP/M
2316 * -- 52h Microport System V/386
2317 * -- 53h OnTrack Disk Manager, write-only partition???
2318 * -- 54h OnTrack Disk Manager (DDO)
2319 * -- 56h GoldenBow VFeature
2320 * -- 61h SpeedStor
2321 * -- 63h Unix SysV/386, 386/ix
2322 * -- 63h Mach, MtXinu BSD 4.3 on Mach
2323 * -- 63h GNU HURD
2324 * -- 64h Novell NetWare 286
2325 * -- 65h Novell NetWare (3.11)
2326 * -- 67h Novell
2327 * -- 68h Novell
2328 * -- 69h Novell
2329 * -- 70h DiskSecure Multi-Boot
2330 * -- 71h officially listed as reserved
2331 * -- 73h officially listed as reserved
2332 * -- 74h officially listed as reserved
2333 * -- 75h PC/IX
2334 * -- 76h officially listed as reserved
2335 * -- 80h Minix v1.1 - 1.4a
2336 * -- 81h Minix v1.4b+
2337 * -- 81h Linux
2338 * -- 81h Mitac Advanced Disk Manager
2339 * -- 82h Linux Swap partition
2340 * -- 82h Prime
2341 * -- 83h Linux native file system (ext2fs/xiafs)
2342 * -- 84h OS/2-renumbered type 04h partition (related to hiding DOS C: drive)
2343 * -- 86h FAT16 volume/stripe set (Windows NT)
2344 * -- 87h HPFS Fault-Tolerant mirrored partition
2345 * -- 87h NTFS volume/stripe set
2346 * -- 93h Amoeba file system
2347 * -- 94h Amoeba bad block table
2348 * -- A0h Phoenix NoteBIOS Power Management "Save-to-Disk" partition
2349 * -- A1h officially listed as reserved
2350 * -- A3h officially listed as reserved
2351 * -- A4h officially listed as reserved
2352 * -- A5h FreeBSD, BSD/386
2353 * -- A6h officially listed as reserved
2354 * -- B1h officially listed as reserved
2355 * -- B3h officially listed as reserved
2356 * -- B4h officially listed as reserved
2357 * -- B6h officially listed as reserved
2358 * -- B7h BSDI file system (secondarily swap)
2359 * -- B8h BSDI swap partition (secondarily file system)
2360 * -- C1h DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT partition
2361 * -- C4h DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT partition
2362 * -- C6h DR DOS 6.0 LOGIN.EXE-secured Huge partition
2363 * -- C6h corrupted FAT16 volume/stripe set (Windows NT)
2364 * -- C7h Syrinx Boot
2365 * -- C7h corrupted NTFS volume/stripe set
2366 * -- D8h CP/M-86
2367 * -- DBh CP/M, Concurrent CP/M, Concurrent DOS
2368 * -- DBh CTOS (Convergent Technologies OS)
2369 * -- E1h SpeedStor 12-bit FAT extended partition
2370 * -- E3h DOS read-only
2371 * -- E3h Storage Dimensions
2372 * -- E4h SpeedStor 16-bit FAT extended partition
2373 * -- E5h officially listed as reserved
2374 * -- E6h officially listed as reserved
2375 * -- F1h Storage Dimensions
2376 * -- F2h DOS 3.3+ secondary partition
2377 * -- F3h officially listed as reserved
2378 * -- F4h SpeedStor
2379 * -- F4h Storage Dimensions
2380 * -- F6h officially listed as reserved
2381 * -- FEh LANstep
2382 * -- FEh IBM PS/2 IML
2383 * -- FFh Xenix bad block table
2384 *
2385 * Note: for partition type 07h, one should inspect the partition boot record
2386 * for the actual file system type
2387 *
2388 * Based on code (C) Dmitry A. Steklenev.
2389 *
2390 *@@added V0.9.0 [umoeller]
2391 */
2392
2393const char* doshType2FSName(unsigned char bFSType) // in: FS type
2394{
2395 PSZ zFSName = NULL;
2396
2397 switch (bFSType)
2398 {
2399 case PAR_UNUSED:
2400 zFSName = "UNUSED ";
2401 break;
2402 case PAR_FAT12SMALL:
2403 zFSName = "FAT-12 ";
2404 break;
2405 case PAR_XENIXROOT:
2406 zFSName = "XENIX ";
2407 break;
2408 case PAR_XENIXUSER:
2409 zFSName = "XENIX ";
2410 break;
2411 case PAR_FAT16SMALL:
2412 zFSName = "FAT-16 ";
2413 break;
2414 case PAR_EXTENDED:
2415 zFSName = "EXTEND ";
2416 break;
2417 case PAR_FAT16BIG:
2418 zFSName = "BIGDOS ";
2419 break;
2420 case PAR_HPFS:
2421 zFSName = "HPFS ";
2422 break;
2423 case PAR_AIXBOOT:
2424 zFSName = "AIX ";
2425 break;
2426 case PAR_AIXDATA:
2427 zFSName = "AIX ";
2428 break;
2429 case PAR_BOOTMANAGER:
2430 zFSName = "BOOTMNG";
2431 break;
2432 case PAR_WINDOWS95:
2433 zFSName = "WIN95 ";
2434 break;
2435 case PAR_WINDOWS95LB:
2436 zFSName = "WIN95 ";
2437 break;
2438 case PAR_VFAT16BIG:
2439 zFSName = "VFAT ";
2440 break;
2441 case PAR_VFAT16EXT:
2442 zFSName = "VFAT ";
2443 break;
2444 case PAR_OPUS:
2445 zFSName = "OPUS ";
2446 break;
2447 case PAR_HID12SMALL:
2448 zFSName = "FAT-12*";
2449 break;
2450 case PAR_COMPAQDIAG:
2451 zFSName = "COMPAQ ";
2452 break;
2453 case PAR_HID16SMALL:
2454 zFSName = "FAT-16*";
2455 break;
2456 case PAR_HID16BIG:
2457 zFSName = "BIGDOS*";
2458 break;
2459 case PAR_HIDHPFS:
2460 zFSName = "HPFS* ";
2461 break;
2462 case PAR_WINDOWSSWP:
2463 zFSName = "WINSWAP";
2464 break;
2465 case PAR_NECDOS:
2466 zFSName = "NECDOS ";
2467 break;
2468 case PAR_THEOS:
2469 zFSName = "THEOS ";
2470 break;
2471 case PAR_VENIX:
2472 zFSName = "VENIX ";
2473 break;
2474 case PAR_RISCBOOT:
2475 zFSName = "RISC ";
2476 break;
2477 case PAR_SFS:
2478 zFSName = "SFS ";
2479 break;
2480 case PAR_ONTRACK:
2481 zFSName = "ONTRACK";
2482 break;
2483 case PAR_ONTRACKEXT:
2484 zFSName = "ONTRACK";
2485 break;
2486 case PAR_CPM:
2487 zFSName = "CP/M ";
2488 break;
2489 case PAR_UNIXSYSV:
2490 zFSName = "UNIX ";
2491 break;
2492 case PAR_NOVELL_64:
2493 zFSName = "NOVELL ";
2494 break;
2495 case PAR_NOVELL_65:
2496 zFSName = "NOVELL ";
2497 break;
2498 case PAR_NOVELL_67:
2499 zFSName = "NOVELL ";
2500 break;
2501 case PAR_NOVELL_68:
2502 zFSName = "NOVELL ";
2503 break;
2504 case PAR_NOVELL_69:
2505 zFSName = "NOVELL ";
2506 break;
2507 case PAR_PCIX:
2508 zFSName = "PCIX ";
2509 break;
2510 case PAR_MINIX:
2511 zFSName = "MINIX ";
2512 break;
2513 case PAR_LINUX:
2514 zFSName = "LINUX ";
2515 break;
2516 case PAR_LINUXSWAP:
2517 zFSName = "LNXSWP ";
2518 break;
2519 case PAR_LINUXFILE:
2520 zFSName = "LINUX ";
2521 break;
2522 case PAR_FREEBSD:
2523 zFSName = "FREEBSD";
2524 break;
2525 case PAR_BBT:
2526 zFSName = "BBT ";
2527 break;
2528
2529 default:
2530 zFSName = " ";
2531 break;
2532 }
2533 return zFSName;
2534}
2535
2536/*
2537 * AppendPartition:
2538 * this appends the given partition information to
2539 * the given partition list. To do this, a new
2540 * PARTITIONINFO structure is created and appended
2541 * in a list (managed thru the PARTITIONINFO.pNext
2542 * items).
2543 *
2544 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
2545 * With each call of this function, this pointer is advanced
2546 * to point to the newly created PARTITIONINFO, so before
2547 * calling this function for the first time,
2548 *
2549 * Based on code (C) Dmitry A. Steklenev.
2550 *
2551 *@@added V0.9.0 [umoeller]
2552 */
2553
2554APIRET AppendPartition(PARTITIONINFO **pppiFirst,
2555 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
2556 PUSHORT posCount, // in/out: partition count
2557 BYTE bDisk, // in: disk of partition
2558 const char *pszBootName, // in: boot partition name
2559 CHAR cLetter, // in/out: drive letter
2560 BYTE bFsType, // in: file system type
2561 BOOL fPrimary, // in: primary?
2562 BOOL fBootable,
2563 ULONG ulSectors) // in: no. of sectors
2564{
2565 APIRET arc = NO_ERROR;
2566 PPARTITIONINFO ppiNew = NEW(PARTITIONINFO);
2567 if (ppiNew)
2568 {
2569 ZERO(ppiNew);
2570
2571 // store data
2572 ppiNew->bDisk = bDisk;
2573 if ((fBootable) && (pszBootName) )
2574 {
2575 memcpy(ppiNew->szBootName, pszBootName, 8);
2576 ppiNew->szBootName[8] = 0;
2577 }
2578 else
2579 ppiNew->szBootName[0] = 0;
2580 ppiNew->cLetter = cLetter;
2581 ppiNew->bFSType = bFsType;
2582 strcpy(ppiNew->szFSType,
2583 doshType2FSName(bFsType));
2584 ppiNew->fPrimary = fPrimary;
2585 ppiNew->fBootable = fBootable;
2586 ppiNew->ulSize = ulSectors / 2048;
2587
2588 ppiNew->pNext = NULL;
2589
2590 (*posCount)++;
2591
2592 if (*pppiFirst == (PPARTITIONINFO)NULL)
2593 {
2594 // first call:
2595 *pppiFirst = ppiNew;
2596 *pppiThis = ppiNew;
2597 }
2598 else
2599 {
2600 // append to list
2601 (**pppiThis).pNext = ppiNew;
2602 *pppiThis = ppiNew;
2603 }
2604 }
2605 else
2606 arc = ERROR_NOT_ENOUGH_MEMORY;
2607
2608 return (arc);
2609}
2610
2611// Sector and Cylinder values are actually 6 bits and 10 bits:
2612//
2613// 1 1 1 1 1 1
2614// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
2615// ³c c c c c c c c C c S s s s s s³
2616// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
2617//
2618// The high two bits of the second byte are used as the high bits
2619// of a 10-bit value. This allows for as many as 1024 cylinders
2620// and 64 sectors per cylinder.
2621
2622/*
2623 * GetCyl:
2624 * get cylinder number.
2625 *
2626 * Based on code (C) Dmitry A. Steklenev.
2627 *
2628 *@@added V0.9.0 [umoeller]
2629 */
2630
2631static USHORT GetCyl(USHORT rBeginSecCyl)
2632{
2633 return ( (rBeginSecCyl & 0x00C0) << 2)
2634 + ((rBeginSecCyl & 0xFF00) >> 8);
2635}
2636
2637/*
2638 * GetSec:
2639 * get sector number.
2640 *
2641 * Based on code (C) Dmitry A. Steklenev.
2642 *
2643 *@@added V0.9.0 [umoeller]
2644 */
2645
2646static USHORT GetSec(USHORT rBeginSecCyl)
2647{
2648 return rBeginSecCyl & 0x003F;
2649}
2650
2651/*
2652 *@@ doshGetBootManager:
2653 * this goes thru the master boot records on all
2654 * disks to find the boot manager partition.
2655 *
2656 * Returns:
2657 *
2658 * -- NO_ERROR: boot manager found; in that case,
2659 * information about the boot manager
2660 * is written into *pusDisk, *pusPart,
2661 * *BmInfo. Any of these pointers can
2662 * be NULL if you're not interested.
2663 *
2664 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
2665 *
2666 * Based on code (C) Dmitry A. Steklenev.
2667 *
2668 *@@added V0.9.0 [umoeller]
2669 */
2670
2671APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
2672 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
2673 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
2674{
2675 APIRET arc = NO_ERROR;
2676 USHORT count = doshQueryDiskCount(); // Physical disk number
2677 MBR_INFO MBoot; // Master Boot
2678 USHORT usDisk;
2679
2680 if (count > 8) // Not above 8 disks
2681 count = 8;
2682
2683 for (usDisk = 1; usDisk <= count; usDisk++)
2684 {
2685 USHORT usPrim = 0;
2686
2687 // for each disk, read the MBR, which has the
2688 // primary partitions
2689 if ((arc = doshReadSector(usDisk,
2690 &MBoot,
2691 0, // head
2692 0, // cylinder
2693 1))) // sector
2694 return (arc);
2695
2696 // scan primary partitions for whether
2697 // BootManager partition exists
2698 for (usPrim = 0; usPrim < 4; usPrim++)
2699 {
2700 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
2701 {
2702 // this is boot manager:
2703 if (pBmInfo)
2704 *pBmInfo = MBoot.sPrtnInfo[usPrim];
2705 if (pusPart)
2706 *pusPart = usPrim;
2707 if (pusDisk)
2708 *pusDisk = usDisk;
2709 // stop scanning
2710 return (NO_ERROR);
2711 }
2712 }
2713 }
2714
2715 return (ERROR_NOT_SUPPORTED);
2716}
2717
2718/*
2719 * GetPrimaryPartitions:
2720 * this returns the primary partitions.
2721 *
2722 * This gets called from doshGetPartitionsList.
2723 *
2724 * Returns:
2725 *
2726 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
2727 *
2728 * Based on code (C) Dmitry A. Steklenev.
2729 *
2730 *@@added V0.9.0 [umoeller]
2731 */
2732
2733APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
2734 PARTITIONINFO **pppiThis,
2735 PUSHORT posCount, // in/out: partition count
2736 PCHAR pcLetter, // in/out: drive letter counter
2737 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
2738 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
2739 UINT iDisk) // in: system's physical disk count
2740{
2741 APIRET arc = NO_ERROR;
2742
2743 if (!pBmInfo)
2744 arc = ERROR_INVALID_PARAMETER;
2745 else
2746 {
2747 SYS_INFO MName[32]; // Name Space from Boot Manager
2748 memset(&MName, 0, sizeof(MName));
2749
2750 // read boot manager name table;
2751 // this is in the boot manager primary partition
2752 // at sector offset 3 (?!?)
2753 if (!(arc = doshReadSector(BmDisk,
2754 &MName,
2755 // head, cylinder, sector of bmgr primary partition:
2756 pBmInfo->bBeginHead,
2757 GetCyl(pBmInfo->rBeginSecCyl),
2758 GetSec(pBmInfo->rBeginSecCyl) + 3)))
2759 {
2760 // got bmgr name table:
2761 MBR_INFO MBoot; // Master Boot
2762 USHORT i;
2763
2764 // read master boot record of this disk
2765 if (!(arc = doshReadSector(iDisk,
2766 &MBoot,
2767 0, // head
2768 0, // cylinder
2769 1))) // sector
2770 {
2771 for (i = 0;
2772 i < 4; // there can be only four primary partitions
2773 i++)
2774 {
2775 // skip unused partition, BootManager or Extended partition
2776 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
2777 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER) // skip boot manager
2778 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_EXTENDED) // skip extended
2779 )
2780 {
2781 BOOL fBootable = ( (pBmInfo)
2782 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
2783 );
2784 // store this partition
2785 if ((arc = AppendPartition(pppiFirst,
2786 pppiThis,
2787 posCount,
2788 iDisk,
2789 (fBootable)
2790 ? (char*)&MName[(iDisk - 1) * 4 + i].name
2791 : "",
2792 *pcLetter,
2793 MBoot.sPrtnInfo[i].bFileSysCode,
2794 TRUE, // primary
2795 fBootable,
2796 MBoot.sPrtnInfo[i].lTotalSects)))
2797 return (arc);
2798 }
2799 }
2800 }
2801 }
2802 }
2803
2804 return (arc);
2805}
2806
2807/*
2808 * GetLogicalDrives:
2809 * this returns info for the logical drives
2810 * in the extended partition. This gets called
2811 * from GetExtendedPartition.
2812 *
2813 * This gets called from GetExtendedPartition.
2814 *
2815 * Based on code (C) Dmitry A. Steklenev.
2816 *
2817 *@@added V0.9.0 [umoeller]
2818 */
2819
2820APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
2821 PARTITIONINFO **pppiThis,
2822 PUSHORT posCount,
2823 PCHAR pcLetter,
2824 PAR_INFO* PrInfo, // in: MBR entry of extended partition
2825 UINT PrDisk,
2826 PAR_INFO* BmInfo)
2827{
2828 APIRET arc = NO_ERROR;
2829 EXT_INFO MBoot; // Master Boot
2830 USHORT i;
2831
2832 if ((arc = doshReadSector(PrDisk,
2833 &MBoot,
2834 PrInfo->bBeginHead,
2835 GetCyl(PrInfo->rBeginSecCyl),
2836 GetSec(PrInfo->rBeginSecCyl))))
2837 return (arc);
2838
2839 for (i = 0; i < 4; i++)
2840 {
2841 // skip unused partition or BootManager partition
2842 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
2843 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER)
2844 )
2845 {
2846 BOOL fBootable = FALSE;
2847 BOOL fAssignLetter = FALSE;
2848
2849 // special work around extended partition
2850 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
2851 {
2852 if ((arc = GetLogicalDrives(pppiFirst,
2853 pppiThis,
2854 posCount,
2855 pcLetter,
2856 &MBoot.sPrtnInfo[i],
2857 PrDisk,
2858 BmInfo)))
2859 return (arc);
2860
2861 continue;
2862 }
2863
2864 // raise driver letter if OS/2 would recognize this drive
2865 if ( (MBoot.sPrtnInfo[i].bFileSysCode < PAR_PCIX)
2866 )
2867 fAssignLetter = TRUE;
2868
2869 if (fAssignLetter)
2870 (*pcLetter)++;
2871
2872 fBootable = ( (BmInfo)
2873 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
2874 );
2875
2876 if ((arc = AppendPartition(pppiFirst,
2877 pppiThis,
2878 posCount,
2879 PrDisk,
2880 (fBootable)
2881 ? (char*)&MBoot.sBmNames[i].name
2882 : "",
2883 (fAssignLetter)
2884 ? *pcLetter
2885 : ' ',
2886 MBoot.sPrtnInfo[i].bFileSysCode,
2887 FALSE, // primary
2888 fBootable, // bootable
2889 MBoot.sPrtnInfo[i].lTotalSects)))
2890 return (arc);
2891 }
2892 }
2893
2894 return (NO_ERROR);
2895}
2896
2897/*
2898 * GetExtendedPartition:
2899 * this finds the extended partition on the given
2900 * drive and calls GetLogicalDrives in turn.
2901 *
2902 * This gets called from doshGetPartitionsList.
2903 *
2904 * Based on code (C) Dmitry A. Steklenev.
2905 *
2906 *@@added V0.9.0 [umoeller]
2907 */
2908
2909APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
2910 PARTITIONINFO **pppiThis,
2911 PUSHORT posCount,
2912 PCHAR pcLetter,
2913 PAR_INFO* BmInfo,
2914 UINT iDisk) // in: disk to query
2915{
2916 APIRET arc = NO_ERROR;
2917 MBR_INFO MBoot; // Master Boot
2918 USHORT i;
2919
2920 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
2921 return (arc);
2922
2923 // go thru MBR entries to find extended partition
2924 for (i = 0;
2925 i < 4;
2926 i++)
2927 {
2928 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
2929 {
2930 if ((arc = GetLogicalDrives(pppiFirst,
2931 pppiThis,
2932 posCount,
2933 pcLetter,
2934 &MBoot.sPrtnInfo[i],
2935 iDisk,
2936 BmInfo)))
2937 return (arc);
2938 }
2939 }
2940
2941 return (NO_ERROR);
2942}
2943
2944/*
2945 *@@ CleanPartitionInfos:
2946 *
2947 *@@added V0.9.9 (2001-04-07) [umoeller]
2948 */
2949
2950VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
2951{
2952 while (ppiThis)
2953 {
2954 PPARTITIONINFO ppiNext = ppiThis->pNext;
2955 free(ppiThis);
2956 ppiThis = ppiNext;
2957 }
2958}
2959
2960/*
2961 *@@ doshGetPartitionsList:
2962 * this returns lots of information about the
2963 * partitions on all physical disks, which is
2964 * read directly from the MBRs and partition
2965 * tables.
2966 *
2967 * If NO_ERROR is returned by this function,
2968 * *ppPartitionInfo points to a linked list of
2969 * PARTITIONINFO structures, which has
2970 * *pusPartitionCount items.
2971 *
2972 * In that case, use doshFreePartitionsList to
2973 * free the resources allocated by this function.
2974 *
2975 * What this function returns depends on whether
2976 * LVM is installed.
2977 *
2978 * -- If LVM.DLL is found on the LIBPATH, this opens
2979 * the LVM engine and returns the info from the
2980 * LVM engine in the PARTITIONINFO structures.
2981 * The partitions are then sorted by disk in
2982 * ascending order.
2983 *
2984 * -- Otherwise, we parse the partition tables
2985 * manually. The linked list then starts out with
2986 * all the primary partitions, followed by the
2987 * logical drives in the extended partitions.
2988 * This function attempts to guess the correct drive
2989 * letters and stores these with the PARTITIONINFO
2990 * items, but there's no guarantee that this is
2991 * correct. We correctly ignore Linux partitions here
2992 * and give all primary partitions the C: letter, but
2993 * I have no idea what happens with NTFS partitions,
2994 * since I have none.
2995 *
2996 * If an error != NO_ERROR is returned, *pusContext
2997 * will be set to one of the following:
2998 *
2999 * -- 1: boot manager not found
3000 *
3001 * -- 2: primary partitions error
3002 *
3003 * -- 3: secondary partitions error
3004 *
3005 * -- 0: something else.
3006 *
3007 * Based on code (C) Dmitry A. Steklenev.
3008 *
3009 *@@added V0.9.0 [umoeller]
3010 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
3011 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
3012 */
3013
3014APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
3015 PUSHORT pusContext) // out: error context
3016{
3017 APIRET arc = NO_ERROR;
3018
3019 PLVMINFO pLVMInfo = NULL;
3020
3021 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3022 *ppiTemp = NULL;
3023 USHORT cPartitions = 0; // bootable partition count
3024
3025 if (!ppList)
3026 return (ERROR_INVALID_PARAMETER);
3027
3028 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
3029 {
3030 // LVM installed:
3031 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
3032 &pPartitionInfos, // out: partitions array
3033 &cPartitions); // out: partitions count
3034 // copied to output below
3035
3036 if (arc)
3037 {
3038 // error: start over
3039 doshFreeLVMInfo(pLVMInfo);
3040 CleanPartitionInfos(pPartitionInfos);
3041 pPartitionInfos = NULL;
3042 cPartitions = 0;
3043 }
3044 }
3045
3046 if (arc)
3047 {
3048 // LVM not installed, or failed:
3049 // parse partitions manually
3050 PAR_INFO BmInfo; // BootManager partition
3051 USHORT usBmDisk; // BootManager disk
3052 USHORT cDisks = doshQueryDiskCount(); // physical disks count
3053 USHORT i;
3054
3055 CHAR cLetter = 'C'; // first drive letter
3056
3057 // start over
3058 arc = NO_ERROR;
3059
3060 if (cDisks > 8) // Not above 8 disks
3061 cDisks = 8;
3062
3063 // get boot manager disk and info
3064 if ((arc = doshGetBootManager(&usBmDisk,
3065 NULL,
3066 &BmInfo)) != NO_ERROR)
3067 {
3068 *pusContext = 1;
3069 }
3070 else
3071 {
3072 // on each disk, read primary partitions
3073 for (i = 1; i <= cDisks; i++)
3074 {
3075 if ((arc = GetPrimaryPartitions(&pPartitionInfos,
3076 &ppiTemp,
3077 &cPartitions,
3078 &cLetter,
3079 usBmDisk,
3080 usBmDisk ? &BmInfo : 0,
3081 i)))
3082 {
3083 *pusContext = 2;
3084 }
3085 }
3086
3087 if (!arc && usBmDisk)
3088 {
3089 // boot manager found:
3090 // on each disk, read extended partition
3091 // with logical drives
3092 for (i = 1; i <= cDisks; i++)
3093 {
3094 if ((arc = GetExtendedPartition(&pPartitionInfos,
3095 &ppiTemp,
3096 &cPartitions,
3097 &cLetter,
3098 &BmInfo,
3099 i)))
3100 {
3101 *pusContext = 3;
3102 }
3103 }
3104 }
3105 } // end else if ((arc = doshGetBootManager(&usBmDisk,
3106 } // end else if (!doshQueryLVMInfo(&pLVMInfo))
3107
3108 if (!arc)
3109 {
3110 // no error so far:
3111 *pusContext = 0;
3112
3113 *ppList = NEW(PARTITIONSLIST);
3114 if (!(*ppList))
3115 arc = ERROR_NOT_ENOUGH_MEMORY;
3116 else
3117 {
3118 ZERO(*ppList);
3119
3120 (*ppList)->pPartitionInfo = pPartitionInfos;
3121 (*ppList)->cPartitions = cPartitions;
3122
3123 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
3124 }
3125 }
3126
3127 if (arc)
3128 CleanPartitionInfos(pPartitionInfos);
3129
3130 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
3131
3132 return (arc);
3133}
3134
3135/*
3136 *@@ doshFreePartitionsList:
3137 * this frees the resources allocated by
3138 * doshGetPartitionsList.
3139 *
3140 *@@added V0.9.0 [umoeller]
3141 */
3142
3143APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
3144{
3145 if (!ppList)
3146 return (ERROR_INVALID_PARAMETER);
3147 else
3148 {
3149 CleanPartitionInfos(ppList->pPartitionInfo);
3150 doshFreeLVMInfo(ppList->pLVMInfo);
3151 free(ppList);
3152 }
3153
3154 return (NO_ERROR);
3155}
3156
3157/********************************************************************
3158 *
3159 * LVM declarations
3160 *
3161 ********************************************************************/
3162
3163/*
3164 *@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
3165 * functions for transparently interfacing LVM.DLL.
3166 */
3167
3168typedef unsigned char BOOLEAN;
3169typedef unsigned short int CARDINAL16;
3170typedef unsigned long CARDINAL32;
3171typedef unsigned int CARDINAL;
3172typedef unsigned long DoubleWord;
3173
3174#ifdef ADDRESS
3175#undef ADDRESS
3176#endif
3177
3178typedef void* ADDRESS;
3179
3180#pragma pack(1)
3181
3182#define DISK_NAME_SIZE 20
3183#define FILESYSTEM_NAME_SIZE 20
3184#define PARTITION_NAME_SIZE 20
3185#define VOLUME_NAME_SIZE 20
3186
3187/*
3188 *@@ Drive_Control_Record:
3189 * invariant for a disk drive.
3190 *
3191 *@@added V0.9.9 (2001-04-07) [umoeller]
3192 */
3193
3194typedef struct _Drive_Control_Record
3195{
3196 CARDINAL32 Drive_Number; // OS/2 Drive Number for this drive.
3197 CARDINAL32 Drive_Size; // The total number of sectors on the drive.
3198 DoubleWord Drive_Serial_Number; // The serial number assigned to this drive. For info. purposes only.
3199 ADDRESS Drive_Handle; // Handle used for operations on the disk that this record corresponds to.
3200 CARDINAL32 Cylinder_Count; // The number of cylinders on the drive.
3201 CARDINAL32 Heads_Per_Cylinder; // The number of heads per cylinder for this drive.
3202 CARDINAL32 Sectors_Per_Track; // The number of sectors per track for this drive.
3203 BOOLEAN Drive_Is_PRM; // Set to TRUE if this drive is a PRM.
3204 BYTE Reserved[3]; // Alignment.
3205} Drive_Control_Record;
3206
3207/*
3208 *@@ Drive_Control_Array:
3209 * returned by the Get_Drive_Control_Data function
3210 *
3211 *@@added V0.9.9 (2001-04-07) [umoeller]
3212 */
3213
3214typedef struct _Drive_Control_Array
3215{
3216 Drive_Control_Record * Drive_Control_Data; // An array of drive control records.
3217 CARDINAL32 Count; // The number of entries in the array of drive control records.
3218} Drive_Control_Array;
3219
3220/*
3221 *@@ Drive_Information_Record:
3222 * defines the information that can be changed for a specific disk drive.
3223 *
3224 *@@added V0.9.9 (2001-04-07) [umoeller]
3225 */
3226
3227typedef struct _Drive_Information_Record
3228{
3229 CARDINAL32 Total_Available_Sectors; // The number of sectors on the disk which are not currently assigned to a partition.
3230 CARDINAL32 Largest_Free_Block_Of_Sectors; // The number of sectors in the largest contiguous block of available sectors.
3231 BOOLEAN Corrupt_Partition_Table; // If TRUE, then the partitioning information found on the drive is incorrect!
3232 BOOLEAN Unusable; // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
3233 BOOLEAN IO_Error; // If TRUE, then the last I/O operation on this drive failed!
3234 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).
3235 char Drive_Name[DISK_NAME_SIZE]; // User assigned name for this disk drive.
3236} Drive_Information_Record;
3237
3238/*
3239 *@@ Partition_Information_Record:
3240 *
3241 *@@added V0.9.9 (2001-04-07) [umoeller]
3242 */
3243
3244typedef struct _Partition_Information_Record
3245{
3246 ADDRESS Partition_Handle;
3247 // The handle used to perform operations on this partition.
3248 ADDRESS Volume_Handle;
3249 // If this partition is part of a volume, this will be the handle of
3250 // the volume. If this partition is NOT part of a volume, then this
3251 // handle will be 0.
3252 ADDRESS Drive_Handle;
3253 // The handle for the drive this partition resides on.
3254 DoubleWord Partition_Serial_Number;
3255 // The serial number assigned to this partition.
3256 CARDINAL32 Partition_Start;
3257 // The LBA of the first sector of the partition.
3258 CARDINAL32 True_Partition_Size;
3259 // The total number of sectors comprising the partition.
3260 CARDINAL32 Usable_Partition_Size;
3261 // The size of the partition as reported to the IFSM. This is the
3262 // size of the partition less any LVM overhead.
3263 CARDINAL32 Boot_Limit;
3264 // The maximum number of sectors from this block of free space that
3265 // can be used to create a bootable partition if you allocate from the
3266 // beginning of the block of free space.
3267 BOOLEAN Spanned_Volume;
3268 // TRUE if this partition is part of a multi-partition volume.
3269 BOOLEAN Primary_Partition;
3270 // True or False. Any non-zero value here indicates that this partition
3271 // is a primary partition. Zero here indicates that this partition is
3272 // a "logical drive" - i.e. it resides inside of an extended partition.
3273 BYTE Active_Flag;
3274 // 80 = Partition is marked as being active.
3275 // 0 = Partition is not active.
3276 BYTE OS_Flag;
3277 // This field is from the partition table. It is known as the OS flag,
3278 // the Partition Type Field, Filesystem Type, and various other names.
3279 // Values of interest
3280 // If this field is: (values are in hex)
3281 // 07 = The partition is a compatibility partition formatted for use
3282 // with an installable filesystem, such as HPFS or JFS.
3283 // 00 = Unformatted partition
3284 // 01 = FAT12 filesystem is in use on this partition.
3285 // 04 = FAT16 filesystem is in use on this partition.
3286 // 0A = OS/2 Boot Manager Partition
3287 // 35 = LVM partition
3288 // 84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
3289 BYTE Partition_Type;
3290 // 0 = Free Space
3291 // 1 = LVM Partition (Part of an LVM Volume.)
3292 // 2 = Compatibility Partition
3293 // All other values are reserved for future use.
3294 BYTE Partition_Status;
3295 // 0 = Free Space
3296 // 1 = In Use - i.e. already assigned to a volume.
3297 // 2 = Available - i.e. not currently assigned to a volume.
3298 BOOLEAN On_Boot_Manager_Menu;
3299 // Set to TRUE if this partition is not part of a Volume yet is on the
3300 // Boot Manager Menu.
3301 BYTE Reserved;
3302 // Alignment.
3303 char Volume_Drive_Letter;
3304 // The drive letter assigned to the volume that this partition is a part of.
3305 char Drive_Name[DISK_NAME_SIZE];
3306 // User assigned name for this disk drive.
3307 char File_System_Name[FILESYSTEM_NAME_SIZE];
3308 // The name of the filesystem in use on this partition, if it is known.
3309 char Partition_Name[PARTITION_NAME_SIZE];
3310 // The user assigned name for this partition.
3311 char Volume_Name[VOLUME_NAME_SIZE];
3312 // If this partition is part of a volume, then this will be the
3313 // name of the volume that this partition is a part of. If this
3314 // record represents free space, then the Volume_Name will be
3315 // "FREE SPACE xx", where xx is a unique numeric ID generated by
3316 // LVM.DLL. Otherwise it will be an empty string.
3317} Partition_Information_Record;
3318
3319// The following defines are for use with the Partition_Type field in the
3320// Partition_Information_Record.
3321#define FREE_SPACE_PARTITION 0
3322#define LVM_PARTITION 1
3323#define COMPATIBILITY_PARTITION 2
3324
3325// The following defines are for use with the Partition_Status field in the
3326// Partition_Information_Record.
3327#define PARTITION_IS_IN_USE 1
3328#define PARTITION_IS_AVAILABLE 2
3329#define PARTITION_IS_FREE_SPACE 0
3330
3331/*
3332 *@@ Partition_Information_Array:
3333 * returned by various functions in the LVM Engine.
3334 *
3335 *@@added V0.9.9 (2001-04-07) [umoeller]
3336 */
3337
3338typedef struct _Partition_Information_Array
3339{
3340 Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
3341 CARDINAL32 Count; // The number of entries in the Partition_Array.
3342} Partition_Information_Array;
3343
3344/*
3345 *@@ Volume_Information_Record:
3346 * variable information for a volume.
3347 *
3348 *@@added V0.9.9 (2001-04-07) [umoeller]
3349 */
3350
3351typedef struct _Volume_Information_Record
3352{
3353 CARDINAL32 Volume_Size;
3354 // The number of sectors comprising the volume.
3355 CARDINAL32 Partition_Count;
3356 // The number of partitions which comprise this volume.
3357 CARDINAL32 Drive_Letter_Conflict;
3358 // 0 indicates that the drive letter preference for this volume is unique.
3359 // 1 indicates that the drive letter preference for this volume
3360 // is not unique, but this volume got its preferred drive letter anyway.
3361 // 2 indicates that the drive letter preference for this volume
3362 // is not unique, and this volume did NOT get its preferred drive letter.
3363 // 4 indicates that this volume is currently "hidden" - i.e. it has
3364 // no drive letter preference at the current time.
3365 BOOLEAN Compatibility_Volume;
3366 // TRUE if this is for a compatibility volume, FALSE otherwise.
3367 BOOLEAN Bootable;
3368 // Set to TRUE if this volume appears on the Boot Manager menu, or if it is
3369 // a compatibility volume and its corresponding partition is the first active
3370 // primary partition on the first drive.
3371 char Drive_Letter_Preference;
3372 // The drive letter that this volume desires to be.
3373 char Current_Drive_Letter;
3374 // The drive letter currently used to access this volume.
3375 // May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
3376 // is already in use by another volume ).
3377 char Initial_Drive_Letter;
3378 // The drive letter assigned to this volume by the operating system
3379 // when LVM was started. This may be different from the
3380 // Drive_Letter_Preference if there were conflicts, and
3381 // may be different from the Current_Drive_Letter. This
3382 // will be 0x0 if the Volume did not exist when the LVM Engine
3383 // was opened (i.e. it was created during this LVM session).
3384 BOOLEAN New_Volume;
3385 // Set to FALSE if this volume existed before the LVM Engine was
3386 // opened. Set to TRUE if this volume was created after the LVM
3387 // Engine was opened.
3388 BYTE Status;
3389 // 0 = None.
3390 // 1 = Bootable
3391 // 2 = Startable
3392 // 3 = Installable.
3393 BYTE Reserved_1;
3394 char Volume_Name[VOLUME_NAME_SIZE];
3395 // The user assigned name for this volume.
3396 char File_System_Name[FILESYSTEM_NAME_SIZE];
3397 // The name of the filesystem in use on this partition, if it
3398 // is known.
3399} Volume_Information_Record;
3400
3401#pragma pack()
3402
3403/********************************************************************
3404 *
3405 * Quick LVM Interface API
3406 *
3407 ********************************************************************/
3408
3409/*
3410 *@@ LVMINFOPRIVATE:
3411 * private structure used by doshQueryLVMInfo.
3412 * This is what the LVMINFO pointer really
3413 * points to.
3414 *
3415 *@@added V0.9.9 (2001-04-07) [umoeller]
3416 */
3417
3418typedef struct _LVMINFOPRIVATE
3419{
3420 LVMINFO LVMInfo; // public structure (dosh.h)
3421
3422 // function pointers resolved from LVM.DLL
3423
3424 void (* _System Open_LVM_Engine)(BOOLEAN Ignore_CHS,
3425 CARDINAL32 *Error_Code);
3426
3427 void (* _System Free_Engine_Memory)(ADDRESS Object);
3428
3429 void (* _System Close_LVM_Engine)(void);
3430
3431 Drive_Control_Array (* _System
3432 Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
3433
3434 Drive_Information_Record (* _System
3435 Get_Drive_Status)(ADDRESS Drive_Handle,
3436 CARDINAL32 *Error_Code);
3437
3438 Partition_Information_Array (* _System
3439 Get_Partitions)(ADDRESS Handle,
3440 CARDINAL32 *Error_Code);
3441
3442 Volume_Information_Record (*_System
3443 Get_Volume_Information)(ADDRESS Volume_Handle,
3444 CARDINAL32 *Error_Code);
3445
3446} LVMINFOPRIVATE, *PLVMINFOPRIVATE;
3447
3448#define LVM_ERROR_FIRST 20000
3449
3450/*
3451 *@@ doshQueryLVMInfo:
3452 * creates an LVMINFO structure if LVM is installed.
3453 * Returns that structure (which the caller must free
3454 * using doshFreeLVMInfo) or NULL if LVM.DLL was not
3455 * found along the LIBPATH.
3456 *
3457 *@@added V0.9.9 (2001-04-07) [umoeller]
3458 */
3459
3460APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
3461{
3462 APIRET arc = NO_ERROR;
3463 CHAR szError[100];
3464 PLVMINFOPRIVATE pLVMInfo = NULL;
3465 HMODULE hmodLVM = NULLHANDLE;
3466
3467 if (!(arc = DosLoadModule(szError,
3468 sizeof(szError),
3469 "LVM",
3470 &hmodLVM)))
3471 {
3472 // got LVM.DLL:
3473 pLVMInfo = NEW(LVMINFOPRIVATE);
3474 if (!pLVMInfo)
3475 arc = ERROR_NOT_ENOUGH_MEMORY;
3476 else
3477 {
3478 // array of function pointers to be resolved from LVM.DLL
3479 RESOLVEFUNCTION aFunctions[] =
3480 {
3481 "Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
3482 "Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
3483 "Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
3484 "Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
3485 "Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
3486 "Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
3487 "Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
3488 };
3489 ULONG ul;
3490
3491 ZERO(pLVMInfo);
3492
3493 pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
3494
3495 // now resolve function pointers
3496 for (ul = 0;
3497 ul < ARRAYITEMCOUNT(aFunctions);
3498 ul++)
3499 {
3500 PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
3501 arc = DosQueryProcAddr(hmodLVM,
3502 0, // ordinal, ignored
3503 (PSZ)pFuncThis->pcszFunctionName,
3504 pFuncThis->ppFuncAddress);
3505 if (!pFuncThis->ppFuncAddress)
3506 arc = ERROR_INVALID_NAME;
3507
3508 if (arc)
3509 break;
3510 }
3511 }
3512 }
3513
3514 if (arc)
3515 doshFreeLVMInfo((PLVMINFO)pLVMInfo);
3516 else
3517 *ppLVMInfo = (PLVMINFO)pLVMInfo;
3518
3519 return (arc);
3520}
3521
3522/*
3523 *@@ doshReadLVMPartitions:
3524 * using the LVMINFO parameter from doshQueryLVMInfo,
3525 * builds an array of PARTITIONINFO structures with
3526 * the data returned from LVM.DLL.
3527 *
3528 *@@added V0.9.9 (2001-04-07) [umoeller]
3529 */
3530
3531APIRET doshReadLVMPartitions(PLVMINFO pInfo, // in: LVM info
3532 PPARTITIONINFO *ppPartitionInfo, // out: partitions array
3533 PUSHORT pcPartitions) // out: partitions count
3534{
3535 APIRET arc = NO_ERROR;
3536 CARDINAL32 Error = 0;
3537
3538 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3539 *ppiTemp = NULL;
3540 USHORT cPartitions = 0; // bootable partition count
3541 PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
3542
3543 _Pmpf((__FUNCTION__ ": entering"));
3544
3545 if (!pLVMInfo)
3546 return (ERROR_INVALID_PARAMETER);
3547
3548 // initialize LVM engine
3549 pLVMInfo->Open_LVM_Engine(TRUE,
3550 &Error);
3551
3552 _Pmpf((" Open_LVM_Engine Error: %d"));
3553
3554 if (!Error)
3555 {
3556 Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
3557 // member records to be freed
3558
3559 _Pmpf((" Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
3560
3561 if ( (!Error)
3562 && (DCA.Count)
3563 )
3564 {
3565 // DCA.Drive_Control_Data now contains drive information records;
3566 // this must be freed
3567 ULONG ulDisk;
3568
3569 for (ulDisk = 0;
3570 ulDisk < DCA.Count;
3571 ulDisk++)
3572 {
3573 Drive_Control_Record *pDriveControlRecord
3574 = &DCA.Drive_Control_Data[ulDisk];
3575 ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
3576
3577 Drive_Information_Record pDriveInfoRecord
3578 = pLVMInfo->Get_Drive_Status(hDrive,
3579 &Error);
3580
3581 _Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
3582
3583 if (!Error)
3584 {
3585 Partition_Information_Array PIA
3586 = pLVMInfo->Get_Partitions(hDrive,
3587 &Error);
3588
3589 _Pmpf((" Get_Partitions Error: %d", Error));
3590
3591 if (!Error)
3592 {
3593 // PIA.Partition_Array now contains
3594 // Partition_Information_Record; must be freed
3595
3596 // now go thru partitions of this drive
3597 ULONG ulPart;
3598 for (ulPart = 0;
3599 ulPart < PIA.Count;
3600 ulPart++)
3601 {
3602 Partition_Information_Record *pPartition
3603 = &PIA.Partition_Array[ulPart];
3604 Volume_Information_Record VolumeInfo;
3605
3606 const char *pcszBootName = NULL; // for now
3607 BOOL fBootable = FALSE;
3608
3609 if (pPartition->Volume_Handle)
3610 {
3611 // this partition is part of a volume:
3612 // only then can it be bootable...
3613 // get the volume info
3614 VolumeInfo
3615 = pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
3616 &Error);
3617 pcszBootName = VolumeInfo.Volume_Name;
3618
3619 fBootable = (VolumeInfo.Status == 1);
3620 }
3621
3622
3623 if (arc = AppendPartition(&pPartitionInfos,
3624 &ppiTemp,
3625 &cPartitions,
3626 ulDisk + 1,
3627 pcszBootName,
3628 pPartition->Volume_Drive_Letter,
3629 pPartition->OS_Flag, // FS type
3630 pPartition->Primary_Partition,
3631 fBootable,
3632 pPartition->True_Partition_Size))
3633 break;
3634 }
3635
3636 // clean up partitions
3637 pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
3638 }
3639 }
3640 else
3641 // error:
3642 break;
3643 }
3644
3645 // clean up drive data
3646 pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
3647 }
3648 }
3649
3650 // close LVM
3651 pLVMInfo->Close_LVM_Engine();
3652
3653 if (Error)
3654 {
3655 // if we got an error, return it with the
3656 // LVM error offset
3657 arc = LVM_ERROR_FIRST + Error;
3658
3659 CleanPartitionInfos(pPartitionInfos);
3660 }
3661
3662 if (!arc)
3663 {
3664 *ppPartitionInfo = pPartitionInfos;
3665 *pcPartitions = cPartitions;
3666 }
3667
3668 _Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
3669
3670 return (arc);
3671}
3672
3673/*
3674 *@@ doshFreeLVMInfo:
3675 *
3676 *@@added V0.9.9 (2001-04-07) [umoeller]
3677 */
3678
3679VOID doshFreeLVMInfo(PLVMINFO pInfo)
3680{
3681 if (pInfo)
3682 {
3683 if (pInfo->hmodLVM)
3684 DosFreeModule(pInfo->hmodLVM);
3685
3686 free(pInfo);
3687 }
3688}
Note: See TracBrowser for help on using the repository browser.